diff options
518 files changed, 30304 insertions, 17003 deletions
@@ -81,6 +81,14 @@ S: University of Notre Dame S: Notre Dame, Indiana S: USA +N: James Banks +E: james.banks@caldera.com +D: TLAN network driver +S: Caldera, Inc. +S: 633 South 550 East +S: Provo, UT 84606 +S: USA + N: Peter Bauer E: 100136.3530@compuserve.com D: Driver for depca-ethernet-board @@ -107,7 +115,9 @@ S: USA N: Randolph Bentson E: bentson@grieg.seaslug.org -D: author of driver for Cyclades Cyclom-Y async mux +D: author of driver for Cyclom-Y and Cyclades-Z async mux +P: 1024/39ED5729 5C A8 7A F4 B2 7A D1 3E B5 3B 81 CF 47 30 11 71 +W: http://www.aa.net/~bentson/ S: 2322 37th Ave SW S: Seattle, Washington 98126-2010 S: USA @@ -793,6 +803,12 @@ S: Schlehenweg 9 S: D-91080 Uttenreuth S: Germany +N: Jaroslav Kysela +E: perex@jcu.cz +D: Original Author and Maintainer for HP 10/100 Mbit Network Adapters +W: http://www.pf.jcu.cz/~perex +S: Unix Centre of Pedagogical Faculty, University of South Bohemia + N: Bas Laarhoven E: bas@vimec.nl D: Loadable modules and ftape driver @@ -842,6 +858,13 @@ S: PO Box 371 S: North Little Rock, Arkansas 72115 S: US +N: Siegfried "Frieder" Loeffler (dg1sek) +E: floeff@tunix.mathematik.uni-stuttgart.de, fl@LF.net +W: http://www.mathematik.uni-stuttgart.de/~floeff +D: Busmaster driver for HP 10/100 Mbit Network Adapters +S: University of Stuttgart, Germany and +S: Ecole Nationale Superieure des Telecommunications, Paris + N: Martin von Loewis E: loewis@informatik.hu-berlin.de D: script binary format @@ -956,12 +979,13 @@ S: D-91056 Erlangen S: Germany N: Michael Meskes -E: meskes@informatik.rwth-aachen.de +E: meskes@topsystem.de +P: 1024/04B6E8F5 6C 77 33 CA CC D6 22 03 AB AB 15 A3 AE AD 39 7D D: Kernel hacker. Software watchdog daemon. D: Maintainer of several Debian packages -S: Lehrstuhl fuer angewandte Mathematik insb. Informatik -S: RWTH-Aachen -S: D-52056 Aachen +S: topsystem Systemhaus GmbH +S: Europark A2, Adenauerstr. 20 +S: D-52146 Wuerselen S: Germany N: Nigel Metheringham @@ -1055,8 +1079,9 @@ S: FIN-00330 Helsingfors S: Finland N: Jonathan Naylor -E: jsn@cs.nott.ac.uk +E: g4klx@g4klx.demon.co.uk E: g4klx@amsat.org +W: http://zone.pspt.fi/~jsn/ D: AX.25, NET/ROM and ROSE amateur radio protocol suites D: CCITT X.25 PLP and LAPB. S: 24 Castle View Drive diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 4e9e6678d..a9274b91a 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -287,17 +287,19 @@ CONFIG_BLK_DEV_RZ1000 Linux. This may slow disk throughput by a few percent, but at least things will operate 100% reliably. If unsure, say Y. -Intel 82371 PIIX (Triton I/II) DMA support +Intel 82371 PIIX (Triton I/II), VIA VP-1 DMA support CONFIG_BLK_DEV_TRITON If your PCI system uses an IDE harddrive (as opposed to SCSI, say) and includes the Intel Triton I/II IDE interface chipset (i82371FB, - i82371SB or i82371AB), you will want to enable this option to allow - use of bus-mastering DMA data transfers. Read the comments at the + i82371SB or i82371AB), or the VIA VP-1 IDE interface chipset + (VT82C586), you will want to enable this option to allow use of + bus-mastering DMA data transfers. Read the comments at the beginning of drivers/block/triton.c and Documentation/ide.txt. You can get the latest version of the hdparm utility via ftp (user: anonymous) from sunsite.unc.edu/pub/Linux/kernel/patches/diskdrives/; it is - used to tune your harddisk. It is safe to say Y to this question. + used to tune your harddisk. + It is safe to say Y to this question. Other IDE chipset support CONFIG_IDE_CHIPSETS @@ -541,6 +543,19 @@ CONFIG_FIREWALL proxy server). Chances are that you should use this on every machine being run as a router and not on any regular host. If unsure, say N. +SYN flood protection +CONFIG_SYN_COOKIES + Normal TCP/IP networking is open to an attack known as SYN flooding. + This attack prevents legitimate users from being able to connect to + your computer and requires very little work for the attacker. + SYN cookies provide protection against this type of attack. With + this option turned on the TCP/IP stack will use a cryptographic + challenge protocol known as SYN cookies to enable legitimate users + to continue to connect, even when your machine is under attack. + Note that SYN cookies aren't enabled per default, you need to add + echo 1 >/proc/sys/net/ipv4/tcp_syncookies to one of your startup scripts + (e.g. /etc/rc.local or /etc/rc.d/rc.local). + Socket Security API Support (EXPERIMENTAL) CONFIG_NET_SECURITY Enable use of the socket security API. Note that Linux does not include @@ -1331,7 +1346,7 @@ CONFIG_IPDDP networking available. This feature is experimental. Please see http://www.maths.unm.edu/~bradford/ltpc.html for support software. -LocalTalk PC card support +Apple/Farallon LocalTalk PC card support CONFIG_LTPC This allows you to use the AppleTalk PC card to connect to LocalTalk networks. The card is also known as the Farallon PhoneNet PC card. @@ -1343,6 +1358,26 @@ CONFIG_LTPC See README.ltpc in the drivers/net directory, and the web site http://www.math.unm.edu/~bradford/ltpc.html +COPS LocalTalk PC card support +CONFIG_COPS + This allows you to use the COPS AppleTalk card to connect to LocalTalk + networks. You also need version 1.3.3 or later of the netatalk package. + This driver is experimental, which means that it may not work. + In particular the module support is not yet working for the 2.1.xx + kernels, so choose Y or N, but not M for now. + See the web site http://www.math.unm.edu/~bradford/ltpc.html for localtalk + IP tools. + +Dayna firmware support +CONFIG_COPS_DAYNA + Support COPS compatible cards with Dayna style firmware (Dayna DL2000/ + Daynatalk/PC (half length), COPS LT-95, Farallon PhoneNET PC III) + +Tangent firmware support +CONFIG_COPS_TANGENT + Support COPS compatible cards with Tangent style firmware (Tangent ATB_II, + Novell NL-1000, Daystar Digital LT-200 + Amateur Radio AX.25 Level 2 CONFIG_AX25 This is the protocol used for computer communication over amateur @@ -3280,6 +3315,14 @@ CONFIG_ETH16I Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. +TI ThunderLAN support (EXPERIMENTAL) +CONFIG_TLAN + If you have a TLAN based network card which is supported by this + driver, say Y and read the Ethernet-HOWTO. Devices currently supported + are the Compaq Netelligent 10, Netelligent 10/100, and Internal + NetFlex 3. This driver is also available as a module. Please email + feedback to james.banks@caldera.com. + Zenith Z-Note support CONFIG_ZNET The Zenith Z-Note notebook computer has a built-in network @@ -4357,6 +4400,12 @@ CONFIG_82C710_MOUSE doesn't work. Read the Busmouse-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. +PC110 digitizer pad support +CONFIG_PC110_PAD + This drives the digitizer pad on the IBM PC110 palmtop (see + http://toy.cabi.net). It can turn the digitizer pad into a + mouse emulation with tap gestures or into an absolute pad. + Microsoft busmouse support CONFIG_MS_BUSMOUSE These animals (also called Inport mice) are connected to an diff --git a/Documentation/binfmt_misc.txt b/Documentation/binfmt_misc.txt index 81d49b2bc..aef3b271a 100644 --- a/Documentation/binfmt_misc.txt +++ b/Documentation/binfmt_misc.txt @@ -1,26 +1,26 @@ - Kernel Support for miscellaneous (your favourite) Binary Formats v1.1 - ==================================================================== + Kernel Support for miscellaneous (your favourite) Binary Formats v1.1 + ===================================================================== This Kernel feature allows to invoke almost (for restrictions see below) every -program by simply typing it's name in the shell. +program by simply typing its name in the shell. This includes for example compiled Java(TM), Python or Emacs programs. -To achieve this you must tell binfmt_misc which interpreter has to be invoked with -which binary. Binfmt_misc recognises the binary-type by matching some bytes at the -beginning of the file with a magic byte sequence (masking out specified bits) you -have supplied. Binfmt_misc can also recognise a filename extension (aka .com) and -optionally strip it off. +To achieve this you must tell binfmt_misc which interpreter has to be invoked +with which binary. Binfmt_misc recognises the binary-type by matching some bytes +at the beginning of the file with a magic byte sequence (masking out specified +bits) you have supplied. Binfmt_misc can also recognise a filename extension +(aka .com) and optionally strip it off. To actually register a new binary type, you have to set up a string looking like -:name:type:offset:magic:mask:interpreter: (where you can choose the ':' upon your -needs) and echo it to /proc/sys/fs/binfmt_misc/register. +:name:type:offset:magic:mask:interpreter: (where you can choose the ':' upon +your needs) and echo it to /proc/sys/fs/binfmt_misc/register. Here is what the fields mean: - 'name' is an identifier string. A new /proc file will be created with this - this name below /proc/sys/fs/binfmt_misc + name below /proc/sys/fs/binfmt_misc - 'type' is the type of recognition. Give 'M' for magic and 'E' for extension. - Give the corresponding lowercase letter to let binfmt_misc strip of the + Give the corresponding lowercase letter to let binfmt_misc strip off the filename extension. - - 'offset' is the offset of the magic/mask in the file counted in bytes. This + - 'offset' is the offset of the magic/mask in the file, counted in bytes. This defaults to 0 if you omit it (i.e. you write ':name:type::magic...') - 'magic' is the byte sequence binfmt_misc is matching for. The magic string may contain hex-encoded characters like \x0a or \xA4. In a shell environment @@ -28,33 +28,34 @@ Here is what the fields mean: If you chose filename extension matching, this is the extension to be recognised (the \x0a specials are not allowed). Extension matching is case sensitive! - - 'mask' is an (optional, defaults to all 0xff) mask. You can mask out some bits - from matching by supplying a string like magic and as long as magic. The - mask is anded with the byte sequence of the file. + - 'mask' is an (optional, defaults to all 0xff) mask. You can mask out some + bits from matching by supplying a string like magic and as long as magic. + The mask is anded with the byte sequence of the file. - 'interpreter' is the program that should be invoked with the binary as first argument (specify the full path) There are some restrictions: - the whole register string may not exceed 255 characters - - the magic must resist in the first 128 bytes of the file, i.e. offset+size(magic) - has to be less than 128 + - the magic must resist in the first 128 bytes of the file, i.e. + offset+size(magic) has to be less than 128 - the interpreter string may not exceed 127 characters -You may want to add the binary formats in one of your /etc/rc scripts during boot-up. -Read the manual of your init program to figure out how to do this right. +You may want to add the binary formats in one of your /etc/rc scripts during +boot-up. Read the manual of your init program to figure out how to do this +right. A few examples (assumed you are in /proc/sys/fs/binfmt_misc): -- enable Java(TM)-support (like binfmt_java): - echo ":Java:M::\xca\xfe\xba\xbe::/usr/local/bin/java:" > register - echo :Applet:M::\<\!--applet::/usr/local/bin/appletviewer: > register - enable support for em86 (like binfmt_em86, for Alpha AXP only): echo ":i386:M::\x7fELF\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfb\xff\xff:/bin/em86:" > register echo ":i486:M::\x7fELF\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfb\xff\xff:/bin/em86:" > register + - enable support for packed DOS applications (pre-configured dosemu hdimages): echo ":DEXE:M::\x0eDEX::/usr/bin/dosexec:" > register + - enable support for DOS/Windows executables (using mzloader and dosemu/wine): echo ":DOSWin:M::MZ::/usr/sbin/mzloader:" > register - echo ":DOS:E::com::/usr/sbin/mzloader:" > register + echo ":DOScom:E::com::/usr/sbin/mzloader:" > register + echo ":DOSexe:E::exe::/usr/sbin/mzloader:" > register You can enable/disable binfmt_misc or one binary type by echoing 0 (to disable) @@ -65,16 +66,34 @@ You can remove one entry or all entries by echoing -1 to /proc/.../the_name or /proc/sys/fs/binfmt_misc/status. +Emulating binfmt_java: +====================== + +To emulate binfmt_java the following register-strings are necessary +(the first two for byte-compiled Java binaries, the third for applets +contained in a html-file). Register exactly in this order! + ":Java:M::\xca\xfe\xba\xbe::/usr/local/java/bin/java:" + ":JavaC:e::class::/usr/local/java/bin/java:" + ":Applet:E::html::/usr/local/java/bin/appletviewer:" + +To add a Java-executable to your path you can either make a symbolic +link to the .class file elsewhere in your path (cut the .class-extension +in the destination name for convenience) or add the directory of your +.class files to your PATH environment. In both cases, ensure that the +.class files are in your CLASSPATH environment! + +This is sort of ugly - Javas filename handling is just broken. + + HINTS: ====== -If your interpreter does not look at the PATH to determine the full name of the -program, you need to invoke a wrapper-script (like the following for java) first: +If you want to pass special arguments to your interpreter, you can +write a wrapper script for it. -#!/bin/sh -FOO=`which $1` || exit 1 -shift -/usr/local/bin/java $FOO ${1+$@} +Your interpreter should NOT look in the PATH for the filename; the +kernel passes it the full filename to use. Using the PATH can cause +unexpected behaviour and be a security hazard. There is a web page about binfmt_misc at diff --git a/Documentation/ioctl-number.txt b/Documentation/ioctl-number.txt index 7d86d3168..790bb18c7 100644 --- a/Documentation/ioctl-number.txt +++ b/Documentation/ioctl-number.txt @@ -91,6 +91,7 @@ Code Seq# Include File Comments <mailto:natalia@nikhefk.nikhef.nl> 'c' all linux/comstats.h 'f' all linux/ext2_fs.h +'k' all asm-sparc/kbio.h, asm-sparc64/kbio.h 'l' 00-3F linux/tcfs_fs.h in development: <http://mikonos.dia.unisa.it/tcfs> 'm' all linux/mtio.h conflict! diff --git a/Documentation/m68k/amiboot.txt b/Documentation/m68k/amiboot.txt index c119c6357..c6b369e28 100644 --- a/Documentation/m68k/amiboot.txt +++ b/Documentation/m68k/amiboot.txt @@ -1,9 +1,9 @@ - Linux/m68k Amiga Bootstrap version 5.5 + Linux/m68k Amiga Bootstrap version 5.6 -------------------------------------- Maintained by Geert Uytterhoeven (Geert.Uytterhoeven@cs.kuleuven.ac.be) -Last revised: March 27, 1997 +Last revised: June 12, 1997 0. Introduction @@ -22,7 +22,7 @@ and the Installation Guide first. Although the Installation Guide is getting a bit outdated, it's still a good starting point. -Amiboot 5.5 is meant for Linux/m68k 2.0.x, 2.1.x or higher (kernel bootinfo +Amiboot 5.6 is meant for Linux/m68k 2.0.x, 2.1.x or higher (kernel bootinfo interface versions 1.x and 2.x). Please use an older version for older kernels. diff --git a/Documentation/networking/README.cops b/Documentation/networking/README.cops new file mode 100644 index 000000000..7956fd213 --- /dev/null +++ b/Documentation/networking/README.cops @@ -0,0 +1,39 @@ +README for the COPS LocalTalk Linux driver (cops.c). + By Jay Schulist <Jay.Schulist@spacs.k12.wi.us> + +This driver compiles well against 2.1.29 - 2.1.41. + +Building the driver from the cops-0.0*.tar.gz file. +1. Untar the cops-0.0*.tar.gz it will make a directory called cops. +2. Copy the cops-kernel.diff to /usr/src/ and then type patch -p0 < cops-kernel.diff +3. In the cops driver directory type make install. It will copy the driver to + the linux/drivers/net directory. +4. When you configure your kernel select Y or M for COPS LocalTalk PC support. + Also make sure you have choosen Appletalk support. +5. Compile like usual and you should bet set. + +This driver has 2 modes and they are: Dayna mode and Tangent mode. +Each mode corresponds with the type of card. It has been found +that there are 2 main types of cards and all other cards are +the same and just have different names or only have minor differences +such as more IO ports. As this driver is tested it will +become more clear on exactly what cards are supported. The driver +defaults to using Dayna mode. To change the drivers mode if you build +a driver with dual support use board_type=1 or board_type=2 for +dayna and tangent in the insmod. + +Operation/loading of the driver. +Use modprobe like this: /sbin/modprobe cops.o (IO #) (IRQ #) +If you do not specify any options the driver will try and use the IO = 0x240, +IRQ = 5. As of right now I would only use IRQ 5 for the card, if autoprobing. + +Use ifconfig like this: /sbin/ifconfig lt0 127.0.0.34 up + +You will need to configure atalkd with something like the following to make +it work with the cops.c driver. + +dummy -seed -phase 2 -net 2000 -addr 2000.10 -zone "1033" +lt0 -seed -phase 1 -net 1000 -addr 1000.50 -zone "1033" +- Or - +eth0 -seed -phase 2 -net 3000 -addr 3000.20 -zone "1033" +lt0 -seed -phase 1 -net 1000 -addr 1000.50 -zone "1033" diff --git a/Documentation/networking/ax25.txt b/Documentation/networking/ax25.txt index 937b9efe6..7572cf733 100644 --- a/Documentation/networking/ax25.txt +++ b/Documentation/networking/ax25.txt @@ -1,6 +1,6 @@ To use the amateur radio protocols within Linux you will need to get a suitable copy of the AX.25 Utilities. More detailed information about these -and associated programs can be found on http://www.cs.nott.ac.uk/~jsn/. +and associated programs can be found on http://zone.pspt.fi/~jsn/. For more information about the AX.25, NET/ROM and ROSE protocol stacks, see the AX25-HOWTO written by Terry Dawson <terry@perf.no.itg.telstra.com.au> @@ -13,4 +13,4 @@ of the message, the subject field is ignored. Jonathan G4KLX -jsn@cs.nott.ac.uk +g4klx@g4klx.demon.co.uk diff --git a/Documentation/networking/net-modules.txt b/Documentation/networking/net-modules.txt index bdf5a34a1..f0f3ab470 100644 --- a/Documentation/networking/net-modules.txt +++ b/Documentation/networking/net-modules.txt @@ -159,6 +159,13 @@ atp.c: *Not modularized* (Probes ports: 0x378, 0x278, 0x3BC; fixed IRQs: 5 and 7 ) +cops.c: + io = 0x240 + irq = 5 + nodeid = 0 (AutoSelect = 0, NodeID 1-254 is hand selected.) + (Probes ports: 0x240, 0x340, 0x200, 0x210, 0x220, 0x230, 0x260, + 0x2A0, 0x300, 0x310, 0x320, 0x330, 0x350, 0x360) + de4x5.c: io = 0x000b irq = 10 diff --git a/Documentation/networking/wan-router.txt b/Documentation/networking/wan-router.txt index f95bc9f14..3062f43d5 100644 --- a/Documentation/networking/wan-router.txt +++ b/Documentation/networking/wan-router.txt @@ -1,10 +1,11 @@ ------------------------------------------------------------------------------ WAN Router for Linux Operating System ------------------------------------------------------------------------------ -Version 1.0.0 -December 31, 1996 -Author: Gene Kozin <genek@compuserve.com> -Copyright (c) 1995-1996 Sangoma Technologies Inc. +Version 1.0.3 - June 3, 1997 +Version 1.0.1 - January 30, 1997 +Author: Jaspreet Singh <jaspreet@sangoma.com> + Gene Kozin <genek@compuserve.com> +Copyright (c) 1995-1997 Sangoma Technologies Inc. ------------------------------------------------------------------------------ INTRODUCTION @@ -95,12 +96,6 @@ Ave, Cambridge, MA 02139, USA. -KNOWN BUGS AND LIMITATIONS - -/proc user interface is not complete yet. - - - ACKNOLEGEMENTS This product is based on the WANPIPE(tm) Multiprotocol WAN Router developed @@ -122,9 +117,19 @@ product. REVISION HISTORY +1.0.3 June 3, 1997 + o UDP port for multiple boards (Frame relay, PPP) + o Continuous Transmission of Configure Request Packet for PPP (this + support is only added for 508 cards) + o Connection Timeout for PPP changed from 900 to 0 + o Flow Control for multiple boards and multiple channels (Frame Relay) + +1.0.1 January 30, 1997 + + o Implemented user-readable status and statistics via /proc filesystem + +1.0.0 December 31, 1996 -1.0.0 December 31, 1996 --------------------------- - o Initial version. + o Initial version >>>>>> END <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/Documentation/networking/x25.txt b/Documentation/networking/x25.txt index 09681d565..67f17e644 100644 --- a/Documentation/networking/x25.txt +++ b/Documentation/networking/x25.txt @@ -41,5 +41,4 @@ The contents of the Subject line are ignored. Jonathan -jsn@cs.nott.ac.uk g4klx@g4klx.demon.co.uk diff --git a/MAINTAINERS b/MAINTAINERS index 9012dede9..0b32ffe38 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -95,6 +95,12 @@ M: phil@tazenda.demon.co.uk L: linux-net@vger.rutgers.edu S: Maintained +TLAN NETWORK DRIVER +P: James Banks +M: james.banks@caldera.com +L: linux-net@vger.rutgers.edu +S: Supported + DIGI RIGHTSWITCH NETWORK DRIVER P: Rick Richardson M: rick@dgii.com @@ -107,6 +113,11 @@ P: Jean Tourrilhes M: jt@hplb.hpl.hp.com S: Maintained +HP100: Driver for HP 10/100 Mbit/s Network Adapter Series +P: Jarsolav Kysela +M: perex@jcu.cz +S: Maintained + APM DRIVER P: Rik Faith & Stephen Rothwell M: faith@cs.unc.edu, Stephen.Rothwell@canb.auug.org.au @@ -217,10 +228,10 @@ L: linux-tape@vger.rutgers.edu S: Maintained IPX NETWORK LAYER -P: Alan Cox [for the moment] -M: net-patches@lxorguk.ukuu.org.uk -L: linux-ipx@vger.rutgers.edu [will change] -S: Maintained +P: +M: +L: +S: Orphan IDE DRIVER [GENERAL] P: Mark Lord @@ -267,6 +278,13 @@ W: http://lena.fnet.fr/ L: linux-mips@fnet.fr S: Maintained +MIPS: +P: Ralf Baechle +M: ralf@gnu.ai.mit.edu +W: http://lena.fnet.fr/ +L: linux-mips@fnet.fr +S: Maintained + NCP FILESYSTEM: P: Volker Lendecke M: lendecke@Math.Uni-Goettingen.de @@ -280,10 +298,10 @@ L: linux-hams@vger.rutgers.edu S: Maintained NETWORKING [GENERAL]: -P: Alan Cox -M: net-patches@lxorguk.ukuu.org.uk +P: Networking Teak +M: netdev@nuclecu.unam.mx L: linux-net@vger.rutgers.edu -W: http://www.uk.linux.org/NetNews.html +W: http://www.uk.linux.org/NetNews.html (2.0 only) S: Maintained NETWORKING [IPv4/IPv6]: @@ -326,8 +344,8 @@ L: samba@listproc.anu.edu.au S: Maintained SMP: (except SPARC) -P: Alan Cox -M: smp-patches@lxorguk.ukuu.org.uk +P: Linus Torvalds +M: torvalds@transmeta.com L: linux-smp@vger.rutgers.edu S: Maintained @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 1 -SUBLEVEL = 43 +SUBLEVEL = 46 ARCH = mips diff --git a/arch/alpha/defconfig b/arch/alpha/defconfig index 0c3c65d28..a36ec98a9 100644 --- a/arch/alpha/defconfig +++ b/arch/alpha/defconfig @@ -66,7 +66,7 @@ CONFIG_BLK_DEV_FD=y # # CONFIG_BLK_DEV_LOOP is not set # CONFIG_BLK_DEV_MD is not set -CONFIG_BLK_DEV_RAM=y +# CONFIG_BLK_DEV_RAM is not set # CONFIG_BLK_DEV_INITRD is not set # CONFIG_BLK_DEV_XD is not set # CONFIG_BLK_DEV_EZ is not set @@ -83,6 +83,7 @@ CONFIG_INET=y # CONFIG_IP_ACCT is not set # CONFIG_IP_ROUTER is not set # CONFIG_NET_IPIP is not set +# CONFIG_SYN_COOKIES is not set # # (it is safe to leave these untouched) @@ -196,8 +197,8 @@ CONFIG_DE4X5=y # CONFIG_QUOTA is not set # CONFIG_MINIX_FS is not set CONFIG_EXT2_FS=y -CONFIG_FAT_FS=y -CONFIG_MSDOS_FS=y +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set # CONFIG_VFAT_FS is not set # CONFIG_UMSDOS_FS is not set CONFIG_PROC_FS=y @@ -245,3 +246,4 @@ CONFIG_PSMOUSE=y # Kernel hacking # # CONFIG_PROFILE is not set +# CONFIG_MAGIC_SYSRQ is not set diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index 3b3d8574b..f725e2aba 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -135,30 +135,46 @@ asmlinkage int osf_getdirentries(unsigned int fd, struct osf_dirent *dirent, { int error; struct file *file; + struct dentry *dentry; + struct inode *inode; struct osf_dirent_callback buf; - if (fd >= NR_OPEN || !(file = current->files->fd[fd])) - return -EBADF; - if (!file->f_op || !file->f_op->readdir) - return -ENOTDIR; - error = verify_area(VERIFY_WRITE, dirent, count); - if (error) - return error; - if (basep) { - error = verify_area(VERIFY_WRITE, basep, sizeof(long)); - if (error) - return error; - } + error = -EBADF; + if (fd >= NR_OPEN) + goto out; + + file = current->files->fd[fd]; + if (!file) + goto out; + + dentry = file->f_dentry; + if (!dentry) + goto out; + + inode = dentry->d_inode; + if (!inode) + goto out; + buf.dirent = dirent; buf.basep = basep; buf.count = count; buf.error = 0; - error = file->f_op->readdir(file->f_inode, file, &buf, osf_filldir); + + error = -ENOTDIR; + if (!file->f_op || !file->f_op->readdir) + goto out; + + error = file->f_op->readdir(inode, file, &buf, osf_filldir); if (error < 0) - return error; + goto out; + + error = buf.error; if (count == buf.count) - return buf.error; - return count - buf.count; + goto out; + + error = count - buf.count; +out: + return error; } /* @@ -267,76 +283,73 @@ struct osf_statfs { __kernel_fsid_t f_fsid; } *osf_stat; -static void linux_to_osf_statfs(struct statfs *linux_stat, struct osf_statfs *osf_stat) +static int linux_to_osf_statfs(struct statfs *linux_stat, struct osf_statfs *osf_stat, unsigned long bufsiz) { - osf_stat->f_type = linux_stat->f_type; - osf_stat->f_flags = 0; /* mount flags */ + struct osf_statfs tmp_stat; + + tmp_stat.f_type = linux_stat->f_type; + tmp_stat.f_flags = 0; /* mount flags */ /* Linux doesn't provide a "fundamental filesystem block size": */ - osf_stat->f_fsize = linux_stat->f_bsize; - osf_stat->f_bsize = linux_stat->f_bsize; - osf_stat->f_blocks = linux_stat->f_blocks; - osf_stat->f_bfree = linux_stat->f_bfree; - osf_stat->f_bavail = linux_stat->f_bavail; - osf_stat->f_files = linux_stat->f_files; - osf_stat->f_ffree = linux_stat->f_ffree; - osf_stat->f_fsid = linux_stat->f_fsid; + tmp_stat.f_fsize = linux_stat->f_bsize; + tmp_stat.f_bsize = linux_stat->f_bsize; + tmp_stat.f_blocks = linux_stat->f_blocks; + tmp_stat.f_bfree = linux_stat->f_bfree; + tmp_stat.f_bavail = linux_stat->f_bavail; + tmp_stat.f_files = linux_stat->f_files; + tmp_stat.f_ffree = linux_stat->f_ffree; + tmp_stat.f_fsid = linux_stat->f_fsid; + if (bufsiz > sizeof(tmp_stat)) + bufsiz = sizeof(tmp_stat); + return copy_to_user(osf_stat, &tmp_stat, bufsiz) ? -EFAULT : 0; } +static int do_osf_statfs(struct dentry * dentry, struct osf_statfs *buffer, unsigned long bufsiz) +{ + struct statfs linux_stat; + struct inode * inode = dentry->d_inode; + struct super_block * sb = inode->i_sb; + int error; + + error = -ENOSYS; + if (sb->s_op->statfs) { + set_fs(KERNEL_DS); + error = sb->s_op->statfs(sb, &linux_stat, sizeof(linux_stat)); + set_fs(USER_DS); + if (!error) + error = linux_to_osf_statfs(&linux_stat, buffer, bufsiz); + } + return error; +} asmlinkage int osf_statfs(char *path, struct osf_statfs *buffer, unsigned long bufsiz) { - struct statfs linux_stat; - struct inode *inode; + struct dentry *dentry; int retval; lock_kernel(); - if (bufsiz > sizeof(struct osf_statfs)) - bufsiz = sizeof(struct osf_statfs); - retval = verify_area(VERIFY_WRITE, buffer, bufsiz); - if (retval) - goto out; - retval = namei(NAM_FOLLOW_LINK, path, &inode); - if (retval) - goto out; - retval = -ENOSYS; - if (!inode->i_sb->s_op->statfs) { - iput(inode); - goto out; + dentry = namei(path); + retval = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + retval = do_osf_statfs(dentry, buffer, bufsiz); + dput(dentry); } - inode->i_sb->s_op->statfs(inode->i_sb, &linux_stat, sizeof(linux_stat)); - linux_to_osf_statfs(&linux_stat, buffer); - iput(inode); - retval = 0; -out: unlock_kernel(); return retval; } asmlinkage int osf_fstatfs(unsigned long fd, struct osf_statfs *buffer, unsigned long bufsiz) { - struct statfs linux_stat; struct file *file; - struct inode *inode; + struct dentry *dentry; int retval; lock_kernel(); - retval = verify_area(VERIFY_WRITE, buffer, bufsiz); - if (retval) - goto out; - if (bufsiz > sizeof(struct osf_statfs)) - bufsiz = sizeof(struct osf_statfs); retval = -EBADF; if (fd >= NR_OPEN || !(file = current->files->fd[fd])) goto out; - retval = -ENOENT; - if (!(inode = file->f_inode)) - goto out; - retval = -ENOSYS; - if (!inode->i_sb->s_op->statfs) - goto out; - inode->i_sb->s_op->statfs(inode->i_sb, &linux_stat, sizeof(linux_stat)); - linux_to_osf_statfs(&linux_stat, buffer); - retval = 0; + dentry = file->f_dentry; + if (dentry) + retval = do_osf_statfs(dentry, buffer, bufsiz); out: unlock_kernel(); return retval; @@ -369,56 +382,60 @@ struct procfs_args { uid_t exroot; }; -static int getdev(const char *name, int rdonly, struct inode **ino) +static int getdev(const char *name, int rdonly, struct dentry **dp) { kdev_t dev; + struct dentry *dentry; struct inode *inode; struct file_operations *fops; int retval; - retval = namei(NAM_FOLLOW_LINK, name, &inode); - if (retval) + dentry = namei(name); + retval = PTR_ERR(dentry); + if (IS_ERR(dentry)) return retval; + + inode = dentry->d_inode; if (!S_ISBLK(inode->i_mode)) { - iput(inode); + dput(dentry); return -ENOTBLK; } if (IS_NODEV(inode)) { - iput(inode); + dput(dentry); return -EACCES; } dev = inode->i_rdev; if (MAJOR(dev) >= MAX_BLKDEV) { - iput(inode); + dput(dentry); return -ENXIO; } fops = get_blkfops(MAJOR(dev)); if (!fops) { - iput(inode); + dput(dentry); return -ENODEV; } if (fops->open) { struct file dummy; memset(&dummy, 0, sizeof(dummy)); - dummy.f_inode = inode; + dummy.f_dentry = dentry; dummy.f_mode = rdonly ? 1 : 3; retval = fops->open(inode, &dummy); if (retval) { - iput(inode); + dput(dentry); return retval; } } - *ino = inode; + *dp = dentry; return 0; } -static void putdev(struct inode *inode) +static void putdev(struct dentry *dentry) { struct file_operations *fops; - fops = get_blkfops(MAJOR(inode->i_rdev)); + fops = get_blkfops(MAJOR(dentry->d_inode->i_rdev)); if (fops->release) - fops->release(inode, NULL); + fops->release(dentry->d_inode, NULL); } /* @@ -429,40 +446,40 @@ static void putdev(struct inode *inode) static int osf_ufs_mount(char *dirname, struct ufs_args *args, int flags) { int retval; - struct inode *inode; + struct dentry *dentry; struct cdfs_args tmp; retval = verify_area(VERIFY_READ, args, sizeof(*args)); if (retval) return retval; copy_from_user(&tmp, args, sizeof(tmp)); - retval = getdev(tmp.devname, 0, &inode); + retval = getdev(tmp.devname, 0, &dentry); if (retval) return retval; - retval = do_mount(inode->i_rdev, tmp.devname, dirname, "ext2", flags, NULL); + retval = do_mount(dentry->d_inode->i_rdev, tmp.devname, dirname, "ext2", flags, NULL); if (retval) - putdev(inode); - iput(inode); + putdev(dentry); + dput(dentry); return retval; } static int osf_cdfs_mount(char *dirname, struct cdfs_args *args, int flags) { int retval; - struct inode *inode; + struct dentry * dentry; struct cdfs_args tmp; retval = verify_area(VERIFY_READ, args, sizeof(*args)); if (retval) return retval; copy_from_user(&tmp, args, sizeof(tmp)); - retval = getdev(tmp.devname, 1, &inode); + retval = getdev(tmp.devname, 1, &dentry); if (retval) return retval; - retval = do_mount(inode->i_rdev, tmp.devname, dirname, "iso9660", flags, NULL); + retval = do_mount(dentry->d_inode->i_rdev, tmp.devname, dirname, "iso9660", flags, NULL); if (retval) - putdev(inode); - iput(inode); + putdev(dentry); + dput(dentry); return retval; } @@ -876,6 +893,9 @@ asmlinkage unsigned long osf_getsysinfo(unsigned long op, void *buffer, return -EOPNOTSUPP; } +/* Dummy functions for now */ +#define wrfpcr(x) do { } while (0) +#define rdfpcr() 0 asmlinkage unsigned long osf_setsysinfo(unsigned long op, void *buffer, unsigned long nbytes, diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c index b6c97e726..81744663d 100644 --- a/arch/alpha/kernel/process.c +++ b/arch/alpha/kernel/process.c @@ -308,8 +308,9 @@ asmlinkage int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2, char * filename; lock_kernel(); - error = getname((char *) a0, &filename); - if (error) + filename = getname((char *) a0); + error = PTR_ERR(filename); + if (IS_ERR(filename)) goto out; error = do_execve(filename, (char **) a1, (char **) a2, ®s); putname(filename); diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c index 02fa5a35d..c3838a8bb 100644 --- a/arch/alpha/kernel/traps.c +++ b/arch/alpha/kernel/traps.c @@ -24,13 +24,14 @@ void die_if_kernel(char * str, struct pt_regs * regs, long err, unsigned long *r9_15) { long i; - unsigned long sp, ra; + unsigned long ra; unsigned int * pc; + unsigned long * sp; if (regs->ps & 8) return; printk("%s(%d): %s %ld\n", current->comm, current->pid, str, err); - sp = (unsigned long) (regs+1); + sp = (unsigned long *) (regs+1); __get_user(ra, (unsigned long *)sp); printk("pc = [<%016lx>] ps = %04lx\n", regs->pc, regs->ps); printk("rp = [<%016lx>] ra = [<%016lx>]\n", regs->r26, ra); @@ -54,7 +55,7 @@ void die_if_kernel(char * str, struct pt_regs * regs, long err, printk("r22= %016lx r23= %016lx\n", regs->r22, regs->r23); printk("r24= %016lx r25= %016lx\n", regs->r24, regs->r25); printk("r27= %016lx r28= %016lx\n", regs->r27, regs->r28); - printk("gp = %016lx sp = %016lx\n", regs->gp, sp); + printk("gp = %016lx sp = %p\n", regs->gp, sp); printk("Code:"); pc = (unsigned int *) regs->pc; @@ -65,6 +66,19 @@ void die_if_kernel(char * str, struct pt_regs * regs, long err, printk("%c%08x%c",i?' ':'<',insn,i?' ':'>'); } printk("\n"); + printk("Trace:"); + while (0x1ff8 & (unsigned long) sp) { + extern unsigned long _stext, _etext; + unsigned long tmp = *sp; + sp++; + if (tmp < (unsigned long) &_stext) + continue; + if (tmp >= (unsigned long) &_etext) + continue; + printk(" [<%lx>]", tmp); + } + printk("\n"); + do_exit(SIGSEGV); } diff --git a/arch/alpha/mm/fault.c b/arch/alpha/mm/fault.c index a8bc34108..72d4224f7 100644 --- a/arch/alpha/mm/fault.c +++ b/arch/alpha/mm/fault.c @@ -67,8 +67,7 @@ asmlinkage void do_page_fault(unsigned long address, unsigned long mmcsr, long cause, struct pt_regs *regs) { struct vm_area_struct * vma; - struct task_struct *tsk = current; - struct mm_struct *mm = tsk->mm; + struct mm_struct *mm = current->mm; unsigned fixup; down(&mm->mmap_sem); @@ -97,7 +96,7 @@ good_area: if (!(vma->vm_flags & VM_WRITE)) goto bad_area; } - handle_mm_fault(tsk, vma, address, cause > 0); + handle_mm_fault(current, vma, address, cause > 0); up(&mm->mmap_sem); return; @@ -108,23 +107,20 @@ good_area: bad_area: up(&mm->mmap_sem); + if (user_mode(regs)) { + force_sig(SIGSEGV, current); + return; + } + /* Are we prepared to handle this fault as an exception? */ if ((fixup = search_exception_table(regs->pc)) != 0) { unsigned long newpc; newpc = fixup_exception(dpf_reg, fixup, regs->pc); - printk("Taking exception at [<%lx>] (%lx)\n", regs->pc, newpc); + printk("%s: Exception at [<%lx>] (%lx)\n", current->comm, regs->pc, newpc); regs->pc = newpc; return; } - if (user_mode(regs)) { - printk("%s: memory violation at pc=%08lx ra=%08lx " - "(bad address = %08lx)\n", - tsk->comm, regs->pc, regs->r26, address); - die_if_kernel("oops", regs, cause, (unsigned long*)regs - 16); - force_sig(SIGSEGV, tsk); - return; - } /* * Oops. The kernel tried to access some bad page. We'll have to * terminate things with extreme prejudice. diff --git a/arch/i386/boot/compressed/Makefile b/arch/i386/boot/compressed/Makefile index 9a78bca69..653c12eba 100644 --- a/arch/i386/boot/compressed/Makefile +++ b/arch/i386/boot/compressed/Makefile @@ -48,7 +48,7 @@ endif piggy.o: $(SYSTEM) - tmppiggy=/tmp/$$$$piggy; \ + tmppiggy=_tmp_$$$$piggy; \ rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk; \ $(OBJCOPY) $(SYSTEM) $$tmppiggy; \ gzip -f -9 < $$tmppiggy > $$tmppiggy.gz; \ diff --git a/arch/i386/defconfig b/arch/i386/defconfig index ab30a25c8..6e7700c91 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -80,6 +80,7 @@ CONFIG_INET=y # CONFIG_IP_ACCT is not set # CONFIG_IP_ROUTER is not set # CONFIG_NET_IPIP is not set +# CONFIG_SYN_COOKIES is not set # # (it is safe to leave these untouched) @@ -191,14 +192,11 @@ CONFIG_EEXPRESS_PRO100=y # Filesystems # # CONFIG_QUOTA is not set -# CONFIG_DCACHE_PRELOAD is not set -# CONFIG_OMIRR is not set -# CONFIG_TRANS_NAMES is not set -CONFIG_MINIX_FS=y +# CONFIG_MINIX_FS is not set CONFIG_EXT2_FS=y -CONFIG_FAT_FS=y -CONFIG_MSDOS_FS=y -CONFIG_VFAT_FS=y +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set # CONFIG_UMSDOS_FS is not set CONFIG_PROC_FS=y CONFIG_NFS_FS=y @@ -229,12 +227,14 @@ CONFIG_MOUSE=y # CONFIG_MS_BUSMOUSE is not set CONFIG_PSMOUSE=y CONFIG_82C710_MOUSE=y +# CONFIG_PC110_PAD is not set # CONFIG_UMISC is not set # CONFIG_QIC02_TAPE is not set # CONFIG_FTAPE is not set # CONFIG_APM is not set # CONFIG_WATCHDOG is not set # CONFIG_RTC is not set +# CONFIG_JOYSTICK is not set # # Sound diff --git a/arch/i386/kernel/bios32.c b/arch/i386/kernel/bios32.c index 157e62b2d..8f7a796fe 100644 --- a/arch/i386/kernel/bios32.c +++ b/arch/i386/kernel/bios32.c @@ -1,7 +1,7 @@ /* * bios32.c - BIOS32, PCI BIOS functions. * - * $Id: bios32.c,v 1.11 1997/05/07 13:35:21 mj Exp $ + * $Id: bios32.c,v 1.1.1.1 1997/06/01 03:16:32 ralf Exp $ * * Sponsored by * iX Multiuser Multitasking Magazine @@ -59,6 +59,8 @@ * * May 7, 1997 : Added some missing cli()'s. [mj] * + * Jun 20, 1997 : Corrected problems in "conf1" type accesses. + * (paubert@iram.es) */ #include <linux/config.h> @@ -512,16 +514,7 @@ static int pci_conf1_read_config_byte(unsigned char bus, unsigned char device_fn save_flags(flags); cli(); outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); - switch (where & 3) { - case 0: *value = inb(0xCFC); - break; - case 1: *value = inb(0xCFD); - break; - case 2: *value = inb(0xCFE); - break; - case 3: *value = inb(0xCFF); - break; - } + *value = inb(0xCFC + (where&3)); restore_flags(flags); return PCIBIOS_SUCCESSFUL; } @@ -531,12 +524,10 @@ static int pci_conf1_read_config_word (unsigned char bus, { unsigned long flags; + if (where&1) return PCIBIOS_BAD_REGISTER_NUMBER; save_flags(flags); cli(); outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); - if (where & 2) - *value = inw(0xCFE); - else - *value = inw(0xCFC); + *value = inw(0xCFC + (where&2)); restore_flags(flags); return PCIBIOS_SUCCESSFUL; } @@ -546,6 +537,7 @@ static int pci_conf1_read_config_dword (unsigned char bus, unsigned char device_ { unsigned long flags; + if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER; save_flags(flags); cli(); outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); *value = inl(0xCFC); @@ -560,7 +552,7 @@ static int pci_conf1_write_config_byte (unsigned char bus, unsigned char device_ save_flags(flags); cli(); outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); - outb(value, 0xCFC); + outb(value, 0xCFC + (where&3)); restore_flags(flags); return PCIBIOS_SUCCESSFUL; } @@ -570,9 +562,10 @@ static int pci_conf1_write_config_word (unsigned char bus, unsigned char device_ { unsigned long flags; + if (where&1) return PCIBIOS_BAD_REGISTER_NUMBER; save_flags(flags); cli(); outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); - outw(value, 0xCFC); + outw(value, 0xCFC + (where&2)); restore_flags(flags); return PCIBIOS_SUCCESSFUL; } @@ -582,6 +575,7 @@ static int pci_conf1_write_config_dword (unsigned char bus, unsigned char device { unsigned long flags; + if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER; save_flags(flags); cli(); outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); outl(value, 0xCFC); diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S index ac67da797..e7b9e0779 100644 --- a/arch/i386/kernel/entry.S +++ b/arch/i386/kernel/entry.S @@ -526,6 +526,8 @@ ENTRY(sys_call_table) .long SYMBOL_NAME(sys_query_module) .long SYMBOL_NAME(sys_poll) .long SYMBOL_NAME(sys_nfsservctl) - .rept NR_syscalls-169 + .long SYMBOL_NAME(sys_setresgid) /* 170 */ + .long SYMBOL_NAME(sys_getresgid) + .rept NR_syscalls-171 .long SYMBOL_NAME(sys_ni_syscall) .endr diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index eedb1d0fe..2e0f3e084 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -82,12 +82,13 @@ static inline void mask_and_ack_irq(int irq_nr) if (irq_nr & 8) { inb(0xA1); /* DUMMY */ outb(cached_A1,0xA1); + outb(0x62,0x20); /* Specific EOI to cascade */ outb(0x20,0xA0); } else { inb(0x21); /* DUMMY */ outb(cached_21,0x21); + outb(0x20,0x20); } - outb(0x20,0x20); spin_unlock(&irq_controller_lock); } @@ -207,7 +208,7 @@ static void math_error_irq(int cpl, void *dev_id, struct pt_regs *regs) math_error(); } -static struct irqaction irq13 = { math_error_irq, 0, 0, "math error", NULL, NULL }; +static struct irqaction irq13 = { math_error_irq, 0, 0, "fpu", NULL, NULL }; /* * IRQ2 is cascade interrupt to second interrupt controller diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index 33842a21f..a8fe0315b 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -623,8 +623,9 @@ asmlinkage int sys_execve(struct pt_regs regs) char * filename; lock_kernel(); - error = getname((char *) regs.ebx, &filename); - if (error) + filename = getname((char *) regs.ebx); + error = PTR_ERR(filename); + if (IS_ERR(filename)) goto out; error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, ®s); putname(filename); diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index f62744d11..4dd8edf76 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -219,7 +219,7 @@ __initfunc(void setup_arch(char **cmdline_p, request_region(0x40,0x20,"timer"); request_region(0x80,0x10,"dma page reg"); request_region(0xc0,0x20,"dma2"); - request_region(0xf0,0x10,"npu"); + request_region(0xf0,0x10,"fpu"); } static const char * i486model(unsigned int nr) @@ -244,6 +244,17 @@ static const char * i586model(unsigned int nr) return NULL; } +static const char * k5model(unsigned int nr) +{ + static const char *model[] = { + "SSA5 (PR-75, PR-90, PR-100)", "5k86 (PR-120, PR-133)", + "5k86 (PR-166)", "5k86 (PR-200)", "", "", "K6" + }; + if (nr < sizeof(model)/sizeof(char *)) + return model[nr]; + return NULL; +} + static const char * i686model(unsigned int nr) { static const char *model[] = { @@ -263,7 +274,11 @@ static const char * getmodel(int x86, int model) p = i486model(model); break; case 5: - p = i586model(model); + if(strcmp(x86_vendor_id, "AuthenticAMD") == 0){ + p = k5model(model); + } else { + p = i586model(model); + } break; case 6: p = i686model(model); diff --git a/arch/i386/mm/fault.c b/arch/i386/mm/fault.c index b0404a6a9..e4847c070 100644 --- a/arch/i386/mm/fault.c +++ b/arch/i386/mm/fault.c @@ -161,23 +161,25 @@ good_area: bad_area: up(&mm->mmap_sem); - /* Are we prepared to handle this fault? */ + /* User mode accesses just cause a SIGSEGV */ + if (error_code & 4) { + tsk->tss.cr2 = address; + tsk->tss.error_code = error_code; + tsk->tss.trap_no = 14; + force_sig(SIGSEGV, tsk); + goto out; + } + + /* Are we prepared to handle this kernel fault? */ if ((fixup = search_exception_table(regs->eip)) != 0) { printk(KERN_DEBUG "%s: Exception at [<%lx>] (%lx)\n", - current->comm, + tsk->comm, regs->eip, fixup); regs->eip = fixup; goto out; } - if (error_code & 4) { - tsk->tss.cr2 = address; - tsk->tss.error_code = error_code; - tsk->tss.trap_no = 14; - force_sig(SIGSEGV, tsk); - goto out; - } /* * Oops. The kernel tried to access some bad page. We'll have to * terminate things with extreme prejudice. diff --git a/arch/m68k/amiga/amifb.c b/arch/m68k/amiga/amifb.c index 15e424ea7..eb72970d7 100644 --- a/arch/m68k/amiga/amifb.c +++ b/arch/m68k/amiga/amifb.c @@ -1307,7 +1307,6 @@ static void ami_rebuild_copper(void); */ extern unsigned short ami_intena_vals[]; -extern void amiga_init_sound(void); /* * Support for Graphics Boards @@ -1811,11 +1810,6 @@ __initfunc(struct fb_info *amiga_fb_init(long *mem_start)) u_long chipptr; /* - * Our beloved beeper - */ - amiga_init_sound(); - - /* * Check for a Graphics Board */ diff --git a/arch/m68k/amiga/config.c b/arch/m68k/amiga/config.c index e36016306..1571e1a0f 100644 --- a/arch/m68k/amiga/config.c +++ b/arch/m68k/amiga/config.c @@ -85,6 +85,7 @@ static void amiga_serial_console_write(const char *s, unsigned int count); static void amiga_debug_init(void); extern void amiga_video_setup(char *, int *); +extern void amiga_init_sound(void); static struct console amiga_console_driver = { NULL, NULL, amiga_wait_key @@ -799,14 +800,18 @@ void amiga_serial_gets(char *s, int len) __initfunc(static void amiga_debug_init(void)) { - if (!strcmp( m68k_debug_device, "ser" )) { - /* no initialization required (?) */ - amiga_console_driver.write = amiga_serial_console_write; - } else if (!strcmp( m68k_debug_device, "mem" )) { - amiga_savekmsg_init(); - amiga_console_driver.write = amiga_mem_console_write; - } - register_console(&amiga_console_driver); + if (!strcmp( m68k_debug_device, "ser" )) { + /* no initialization required (?) */ + amiga_console_driver.write = amiga_serial_console_write; + } else if (!strcmp( m68k_debug_device, "mem" )) { + amiga_savekmsg_init(); + amiga_console_driver.write = amiga_mem_console_write; + } + register_console(&amiga_console_driver); + + /* our beloved beeper */ + if (AMIGAHW_PRESENT(AMI_AUDIO)) + amiga_init_sound(); } diff --git a/arch/m68k/amiga/cyberfb.c b/arch/m68k/amiga/cyberfb.c index 31e2a4608..b7802a67c 100644 --- a/arch/m68k/amiga/cyberfb.c +++ b/arch/m68k/amiga/cyberfb.c @@ -671,7 +671,7 @@ Cyber_WaitQueue (0x8000); * Rectangle Fill Solid */ void Cyber_RectFill (u_short x, u_short y, u_short width, u_short height, - u_short mode, u_short color) + u_short mode, u_short fcolor) { u_short blitcmd = S3_FILLEDRECT; diff --git a/arch/m68k/atari/atakeyb.c b/arch/m68k/atari/atakeyb.c index d5d45be01..e09743e79 100644 --- a/arch/m68k/atari/atakeyb.c +++ b/arch/m68k/atari/atakeyb.c @@ -419,7 +419,7 @@ static void keyboard_interrupt(int irq, void *dummy, struct pt_regs *fp) * break_flag... * */ int keyval = plain_map[scancode], keytyp; - + set_bit( scancode, broken_keys ); self_test_last_rcv = jiffies; keyval = plain_map[scancode]; diff --git a/arch/m68k/boot/amiga/bootstrap.c b/arch/m68k/boot/amiga/bootstrap.c index b109466ed..41ac75ada 100644 --- a/arch/m68k/boot/amiga/bootstrap.c +++ b/arch/m68k/boot/amiga/bootstrap.c @@ -36,10 +36,12 @@ #include <stdarg.h> #include <string.h> #include <sys/file.h> -#include <sys/types.h> #include <unistd.h> /* required Linux/m68k include files */ +#define __KERNEL_STRICT_NAMES /* This is ugly, I know */ +#define _LINUX_POSIX_TYPES_H +#include <asm/posix_types.h> #include <linux/a.out.h> #include <linux/elf.h> #include <asm/amigahw.h> diff --git a/arch/m68k/boot/amiga/linuxboot.c b/arch/m68k/boot/amiga/linuxboot.c index 23b7fa9d0..74b873bf2 100644 --- a/arch/m68k/boot/amiga/linuxboot.c +++ b/arch/m68k/boot/amiga/linuxboot.c @@ -22,9 +22,11 @@ * for more details. * * History: + * 11 Jun 1997 Fix for unpadded gzipped ramdisks with bootinfo interface + * version 1.0 * 27 Mar 1997 FPU-less machines couldn't boot kernels that use bootinfo * interface version 1.0 (Geert) - * 03 Feb 1997 Implemented kernel decompression (Geert, based on Roman's + * 3 Feb 1997 Implemented kernel decompression (Geert, based on Roman's * code for ataboot) * 30 Dec 1996 Reverted the CPU detection to the old scheme * New boot parameter override scheme (Geert) @@ -55,7 +57,6 @@ #include <stddef.h> #include <string.h> #include <errno.h> -#include <sys/types.h> #include <linux/a.out.h> #include <linux/elf.h> @@ -70,6 +71,10 @@ #undef custom #define custom ((*(volatile struct CUSTOM *)(CUSTOM_PHYSADDR))) +/* a.out linkage conventions */ +#undef SYMBOL_NAME_STR +#define SYMBOL_NAME_STR(X) "_"#X + /* temporary stack size */ #define TEMP_STACKSIZE (256) @@ -130,10 +135,9 @@ static int add_bi_record(u_short tag, u_short size, const void *data); static int add_bi_string(u_short tag, const u_char *s); static int check_bootinfo_version(const char *memptr); static void start_kernel(void (*startfunc)(), char *stackp, char *memptr, - u_long start_mem, u_long mem_size, u_long rd_size, - u_long kernel_size) __attribute__ ((noreturn)); + u_long start_mem, u_long kernel_size, u_long rd_dest, + u_long rd_size) __attribute__ ((noreturn)); asmlinkage u_long maprommed(void); -asmlinkage u_long check346(void); #ifdef ZKERNEL static int load_zkernel(int fd); static int KRead(int fd, void *buf, int cnt); @@ -682,7 +686,7 @@ u_long linuxboot(const struct linuxboot_args *args) if (debugflag) { if (bi.ramdisk.size) Printf("RAM disk at 0x%08lx, size is %ldK\n", - (u_long)memptr+kernel_size, bi.ramdisk.size>>10); + (u_long)memptr+kernel_size+bi_size, bi.ramdisk.size>>10); if (elf_kernel) { PutChar('\n'); @@ -703,11 +707,11 @@ u_long linuxboot(const struct linuxboot_args *args) Printf("\nKernel entry is 0x%08lx\n", elf_kernel ? kexec_elf.e_entry : kexec.a_entry); - Printf("ramdisk dest top is 0x%08lx\n", start_mem+mem_size); + Printf("ramdisk dest is 0x%08lx\n", bi.ramdisk.addr); Printf("ramdisk lower limit is 0x%08lx\n", - (u_long)(memptr+kernel_size)); + (u_long)memptr+kernel_size+bi_size); Printf("ramdisk src top is 0x%08lx\n", - (u_long)(memptr+kernel_size)+rd_size); + (u_long)memptr+kernel_size+bi_size+rd_size); Puts("\nType a key to continue the Linux/m68k boot..."); GetChar(); @@ -743,7 +747,7 @@ u_long linuxboot(const struct linuxboot_args *args) /* execute the copy-and-go code (from CHIP RAM) */ start_kernel(startfunc, (char *)stack+TEMP_STACKSIZE, memptr, start_mem, - mem_size, rd_size, kernel_size); + kernel_size, bi.ramdisk.addr, rd_size); /* Clean up and exit in case of a failure */ Fail: @@ -1065,6 +1069,7 @@ static int create_compat_bootinfo(void) compat_bootinfo.memory[i].size = bi.memory[i].size; } if (bi.ramdisk.size) { + bi.ramdisk.addr &= 0xfffffc00; compat_bootinfo.ramdisk_size = (bi.ramdisk.size+1023)/1024; compat_bootinfo.ramdisk_addr = bi.ramdisk.addr; } else { @@ -1151,14 +1156,14 @@ static int check_bootinfo_version(const char *memptr) */ static void start_kernel(void (*startfunc)(), char *stackp, char *memptr, - u_long start_mem, u_long mem_size, u_long rd_size, - u_long kernel_size) + u_long start_mem, u_long kernel_size, u_long rd_dest, + u_long rd_size) { register void (*a0)() __asm("a0") = startfunc; register char *a2 __asm("a2") = stackp; register char *a3 __asm("a3") = memptr; register u_long a4 __asm("a4") = start_mem; - register u_long d0 __asm("d0") = mem_size; + register u_long d0 __asm("d0") = rd_dest; register u_long d1 __asm("d1") = rd_size; register u_long d2 __asm("d2") = kernel_size; register u_long d3 __asm("d3") = bi_size; @@ -1182,7 +1187,7 @@ static void start_kernel(void (*startfunc)(), char *stackp, char *memptr, * * a3 = memptr * a4 = start_mem - * d0 = mem_size + * d0 = rd_dest * d1 = rd_size * d2 = kernel_size * d3 = bi_size @@ -1210,18 +1215,16 @@ SYMBOL_NAME_STR(copyall) ": dbra d7,1b | *dest++ = *src++ | /* copy the ramdisk to the top of memory */ - | /* (from back to front) */ - movel a4,a1 | dest = (u_long *)(start_mem+mem_size); - addl d0,a1 - movel a3,a2 | limit = (u_long *)(memptr+kernel_size + - addl d2,a2 | bi_size); - addl d3,a2 - movel a2,a0 | src = (u_long *)((u_long)limit+rd_size); - addl d1,a0 + movel a3,a0 | src = (u_long *)(memptr+kernel_size+bi_size); + addl d2,a0 + addl d3,a0 + movel d0,a1 | dest = (u_long *)rd_dest; + movel a0,a2 | limit = (u_long *)(memptr+kernel_size+ + addl d1,a2 | bi_size+rd_size); 1: cmpl a0,a2 - beqs 2f | while (src > limit) - moveb a0@-,a1@- | *--dest = *--src; - bras 1b + jeq 2f | while (src > limit) + moveb a0@+,a1@+ | *dest++ = *src++; + jra 1b 2: | /* jump to start of kernel */ movel a4,a0 | jump_to (start_mem); diff --git a/arch/m68k/config.in b/arch/m68k/config.in index df6a45a9d..b963687ae 100644 --- a/arch/m68k/config.in +++ b/arch/m68k/config.in @@ -52,6 +52,10 @@ bool 'System V IPC' CONFIG_SYSVIPC bool 'Sysctl support' CONFIG_SYSCTL tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC +fi + if [ "$CONFIG_AMIGA" = "y" ]; then bool 'Amiga AutoConfig Identification' CONFIG_ZORRO bool 'Amiga OCS chipset support' CONFIG_AMIFB_OCS @@ -153,6 +157,7 @@ if [ "$CONFIG_AMIGA" = "y" ]; then bool 'A4000T SCSI support' CONFIG_A4000T_SCSI bool 'A4091 SCSI support' CONFIG_A4091_SCSI bool 'WarpEngine SCSI support' CONFIG_WARPENGINE_SCSI + bool 'GVP Turbo 040/060 SCSI support' CONFIG_GVP_TURBO_SCSI fi fi if [ "$CONFIG_ATARI" = "y" ]; then @@ -273,5 +278,6 @@ bool 'Kernel profiling support' CONFIG_PROFILE if [ "$CONFIG_PROFILE" = "y" ]; then int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 fi +bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ bool 'Remote debugging support' CONFIG_KGDB endmenu diff --git a/arch/m68k/console/fbcon.c b/arch/m68k/console/fbcon.c index 62457c0cb..5065e5769 100644 --- a/arch/m68k/console/fbcon.c +++ b/arch/m68k/console/fbcon.c @@ -97,7 +97,6 @@ extern int console_blanked; #undef CONFIG_FBCON_CYBER #undef CONFIG_FBCON_RETINAZ3 - /* Monochrome is default */ #define CONFIG_FBCON_MONO @@ -450,14 +449,17 @@ static void putcs_cyber(struct vc_data *conp, struct display *p, const char *s, int count, int yy, int xx); static void rev_char_cyber(struct display *p, int xx, int yy); -extern void Cyber_WaitQueue(u_short fifo); +extern void Cyber_WaitQueue(unsigned short fifo); extern void Cyber_WaitBlit(void); -extern void Cyber_BitBLT(u_short curx, u_short cury, u_short destx, - u_short desty, u_short width, u_short height, - u_short mode); -extern void Cyber_RectFill(u_short xx, u_short yy, u_short width, u_short height, - u_short mode, u_short color); -extern void Cyber_MoveCursor(u_short xx, u_short yy); +extern void Cyber_BitBLT(unsigned short curx, unsigned short cury, + unsigned short destx, unsigned short desty, + unsigned short width, unsigned short height, + unsigned short mode); +extern void Cyber_RectFill(unsigned short xx, unsigned short yy, + unsigned short width, unsigned short + height, unsigned short mode, + unsigned short fcolor); +extern void Cyber_MoveCursor(unsigned short xx, unsigned short yy); #endif /* CONFIG_FBCON_CYBER */ #ifdef CONFIG_FBCON_RETINAZ3 @@ -3835,7 +3837,7 @@ static void putc_cyber(struct vc_data *conp, struct display *p, int c, int yy, c &= 0xff; - dest = p->screen_base+y*p->fontheight*p->next_line+8*x; + dest = p->screen_base + yy * p->fontheight * p->next_line + 8 * xx; cdat = p->fontdata+(c*p->fontheight); fg = disp->fgcol; bg = disp->bgcol; @@ -3874,7 +3876,7 @@ static void putcs_cyber(struct vc_data *conp, struct display *p, const char *s, u_char c, d; u_char fg, bg; - dest0 = p->screen_base+y*p->fontheight*p->next_line+8*x; + dest0 = p->screen_base + yy * p->fontheight * p->next_line + 8 * xx; fg = disp->fgcol; bg = disp->bgcol; revs = conp->vc_reverse; @@ -3918,7 +3920,7 @@ static void rev_char_cyber(struct display *p, int xx, int yy) fg = disp->fgcol; bg = disp->bgcol; - dest = p->screen_base+y*p->fontheight*p->next_line+8*x; + dest = p->screen_base + yy * p->fontheight * p->next_line + 8 * xx; Cyber_WaitBlit(); for (rows = p->fontheight; rows--; dest += p->next_line) { *dest = (*dest == fg) ? bg : fg; diff --git a/arch/m68k/defconfig b/arch/m68k/defconfig index 65f71d5a9..18763c58b 100644 --- a/arch/m68k/defconfig +++ b/arch/m68k/defconfig @@ -152,6 +152,9 @@ CONFIG_NETDEVICES=y # Filesystems # # CONFIG_QUOTA is not set +# CONFIG_DCACHE_PRELOAD is not set +# CONFIG_OMIRR is not set +# CONFIG_TRANS_NAMES is not set CONFIG_MINIX_FS=y CONFIG_EXT2_FS=y CONFIG_FAT_FS=y diff --git a/arch/m68k/ifpsp060/iskeleton.S b/arch/m68k/ifpsp060/iskeleton.S index 3c310c237..35542d4f1 100644 --- a/arch/m68k/ifpsp060/iskeleton.S +++ b/arch/m68k/ifpsp060/iskeleton.S @@ -35,24 +35,7 @@ | #include <linux/linkage.h> - -/* - * This has to match entry.S - */ -LOFF_ORIG_D0 = 0x24 - -#define curptr a2 - -#define SAVE_ALL \ - clrl %sp@-; /* stk_adj */ \ - movel %d0,%sp@-; /* orig d0 */ \ - movel %d0,%sp@-; /* d0 */ \ - moveml %d1-%d5/%a0-%a1/%curptr,%sp@-; - -#define GET_CURRENT(tmp) \ - movel %sp,tmp; \ - andw &-8192,tmp; \ - movel tmp,%curptr; +#include <asm/entry.h> |################################ | (1) EXAMPLE CALL-OUTS # @@ -92,9 +75,8 @@ Lnotkern: bne Lmustsched rte Lmustsched: - SAVE_ALL - moveq #-1,%d0 - movel %d0,%sp@(LOFF_ORIG_D0) | indicate stack frame not for syscall + SAVE_ALL_INT + GET_CURRENT(%d0) bral SYMBOL_NAME(ret_from_exception) | deliver signals, reschedule etc.. diff --git a/arch/m68k/kernel/console.c b/arch/m68k/kernel/console.c index 31608e904..204f4d3cc 100644 --- a/arch/m68k/kernel/console.c +++ b/arch/m68k/kernel/console.c @@ -109,6 +109,7 @@ #include <linux/interrupt.h> #include <linux/tty.h> #include <linux/tty_flip.h> +#include <linux/console.h> #include <linux/kernel.h> #include <linux/string.h> #include <linux/errno.h> @@ -164,9 +165,12 @@ extern void set_palette(void); void poke_blanked_console(void); void do_blank_screen(int); +#if 0 +/* Make sure there are no references left to this variables. */ unsigned long video_num_lines; unsigned long video_num_columns; unsigned long video_size_row; +#endif static int printable = 0; /* Is console ready for printing? */ unsigned long video_font_height; /* Height of current screen font */ diff --git a/arch/m68k/mm/memory.c b/arch/m68k/mm/memory.c index dea7695e8..a135143b2 100644 --- a/arch/m68k/mm/memory.c +++ b/arch/m68k/mm/memory.c @@ -555,16 +555,18 @@ void cache_clear (unsigned long paddr, int len) int tmp; /* - * cwe need special treatment for the first page, in case it - * is not page-aligned. + * We need special treatment for the first page, in case it + * is not page-aligned. Page align the addresses to work + * around bug I17 in the 68060. */ if ((tmp = -paddr & (PAGE_SIZE - 1))) { - pushcl040(paddr); + pushcl040(paddr & PAGE_MASK); if ((len -= tmp) <= 0) return; paddr += tmp; } tmp = PAGE_SIZE; + paddr &= PAGE_MASK; while ((len -= tmp) >= 0) { clear040(paddr); paddr += tmp; @@ -600,6 +602,13 @@ void cache_push (unsigned long paddr, int len) * the '060! */ len += paddr & (PAGE_SIZE - 1); + + /* + * Work around bug I17 in the 68060 affecting some instruction + * lines not being invalidated properly. + */ + paddr &= PAGE_MASK; + do { pushcli040(paddr); paddr += tmp; @@ -638,6 +647,13 @@ void cache_push_v (unsigned long vaddr, int len) /* on 68040, push cache lines for pages in the range */ len += vaddr & (PAGE_SIZE - 1); + + /* + * Work around bug I17 in the 68060 affecting some instruction + * lines not being invalidated properly. + */ + vaddr &= PAGE_MASK; + do { pushv040(vaddr); vaddr += tmp; diff --git a/arch/mips/defconfig b/arch/mips/defconfig index 9c6fb9073..099cb8406 100644 --- a/arch/mips/defconfig +++ b/arch/mips/defconfig @@ -86,6 +86,7 @@ CONFIG_INET=y # CONFIG_IP_ACCT is not set # CONFIG_IP_ROUTER is not set # CONFIG_NET_IPIP is not set +# CONFIG_SYN_COOKIES is not set # # (it is safe to leave these untouched) diff --git a/arch/mips/kernel/irixelf.c b/arch/mips/kernel/irixelf.c index fc0d542fc..dabd91762 100644 --- a/arch/mips/kernel/irixelf.c +++ b/arch/mips/kernel/irixelf.c @@ -228,7 +228,7 @@ unsigned long * create_irix_tables(char * p, int argc, int envc, * an ELF header. */ static unsigned int load_irix_interp(struct elfhdr * interp_elf_ex, - struct inode * interpreter_inode, + struct dentry * interpreter_dentry, unsigned int *interp_load_addr) { struct file * file; @@ -256,8 +256,8 @@ static unsigned int load_irix_interp(struct elfhdr * interp_elf_ex, if((interp_elf_ex->e_type != ET_EXEC && interp_elf_ex->e_type != ET_DYN) || !elf_check_arch(interp_elf_ex->e_machine) || - (!interpreter_inode->i_op || - !interpreter_inode->i_op->default_file_ops->mmap)){ + (!interpreter_dentry->d_inode->i_op || + !interpreter_dentry->d_inode->i_op->default_file_ops->mmap)) { printk("IRIX interp has bad e_type %d\n", interp_elf_ex->e_type); return 0xffffffff; } @@ -288,7 +288,7 @@ static unsigned int load_irix_interp(struct elfhdr * interp_elf_ex, return 0xffffffff; } - retval = read_exec(interpreter_inode, interp_elf_ex->e_phoff, + retval = read_exec(interpreter_dentry, interp_elf_ex->e_phoff, (char *) elf_phdata, sizeof(struct elf_phdr) * interp_elf_ex->e_phnum, 1); @@ -296,7 +296,7 @@ static unsigned int load_irix_interp(struct elfhdr * interp_elf_ex, dump_phdrs(elf_phdata, interp_elf_ex->e_phnum); #endif - elf_exec_fileno = open_inode(interpreter_inode, O_RDONLY); + elf_exec_fileno = open_dentry(interpreter_dentry, O_RDONLY); if (elf_exec_fileno < 0) { printk("Could not open IRIX interp inode.\n"); kfree(elf_phdata); @@ -408,8 +408,9 @@ static int verify_binary(struct elfhdr *ehp, struct linux_binprm *bprm) /* First of all, some simple consistency checks */ if((ehp->e_type != ET_EXEC && ehp->e_type != ET_DYN) || !elf_check_arch(ehp->e_machine) || - (!bprm->inode->i_op || !bprm->inode->i_op->default_file_ops || - !bprm->inode->i_op->default_file_ops->mmap)) { + (!bprm->dentry->d_inode->i_op || + !bprm->dentry->d_inode->i_op->default_file_ops || + !bprm->dentry->d_inode->i_op->default_file_ops->mmap)) { return -ENOEXEC; } @@ -435,13 +436,14 @@ static int verify_binary(struct elfhdr *ehp, struct linux_binprm *bprm) /* Look for an IRIX ELF interpreter. */ static inline int look_for_irix_interpreter(char **name, - struct inode **interpreter_inode, + struct dentry **interpreter_dentry, struct elfhdr *interp_elf_ex, struct elf_phdr *epp, struct linux_binprm *bprm, int pnum) { int i, old_fs; int retval = -EINVAL; + struct dentry *dentry = NULL; *name = NULL; for(i = 0; i < pnum; i++, epp++) { @@ -450,7 +452,7 @@ static inline int look_for_irix_interpreter(char **name, /* It is illegal to have two interpreters for one executable. */ if(*name != NULL) - goto losing; + goto out; *name = (char *) kmalloc((epp->p_filesz + strlen(IRIX_INTERP_PREFIX)), @@ -459,26 +461,31 @@ static inline int look_for_irix_interpreter(char **name, return -ENOMEM; strcpy(*name, IRIX_INTERP_PREFIX); - retval = read_exec(bprm->inode, epp->p_offset, (*name + 16), + retval = read_exec(bprm->dentry, epp->p_offset, (*name + 16), epp->p_filesz, 1); if(retval < 0) - goto losing; + goto out; old_fs = get_fs(); set_fs(get_ds()); - retval = namei(NAM_FOLLOW_LINK, *name, interpreter_inode); + dentry = namei(*name); set_fs(old_fs); - if(retval < 0) - goto losing; + if(IS_ERR(dentry)) { + retval = PTR_ERR(dentry); + goto out; + } - retval = read_exec(*interpreter_inode, 0, bprm->buf, 128, 1); - if(retval < 0) - goto losing; + retval = read_exec(dentry, 0, bprm->buf, 128, 1); + if(retval) + goto dput_and_out; *interp_elf_ex = *((struct elfhdr *) bprm->buf); } + *interpreter_dentry = dentry; return 0; -losing: +dput_and_out: + dput(dentry); +out: kfree(*name); return retval; } @@ -538,7 +545,7 @@ static inline void map_executable(struct file *fp, struct elf_phdr *epp, int pnu } static inline int map_interpreter(struct elf_phdr *epp, struct elfhdr *ihp, - struct inode *iino, unsigned int *iladdr, + struct dentry *identry, unsigned int *iladdr, int pnum, int old_fs, unsigned int *eentry) { @@ -554,11 +561,11 @@ static inline int map_interpreter(struct elf_phdr *epp, struct elfhdr *ihp, return -1; set_fs(old_fs); - *eentry = load_irix_interp(ihp, iino, iladdr); + *eentry = load_irix_interp(ihp, identry, iladdr); old_fs = get_fs(); set_fs(get_ds()); - iput(iino); + dput(identry); if(*eentry == 0xffffffff) return -1; @@ -573,7 +580,7 @@ static inline int do_load_irix_binary(struct linux_binprm * bprm, struct pt_regs * regs) { struct elfhdr elf_ex, interp_elf_ex; - struct inode *interpreter_inode; + struct dentry *interpreter_dentry; struct elf_phdr *elf_phdata, *elf_ihdr, *elf_ephdr; unsigned int load_addr, elf_bss, elf_brk; unsigned int elf_entry, interp_load_addr = 0; @@ -599,7 +606,7 @@ static inline int do_load_irix_binary(struct linux_binprm * bprm, if (elf_phdata == NULL) return -ENOMEM; - retval = read_exec(bprm->inode, elf_ex.e_phoff, (char *) elf_phdata, + retval = read_exec(bprm->dentry, elf_ex.e_phoff, (char *) elf_phdata, elf_ex.e_phentsize * elf_ex.e_phnum, 1); if (retval < 0) { kfree (elf_phdata); @@ -629,7 +636,7 @@ static inline int do_load_irix_binary(struct linux_binprm * bprm, elf_bss = 0; elf_brk = 0; - elf_exec_fileno = open_inode(bprm->inode, O_RDONLY); + elf_exec_fileno = open_dentry(bprm->dentry, O_RDONLY); if (elf_exec_fileno < 0) { kfree (elf_phdata); @@ -642,7 +649,8 @@ static inline int do_load_irix_binary(struct linux_binprm * bprm, end_code = 0; end_data = 0; - retval = look_for_irix_interpreter(&elf_interpreter, &interpreter_inode, + retval = look_for_irix_interpreter(&elf_interpreter, + &interpreter_dentry, &interp_elf_ex, elf_phdata, bprm, elf_ex.e_phnum); if(retval) { @@ -703,7 +711,7 @@ static inline int do_load_irix_binary(struct linux_binprm * bprm, if(elf_interpreter) { retval = map_interpreter(elf_phdata, &interp_elf_ex, - interpreter_inode, &interp_load_addr, + interpreter_dentry, &interp_load_addr, elf_ex.e_phnum, old_fs, &elf_entry); kfree(elf_interpreter); if(retval) { @@ -795,7 +803,8 @@ static inline int do_load_irix_library(int fd) struct file * file; struct elfhdr elf_ex; struct elf_phdr *elf_phdata = NULL; - struct inode * inode; + struct dentry *dentry; + struct inode *inode; unsigned int len; int elf_bss; int retval; @@ -805,7 +814,8 @@ static inline int do_load_irix_library(int fd) len = 0; file = current->files->fd[fd]; - inode = file->f_inode; + dentry = file->f_dentry; + inode = dentry->d_inode; elf_bss = 0; if (!file || !file->f_op) @@ -831,7 +841,8 @@ static inline int do_load_irix_library(int fd) /* First of all, some simple consistency checks. */ if(elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 || !elf_check_arch(elf_ex.e_machine) || - (!inode->i_op || !inode->i_op->default_file_ops->mmap)) + (!dentry->d_inode->i_op || + !dentry->d_inode->i_op->default_file_ops->mmap)) return -ENOEXEC; /* Now read in all of the header information. */ @@ -843,7 +854,7 @@ static inline int do_load_irix_library(int fd) if (elf_phdata == NULL) return -ENOMEM; - retval = read_exec(inode, elf_ex.e_phoff, (char *) elf_phdata, + retval = read_exec(dentry, elf_ex.e_phoff, (char *) elf_phdata, sizeof(struct elf_phdr) * elf_ex.e_phnum, 1); j = 0; @@ -973,14 +984,13 @@ unsigned long irix_mapelf(int fd, struct elf_phdr *user_phdrp, int cnt) */ static int dump_write(struct file *file, const void *addr, int nr) { - file->f_inode->i_status |= ST_MODIFIED; - return file->f_op->write(file->f_inode, file, addr, nr) == nr; + return file->f_op->write(file->f_dentry->d_inode, file, addr, nr) == nr; } static int dump_seek(struct file *file, off_t off) { if (file->f_op->llseek) { - if (file->f_op->llseek(file->f_inode, file, off, 0) != off) + if (file->f_op->llseek(file->f_dentry->d_inode, file, off, 0) != off) return 0; } else file->f_pos = off; @@ -1071,6 +1081,7 @@ static int irix_core_dump(long signr, struct pt_regs * regs) { int has_dumped = 0; struct file file; + struct dentry *dentry; struct inode *inode; unsigned short fs; char corefile[6+sizeof(current->comm)]; @@ -1138,30 +1149,24 @@ static int irix_core_dump(long signr, struct pt_regs * regs) fs = get_fs(); set_fs(KERNEL_DS); - memcpy(corefile,"core.",5); + memcpy(corefile,"core.", 5); #if 0 memcpy(corefile+5,current->comm,sizeof(current->comm)); #else corefile[4] = '\0'; #endif - if (open_namei(corefile,O_CREAT | 2 | O_TRUNC,0600,&inode,NULL)) { + dentry = open_namei(corefile, O_CREAT | 2 | O_TRUNC, 0600); + if (IS_ERR(dentry)) { inode = NULL; goto end_coredump; } + inode = dentry->d_inode; if (!S_ISREG(inode->i_mode)) goto end_coredump; if (!inode->i_op || !inode->i_op->default_file_ops) goto end_coredump; - file.f_mode = 3; - file.f_flags = 0; - file.f_count = 1; - file.f_inode = inode; - file.f_pos = 0; - file.f_reada = 0; - file.f_op = inode->i_op->default_file_ops; - if (file.f_op->open) - if (file.f_op->open(inode,&file)) - goto end_coredump; + if (init_private_file(&file, dentry, 3)) + goto end_coredump; if (!file.f_op->write) goto close_coredump; has_dumped = 1; @@ -1330,11 +1335,11 @@ static int irix_core_dump(long signr, struct pt_regs * regs) close_coredump: if (file.f_op->release) - file.f_op->release(inode,&file); + file.f_op->release(inode, &file); end_coredump: set_fs(fs); - iput(inode); + dput(dentry); #ifndef CONFIG_BINFMT_ELF MOD_DEC_USE_COUNT; #endif diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c index 2faf604c7..f1b117a63 100644 --- a/arch/mips/kernel/syscall.c +++ b/arch/mips/kernel/syscall.c @@ -131,20 +131,21 @@ asmlinkage int sys_clone(struct pt_regs *regs) */ asmlinkage int sys_execve(struct pt_regs *regs) { - int res; + int error; char * filename; lock_kernel(); - res = getname((char *) (long)regs->regs[4], &filename); - if (res) + filename = getname((char *) (long)regs->regs[4]); + error = PTR_ERR(filename); + if (IS_ERR(filename)) goto out; - res = do_execve(filename, (char **) (long)regs->regs[5], - (char **) (long)regs->regs[6], regs); + error = do_execve(filename, (char **) (long)regs->regs[5], + (char **) (long)regs->regs[6], regs); putname(filename); out: unlock_kernel(); - return res; + return error; } /* diff --git a/arch/mips/kernel/syscalls.h b/arch/mips/kernel/syscalls.h index b8fc556fa..66d66ce5b 100644 --- a/arch/mips/kernel/syscalls.h +++ b/arch/mips/kernel/syscalls.h @@ -7,7 +7,7 @@ * * Copyright (C) 1995, 1996, 1997 by Ralf Baechle * - * $Id: syscalls.h,v 1.3 1997/06/20 09:48:49 ralf Exp $ + * $Id: syscalls.h,v 1.4 1997/06/25 20:07:40 ralf Exp $ */ /* @@ -208,3 +208,5 @@ SYS(sys_getresuid, 3) SYS(sys_query_module, 5) SYS(sys_poll, 3) SYS(sys_nfsservctl, 3) +SYS(sys_setresgid, 3) /* 4190 */ +SYS(sys_getresgid, 3) diff --git a/arch/mips/kernel/sysirix.c b/arch/mips/kernel/sysirix.c index 95a0dc1c6..e87e5084b 100644 --- a/arch/mips/kernel/sysirix.c +++ b/arch/mips/kernel/sysirix.c @@ -1,4 +1,4 @@ -/* $Id: sysirix.c,v 1.14 1996/07/14 01:59:51 dm Exp $ +/* $Id: sysirix.c,v 1.4 1997/07/19 19:03:18 root Exp $ * sysirix.c: IRIX system call emulation. * * Copyright (C) 1996 David S. Miller @@ -652,6 +652,7 @@ struct irix_statfs { asmlinkage int irix_statfs(const char *path, struct irix_statfs *buf, int len, int fs_type) { + struct dentry *dentry; struct inode *inode; struct statfs kbuf; int error, old_fs, i; @@ -664,20 +665,19 @@ asmlinkage int irix_statfs(const char *path, struct irix_statfs *buf, error = verify_area(VERIFY_WRITE, buf, sizeof(struct irix_statfs)); if (error) goto out; - error = namei(NAM_FOLLOW_LINK, path, &inode); - if (error) - goto out; - if (!inode->i_sb->s_op->statfs) { - iput(inode); - error = -ENOSYS; + dentry = namei(path); + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto out; - } + inode = dentry->d_inode; old_fs = get_fs(); set_fs(get_ds()); - inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, sizeof(struct statfs)); + error = inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, + sizeof(struct statfs)); set_fs(old_fs); + if (error) + goto dput_and_out; - iput(inode); __put_user(kbuf.f_type, &buf->f_type); __put_user(kbuf.f_bsize, &buf->f_bsize); __put_user(kbuf.f_frsize, &buf->f_frsize); @@ -691,6 +691,8 @@ asmlinkage int irix_statfs(const char *path, struct irix_statfs *buf, } error = 0; +dput_and_out: + dput(dentry); out: unlock_kernel(); return error; @@ -698,7 +700,8 @@ out: asmlinkage int irix_fstatfs(unsigned int fd, struct irix_statfs *buf) { - struct inode * inode; + struct dentry *dentry; + struct inode *inode; struct statfs kbuf; struct file *file; int error, old_fs, i; @@ -711,18 +714,29 @@ asmlinkage int irix_fstatfs(unsigned int fd, struct irix_statfs *buf) error = -EBADF; goto out; } - if (!(inode = file->f_inode)) { + if (!(dentry = file->f_dentry)) { + error = -ENOENT; + goto out; + } + if (!(inode = dentry->d_inode)) { error = -ENOENT; goto out; } + if (!inode->i_sb) { + error = -ENODEV; + goto out; + } if (!inode->i_sb->s_op->statfs) { error = -ENOSYS; goto out; } old_fs = get_fs(); set_fs(get_ds()); - inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, sizeof(struct statfs)); + error = inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, + sizeof(struct statfs)); set_fs(old_fs); + if (error) + goto out; __put_user(kbuf.f_type, &buf->f_type); __put_user(kbuf.f_bsize, &buf->f_bsize); @@ -789,13 +803,14 @@ out: asmlinkage int irix_exec(struct pt_regs *regs) { int error, base = 0; - char * filename; + char *filename; lock_kernel(); if(regs->regs[2] == 1000) base = 1; - error = getname((char *) (long)regs->regs[base + 4], &filename); - if (error) + filename = getname((char *) (long)regs->regs[base + 4]); + error = PTR_ERR(filename); + if (IS_ERR(filename)) goto out; error = do_execve(filename, (char **) (long)regs->regs[base + 5], (char **) 0, regs); @@ -809,13 +824,14 @@ out: asmlinkage int irix_exece(struct pt_regs *regs) { int error, base = 0; - char * filename; + char *filename; lock_kernel(); if(regs->regs[2] == 1000) base = 1; - error = getname((char *) (long)regs->regs[base + 4], &filename); - if (error) + filename = getname((char *) (long)regs->regs[base + 4]); + error = PTR_ERR(filename); + if (IS_ERR(filename)) goto out; error = do_execve(filename, (char **) (long)regs->regs[base + 5], (char **) (long)regs->regs[base + 6], regs); @@ -1380,6 +1396,7 @@ struct irix_statvfs { asmlinkage int irix_statvfs(char *fname, struct irix_statvfs *buf) { + struct dentry *dentry; struct inode *inode; struct statfs kbuf; int error, old_fs, i; @@ -1390,20 +1407,23 @@ asmlinkage int irix_statvfs(char *fname, struct irix_statvfs *buf) error = verify_area(VERIFY_WRITE, buf, sizeof(struct irix_statvfs)); if(error) goto out; - error = namei(NAM_FOLLOW_LINK, fname, &inode); - if(error) - goto out; - if(!inode->i_sb->s_op->statfs) { - iput(inode); - error = -ENOSYS; + dentry = namei(fname); + error = PTR_ERR(dentry); + if(!IS_ERR(dentry)) goto out; - } + inode = dentry->d_inode; + + error = -ENOSYS; + if(!inode->i_sb->s_op->statfs) + goto dput_and_out; old_fs = get_fs(); set_fs(get_ds()); - inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, sizeof(struct statfs)); + error = inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, + sizeof(struct statfs)); set_fs(old_fs); + if (error) + goto dput_and_out; - iput(inode); __put_user(kbuf.f_bsize, &buf->f_bsize); __put_user(kbuf.f_frsize, &buf->f_frsize); __put_user(kbuf.f_blocks, &buf->f_blocks); @@ -1426,6 +1446,8 @@ asmlinkage int irix_statvfs(char *fname, struct irix_statvfs *buf) error = 0; +dput_and_out: + dput(dentry); out: unlock_kernel(); return error; @@ -1433,7 +1455,8 @@ out: asmlinkage int irix_fstatvfs(int fd, struct irix_statvfs *buf) { - struct inode * inode; + struct dentry *dentry; + struct inode *inode; struct statfs kbuf; struct file *file; int error, old_fs, i; @@ -1449,7 +1472,11 @@ asmlinkage int irix_fstatvfs(int fd, struct irix_statvfs *buf) error = -EBADF; goto out; } - if (!(inode = file->f_inode)) { + if (!(dentry = file->f_dentry)) { + error = -ENOENT; + goto out; + } + if (!(inode = dentry->d_inode)) { error = -ENOENT; goto out; } @@ -1459,8 +1486,11 @@ asmlinkage int irix_fstatvfs(int fd, struct irix_statvfs *buf) } old_fs = get_fs(); set_fs(get_ds()); - inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, sizeof(struct statfs)); + error = inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, + sizeof(struct statfs)); set_fs(old_fs); + if (error) + goto out; __put_user(kbuf.f_bsize, &buf->f_bsize); __put_user(kbuf.f_frsize, &buf->f_frsize); @@ -1489,26 +1519,28 @@ out: return error; } -#define NOFOLLOW_LINKS NAM_FOLLOW_TRAILSLASH -#define FOLLOW_LINKS NAM_FOLLOW_LINK +#define NOFOLLOW_LINKS 0 +#define FOLLOW_LINKS 1 -static inline int chown_common(char *filename, uid_t user, gid_t group, int follow) +static inline int chown_common(uid_t user, gid_t group, struct dentry *dentry) { struct inode * inode; int error; struct iattr newattrs; - error = namei(follow, filename,&inode); - if (error) - return error; - if (IS_RDONLY(inode)) { - iput(inode); - return -EROFS; - } - if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) { - iput(inode); - return -EPERM; - } + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) + goto out; + inode = dentry->d_inode; + + error = -EROFS; + if (IS_RDONLY(inode)) + goto dput_and_out; + + error = -EPERM; + if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) + goto dput_and_out; + if (user == (uid_t) -1) user = inode->i_uid; if (group == (gid_t) -1) @@ -1534,38 +1566,45 @@ static inline int chown_common(char *filename, uid_t user, gid_t group, int foll newattrs.ia_mode &= ~S_ISGID; newattrs.ia_valid |= ATTR_MODE; } - inode->i_dirt = 1; if (inode->i_sb->dq_op) { inode->i_sb->dq_op->initialize(inode, -1); + error = -EDQUOT; if (inode->i_sb->dq_op->transfer(inode, &newattrs, 0)) - return -EDQUOT; + goto dput_and_out; error = notify_change(inode, &newattrs); if (error) inode->i_sb->dq_op->transfer(inode, &newattrs, 1); } else error = notify_change(inode, &newattrs); - iput(inode); - return(error); + +dput_and_out: + dput(dentry); +out: + return error; } -asmlinkage int irix_chown(char *fname, int uid, int gid) +asmlinkage int irix_chown(const char *filename, int uid, int gid) { int retval; + struct dentry *dentry; lock_kernel(); /* Do follow any and all links... */ - retval = chown_common(fname, uid, gid, FOLLOW_LINKS); + dentry = namei(filename); + retval = chown_common(uid, gid, dentry); unlock_kernel(); return retval; } -asmlinkage int irix_lchown(char *fname, int uid, int gid) +asmlinkage int irix_lchown(const char *filename, int uid, int gid) { int retval; + struct dentry *dentry; lock_kernel(); /* Do _not_ follow any links... */ - retval = chown_common(fname, uid, gid, NOFOLLOW_LINKS); + dentry = lnamei(filename); + retval = chown_common(uid, gid, dentry); unlock_kernel(); return retval; } @@ -1722,6 +1761,7 @@ struct irix_statvfs64 { asmlinkage int irix_statvfs64(char *fname, struct irix_statvfs64 *buf) { + struct dentry *dentry; struct inode *inode; struct statfs kbuf; int error, old_fs, i; @@ -1731,20 +1771,22 @@ asmlinkage int irix_statvfs64(char *fname, struct irix_statvfs64 *buf) error = verify_area(VERIFY_WRITE, buf, sizeof(struct irix_statvfs)); if(error) goto out; - error = namei(NAM_FOLLOW_LINK, fname, &inode); - if(error) - goto out; - if(!inode->i_sb->s_op->statfs) { - iput(inode); - error = -ENOSYS; + dentry = namei(fname); + error = PTR_ERR(dentry); + if(IS_ERR(dentry)) goto out; - } + error = -ENOSYS; + inode = dentry->d_inode; + if(!inode->i_sb->s_op->statfs) + goto dput_and_out; old_fs = get_fs(); set_fs(get_ds()); - inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, sizeof(struct statfs)); + error = inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, + sizeof(struct statfs)); set_fs(old_fs); + if (error) + goto dput_and_out; - iput(inode); __put_user(kbuf.f_bsize, &buf->f_bsize); __put_user(kbuf.f_frsize, &buf->f_frsize); __put_user(kbuf.f_blocks, &buf->f_blocks); @@ -1767,6 +1809,8 @@ asmlinkage int irix_statvfs64(char *fname, struct irix_statvfs64 *buf) error = 0; +dput_and_out: + dput(dentry); out: unlock_kernel(); return error; @@ -1774,7 +1818,8 @@ out: asmlinkage int irix_fstatvfs64(int fd, struct irix_statvfs *buf) { - struct inode * inode; + struct dentry *dentry; + struct inode *inode; struct statfs kbuf; struct file *file; int error, old_fs, i; @@ -1790,7 +1835,11 @@ asmlinkage int irix_fstatvfs64(int fd, struct irix_statvfs *buf) error = -EBADF; goto out; } - if (!(inode = file->f_inode)) { + if (!(dentry = file->f_dentry)) { + error = -ENOENT; + goto out; + } + if (!(inode = dentry->d_inode)) { error = -ENOENT; goto out; } @@ -1800,8 +1849,11 @@ asmlinkage int irix_fstatvfs64(int fd, struct irix_statvfs *buf) } old_fs = get_fs(); set_fs(get_ds()); - inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, sizeof(struct statfs)); + error = inode->i_sb->s_op->statfs(inode->i_sb, &kbuf, + sizeof(struct statfs)); set_fs(old_fs); + if (error) + goto out; __put_user(kbuf.f_bsize, &buf->f_bsize); __put_user(kbuf.f_frsize, &buf->f_frsize); @@ -1927,6 +1979,8 @@ out: asmlinkage int irix_ngetdents(unsigned int fd, void * dirent, unsigned int count, int *eob) { struct file *file; + struct dentry *dentry; + struct inode *inode; struct irix_dirent32 *lastdirent; struct irix_dirent32_callback buf; int error; @@ -1936,25 +1990,34 @@ asmlinkage int irix_ngetdents(unsigned int fd, void * dirent, unsigned int count printk("[%s:%d] ngetdents(%d, %p, %d, %p) ", current->comm, current->pid, fd, dirent, count, eob); #endif - if (fd >= NR_OPEN || !(file = current->files->fd[fd])) { - error = -EBADF; + error = -EBADF; + if (fd >= NR_OPEN || !(file = current->files->fd[fd])) goto out; - } - if (!file->f_op || !file->f_op->readdir) { - error = -ENOTDIR; + + dentry = file->f_dentry; + if (!dentry) goto out; - } - if(verify_area(VERIFY_WRITE, dirent, count) || - verify_area(VERIFY_WRITE, eob, sizeof(*eob))) { - error = -EFAULT; + + inode = dentry->d_inode; + if (!inode) goto out; - } + + error = -ENOTDIR; + if (!file->f_op || !file->f_op->readdir) + goto out; + + error = -EFAULT; + if(!access_ok(VERIFY_WRITE, dirent, count) || + !access_ok(VERIFY_WRITE, eob, sizeof(*eob))) + goto out; + __put_user(0, eob); buf.current_dir = (struct irix_dirent32 *) dirent; buf.previous = NULL; buf.count = count; buf.error = 0; - error = file->f_op->readdir(file->f_inode, file, &buf, irix_filldir32); + + error = file->f_op->readdir(inode, file, &buf, irix_filldir32); if (error < 0) goto out; lastdirent = buf.previous; @@ -2027,6 +2090,8 @@ out: asmlinkage int irix_getdents64(int fd, void *dirent, int cnt) { struct file *file; + struct dentry *dentry; + struct inode *inode; struct irix_dirent64 *lastdirent; struct irix_dirent64_callback buf; int error; @@ -2036,28 +2101,35 @@ asmlinkage int irix_getdents64(int fd, void *dirent, int cnt) printk("[%s:%d] getdents64(%d, %p, %d) ", current->comm, current->pid, fd, dirent, cnt); #endif - if (fd >= NR_OPEN || !(file = current->files->fd[fd])) { - error = -EBADF; + error = -EBADF; + if (fd >= NR_OPEN || !(file = current->files->fd[fd])) goto out; - } - if (!file->f_op || !file->f_op->readdir) { - error = -ENOTDIR; + + dentry = file->f_dentry; + if (!dentry) goto out; - } - if(verify_area(VERIFY_WRITE, dirent, cnt)) { - error = -EFAULT; + + inode = dentry->d_inode; + if (!inode) goto out; - } - if(cnt < (sizeof(struct irix_dirent64) + 255)) { - error = -EINVAL; + + error = -ENOTDIR; + if (!file->f_op || !file->f_op->readdir) + goto out; + + error = -EFAULT; + if(!access_ok(VERIFY_WRITE, dirent, cnt)) + goto out; + + error = -EINVAL; + if(cnt < (sizeof(struct irix_dirent64) + 255)) goto out; - } buf.curr = (struct irix_dirent64 *) dirent; buf.previous = NULL; buf.count = cnt; buf.error = 0; - error = file->f_op->readdir(file->f_inode, file, &buf, irix_filldir64); + error = file->f_op->readdir(inode, file, &buf, irix_filldir64); if (error < 0) goto out; lastdirent = buf.previous; @@ -2079,6 +2151,8 @@ out: asmlinkage int irix_ngetdents64(int fd, void *dirent, int cnt, int *eob) { struct file *file; + struct dentry *dentry; + struct inode *inode; struct irix_dirent64 *lastdirent; struct irix_dirent64_callback buf; int error; @@ -2088,30 +2162,37 @@ asmlinkage int irix_ngetdents64(int fd, void *dirent, int cnt, int *eob) printk("[%s:%d] ngetdents64(%d, %p, %d) ", current->comm, current->pid, fd, dirent, cnt); #endif - if (fd >= NR_OPEN || !(file = current->files->fd[fd])) { - error = -EBADF; + error = -EBADF; + if (fd >= NR_OPEN || !(file = current->files->fd[fd])) goto out; - } - if (!file->f_op || !file->f_op->readdir) { - error = -ENOTDIR; + + dentry = file->f_dentry; + if (!dentry) goto out; - } - if(verify_area(VERIFY_WRITE, dirent, cnt) || - verify_area(VERIFY_WRITE, eob, sizeof(*eob))) { - error = -EFAULT; + + inode = dentry->d_inode; + if (!inode) goto out; - } - if(cnt < (sizeof(struct irix_dirent64) + 255)) { - error = -EINVAL; + + error = -ENOTDIR; + if (!file->f_op || !file->f_op->readdir) + goto out; + + error = -EFAULT; + if(!access_ok(VERIFY_WRITE, dirent, cnt) || + !access_ok(VERIFY_WRITE, eob, sizeof(*eob))) + goto out; + + error = -EINVAL; + if(cnt < (sizeof(struct irix_dirent64) + 255)) goto out; - } *eob = 0; buf.curr = (struct irix_dirent64 *) dirent; buf.previous = NULL; buf.count = cnt; buf.error = 0; - error = file->f_op->readdir(file->f_inode, file, &buf, irix_filldir64); + error = file->f_op->readdir(inode, file, &buf, irix_filldir64); if (error < 0) goto out; lastdirent = buf.previous; diff --git a/arch/mips/kernel/sysmips.c b/arch/mips/kernel/sysmips.c index ae99466e4..e73598345 100644 --- a/arch/mips/kernel/sysmips.c +++ b/arch/mips/kernel/sysmips.c @@ -7,7 +7,7 @@ * * Copyright (C) 1995, 1996, 1997 by Ralf Baechle * - * $Id: sysmips.c,v 1.2 1997/06/28 23:26:25 ralf Exp $ + * $Id: sysmips.c,v 1.2 1997/07/01 08:59:08 ralf Exp $ */ #include <linux/errno.h> #include <linux/linkage.h> @@ -57,18 +57,21 @@ sys_sysmips(int cmd, int arg1, int arg2, int arg3) switch(cmd) { case SETNAME: - if (!suser()) { - retval = -EPERM; + retval = -EPERM; + if (!suser()) goto out; - } + name = (char *) arg1; len = strlen_user(name); + + retval = len; if (len < 0) - retval = len; goto out; + + retval = -EINVAL; if (len == 0 || len > __NEW_UTS_LEN) - retval = -EINVAL; goto out; + copy_from_user(system_utsname.nodename, name, len); system_utsname.nodename[len] = '\0'; retval = 0; diff --git a/arch/mips/mm/fault.c b/arch/mips/mm/fault.c index 3ad703b1f..f1ebab648 100644 --- a/arch/mips/mm/fault.c +++ b/arch/mips/mm/fault.c @@ -81,17 +81,6 @@ good_area: */ bad_area: up(&mm->mmap_sem); - /* Did we have an exception handler installed? */ - - fixup = search_exception_table(regs->cp0_epc); - if (fixup) { - long new_epc; - new_epc = fixup_exception(dpf_reg, fixup, regs->cp0_epc); - printk(KERN_DEBUG "Exception at [<%lx>] (%lx)\n", - regs->cp0_epc, new_epc); - regs->cp0_epc = new_epc; - goto out; - } if (user_mode(regs)) { tsk->tss.cp0_badvaddr = address; @@ -111,6 +100,18 @@ bad_area: force_sig(SIGSEGV, tsk); goto out; } + + /* Did we have an exception handler installed? */ + fixup = search_exception_table(regs->cp0_epc); + if (fixup) { + long new_epc; + new_epc = fixup_exception(dpf_reg, fixup, regs->cp0_epc); + printk(KERN_DEBUG "%s: Exception at [<%lx>] (%lx)\n", + tsk->comm, regs->cp0_epc, new_epc); + regs->cp0_epc = new_epc; + goto out; + } + /* * Oops. The kernel tried to access some bad page. We'll have to * terminate things with extreme prejudice. diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index 7a4b6df4b..b62e571d6 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -156,7 +156,6 @@ static inline void zeropage(unsigned long page) { flush_page_to_ram(page); - sync_mem(); __zeropage(page); } diff --git a/arch/sparc/Makefile b/arch/sparc/Makefile index 92c73de32..7a8d46a07 100644 --- a/arch/sparc/Makefile +++ b/arch/sparc/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.28 1997/05/01 01:41:21 davem Exp $ +# $Id: Makefile,v 1.29 1997/07/11 11:05:23 jj Exp $ # sparc/Makefile # # Makefile for the architecture dependent flags and dependencies on the @@ -45,3 +45,6 @@ archdep: check_asm: $(MAKE) -C arch/sparc/kernel check_asm + +tftpboot.img: + $(MAKE) -C arch/sparc/boot tftpboot.img diff --git a/arch/sparc/boot/Makefile b/arch/sparc/boot/Makefile index b9d54e652..af462db3a 100644 --- a/arch/sparc/boot/Makefile +++ b/arch/sparc/boot/Makefile @@ -1,12 +1,23 @@ -# $Id: Makefile,v 1.3 1996/08/04 08:40:58 ecd Exp $ -# Makefile for the Sparc low level /boot module. +# $Id: Makefile,v 1.4 1997/07/11 11:05:18 jj Exp $ +# Makefile for the Sparc boot stuff. # # Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) +# Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + +ROOT_IMG =/usr/src/root.img +ELFTOAOUT =elftoaout all: boot boot: @echo "Nothing special to be done for 'boot' on Linux/SPARC." +tftpboot.img: piggyback + $(ELFTOAOUT) $(TOPDIR)/vmlinux -o tftpboot.img + ./piggyback tftpboot.img $(TOPDIR)/System.map $(ROOT_IMG) + +piggyback: piggyback.c + $(HOSTCC) $(HOSTCFLAGS) -o piggyback piggyback.c + dep: diff --git a/arch/sparc/boot/piggyback.c b/arch/sparc/boot/piggyback.c new file mode 100644 index 000000000..21a32932a --- /dev/null +++ b/arch/sparc/boot/piggyback.c @@ -0,0 +1,100 @@ +/* $Id: piggyback.c,v 1.1 1997/07/18 06:26:14 ralf Exp $ + Simple utility to make a single-image install kernel with initial ramdisk + for Sparc tftpbooting without need to set up nfs. + + Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + + 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 of the License, 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <dirent.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> + +/* Note: run this on an a.out kernel (use elftoaout for it), as PROM looks for a.out image onlly + usage: piggyback vmlinux System.map tail, where tail is gzipped fs of the initial ramdisk */ + +void die(char *str) +{ + perror (str); + exit(1); +} + +int main(int argc,char **argv) +{ + char buffer [1024], *q, *r; + unsigned int i, j, k, start, end, offset; + FILE *map; + struct stat s; + int image, tail; + + if (stat (argv[3], &s) < 0) die (argv[3]); + map = fopen (argv[2], "r"); + if (!map) die(argv[2]); + while (fgets (buffer, 1024, map)) { + if (!strcmp (buffer + 11, "start\n")) + start = strtoul (buffer, NULL, 16); + else if (!strcmp (buffer + 11, "end\n")) + end = strtoul (buffer, NULL, 16); + } + fclose (map); + if ((image = open(argv[1],O_RDWR)) < 0) die(argv[1]); + if (read(image,buffer,512) != 512) die(argv[1]); + if (!memcmp (buffer, "\177ELF", 4)) { + unsigned int *p = (unsigned int *)(buffer + *(unsigned int *)(buffer + 28)); + + i = p[1] + *(unsigned int *)(buffer + 24) - p[2]; + if (lseek(image,i,0) < 0) die("lseek"); + if (read(image,buffer,512) != 512) die(argv[1]); + j = 0; + } else if (*(unsigned int *)buffer == 0x01030107) { + i = j = 32; + } else { + fprintf (stderr, "Not ELF nor a.out. Don't blame me.\n"); + exit(1); + } + k = i; + i += ((*(unsigned short *)(buffer + j + 2))<<2) - 512; + if (lseek(image,i,0) < 0) die("lseek"); + if (read(image,buffer,1024) != 1024) die(argv[1]); + for (q = buffer, r = q + 512; q < r; q += 4) { + if (*q == 'H' && q[1] == 'd' && q[2] == 'r' && q[3] == 'S') + break; + } + if (q == r) { + fprintf (stderr, "Couldn't find headers signature in the kernel.\n"); + exit(1); + } + offset = i + (q - buffer) + 10; + if (lseek(image, offset, 0) < 0) die ("lseek"); + *(unsigned *)buffer = 0; + *(unsigned *)(buffer + 4) = 0x01000000; + *(unsigned *)(buffer + 8) = ((end + 32 + 4095) & ~4095); + *(unsigned *)(buffer + 12) = s.st_size; + if (write(image,buffer+2,14) != 14) die (argv[1]); + if (lseek(image, k - start + ((end + 32 + 4095) & ~4095), 0) < 0) die ("lseek"); + if ((tail = open(argv[3],O_RDONLY)) < 0) die(argv[3]); + while ((i = read (tail,buffer,1024)) > 0) + if (write(image,buffer,i) != i) die (argv[1]); + if (close(image) < 0) die("close"); + if (close(tail) < 0) die("close"); + return 0; +} diff --git a/arch/sparc/config.in b/arch/sparc/config.in index 85566667f..69ed62422 100644 --- a/arch/sparc/config.in +++ b/arch/sparc/config.in @@ -1,4 +1,4 @@ -# $Id: config.in,v 1.35 1997/04/07 06:54:09 davem Exp $ +# $Id: config.in,v 1.36 1997/06/17 03:54:47 davem Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # diff --git a/arch/sparc/defconfig b/arch/sparc/defconfig index a6df2dcc0..9d64ee85d 100644 --- a/arch/sparc/defconfig +++ b/arch/sparc/defconfig @@ -42,7 +42,7 @@ SUN_FB_CGFOURTEEN=y SUN_FB_BWTWO=y SUN_FB_LEO=y TADPOLE_FB_WEITEK=y -SUN_FB_CREATOR=y +#SUN_FB_CREATOR is not set # # Misc Linux/SPARC drivers @@ -64,6 +64,7 @@ CONFIG_SYSVIPC=y CONFIG_SYSCTL=y CONFIG_BINFMT_AOUT=y CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_MISC=y CONFIG_BINFMT_JAVA=m # @@ -103,6 +104,7 @@ CONFIG_IP_MASQUERADE=y # CONFIG_IP_MROUTE is not set CONFIG_IP_ALIAS=m # CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set # # (it is safe to leave these untouched) diff --git a/arch/sparc/kernel/cpu.c b/arch/sparc/kernel/cpu.c index f94ef8b91..aeb5a46c8 100644 --- a/arch/sparc/kernel/cpu.c +++ b/arch/sparc/kernel/cpu.c @@ -76,6 +76,7 @@ struct cpu_iu_info linux_sparc_chips[] = { { 0, 0, "Fujitsu MB86900/1A or LSI L64831 SparcKIT-40"}, /* borned STP1012PGA */ { 0, 4, "Fujitsu MB86904"}, + { 0, 5, "Fujitsu TurboSparc MB86907"}, /* SparcStation2, SparcServer 490 & 690 */ { 1, 0, "LSI Logic Corporation - L64811"}, /* SparcStation2 */ diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c index 1adc9e817..95eed6287 100644 --- a/arch/sparc/kernel/process.c +++ b/arch/sparc/kernel/process.c @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.98 1997/05/14 20:44:54 davem Exp $ +/* $Id: process.c,v 1.99 1997/07/17 02:20:13 davem Exp $ * linux/arch/sparc/kernel/process.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -594,8 +594,9 @@ asmlinkage int sparc_execve(struct pt_regs *regs) base = 1; lock_kernel(); - error = getname((char *) regs->u_regs[base + UREG_I0], &filename); - if(error) + filename = getname((char *)regs->u_regs[base + UREG_I0]); + error = PTR_ERR(filename); + if(IS_ERR(filename)) goto out; error = do_execve(filename, (char **) regs->u_regs[base + UREG_I1], (char **) regs->u_regs[base + UREG_I2], regs); diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c index 38896ab22..cf4146c4a 100644 --- a/arch/sparc/kernel/sparc_ksyms.c +++ b/arch/sparc/kernel/sparc_ksyms.c @@ -1,4 +1,4 @@ -/* $Id: sparc_ksyms.c,v 1.59 1997/05/08 17:45:20 davem Exp $ +/* $Id: sparc_ksyms.c,v 1.60 1997/06/17 13:25:13 jj Exp $ * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -59,6 +59,7 @@ extern void *__memscan_zero(void *, size_t); extern void *__memscan_generic(void *, int, size_t); extern int __memcmp(const void *, const void *, __kernel_size_t); extern int __strncmp(const char *, const char *, __kernel_size_t); +extern char saved_command_line[]; extern void bcopy (const char *, char *, int); extern int __ashrdi3(int, int); @@ -200,7 +201,7 @@ EXPORT_SYMBOL(prom_getproplen); EXPORT_SYMBOL(prom_getproperty); EXPORT_SYMBOL(prom_node_has_property); EXPORT_SYMBOL(prom_setprop); -EXPORT_SYMBOL(prom_getbootargs); +EXPORT_SYMBOL(saved_command_line); EXPORT_SYMBOL(prom_apply_obio_ranges); EXPORT_SYMBOL(prom_getname); EXPORT_SYMBOL(prom_feval); diff --git a/arch/sparc/kernel/sys_sunos.c b/arch/sparc/kernel/sys_sunos.c index 3fafe7f9c..5d0ea0840 100644 --- a/arch/sparc/kernel/sys_sunos.c +++ b/arch/sparc/kernel/sys_sunos.c @@ -1,4 +1,4 @@ -/* $Id: sys_sunos.c,v 1.79 1997/04/23 23:01:15 ecd Exp $ +/* $Id: sys_sunos.c,v 1.80 1997/07/17 02:20:22 davem Exp $ * sys_sunos.c: SunOS specific syscall compatibility support. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -90,10 +90,12 @@ asmlinkage unsigned long sunos_mmap(unsigned long addr, unsigned long len, * of /dev/zero, transform it into an anonymous mapping. * SunOS is so stupid some times... hmph! */ - if(MAJOR(file->f_inode->i_rdev) == MEM_MAJOR && - MINOR(file->f_inode->i_rdev) == 5) { - flags |= MAP_ANONYMOUS; - file = 0; + if(file->f_dentry && file->f_dentry->d_inode) { + if(MAJOR(file->f_dentry->d_inode->i_rdev) == MEM_MAJOR && + MINOR(file->f_dentry->d_inode->i_rdev) == 5) { + flags |= MAP_ANONYMOUS; + file = 0; + } } if(!(flags & MAP_FIXED)) addr = 0; @@ -428,16 +430,32 @@ static int sunos_filldir(void * __buf, const char * name, int namlen, asmlinkage int sunos_getdents(unsigned int fd, void * dirent, int cnt) { struct file * file; + struct dentry * dentry; + struct inode * inode; struct sunos_dirent * lastdirent; struct sunos_dirent_callback buf; int error = -EBADF; lock_kernel(); - if (fd >= SUNOS_NR_OPEN || !(file = current->files->fd[fd])) + if(fd >= SUNOS_NR_OPEN) + goto out; + + file = current->files->fd[fd]; + if(!file) + goto out; + + dentry = file->f_dentry; + if(!dentry) goto out; + + inode = dentry->d_inode; + if(!inode) + goto out; + error = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) goto out; + error = -EINVAL; if(cnt < (sizeof(struct sunos_dirent) + 255)) goto out; @@ -446,13 +464,12 @@ asmlinkage int sunos_getdents(unsigned int fd, void * dirent, int cnt) buf.previous = NULL; buf.count = cnt; buf.error = 0; - error = file->f_op->readdir(file->f_inode, file, &buf, sunos_filldir); + error = file->f_op->readdir(inode, file, &buf, sunos_filldir); if (error < 0) goto out; lastdirent = buf.previous; - if (!lastdirent) { - error = buf.error; - } else { + error = buf.error; + if (lastdirent) { put_user(file->f_pos, &lastdirent->d_off); error = cnt - buf.count; } @@ -503,16 +520,32 @@ static int sunos_filldirentry(void * __buf, const char * name, int namlen, asmlinkage int sunos_getdirentries(unsigned int fd, void * dirent, int cnt, unsigned int *basep) { struct file * file; + struct dentry * dentry; + struct inode * inode; struct sunos_direntry * lastdirent; struct sunos_direntry_callback buf; int error = -EBADF; lock_kernel(); - if (fd >= SUNOS_NR_OPEN || !(file = current->files->fd[fd])) + if(fd >= SUNOS_NR_OPEN) goto out; + + file = current->files->fd[fd]; + if(!file) + goto out; + + dentry = file->f_dentry; + if(!dentry) + goto out; + + inode = dentry->d_inode; + if(!inode) + goto out; + error = -ENOTDIR; if (!file->f_op || !file->f_op->readdir) goto out; + error = -EINVAL; if(cnt < (sizeof(struct sunos_direntry) + 255)) goto out; @@ -521,13 +554,12 @@ asmlinkage int sunos_getdirentries(unsigned int fd, void * dirent, int cnt, unsi buf.previous = NULL; buf.count = cnt; buf.error = 0; - error = file->f_op->readdir(file->f_inode, file, &buf, sunos_filldirentry); + error = file->f_op->readdir(inode, file, &buf, sunos_filldirentry); if (error < 0) goto out; lastdirent = buf.previous; - if (!lastdirent) { - error = buf.error; - } else { + error = buf.error; + if (lastdirent) { put_user(file->f_pos, basep); error = cnt - buf.count; } @@ -725,12 +757,13 @@ sunos_nfs_get_server_fd (int fd, struct sockaddr_in *addr) int try_port; int ret; struct socket *socket; + struct dentry *dentry; struct inode *inode; struct file *file; file = current->files->fd [fd]; - inode = file->f_inode; - if (!inode || !inode->i_sock) + dentry = file->f_dentry; + if(!dentry || !(inode = dentry->d_inode)) return 0; socket = &inode->u.socket_i; diff --git a/arch/sparc/mm/Makefile b/arch/sparc/mm/Makefile index f7b9b367c..84cd1d2e2 100644 --- a/arch/sparc/mm/Makefile +++ b/arch/sparc/mm/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.25 1997/05/03 05:09:11 davem Exp $ +# $Id: Makefile,v 1.26 1997/06/24 15:48:06 jj Exp $ # Makefile for the linux Sparc-specific parts of the memory manager. # # Note! Dependencies are done automagically by 'make dep', which also @@ -9,7 +9,8 @@ O_TARGET := mm.o O_OBJS := fault.o init.o sun4c.o srmmu.o hypersparc.o viking.o \ - tsunami.o loadmmu.o generic.o asyncd.o extable.o + tsunami.o loadmmu.o generic.o asyncd.o extable.o \ + turbosparc.o include $(TOPDIR)/Rules.make @@ -18,6 +19,9 @@ ifdef SMP hypersparc.o: hypersparc.S $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o hypersparc.o hypersparc.S +turbosparc.o: turbosparc.S + $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o turbosparc.o turbosparc.S + viking.o: viking.S $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o viking.o viking.S @@ -29,6 +33,9 @@ else hypersparc.o: hypersparc.S $(CC) -D__ASSEMBLY__ -ansi -c -o hypersparc.o hypersparc.S +turbosparc.o: turbosparc.S + $(CC) -D__ASSEMBLY__ -ansi -c -o turbosparc.o turbosparc.S + viking.o: viking.S $(CC) -D__ASSEMBLY__ -ansi -c -o viking.o viking.S diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c index 7420b98cb..3b9970551 100644 --- a/arch/sparc/mm/srmmu.c +++ b/arch/sparc/mm/srmmu.c @@ -1,9 +1,10 @@ -/* $Id: srmmu.c,v 1.147 1997/05/20 07:58:42 jj Exp $ +/* $Id: srmmu.c,v 1.148 1997/06/24 15:48:02 jj Exp $ * srmmu.c: SRMMU specific routines for memory management. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1995 Peter A. Zaitcev (zaitcev@ithil.mcst.ru) * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ #include <linux/config.h> @@ -36,6 +37,7 @@ #include <asm/ross.h> #include <asm/tsunami.h> #include <asm/swift.h> +#include <asm/turbosparc.h> enum mbus_module srmmu_modtype; unsigned int hwbug_bitmask; @@ -2595,6 +2597,78 @@ __initfunc(static void init_swift(void)) poke_srmmu = poke_swift; } +/* turbosparc.S */ +extern void turbosparc_flush_cache_all(); +extern void turbosparc_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr); + +static void poke_turbosparc(void) +{ + unsigned long mreg = srmmu_get_mmureg(); + unsigned long ccreg; + + /* Clear any crap from the cache or else... */ + turbosparc_flush_cache_all(); + mreg &= ~(TURBOSPARC_ICENABLE | TURBOSPARC_DCENABLE); /* Temporarily disable I & D caches */ + mreg &= ~(TURBOSPARC_PCENABLE); /* Don't check parity */ + srmmu_set_mmureg(mreg); + + ccreg = turbosparc_get_ccreg(); + +#ifdef TURBOSPARC_WRITEBACK + ccreg |= (TURBOSPARC_SNENABLE); /* Do DVMA snooping in Dcache */ + ccreg &= ~(TURBOSPARC_uS2 | TURBOSPARC_WTENABLE); + /* Write-back D-cache, emulate VLSI + * abortion number three, not number one */ +#else + /* For now let's play safe, optimize later */ + ccreg |= (TURBOSPARC_SNENABLE | TURBOSPARC_WTENABLE); + /* Do DVMA snooping in Dcache, Write-thru D-cache */ + ccreg &= ~(TURBOSPARC_uS2); + /* Emulate VLSI abortion number three, not number one */ +#endif + switch (ccreg & 7) { + case 0: /* No SE cache */ + case 7: /* Test mode */ + break; + default: + ccreg |= (TURBOSPARC_SCENABLE); + } + turbosparc_set_ccreg (ccreg); + + mreg |= (TURBOSPARC_ICENABLE | TURBOSPARC_DCENABLE); /* I & D caches on */ + mreg |= (TURBOSPARC_ICSNOOP); /* Icache snooping on */ + srmmu_set_mmureg(mreg); +} + +__initfunc(static void init_turbosparc(void)) +{ + srmmu_name = "Fujitsu TurboSparc"; + srmmu_modtype = TurboSparc; + + flush_cache_all = turbosparc_flush_cache_all; + flush_cache_mm = hypersparc_flush_cache_mm; + flush_cache_page = hypersparc_flush_cache_page; + flush_cache_range = hypersparc_flush_cache_range; + + flush_tlb_all = hypersparc_flush_tlb_all; + flush_tlb_mm = hypersparc_flush_tlb_mm; + flush_tlb_page = hypersparc_flush_tlb_page; + flush_tlb_range = hypersparc_flush_tlb_range; + +#ifdef TURBOSPARC_WRITEBACK + flush_page_to_ram = hypersparc_flush_page_to_ram; + flush_chunk = hypersparc_flush_chunk; +#else + flush_page_to_ram = swift_flush_page_to_ram; + flush_chunk = swift_flush_chunk; +#endif + + flush_sig_insns = turbosparc_flush_sig_insns; + flush_page_for_dma = hypersparc_flush_page_for_dma; + + poke_srmmu = poke_turbosparc; +} + static void poke_tsunami(void) { unsigned long mreg = srmmu_get_mmureg(); @@ -2785,9 +2859,33 @@ __initfunc(static void get_srmmu_type(void)) }; return; } + + /* Now Fujitsu TurboSparc. It might happen that it is + in Swift emulation mode, so we will check later... */ + if (psr_typ == 0 && psr_vers == 5) { + init_turbosparc(); + return; + } /* Next check for Fujitsu Swift. */ if(psr_typ == 0 && psr_vers == 4) { + int cpunode; + char node_str[128]; + + /* Look if it is not a TurboSparc emulating Swift... */ + cpunode = prom_getchild(prom_root_node); + while((cpunode = prom_getsibling(cpunode)) != 0) { + prom_getstring(cpunode, "device_type", node_str, sizeof(node_str)); + if(!strcmp(node_str, "cpu")) { + if (!prom_getintdefault(cpunode, "psr-implementation", 1) && + prom_getintdefault(cpunode, "psr-version", 1) == 5) { + init_turbosparc(); + return; + } + break; + } + } + init_swift(); return; } diff --git a/arch/sparc/mm/turbosparc.S b/arch/sparc/mm/turbosparc.S new file mode 100644 index 000000000..5660d4f84 --- /dev/null +++ b/arch/sparc/mm/turbosparc.S @@ -0,0 +1,46 @@ +/* $Id: turbosparc.S,v 1.1 1997/07/18 06:26:22 ralf Exp $ + * turbosparc.S: High speed Hypersparc mmu/cache operations. + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#include <asm/ptrace.h> +#include <asm/psr.h> +#include <asm/asi.h> +#include <asm/page.h> +#include <asm/pgtsrmmu.h> + +#define WINDOW_FLUSH(tmp1, tmp2) \ + mov 0, tmp1; \ +98: ld [%g6 + AOFF_task_tss + AOFF_thread_uwinmask], tmp2; \ + orcc %g0, tmp2, %g0; \ + add tmp1, 1, tmp1; \ + bne 98b; \ + save %sp, -64, %sp; \ +99: subcc tmp1, 1, tmp1; \ + bne 99b; \ + restore %g0, %g0, %g0; + + .text + .align 4 + + .globl turbosparc_flush_cache_all + .globl turbosparc_flush_sig_insns + +turbosparc_flush_cache_all: + WINDOW_FLUSH(%g4, %g5) + sethi %hi(vac_cache_size), %g4 + ld [%g4 + %lo(vac_cache_size)], %g5 + sethi %hi(vac_line_size), %g1 + ld [%g1 + %lo(vac_line_size)], %g2 +1: + subcc %g5, %g2, %g5 + bne 1b + sta %g0, [%g5] ASI_M_DATAC_TAG + retl + sta %g0, [%g0] ASI_M_IC_FLCLEAR + +turbosparc_flush_sig_insns: + retl + nop diff --git a/arch/sparc/prom/bootstr.c b/arch/sparc/prom/bootstr.c index 1722d9fdc..e7bd9b06d 100644 --- a/arch/sparc/prom/bootstr.c +++ b/arch/sparc/prom/bootstr.c @@ -1,4 +1,4 @@ -/* $Id: bootstr.c,v 1.12 1996/12/18 06:46:54 tridge Exp $ +/* $Id: bootstr.c,v 1.14 1997/06/19 16:28:49 jj Exp $ * bootstr.c: Boot string/argument acquisition from the PROM. * * Copyright(C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -7,13 +7,14 @@ #include <linux/config.h> #include <linux/string.h> #include <asm/oplib.h> +#include <linux/init.h> #define BARG_LEN 256 -static char barg_buf[BARG_LEN]; -static char fetched = 0; +static char barg_buf[BARG_LEN] __initdata = { 0 }; +static char fetched __initdata = 0; -char * -prom_getbootargs(void) +__initfunc(char * +prom_getbootargs(void)) { int iter; char *cp, *arg; diff --git a/arch/sparc/prom/tree.c b/arch/sparc/prom/tree.c index 251b2ee6a..295ce5355 100644 --- a/arch/sparc/prom/tree.c +++ b/arch/sparc/prom/tree.c @@ -1,4 +1,4 @@ -/* $Id: tree.c,v 1.18 1997/05/14 20:45:03 davem Exp $ +/* $Id: tree.c,v 1.19 1997/06/27 14:52:54 jj Exp $ * tree.c: Basic device tree traversal/scanning for the Linux * prom library. * @@ -231,7 +231,7 @@ int prom_getname (int node, char *buffer, int len) /* Return the first property type for node 'node'. */ -char * prom_firstprop(int node) +char * prom_firstprop(int node, char *buffer) { unsigned long flags; char *ret; @@ -248,7 +248,7 @@ char * prom_firstprop(int node) * at node 'node' . Returns NULL string if no more * property types for this node. */ -char * prom_nextprop(int node, char *oprop) +char * prom_nextprop(int node, char *oprop, char *buffer) { char *ret; unsigned long flags; @@ -293,7 +293,7 @@ int prom_node_has_property(int node, char *prop) char *current_property = ""; do { - current_property = prom_nextprop(node, current_property); + current_property = prom_nextprop(node, current_property, NULL); if(!strcmp(current_property, prop)) return 1; } while (*current_property); diff --git a/arch/sparc64/Makefile b/arch/sparc64/Makefile index a70f9ebf8..b8cf06878 100644 --- a/arch/sparc64/Makefile +++ b/arch/sparc64/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.16 1997/05/04 07:21:08 davem Exp $ +# $Id: Makefile,v 1.20 1997/07/11 11:05:29 jj Exp $ # sparc64/Makefile # # Makefile for the architecture dependent flags and dependencies on the @@ -24,8 +24,8 @@ ELF2AOUT64 = elf2aout64 # debugging of the kernel to get the proper debugging information. #CFLAGS := $(CFLAGS) -g -pipe -fcall-used-g5 -fcall-used-g7 -CFLAGS := $(CFLAGS) -pipe \ - -fcall-used-g5 -fcall-used-g7 -Wno-sign-compare +CFLAGS := $(CFLAGS) -pipe -mno-fpu -mtune=ultrasparc -mmedlow \ + -ffixed-g4 -fcall-used-g5 -fcall-used-g7 -Wno-sign-compare LINKFLAGS = -T arch/sparc64/vmlinux.lds @@ -49,3 +49,6 @@ archdep: check_asm: $(MAKE) -C arch/sparc64/kernel check_asm + +tftpboot.img: + $(MAKE) -C arch/sparc64/boot tftpboot.img diff --git a/arch/sparc64/boot/Makefile b/arch/sparc64/boot/Makefile new file mode 100644 index 000000000..ed3fc6cdb --- /dev/null +++ b/arch/sparc64/boot/Makefile @@ -0,0 +1,23 @@ +# $Id: Makefile,v 1.1 1997/07/18 06:26:30 ralf Exp $ +# Makefile for the Sparc64 boot stuff. +# +# Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) +# Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + +ROOT_IMG =/usr/src/root.img +ELFTOAOUT =elftoaout + +all: boot + +boot: + @echo "Nothing special to be done for 'boot' on Linux/UltraSPARC." + +tftpboot.img: piggyback + $(ELFTOAOUT) $(TOPDIR)/vmlinux -o tftpboot.img + ./piggyback tftpboot.img $(TOPDIR)/System.map $(ROOT_IMG) + +piggyback: piggyback.c + $(HOSTCC) $(HOSTCFLAGS) -o piggyback piggyback.c + +dep: + diff --git a/arch/sparc64/boot/piggyback.c b/arch/sparc64/boot/piggyback.c new file mode 100644 index 000000000..869b3492c --- /dev/null +++ b/arch/sparc64/boot/piggyback.c @@ -0,0 +1,109 @@ +/* $Id: piggyback.c,v 1.1 1997/07/18 06:26:30 ralf Exp $ + Simple utility to make a single-image install kernel with initial ramdisk + for Sparc64 tftpbooting without need to set up nfs. + + Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + + 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 of the License, 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <dirent.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> + +/* Note: run this on an a.out kernel (use elftoaout for it), as PROM looks for a.out image onlly + usage: piggyback vmlinux System.map tail, where tail is gzipped fs of the initial ramdisk */ + +void die(char *str) +{ + perror (str); + exit(1); +} + +int main(int argc,char **argv) +{ + char buffer [1024], *q, *r; + unsigned int i, j, k, start, end, offset; + FILE *map; + struct stat s; + int image, tail; + + if (stat (argv[3], &s) < 0) die (argv[3]); + map = fopen (argv[2], "r"); + if (!map) die(argv[2]); + while (fgets (buffer, 1024, map)) { + if (!strcmp (buffer + 19, "start\n")) + start = strtoul (buffer + 8, NULL, 16); + else if (!strcmp (buffer + 19, "end\n")) + end = strtoul (buffer + 8, NULL, 16); + } + fclose (map); + if ((image = open(argv[1],O_RDWR)) < 0) die(argv[1]); + if (read(image,buffer,512) != 512) die(argv[1]); + if (!memcmp (buffer, "\177ELF", 4)) { + unsigned int *p = (unsigned int *)(buffer + *(unsigned int *)(buffer + 28)); + + i = p[1] + *(unsigned int *)(buffer + 24) - p[2]; + if (lseek(image,i,0) < 0) die("lseek"); + if (read(image,buffer,512) != 512) die(argv[1]); + j = 0; + } else if (*(unsigned int *)buffer == 0x01030107) { + i = j = 32; + } else { + fprintf (stderr, "Not ELF nor a.out. Don't blame me.\n"); + exit(1); + } + k = i; + if (j == 32 && buffer[40] == 'H' && buffer[41] == 'd' && buffer[42] == 'r' && buffer[43] == 'S') { + offset = 40 + 10; + } else { + i += ((*(unsigned short *)(buffer + j + 2))<<2) - 512; + if (lseek(image,i,0) < 0) die("lseek"); + if (read(image,buffer,1024) != 1024) die(argv[1]); + for (q = buffer, r = q + 512; q < r; q += 4) { + if (*q == 'H' && q[1] == 'd' && q[2] == 'r' && q[3] == 'S') + break; + } + if (q == r) { + fprintf (stderr, "Couldn't find headers signature in the kernel.\n"); + exit(1); + } + offset = i + (q - buffer) + 10; + } + if (lseek(image, offset, 0) < 0) die ("lseek"); + *(unsigned *)buffer = 0; + *(unsigned *)(buffer + 4) = 0x01000000; + *(unsigned *)(buffer + 8) = ((end + 32 + 8191) & ~8191); + *(unsigned *)(buffer + 12) = s.st_size; + if (write(image,buffer+2,14) != 14) die (argv[1]); + if (lseek(image, 4, 0) < 0) die ("lseek"); + *(unsigned *)buffer = ((end + 32 + 8191) & ~8191) - (start & ~0x3fffffUL) + s.st_size; + *(unsigned *)(buffer + 4) = 0; + *(unsigned *)(buffer + 8) = 0; + if (write(image,buffer,12) != 12) die (argv[1]); + if (lseek(image, k - start + ((end + 32 + 8191) & ~8191), 0) < 0) die ("lseek"); + if ((tail = open(argv[3],O_RDONLY)) < 0) die(argv[3]); + while ((i = read (tail,buffer,1024)) > 0) + if (write(image,buffer,i) != i) die (argv[1]); + if (close(image) < 0) die("close"); + if (close(tail) < 0) die("close"); + return 0; +} diff --git a/arch/sparc64/config.in b/arch/sparc64/config.in index 6354edded..fcbac5c1a 100644 --- a/arch/sparc64/config.in +++ b/arch/sparc64/config.in @@ -1,4 +1,4 @@ -# $Id: config.in,v 1.6 1997/04/17 20:35:42 jj Exp $ +# $Id: config.in,v 1.9 1997/07/04 11:33:05 davem Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # @@ -51,10 +51,10 @@ bool 'Networking support' CONFIG_NET bool 'System V IPC' CONFIG_SYSVIPC bool 'Sysctl support' CONFIG_SYSCTL bool 'Kernel support for Linux/Sparc 32bit binary compatibility' CONFIG_SPARC32_COMPAT -tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT tristate 'Kernel support for 64-bit ELF binaries' CONFIG_BINFMT_ELF if [ "$CONFIG_SPARC32_COMPAT" != "n" ]; then tristate 'Kernel support for 32-bit ELF binaries' CONFIG_BINFMT_ELF32 + bool 'Kernel support for 32-bit (ie. SunOS) a.out binaries' CONFIG_BINFMT_AOUT32 fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA @@ -157,4 +157,5 @@ bool 'Kernel profiling support' CONFIG_PROFILE if [ "$CONFIG_PROFILE" = "y" ]; then int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 fi +bool 'ECache flush trap support at ta 0x72' CONFIG_EC_FLUSH_TRAP endmenu diff --git a/arch/sparc64/defconfig b/arch/sparc64/defconfig index fbc4a5073..92a003853 100644 --- a/arch/sparc64/defconfig +++ b/arch/sparc64/defconfig @@ -5,12 +5,14 @@ # # Code maturity level options # -# CONFIG_EXPERIMENTAL is not set +CONFIG_EXPERIMENTAL=y # # Loadable module support # -# CONFIG_MODULES is not set +CONFIG_MODULES=y +# CONFIG_MODVERSIONS is not set +# CONFIG_KERNELD is not set # # General setup @@ -45,24 +47,31 @@ SUN_FB_CREATOR=y # # Misc Linux/SPARC drivers # -# CONFIG_SUN_OPENPROMIO is not set -# CONFIG_SUN_MOSTEK_RTC is not set -# CONFIG_SUN_OPENPROMFS is not set +CONFIG_SUN_OPENPROMIO=m +CONFIG_SUN_MOSTEK_RTC=y +# CONFIG_SUN_BPP is not set +# CONFIG_SUN_VIDEOPIX is not set +CONFIG_SUN_OPENPROMFS=m CONFIG_NET=y CONFIG_SYSVIPC=y CONFIG_SYSCTL=y CONFIG_SPARC32_COMPAT=y -CONFIG_BINFMT_AOUT=y CONFIG_BINFMT_ELF=y CONFIG_BINFMT_ELF32=y +CONFIG_BINFMT_AOUT32=y +CONFIG_BINFMT_JAVA=m +CONFIG_BINFMT_MISC=m # # Floppy, IDE, and other block devices # # CONFIG_BLK_DEV_FD is not set -# CONFIG_BLK_DEV_MD is not set -# CONFIG_BLK_DEV_RAM is not set -# CONFIG_BLK_DEV_LOOP is not set +CONFIG_BLK_DEV_MD=y +CONFIG_MD_LINEAR=m +CONFIG_MD_STRIPED=m +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_BLK_DEV_LOOP=m # # Networking options @@ -75,6 +84,7 @@ CONFIG_INET=y # CONFIG_IP_ACCT is not set # CONFIG_IP_ROUTER is not set # CONFIG_NET_IPIP is not set +# CONFIG_SYN_COOKIES is not set # # (it is safe to leave these untouched) @@ -84,13 +94,22 @@ CONFIG_INET=y CONFIG_PATH_MTU_DISCOVERY=y CONFIG_IP_NOSR=y CONFIG_SKB_LARGE=y +CONFIG_IPV6=m # # # -# CONFIG_IPX is not set -# CONFIG_ATALK is not set +CONFIG_IPX=m +# CONFIG_IPX_INTERN is not set +# CONFIG_IPX_PPROP_ROUTING is not set +CONFIG_ATALK=m +# CONFIG_IPDDP is not set # CONFIG_AX25 is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_BRIDGE is not set +# CONFIG_LLC is not set +# CONFIG_WAN_ROUTER is not set # # SCSI support @@ -101,10 +120,10 @@ CONFIG_SCSI=y # SCSI support type (disk, tape, CDrom) # CONFIG_BLK_DEV_SD=y -# CONFIG_CHR_DEV_ST is not set +CONFIG_CHR_DEV_ST=y CONFIG_BLK_DEV_SR=y CONFIG_BLK_DEV_SR_VENDOR=y -# CONFIG_CHR_DEV_SG is not set +CONFIG_CHR_DEV_SG=m # # Some SCSI devices (e.g. CD jukebox) support multiple LUNs @@ -116,46 +135,59 @@ CONFIG_SCSI_CONSTANTS=y # SCSI low-level drivers # CONFIG_SCSI_SUNESP=y -# CONFIG_SCSI_QLOGICPTI is not set +CONFIG_SCSI_QLOGICPTI=m # # Network device support # CONFIG_NETDEVICES=y -CONFIG_DUMMY=y -# CONFIG_PPP is not set -# CONFIG_SLIP is not set +CONFIG_DUMMY=m +CONFIG_PPP=m + +# +# CCP compressors for PPP are only built as modules. +# +CONFIG_SLIP=m +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_SMART=y +# CONFIG_SLIP_MODE_SLIP6 is not set CONFIG_SUNLANCE=y -# CONFIG_HAPPYMEAL is not set -# CONFIG_SUNQE is not set -# CONFIG_MYRI_SBUS is not set +CONFIG_HAPPYMEAL=y +CONFIG_SUNQE=m +CONFIG_MYRI_SBUS=m # # Filesystems # # CONFIG_QUOTA is not set -# CONFIG_MINIX_FS is not set +# CONFIG_DCACHE_PRELOAD is not set +# CONFIG_OMIRR is not set +# CONFIG_TRANS_NAMES is not set +CONFIG_MINIX_FS=m CONFIG_EXT2_FS=y -# CONFIG_FAT_FS is not set -# CONFIG_MSDOS_FS is not set -# CONFIG_VFAT_FS is not set +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=m # CONFIG_UMSDOS_FS is not set CONFIG_PROC_FS=y CONFIG_NFS_FS=y CONFIG_ROOT_NFS=y CONFIG_RNFS_BOOTP=y # CONFIG_RNFS_RARP is not set -# CONFIG_NFSD is not set +CONFIG_NFSD=m CONFIG_SUNRPC=y CONFIG_LOCKD=y -# CONFIG_SMB_FS is not set +CONFIG_SMB_FS=m +CONFIG_SMB_WIN95=y +CONFIG_NCP_FS=m CONFIG_ISO9660_FS=y -# CONFIG_HPFS_FS is not set -# CONFIG_SYSV_FS is not set -# CONFIG_AFFS_FS is not set -# CONFIG_ROMFS_FS is not set -# CONFIG_AUTOFS_FS is not set -CONFIG_UFS_FS=y +CONFIG_HPFS_FS=m +CONFIG_SYSV_FS=m +CONFIG_AFFS_FS=m +CONFIG_ROMFS_FS=m +CONFIG_AUTOFS_FS=m +CONFIG_AMIGA_PARTITION=y +CONFIG_UFS_FS=m CONFIG_BSD_DISKLABEL=y CONFIG_SMD_DISKLABEL=y @@ -163,3 +195,4 @@ CONFIG_SMD_DISKLABEL=y # Kernel hacking # # CONFIG_PROFILE is not set +# CONFIG_EC_FLUSH_TRAP is not set diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile index 199360a5f..9e9013735 100644 --- a/arch/sparc64/kernel/Makefile +++ b/arch/sparc64/kernel/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.22 1997/05/27 19:30:17 jj Exp $ +# $Id: Makefile,v 1.28 1997/07/05 09:52:20 davem Exp $ # Makefile for the linux kernel. # # Note! Dependencies are done automagically by 'make dep', which also @@ -16,20 +16,26 @@ all: kernel.o head.o init_task.o O_TARGET := kernel.o -O_OBJS := etrap.o rtrap.o hack.o process.o setup.o cpu.o idprom.o \ - systbls.o traps.o entry.o devices.o auxio.o ioport.o \ - irq.o ptrace.o time.o sys_sparc.o signal.o winfixup.o +O_OBJS := process.o setup.o cpu.o idprom.o \ + systbls.o traps.o devices.o auxio.o ioport.o \ + irq.o ptrace.o time.o sys_sparc.o signal.o \ + unaligned.o sys_sunos32.o sunos_ioctl32.o OX_OBJS := sparc64_ksyms.o ifdef CONFIG_SPARC32_COMPAT - O_OBJS += sys_sparc32.o signal32.o ioctl32.o + O_OBJS += sys32.o sys_sparc32.o signal32.o ioctl32.o endif ifdef CONFIG_BINFMT_ELF32 O_OBJS += binfmt_elf32.o endif -head.o: head.S ttable.S itlb_miss.S dtlb_miss.S dtlb_prot.S +ifdef CONFIG_BINFMT_AOUT32 + O_OBJS += binfmt_aout32.o +endif + +head.o: head.S ttable.S itlb_miss.S dtlb_miss.S dtlb_prot.S etrap.S rtrap.S \ + winfixup.S entry.S $(CC) -D__ASSEMBLY__ -ansi -c $*.S -o $*.o # diff --git a/arch/sparc64/kernel/binfmt_aout32.c b/arch/sparc64/kernel/binfmt_aout32.c new file mode 100644 index 000000000..215aaf06f --- /dev/null +++ b/arch/sparc64/kernel/binfmt_aout32.c @@ -0,0 +1,482 @@ +/* + * linux/fs/binfmt_aout.c + * + * Copyright (C) 1991, 1992, 1996 Linus Torvalds + * + * Hacked a bit by DaveM to make it work with 32-bit SunOS + * binaries on the sparc64 port. + */ + +#include <linux/module.h> + +#include <linux/fs.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/mman.h> +#include <linux/a.out.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/string.h> +#include <linux/stat.h> +#include <linux/fcntl.h> +#include <linux/ptrace.h> +#include <linux/user.h> +#include <linux/malloc.h> +#include <linux/binfmts.h> +#include <linux/personality.h> +#include <linux/init.h> + +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> + +static int load_aout32_binary(struct linux_binprm *, struct pt_regs * regs); +static int load_aout32_library(int fd); +static int aout32_core_dump(long signr, struct pt_regs * regs); + +extern void dump_thread(struct pt_regs *, struct user *); + +static struct linux_binfmt aout32_format = { + NULL, NULL, load_aout32_binary, load_aout32_library, aout32_core_dump +}; + +static void set_brk(unsigned long start, unsigned long end) +{ + start = PAGE_ALIGN(start); + end = PAGE_ALIGN(end); + if (end <= start) + return; + do_mmap(NULL, start, end - start, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE, 0); +} + +/* + * These are the only things you should do on a core-file: use only these + * macros to write out all the necessary info. + */ +#define DUMP_WRITE(addr,nr) \ +while (file.f_op->write(inode,&file,(char *)(addr),(nr)) != (nr)) goto close_coredump + +#define DUMP_SEEK(offset) \ +if (file.f_op->llseek) { \ + if (file.f_op->llseek(inode,&file,(offset),0) != (offset)) \ + goto close_coredump; \ +} else file.f_pos = (offset) + +/* + * Routine writes a core dump image in the current directory. + * Currently only a stub-function. + * + * Note that setuid/setgid files won't make a core-dump if the uid/gid + * changed due to the set[u|g]id. It's enforced by the "current->dumpable" + * field, which also makes sure the core-dumps won't be recursive if the + * dumping of the process results in another error.. + */ + +static inline int +do_aout32_core_dump(long signr, struct pt_regs * regs) +{ + struct dentry * dentry = NULL; + struct inode * inode = NULL; + struct file file; + unsigned short fs; + int has_dumped = 0; + char corefile[6+sizeof(current->comm)]; + unsigned long dump_start, dump_size; + struct user dump; +# define START_DATA(u) (u.u_tsize) +# define START_STACK(u) ((regs->u_regs[UREG_FP]) & ~(PAGE_SIZE - 1)) + + if (!current->dumpable || current->mm->count != 1) + return 0; + current->dumpable = 0; + +/* See if we have enough room to write the upage. */ + if (current->rlim[RLIMIT_CORE].rlim_cur < PAGE_SIZE) + return 0; + fs = get_fs(); + set_fs(KERNEL_DS); + memcpy(corefile,"core.",5); +#if 0 + memcpy(corefile+5,current->comm,sizeof(current->comm)); +#else + corefile[4] = '\0'; +#endif + dentry = open_namei(corefile,O_CREAT | 2 | O_TRUNC, 0600); + if (IS_ERR(dentry)) { + dentry = NULL; + goto end_coredump; + } + inode = dentry->d_inode; + if (!S_ISREG(inode->i_mode)) + goto end_coredump; + if (!inode->i_op || !inode->i_op->default_file_ops) + goto end_coredump; + if (get_write_access(inode)) + goto end_coredump; + file.f_mode = 3; + file.f_flags = 0; + file.f_count = 1; + file.f_dentry = dentry; + file.f_pos = 0; + file.f_reada = 0; + file.f_op = inode->i_op->default_file_ops; + if (file.f_op->open) + if (file.f_op->open(inode,&file)) + goto done_coredump; + if (!file.f_op->write) + goto close_coredump; + has_dumped = 1; + current->flags |= PF_DUMPCORE; + strncpy(dump.u_comm, current->comm, sizeof(current->comm)); + dump.signal = signr; + dump_thread(regs, &dump); + +/* If the size of the dump file exceeds the rlimit, then see what would happen + if we wrote the stack, but not the data area. */ + if ((dump.u_dsize+dump.u_ssize) > + current->rlim[RLIMIT_CORE].rlim_cur) + dump.u_dsize = 0; + +/* Make sure we have enough room to write the stack and data areas. */ + if ((dump.u_ssize) > + current->rlim[RLIMIT_CORE].rlim_cur) + dump.u_ssize = 0; + +/* make sure we actually have a data and stack area to dump */ + set_fs(USER_DS); + if (verify_area(VERIFY_READ, (void *) START_DATA(dump), dump.u_dsize)) + dump.u_dsize = 0; + if (verify_area(VERIFY_READ, (void *) START_STACK(dump), dump.u_ssize)) + dump.u_ssize = 0; + + set_fs(KERNEL_DS); +/* struct user */ + DUMP_WRITE(&dump,sizeof(dump)); +/* now we start writing out the user space info */ + set_fs(USER_DS); +/* Dump the data area */ + if (dump.u_dsize != 0) { + dump_start = START_DATA(dump); + dump_size = dump.u_dsize; + DUMP_WRITE(dump_start,dump_size); + } +/* Now prepare to dump the stack area */ + if (dump.u_ssize != 0) { + dump_start = START_STACK(dump); + dump_size = dump.u_ssize; + DUMP_WRITE(dump_start,dump_size); + } +/* Finally dump the task struct. Not be used by gdb, but could be useful */ + set_fs(KERNEL_DS); + DUMP_WRITE(current,sizeof(*current)); +close_coredump: + if (file.f_op->release) + file.f_op->release(inode,&file); +done_coredump: + put_write_access(inode); +end_coredump: + set_fs(fs); + dput(dentry); + return has_dumped; +} + +static int +aout32_core_dump(long signr, struct pt_regs * regs) +{ + int retval; + + MOD_INC_USE_COUNT; + retval = do_aout32_core_dump(signr, regs); + MOD_DEC_USE_COUNT; + return retval; +} + +/* + * create_aout32_tables() parses the env- and arg-strings in new user + * memory and creates the pointer tables from them, and puts their + * addresses on the "stack", returning the new stack pointer value. + */ +#define A(x) ((unsigned long)x) +static u32 *create_aout32_tables(char * p, struct linux_binprm * bprm) +{ + u32 *argv, *envp; + u32 *sp; + int argc = bprm->argc; + int envc = bprm->envc; + + sp = (u32 *) ((-(unsigned long)sizeof(char *)) & (unsigned long) p); + + /* This imposes the proper stack alignment for a new process. */ + sp = (u32 *) (((unsigned long) sp) & ~7); + if ((envc+argc+3)&1) + --sp; + + sp -= envc+1; + envp = (u32 *) sp; + sp -= argc+1; + argv = (u32 *) sp; + put_user(argc,--sp); + current->mm->arg_start = (unsigned long) p; + while (argc-->0) { + char c; + put_user(((u32)A(p)),argv++); + do { + get_user(c,p++); + } while (c); + } + put_user(NULL,argv); + current->mm->arg_end = current->mm->env_start = (unsigned long) p; + while (envc-->0) { + char c; + put_user(((u32)A(p)),envp++); + do { + get_user(c,p++); + } while (c); + } + put_user(NULL,envp); + current->mm->env_end = (unsigned long) p; + return sp; +} + +/* + * These are the functions used to load a.out style executables and shared + * libraries. There is no binary dependent code anywhere else. + */ + +static inline int do_load_aout32_binary(struct linux_binprm * bprm, + struct pt_regs * regs) +{ + struct exec ex; + struct file * file; + int fd; + unsigned long error; + unsigned long p = bprm->p; + unsigned long fd_offset; + unsigned long rlim; + + ex = *((struct exec *) bprm->buf); /* exec-header */ + if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC && + N_MAGIC(ex) != QMAGIC && N_MAGIC(ex) != NMAGIC) || + N_TRSIZE(ex) || N_DRSIZE(ex) || + bprm->dentry->d_inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) { + return -ENOEXEC; + } + + current->personality = PER_LINUX; + fd_offset = N_TXTOFF(ex); + + /* Check initial limits. This avoids letting people circumvent + * size limits imposed on them by creating programs with large + * arrays in the data or bss. + */ + rlim = current->rlim[RLIMIT_DATA].rlim_cur; + if (rlim >= RLIM_INFINITY) + rlim = ~0; + if (ex.a_data + ex.a_bss > rlim) + return -ENOMEM; + + /* OK, This is the point of no return */ + flush_old_exec(bprm); + memcpy(¤t->tss.core_exec, &ex, sizeof(struct exec)); + + current->mm->end_code = ex.a_text + + (current->mm->start_code = N_TXTADDR(ex)); + current->mm->end_data = ex.a_data + + (current->mm->start_data = N_DATADDR(ex)); + current->mm->brk = ex.a_bss + + (current->mm->start_brk = N_BSSADDR(ex)); + + current->mm->rss = 0; + current->mm->mmap = NULL; + current->suid = current->euid = current->fsuid = bprm->e_uid; + current->sgid = current->egid = current->fsgid = bprm->e_gid; + current->flags &= ~PF_FORKNOEXEC; + if (N_MAGIC(ex) == NMAGIC) { + /* Fuck me plenty... */ + error = do_mmap(NULL, N_TXTADDR(ex), ex.a_text, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_FIXED|MAP_PRIVATE, 0); + read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex), + ex.a_text, 0); + error = do_mmap(NULL, N_DATADDR(ex), ex.a_data, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_FIXED|MAP_PRIVATE, 0); + read_exec(bprm->dentry, fd_offset + ex.a_text, (char *) N_DATADDR(ex), + ex.a_data, 0); + goto beyond_if; + } + + if (N_MAGIC(ex) == OMAGIC) { + do_mmap(NULL, N_TXTADDR(ex) & PAGE_MASK, + ex.a_text+ex.a_data + PAGE_SIZE - 1, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_FIXED|MAP_PRIVATE, 0); + read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex), + ex.a_text+ex.a_data, 0); + } else { + if ((ex.a_text & 0xfff || ex.a_data & 0xfff) && + (N_MAGIC(ex) != NMAGIC)) + printk(KERN_NOTICE "executable not page aligned\n"); + + fd = open_dentry(bprm->dentry, O_RDONLY); + + if (fd < 0) + return fd; + file = current->files->fd[fd]; + if (!file->f_op || !file->f_op->mmap) { + sys_close(fd); + do_mmap(NULL, 0, ex.a_text+ex.a_data, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_FIXED|MAP_PRIVATE, 0); + read_exec(bprm->dentry, fd_offset, + (char *) N_TXTADDR(ex), ex.a_text+ex.a_data, 0); + goto beyond_if; + } + + error = do_mmap(file, N_TXTADDR(ex), ex.a_text, + PROT_READ | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE, + fd_offset); + + if (error != N_TXTADDR(ex)) { + sys_close(fd); + send_sig(SIGKILL, current, 0); + return error; + } + + error = do_mmap(file, N_DATADDR(ex), ex.a_data, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE, + fd_offset + ex.a_text); + sys_close(fd); + if (error != N_DATADDR(ex)) { + send_sig(SIGKILL, current, 0); + return error; + } + } +beyond_if: + if (current->exec_domain && current->exec_domain->module) + __MOD_DEC_USE_COUNT(current->exec_domain->module); + if (current->binfmt && current->binfmt->module) + __MOD_DEC_USE_COUNT(current->binfmt->module); + current->exec_domain = lookup_exec_domain(current->personality); + current->binfmt = &aout32_format; + if (current->exec_domain && current->exec_domain->module) + __MOD_INC_USE_COUNT(current->exec_domain->module); + if (current->binfmt && current->binfmt->module) + __MOD_INC_USE_COUNT(current->binfmt->module); + + set_brk(current->mm->start_brk, current->mm->brk); + + p = setup_arg_pages(p, bprm); + + p = (unsigned long) create_aout32_tables((char *)p, bprm); + current->mm->start_stack = p; + start_thread32(regs, ex.a_entry, p); + if (current->flags & PF_PTRACED) + send_sig(SIGTRAP, current, 0); + return 0; +} + + +static int +load_aout32_binary(struct linux_binprm * bprm, struct pt_regs * regs) +{ + int retval; + + MOD_INC_USE_COUNT; + retval = do_load_aout32_binary(bprm, regs); + MOD_DEC_USE_COUNT; + return retval; +} + +static inline int +do_load_aout32_library(int fd) +{ + struct file * file; + struct exec ex; + struct dentry * dentry; + struct inode * inode; + unsigned int len; + unsigned int bss; + unsigned int start_addr; + unsigned long error; + + file = current->files->fd[fd]; + + if (!file || !file->f_op) + return -EACCES; + + dentry = file->f_dentry; + inode = dentry->d_inode; + + /* Seek into the file */ + if (file->f_op->llseek) { + if ((error = file->f_op->llseek(inode, file, 0, 0)) != 0) + return -ENOEXEC; + } else + file->f_pos = 0; + + set_fs(KERNEL_DS); + error = file->f_op->read(inode, file, (char *) &ex, sizeof(ex)); + set_fs(USER_DS); + if (error != sizeof(ex)) + return -ENOEXEC; + + /* We come in here for the regular a.out style of shared libraries */ + if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || N_TRSIZE(ex) || + N_DRSIZE(ex) || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) || + inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) { + return -ENOEXEC; + } + if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) && + (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) { + printk("N_TXTOFF < BLOCK_SIZE. Please convert library\n"); + return -ENOEXEC; + } + + if (N_FLAGS(ex)) return -ENOEXEC; + + /* For QMAGIC, the starting address is 0x20 into the page. We mask + this off to get the starting address for the page */ + + start_addr = ex.a_entry & 0xfffff000; + + /* Now use mmap to map the library into memory. */ + error = do_mmap(file, start_addr, ex.a_text + ex.a_data, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE, + N_TXTOFF(ex)); + if (error != start_addr) + return error; + len = PAGE_ALIGN(ex.a_text + ex.a_data); + bss = ex.a_text + ex.a_data + ex.a_bss; + if (bss > len) { + error = do_mmap(NULL, start_addr + len, bss-len, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_PRIVATE|MAP_FIXED, 0); + if (error != start_addr + len) + return error; + } + return 0; +} + +static int +load_aout32_library(int fd) +{ + int retval; + + MOD_INC_USE_COUNT; + retval = do_load_aout32_library(fd); + MOD_DEC_USE_COUNT; + return retval; +} + + +__initfunc(int init_aout32_binfmt(void)) +{ + return register_binfmt(&aout32_format); +} diff --git a/arch/sparc64/kernel/binfmt_elf32.c b/arch/sparc64/kernel/binfmt_elf32.c index 05d50fe56..9ab2b7aca 100644 --- a/arch/sparc64/kernel/binfmt_elf32.c +++ b/arch/sparc64/kernel/binfmt_elf32.c @@ -6,6 +6,8 @@ #define ELF_CLASS ELFCLASS32 #define ELF_DATA ELFDATA2MSB; +#define elf_check_arch(x) (((x) == EM_SPARC) || ((x) == EM_SPARC32PLUS)) + #include <asm/processor.h> #include <linux/module.h> #include <linux/config.h> diff --git a/arch/sparc64/kernel/cpu.c b/arch/sparc64/kernel/cpu.c index 695ad680e..d6cdf9162 100644 --- a/arch/sparc64/kernel/cpu.c +++ b/arch/sparc64/kernel/cpu.c @@ -6,7 +6,9 @@ #include <linux/kernel.h> #include <linux/init.h> +#include <asm/asi.h> #include <asm/system.h> +#include <asm/fpumacro.h> struct cpu_iu_info { short manuf; @@ -26,6 +28,7 @@ struct cpu_fp_info { */ struct cpu_fp_info linux_sparc_fpu[] = { { 0x17, 0x10, 0, "UltraSparc I integrated FPU"}, + { 0x22, 0x10, 0, "UltraSparc II integrated FPU"}, { 0x17, 0x11, 0, "UltraSparc II integrated FPU"}, { 0x17, 0x12, 0, "UltraSparc III integrated FPU"}, }; @@ -34,6 +37,7 @@ struct cpu_fp_info linux_sparc_fpu[] = { struct cpu_iu_info linux_sparc_chips[] = { { 0x17, 0x10, "TI UltraSparc I (SpitFire)"}, + { 0x22, 0x10, "TI UltraSparc II (BlackBird)"}, { 0x17, 0x11, "TI UltraSparc II (BlackBird)"}, { 0x17, 0x12, "TI UltraSparc III (Cheetah)"}, /* A guess... */ }; @@ -50,11 +54,20 @@ __initfunc(void cpu_probe(void)) int manuf, impl; unsigned i, cpuid; long ver, fpu_vers; - - cpuid = get_cpuid(); + long fprs; +#ifndef __SMP__ + cpuid = 0; +#else +#error SMP not supported on sparc64 yet + /* cpuid = get_cpuid(); */ +#endif + + fprs = fprs_read (); + fprs_write (FPRS_FEF); __asm__ __volatile__ ("rdpr %%ver, %0; stx %%fsr, [%1]" : "=r" (ver) : "r" (&fpu_vers)); - + fprs_write (fprs); + manuf = ((ver >> 48)&0xffff); impl = ((ver >> 32)&0xffff); diff --git a/arch/sparc64/kernel/devices.c b/arch/sparc64/kernel/devices.c index 6aadd14e0..5e6705896 100644 --- a/arch/sparc64/kernel/devices.c +++ b/arch/sparc64/kernel/devices.c @@ -6,7 +6,6 @@ #include <linux/kernel.h> #include <linux/tasks.h> -#include <linux/config.h> #include <linux/init.h> #include <asm/page.h> @@ -15,7 +14,7 @@ #include <asm/smp.h> struct prom_cpuinfo linux_cpus[NCPUS]; -int linux_num_cpus; +int linux_num_cpus = 0; extern void cpu_probe(void); @@ -54,7 +53,7 @@ device_scan(unsigned long mem_start)) }; if(cpu_ctr == 0) { printk("No CPU nodes found, cannot continue.\n"); - halt(); + prom_halt(); } printk("Found %d CPU prom device tree node(s).\n", cpu_ctr); }; diff --git a/arch/sparc64/kernel/dtlb_miss.S b/arch/sparc64/kernel/dtlb_miss.S index 31b87f3de..b034ef407 100644 --- a/arch/sparc64/kernel/dtlb_miss.S +++ b/arch/sparc64/kernel/dtlb_miss.S @@ -1,4 +1,4 @@ -/* $Id: dtlb_miss.S,v 1.11 1997/04/10 01:59:35 davem Exp $ +/* $Id: dtlb_miss.S,v 1.12 1997/06/26 12:47:08 jj Exp $ * dtlb_miss.S: Data TLB miss code, this is included directly * into the trap table. * @@ -19,9 +19,11 @@ * } * goto longer_processing; * } else { - * if(fault_address >= KERNBASE && - * fault_address < VMALLOC_START) { - * tlb_load(__pa(fault_address) | PAGE_KERNEL); + * if(fault_address >= PAGE_OFFSET) { + * pte_val = PAGE_KERNEL; + * if (fault_address & 0x10000000000) + * pte_val = PAGE_KERNEL_IO; + * tlb_load(__pa(fault_address) | pte_val); * return_from_trap(); * } else { * pgd = pgd_offset(swapper_pg_dir, fault_address); @@ -32,9 +34,9 @@ * This is optimized for user TLB misses on purpose. */ -#define KERN_HIGHBITS (_PAGE_VALID | _PAGE_SZ4MB) +#define KERN_HIGHBITS ((_PAGE_VALID | _PAGE_SZ4MB) ^ 0xfffff80000000000) #define KERN_LOWBITS (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W) -#define KERN_LOWBITS_IO (_PAGE_E | _PAGE_P | _PAGE_W) +#define KERN_LOWBITS_IO ((_PAGE_E | _PAGE_P | _PAGE_W) ^ KERN_LOWBITS) /* ICACHE line 1 */ /*0x00*/ ldxa [%g0] ASI_DMMU, %g1 ! Get TAG_TARGET @@ -57,17 +59,17 @@ 1:/*0x3c*/ retry ! Trap return 3: /* ICACHE line 3 */ - /*0x40*/ sllx %g1, 43, %g5 ! This gets >= VMALLOC_START... - /*0x44*/ brlz,pn %g5, 4f ! ...if now less than zero. - /*0x48*/ andncc %g1, 0x3ff, %g0 ! Slick trick... - /*0x4c*/ be,pn %xcc, 4f ! Yes, it is some PROM mapping - /*0x50*/ srlx %g5, 21, %g5 ! This is now physical page - /*0x54*/ sethi %uhi(KERN_HIGHBITS), %g1 ! Construct PTE - /*0x58*/ sllx %g1, 32, %g1 ! Move priv bits up - /*0x5c*/ or %g1, %g5, %g1 ! Or in the page + /*0x40*/ sllx %g1, 22, %g5 ! This is now physical page + PAGE_OFFSET + /*0x44*/ brgez,pn %g5, 4f ! If >= 0, then walk down page tables + /*0x48*/ sethi %uhi(KERN_HIGHBITS), %g1 ! Construct PTE ^ PAGE_OFFSET + /*0x4c*/ andcc %g3, 0x80, %g0 ! Slick trick... + /*0x50*/ sllx %g1, 32, %g1 ! Move high bits up + /*0x54*/ or %g1, (KERN_LOWBITS), %g1 ! Assume not IO + /*0x58*/ bne,a,pn %icc, 5f ! Is it an IO page? + /*0x5c*/ xor %g1, (KERN_LOWBITS_IO), %g1 ! Aha, it is IO... /* ICACHE line 4 */ - /*0x60*/ or %g1, (KERN_LOWBITS), %g1 ! Set low priv bits +5:/*0x60*/ xor %g1, %g5, %g1 ! Slick trick II... /*0x64*/ stxa %g1, [%g0] ASI_DTLB_DATA_IN ! TLB load /*0x68*/ retry ! Trap return 4:/*0x6c*/ ldxa [%g0] ASI_DMMU_TSB_8KB_PTR, %g1 ! For PTE offset @@ -78,3 +80,4 @@ #undef KERN_HIGHBITS #undef KERN_LOWBITS +#undef KERN_LOWBITS_IO diff --git a/arch/sparc64/kernel/entry.S b/arch/sparc64/kernel/entry.S index 0d95e1b75..a410cfe80 100644 --- a/arch/sparc64/kernel/entry.S +++ b/arch/sparc64/kernel/entry.S @@ -1,7 +1,7 @@ -/* $Id: entry.S,v 1.31 1997/06/02 06:33:25 davem Exp $ +/* $Id: entry.S,v 1.50 1997/07/15 16:53:00 davem Exp $ * arch/sparc64/kernel/entry.S: Sparc64 trap low-level entry points. * - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -25,7 +25,6 @@ #define NR_SYSCALLS 256 /* Each OS is different... */ .text - .align 4 .globl sparc64_dtlb_prot_catch, sparc64_dtlb_refbit_catch .globl sparc64_itlb_refbit_catch @@ -38,24 +37,27 @@ * to update the dirty bit) and since we left crap in the sfsr * it will not get updated properly. */ + .align 32 sparc64_dtlb_prot_catch: wr %g0, ASI_DMMU, %asi rdpr %pstate, %g1 wrpr %g1, PSTATE_AG|PSTATE_MG, %pstate rdpr %tl, %g3 ldxa [%g0 + TLB_TAG_ACCESS] %asi, %g5 - ldxa [%g0 + TLB_SFSR] %asi, %g4 - cmp %g3, 1 stxa %g0, [%g0 + TLB_SFSR] %asi + membar #Sync + cmp %g3, 1 + bgu,a,pn %icc, winfix_trampoline rdpr %tpc, %g3 ba,pt %xcc, etrap rd %pc, %g7 - b,a,pt %xcc, 1f - + b,pt %xcc, 1f + mov 1, %o2 sparc64_dtlb_refbit_catch: srlx %g5, 9, %g4 and %g4, ((_PAGE_PRESENT | _PAGE_READ) >> 9), %g4 + cmp %g4, ((_PAGE_PRESENT | _PAGE_READ) >> 9) be,a,pt %xcc, 2f mov 1, %g4 @@ -64,23 +66,24 @@ sparc64_dtlb_refbit_catch: wrpr %g1, PSTATE_AG|PSTATE_MG, %pstate rdpr %tl, %g3 ldxa [%g0 + TLB_TAG_ACCESS] %asi, %g5 + cmp %g3, 1 - clr %g4 ! sfsr not updated for tlb misses - bgu,a,pn %icc, winfix_trampoline + bgu,pn %icc, winfix_trampoline rdpr %tpc, %g3 - ba,pt %xcc, etrap + b,pt %xcc, etrap rd %pc, %g7 -1: - mov %l5, %o4 ! raw tag access - mov %l4, %o5 ! raw sfsr - srlx %l5, PAGE_SHIFT, %o3 - clr %o1 ! text_fault == 0 - sllx %o3, PAGE_SHIFT, %o3 ! address - and %l4, 0x4, %o2 ! write == sfsr.W + clr %o2 +1: srlx %l5, PAGE_SHIFT, %o1 + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + call do_sparc64_fault - add %sp, STACK_BIAS + REGWIN_SZ, %o0 ! pt_regs ptr - ba,pt %xcc, rtrap + sllx %o1, PAGE_SHIFT, %o1 + b,pt %xcc, rtrap clr %l6 + nop + nop + nop + nop sparc64_itlb_refbit_catch: srlx %g5, 9, %g4 @@ -90,47 +93,119 @@ sparc64_itlb_refbit_catch: mov 1, %g4 rdpr %pstate, %g1 wrpr %g1, PSTATE_AG|PSTATE_MG, %pstate - ba,pt %xcc, etrap - rd %pc, %g7 + rdpr %tpc, %g5 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC], %o3 - mov 1, %o1 ! text_fault == 1 - clr %o2 ! write == 0 - clr %o4 ! tag access (N/A) - clr %o5 ! raw sfsr (N/A) - call do_sparc64_fault - add %sp, STACK_BIAS + REGWIN_SZ, %o0 ! pt_regs ptr - ba,pt %xcc, rtrap - clr %l6 - -2: - sllx %g4, 63, %g4 ! _PAGE_VALID + b,pt %xcc, etrap + rd %pc, %g7 + b,pt %xcc, 1b + clr %o2 +2: sllx %g4, 63, %g4 ! _PAGE_VALID or %g5, _PAGE_ACCESSED, %g5 or %g5, %g4, %g5 stxa %g5, [%g3 + %g1] ASI_PHYS_USE_EC ! store new PTE + stxa %g5, [%g0] ASI_DTLB_DATA_IN ! TLB load retry - -3: - sllx %g4, 63, %g4 ! _PAGE_VALID +3: sllx %g4, 63, %g4 ! _PAGE_VALID or %g5, _PAGE_ACCESSED, %g5 or %g5, %g4, %g5 stxa %g5, [%g3 + %g1] ASI_PHYS_USE_EC ! store new PTE stxa %g5, [%g0] ASI_ITLB_DATA_IN ! TLB load retry + /* This is trivial with the new code... */ + .align 32 + .globl do_fpdis +do_fpdis: + wr %g0, FPRS_FEF, %fprs + ldx [%g6 + AOFF_task_flags], %g2 + sethi %hi(0x00100000), %g4 ! XXX PF_USEDFPU + andcc %g2, %g4, %g0 + + bne,a,pt %xcc, fpload_fromkstk + sethi %hi((((PAGE_SIZE<<1)-((64*4)+(2*8))) & ~(64 - 1))), %g2 + fzero %f0 + fzero %f2 + faddd %f0, %f2, %f4 + fmuld %f0, %f2, %f6 + faddd %f0, %f2, %f8 + fmuld %f0, %f2, %f10 + + faddd %f0, %f2, %f12 + fmuld %f0, %f2, %f14 + faddd %f0, %f2, %f16 + fmuld %f0, %f2, %f18 + faddd %f0, %f2, %f20 + fmuld %f0, %f2, %f22 + faddd %f0, %f2, %f24 + fmuld %f0, %f2, %f26 + + faddd %f0, %f2, %f28 + fmuld %f0, %f2, %f30 + faddd %f0, %f2, %f32 + fmuld %f0, %f2, %f34 + faddd %f0, %f2, %f36 + fmuld %f0, %f2, %f38 + faddd %f0, %f2, %f40 + fmuld %f0, %f2, %f42 + + faddd %f0, %f2, %f44 + fmuld %f0, %f2, %f46 + ldx [%g6 + AOFF_task_flags], %g2 + faddd %f0, %f2, %f48 + fmuld %f0, %f2, %f50 + or %g2, %g4, %g2 + faddd %f0, %f2, %f52 + fmuld %f0, %f2, %f54 + + stx %g2, [%g6 + AOFF_task_flags] + faddd %f0, %f2, %f56 + sethi %hi(empty_zero_page), %g3 + fmuld %f0, %f2, %f58 + + faddd %f0, %f2, %f60 + ldx [%g3], %fsr ! wheee, empty_zero_page + b,pt %xcc, fpdis_exit + wr %g0, 0, %gsr + +fpload_fromkstk: + or %g2, %lo((((PAGE_SIZE<<1)-((64*4)+(2*8))) & ~(64 - 1))), %g2 + add %g6, %g2, %g2 + mov SECONDARY_CONTEXT, %g3 + stxa %g0, [%g3] ASI_DMMU + flush %g2 + wr %g0, ASI_BLK_S, %asi ! grrr, where is ASI_BLK_NUCLEUS 8-( + membar #StoreLoad | #LoadLoad + + ldda [%g2 + 0x000] %asi, %f0 + ldda [%g2 + 0x040] %asi, %f16 + ldda [%g2 + 0x080] %asi, %f32 + ldda [%g2 + 0x0c0] %asi, %f48 + ldx [%g2 + 0x100], %fsr + ldx [%g2 + 0x108], %g2 + membar #Sync + wr %g2, 0, %gsr +fpdis_exit: + rdpr %tstate, %g3 + sethi %hi(TSTATE_PEF), %g4 + or %g3, %g4, %g3 ! anal... + wrpr %g3, %tstate + retry + +#ifdef __SMP__ /* Note check out head.h, this code isn't even used for UP, * for SMP things will be different. In particular the data * registers for cross calls will be: * - * DATA 0: Address of function to call - * DATA 1: Argument 1, place in %g6 - * DATA 2: Argument 2, place in %g7 + * DATA 0: [low 32-bits] Address of function to call, jmp to this + * [high 32-bits] MMU Context Argument 0, place in %g5 + * DATA 1: Address Argument 1, place in %g6 + * DATA 2: Address Argument 2, place in %g7 * * With this method we can do most of the cross-call tlb/cache - * flushing in very quickly. + * flushing very quickly. */ - .align 4 + .align 32 .globl do_ivec do_ivec: ldxa [%g0] ASI_INTR_RECEIVE, %g1 @@ -139,16 +214,14 @@ do_ivec: mov 0x40, %g2 /* Load up Interrupt Vector Data 0 register. */ - sethi %uhi(ivector_to_mask), %g4 + sethi %hi(KERNBASE), %g4 ldxa [%g2] ASI_UDB_INTR_R, %g3 - or %g4, %ulo(ivector_to_mask), %g4 + cmp %g3, %g4 + bgeu,pn %xcc, do_ivec_xcall + nop and %g3, 0x7ff, %g3 - sllx %g4, 32, %g4 - sethi %hi(ivector_to_mask), %g5 sllx %g3, 3, %g3 - or %g5, %lo(ivector_to_mask), %g5 - add %g5, %g4, %g4 - ldx [%g4 + %g3], %g2 + ldx [%g1 + %g3], %g2 brz,pn %g2, do_ivec_spurious nop @@ -163,9 +236,17 @@ do_ivec_return: stxa %g0, [%g0] ASI_INTR_RECEIVE membar #Sync retry - +do_ivec_xcall: + srlx %g3, 32, %g5 + add %g2, 0x10, %g2 + sra %g3, 0, %g3 + ldxa [%g2] ASI_UDB_INTR_R, %g6 + add %g2, 0x10, %g2 + jmpl %g3, %g0 + ldxa [%g2] ASI_UDB_INTR_R, %g7 do_ivec_spurious: stxa %g0, [%g0] ASI_INTR_RECEIVE + membar #Sync rdpr %pstate, %g1 wrpr %g1, PSTATE_IG | PSTATE_AG, %pstate ba,pt %xcc, etrap @@ -174,8 +255,132 @@ do_ivec_spurious: add %sp, STACK_BIAS + REGWIN_SZ, %o0 ba,pt %xcc, rtrap clr %l6 +#endif /* __SMP__ */ + + .globl getcc, setcc +getcc: + ldx [%o0 + PT_V9_TSTATE], %o1 + srlx %o1, 32, %o1 + and %o1, 0xf, %o1 + retl + stx %o1, [%o0 + PT_V9_G1] +setcc: + ldx [%o0 + PT_V9_TSTATE], %o1 + ldx [%o0 + PT_V9_G1], %o2 + or %g0, %ulo(TSTATE_ICC), %o3 + sllx %o3, 32, %o3 + andn %o1, %o3, %o1 + sllx %o2, 32, %o2 + and %o2, %o3, %o2 + or %o1, %o2, %o1 + retl + stx %o1, [%o0 + PT_V9_TSTATE] + +#ifdef CONFIG_BLK_DEV_FD + .globl floppy_hardint +floppy_hardint: + sethi %hi(doing_pdma), %g1 + ld [%g1 + %lo(doing_pdma)], %g2 + brz,pn %g2, floppy_dosoftint + sethi %hi(fdc_status), %g3 + ldx [%g3 + %lo(fdc_status)], %g3 + sethi %hi(pdma_vaddr), %g5 + ldx [%g5 + %lo(pdma_vaddr)], %g4 + sethi %hi(pdma_size), %g5 + ldx [%g5 + %lo(pdma_size)], %g5 + +next_byte: + ldub [%g3], %g7 + andcc %g7, 0x80, %g0 + be,pn %icc, floppy_fifo_emptied + andcc %g7, 0x20, %g0 + be,pn %icc, floppy_overrun + andcc %g7, 0x40, %g0 + be,pn %icc, floppy_write + sub %g5, 1, %g5 + + ldub [%g3 + 1], %g7 + orcc %g0, %g5, %g0 + stb %g7, [%g4] + bne,pn %xcc, next_byte + add %g4, 1, %g4 + + b,pt %xcc, floppy_tdone + nop + +floppy_write: + ldub [%g4], %g7 + orcc %g0, %g5, %g0 + stb %g7, [%g3 + 1] + bne,pn %xcc, next_byte + add %g4, 1, %g4 + +floppy_tdone: + sethi %hi(pdma_vaddr), %g1 + stx %g4, [%g1 + %lo(pdma_vaddr)] + sethi %hi(pdma_size), %g1 + stx %g5, [%g1 + %lo(pdma_size)] + sethi %hi(auxio_register), %g1 + ldx [%g1 + %lo(auxio_register)], %g7 + ldub [%g7], %g5 + or %g5, 0xc2, %g5 + stb %g5, [%g7] + andn %g5, 0x02, %g5 + + nop; nop; nop; nop; nop; nop; + nop; nop; nop; nop; nop; nop; + + stb %g5, [%g7] + sethi %hi(doing_pdma), %g1 + b,pt %xcc, floppy_dosoftint + st %g0, [%g1 + %lo(doing_pdma)] + +floppy_fifo_emptied: + sethi %hi(pdma_vaddr), %g1 + stx %g4, [%g1 + %lo(pdma_vaddr)] + sethi %hi(pdma_size), %g1 + stx %g5, [%g1 + %lo(pdma_size)] + sethi %hi(irq_action), %g1 + or %g1, %lo(irq_action), %g1 + ldx [%g1 + (11 << 3)], %g3 ! irqaction[floppy_irq] + ldx [%g3 + 0x10], %g4 ! action->mask + st %g0, [%g4] ! SYSIO_ICLR_IDLE + membar #Sync ! probably not needed... + retry + +floppy_overrun: + sethi %hi(pdma_vaddr), %g1 + stx %g4, [%g1 + %lo(pdma_vaddr)] + sethi %hi(pdma_size), %g1 + stx %g5, [%g1 + %lo(pdma_size)] + sethi %hi(doing_pdma), %g1 + st %g0, [%g1 + %lo(doing_pdma)] + +floppy_dosoftint: + rdpr %pil, %g2 + wrpr %g0, 15, %pil + b,pt %xcc, etrap_irq + rd %pc, %g7 + + mov 11, %o0 + mov 0, %o1 + call sparc_floppy_irq + add %sp, STACK_BIAS + REGWIN_SZ, %o2 - .globl do_mna + b,pt %xcc, rtrap + clr %l6 + +#endif /* CONFIG_BLK_DEV_FD */ + + /* XXX Here is stuff we still need to write... -DaveM XXX */ + .globl indirect_syscall, netbsd_syscall, solaris_syscall +indirect_syscall: +netbsd_syscall: +solaris_syscall: + retl + nop + + .globl do_mna do_mna: rdpr %tl, %g3 cmp %g3, 1 @@ -195,187 +400,239 @@ breakpoint_trap: ba,pt %xcc, rtrap nop - .globl sys_pipe, sys_execve, sys_sigpause, sys_nis_syscall - .globl sys_sigsuspend, sys_sigreturn - .globl sys32_execve, sys_ptrace - -sys_pipe: - sethi %hi(sparc_pipe), %g1 - add %g1, %g4, %g1 - jmpl %g1 + %lo(sparc_pipe), %g0 - add %sp, STACK_BIAS + REGWIN_SZ, %o0 - -sys_nis_syscall: - sethi %hi(c_sys_nis_syscall), %g1 - add %g1, %g4, %g1 - jmpl %g1 + %lo(c_sys_nis_syscall), %g0 - add %sp, STACK_BIAS + REGWIN_SZ, %o0 - -sys_execve: - sethi %hi(sparc_execve), %g1 - add %g1, %g4, %g1 - jmpl %g1 + %lo(sparc_execve), %g0 - add %sp, STACK_BIAS + REGWIN_SZ, %o0 - -sys32_execve: - sethi %hi(sparc32_execve), %g1 - add %g1, %g4, %g1 - jmpl %g1 + %lo(sparc32_execve), %g0 - add %sp, STACK_BIAS + REGWIN_SZ, %o0 - -sys_sigpause: - /* NOTE: %o0 has a correct value already */ - call do_sigpause - add %sp, STACK_BIAS + REGWIN_SZ, %o1 - - ld [%curptr + AOFF_task_flags], %l5 - andcc %l5, 0x20, %g0 - be,pt %icc, rtrap - clr %l6 - call syscall_trace + /* SunOS uses syscall zero as the 'indirect syscall' it looks + * like indir_syscall(scall_num, arg0, arg1, arg2...); etc. + * This is complete brain damage. + */ + .globl sunos_indir +sunos_indir: + srl %o0, 0, %o0 + mov %o7, %l4 + cmp %o0, NR_SYSCALLS + blu,a,pt %icc, 1f + sll %o0, 0x3, %o0 + sethi %hi(sunos_nosys), %l6 + b,pt %xcc, 2f + or %l6, %lo(sunos_nosys), %l6 +1: sethi %hi(sunos_sys_table), %l7 + or %l7, %lo(sunos_sys_table), %l7 + ldx [%l7 + %o0], %l6 +2: mov %o1, %o0 + mov %o2, %o1 + mov %o3, %o2 + mov %o4, %o3 + mov %o5, %o4 + call %l6 + mov %l4, %o7 + + .globl sunos_getpid +sunos_getpid: + call sys_getppid nop - ba,pt %xcc, rtrap - clr %l6 - -sys_sigsuspend: - call do_sigsuspend - add %sp, STACK_BIAS + REGWIN_SZ, %o0 - - ld [%curptr + AOFF_task_flags], %l5 - andcc %l5, 0x20, %g0 - be,pt %icc, rtrap - clr %l6 - call syscall_trace + call sys_getpid + stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1] + b,pt %xcc, ret_sys_call + stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] + + /* SunOS getuid() returns uid in %o0 and euid in %o1 */ + .globl sunos_getuid +sunos_getuid: + call sys_geteuid nop - ba,pt %xcc, rtrap - clr %l6 - -sys_sigreturn: - call do_sigreturn - add %sp, STACK_BIAS + REGWIN_SZ, %o0 - - ld [%curptr + AOFF_task_flags], %l5 - andcc %l5, 0x20, %g0 - be,pt %icc, rtrap - clr %l6 - call syscall_trace + call sys_getuid + stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1] + b,pt %xcc, ret_sys_call + stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] + + /* SunOS getgid() returns gid in %o0 and egid in %o1 */ + .globl sunos_getgid +sunos_getgid: + call sys_getegid nop - ba,pt %xcc, rtrap - clr %l6 + call sys_getgid + stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1] + b,pt %xcc, ret_sys_call + stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] -sys_ptrace: - call do_ptrace - add %sp, STACK_BIAS + REGWIN_SZ, %o0 + /* SunOS's execv() call only specifies the argv argument, the + * environment settings are the same as the calling processes. + */ + .globl sunos_execv +sunos_execv: + sethi %hi(sparc32_execve), %g1 + stx %g0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2] + jmpl %g1 + %lo(sparc32_execve), %g0 + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + + .globl sys_pipe, sys_execve, sys_sigpause, sys_nis_syscall + .globl sys_sigsuspend, sys_sigreturn + .globl sys32_execve, sys_ptrace + .align 32 +sys_pipe: sethi %hi(sparc_pipe), %g1 + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + jmpl %g1 + %lo(sparc_pipe), %g0 + nop +sys_nis_syscall:sethi %hi(c_sys_nis_syscall), %g1 + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + jmpl %g1 + %lo(c_sys_nis_syscall), %g0 + nop + +sys_execve: sethi %hi(sparc_execve), %g1 + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + jmpl %g1 + %lo(sparc_execve), %g0 + nop +sys32_execve: sethi %hi(sparc32_execve), %g1 + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + jmpl %g1 + %lo(sparc32_execve), %g0 + nop + + /* NOTE: %o0 has a correct value already */ +sys_sigpause: call do_sigpause + add %sp, STACK_BIAS + REGWIN_SZ, %o1 + ldx [%curptr + AOFF_task_flags], %l5 + andcc %l5, 0x20, %g0 + be,pt %icc, rtrap + clr %l6 + call syscall_trace + nop + + ba,pt %xcc, rtrap + clr %l6 +linux_sparc_ni_syscall: + sethi %hi(sys_ni_syscall), %l7 + b,pt %xcc,syscall_is_too_hard + or %l7, %lo(sys_ni_syscall), %l7 + nop + + .align 32 +sys_sigsuspend: call do_sigsuspend + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + ldx [%curptr + AOFF_task_flags], %l5 + andcc %l5, 0x20, %g0 + be,pt %icc, rtrap + clr %l6 + call syscall_trace + nop + + ba,pt %xcc, rtrap + clr %l6 - ld [%curptr + AOFF_task_flags], %l5 - andcc %l5, 0x20, %g0 - be,pt %icc, rtrap - clr %l6 - call syscall_trace - nop - ba,pt %xcc, rtrap - clr %l6 + .align 32 +sys_sigreturn: call do_sigreturn + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + ldx [%curptr + AOFF_task_flags], %l5 + andcc %l5, 0x20, %g0 + be,pt %icc, rtrap + clr %l6 + call syscall_trace + nop + + ba,pt %xcc, rtrap + clr %l6 + + .align 32 +sys_ptrace: call do_ptrace + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + ldx [%curptr + AOFF_task_flags], %l5 + andcc %l5, 0x20, %g0 + be,pt %icc, rtrap + clr %l6 + call syscall_trace + nop + + ba,pt %xcc, rtrap + clr %l6 - /* This is how fork() was meant to be done, 12 instruction entry. -DaveM */ + /* This is how fork() was meant to be done, 12 instruction entry. + * + * I questioned the following code briefly, let me clear things + * up so you must not reason on it like I did. + * + * Know the fork_kpsr etc. we use in the sparc32 port? We don't + * need it here because the only piece of window state we copy to + * the child is the CWP register. Even if the parent sleeps, + * we are safe because we stuck it into pt_regs of the parent + * so it will not change. + * + * XXX This raises the question, whether we can do the same on + * XXX sparc32 to get rid of fork_kpsr _and_ fork_kwim. The + * XXX answer is yes. We stick fork_kpsr in UREG_G0 and + * XXX fork_kwim in UREG_G1 (global registers are considered + * XXX volatile across a system call in the sparc ABI I think + * XXX if it isn't we can use regs->y instead, anyone who depends + * XXX upon the Y register being preserved across a fork deserves + * XXX to lose). + * + * In fact we should take advantage of that fact for other things + * during system calls... + */ .globl sys_fork, sys_vfork, sys_clone + .globl ret_from_syscall, ret_from_smpfork + .align 32 sys_fork: -sys_vfork: - mov SIGCHLD, %o0 - clr %o1 -sys_clone: - mov %o7, %l5 - save %sp, -REGWIN_SZ, %sp - flushw - restore %g0, %g0, %g0 - rdpr %cwp, %o4 - add %sp, STACK_BIAS + REGWIN_SZ, %o2 - movrz %o1, %fp, %o1 - - /* Don't try this at home. */ - stx %o4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G0] - call do_fork - mov %l5, %o7 - -linux_sparc_ni_syscall: - sethi %hi(sys_ni_syscall), %l7 - or %l7, %lo(sys_ni_syscall), %l7 - ba,pt %xcc,syscall_is_too_hard - add %l7, %g4, %l7 - -linux_fast_syscall: - andn %l7, 3, %l7 - mov %i0, %o0 - mov %i1, %o1 - mov %i2, %o2 - jmpl %l7 + %g0, %g0 - mov %i3, %o3 +sys_vfork: mov SIGCHLD, %o0 + clr %o1 +sys_clone: mov %o7, %l5 +/*???*/ save %sp, -REGWIN_SZ, %sp + flushw +/*???*/ restore %g0, %g0, %g0 + rdpr %cwp, %o4 + add %sp, STACK_BIAS + REGWIN_SZ, %o2 + + movrz %o1, %fp, %o1 + stx %o4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G0] + call do_fork + mov %l5, %o7 +#ifdef __SMP__ +ret_from_smpfork: + sethi %hi(scheduler_lock), %o4 + membar #StoreStore | #LoadStore + stb %g0, [%o4 + %lo(scheduler_lock)] +#endif +ret_from_syscall: + b,pt %xcc, ret_sys_call + ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0], %o0 linux_syscall_trace: - call syscall_trace - nop - mov %i0, %o0 - mov %i1, %o1 - mov %i2, %o2 - mov %i3, %o3 - ba,pt %xcc, 2f - mov %i4, %o4 - - .globl ret_from_syscall -ret_from_syscall: - ba,pt %xcc, ret_sys_call - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0], %o0 + call syscall_trace + nop + mov %i0, %o0 + mov %i1, %o1 + mov %i2, %o2 + mov %i3, %o3 + b,pt %xcc, 2f + mov %i4, %o4 /* Linux native and SunOS system calls enter here... */ - .align 4 - .globl linux_sparc_syscall + .align 32 + .globl linux_sparc_syscall, syscall_is_too_hard, ret_sys_call linux_sparc_syscall: /* Direct access to user regs, must faster. */ - cmp %g1, NR_SYSCALLS - add %l7, %g4, %l7 - bgeu,pn %xcc, linux_sparc_ni_syscall - sll %g1, 3, %l4 - ldx [%l7 + %l4], %l7 - andcc %l7, 1, %g0 - bne,pn %icc, linux_fast_syscall - /* Just do the next insn in the delay slot */ - - .globl syscall_is_too_hard + cmp %g1, NR_SYSCALLS ! IEU1 Group + bgeu,pn %xcc, linux_sparc_ni_syscall ! CTI + mov %i0, %o0 ! IEU0 + sll %g1, 3, %l4 ! IEU0 Group + mov %i1, %o1 ! IEU1 + ldx [%l7 + %l4], %l7 ! Load syscall_is_too_hard: -#ifdef SYSCALL_TRACING /* Debugging... */ - mov %g1, %o0 ! o0=scall, o1=ptregs - call syscall_trace_entry - add %sp, STACK_BIAS + REGWIN_SZ, %o1 -#endif - mov %i0, %o0 - mov %i1, %o1 - mov %i2, %o2 - - ldx [%curptr + AOFF_task_flags], %l5 - mov %i3, %o3 - mov %i4, %o4 - andcc %l5, 0x20, %g0 - bne,pn %icc, linux_syscall_trace - mov %i0, %l5 -2: - call %l7 - mov %i5, %o5 - -#ifdef SYSCALL_TRACING /* Debugging... */ - call syscall_trace_exit ! o0=sysret, o1=ptregs - add %sp, STACK_BIAS + REGWIN_SZ, %o1 -#endif + mov %i2, %o2 ! IEU0 Group + ldx [%curptr + AOFF_task_flags], %l5 ! Load + + st %g0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_FPRS] + mov %i3, %o3 ! IEU1 + mov %i4, %o4 ! IEU0 Group + andcc %l5, 0x20, %g0 ! IEU1 2 bubbles + bne,pn %icc, linux_syscall_trace ! CTI Group + mov %i0, %l5 ! IEU0 +2: call %l7 ! CTI Group brk forced + mov %i5, %o5 ! IEU0 stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] - .globl ret_sys_call ret_sys_call: ldx [%curptr + AOFF_task_flags], %l6 - ldx [%curptr + AOFF_task_tss + AOFF_thread_flags], %l2 + sra %o0, 0, %o0 mov %ulo(TSTATE_XCARRY | TSTATE_ICARRY), %g2 - and %l2, SPARC_FLAG_32BIT, %l2 ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE], %g3 - brnz,a,pn %l2, 1f - sra %o0, 0, %o0 -1: cmp %o0, -ENOIOCTLCMD sllx %g2, 32, %g2 bgeu,pn %xcc, 1f @@ -383,13 +640,12 @@ ret_sys_call: /* System call success, clear Carry condition code. */ andn %g3, %g2, %g3 - clr %l6 stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE] bne,pn %icc, linux_syscall_trace2 ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1 ! pc = npc add %l1, 0x4, %l2 !npc = npc+4 stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC] - ba,pt %xcc, rtrap + b,pt %xcc, rtrap_clr_l6 stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] 1: /* System call failure, set Carry condition code. @@ -403,10 +659,10 @@ ret_sys_call: bne,pn %icc, linux_syscall_trace2 ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1 ! pc = npc add %l1, 0x4, %l2 !npc = npc+4 + stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC] - ba,pt %xcc, rtrap + b,pt %xcc, rtrap stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] - linux_syscall_trace2: call syscall_trace add %l1, 0x4, %l2 /* npc = npc+4 */ @@ -414,4 +670,15 @@ linux_syscall_trace2: ba,pt %xcc, rtrap stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC] -/* End of entry.S */ + .align 32 + .globl __flushw_user +__flushw_user: +1: save %sp, -128, %sp + rdpr %otherwin, %g1 + brnz,pt %g1, 1b + add %g2, 1, %g2 +1: sub %g2, 1, %g2 + brnz,pt %g2, 1b + restore %g0, %g0, %g0 +2: retl + mov %g3, %o7 diff --git a/arch/sparc64/kernel/etrap.S b/arch/sparc64/kernel/etrap.S index efb1b48fc..4daf30e21 100644 --- a/arch/sparc64/kernel/etrap.S +++ b/arch/sparc64/kernel/etrap.S @@ -1,4 +1,4 @@ -/* $Id: etrap.S,v 1.21 1997/06/02 06:33:28 davem Exp $ +/* $Id: etrap.S,v 1.30 1997/06/30 10:31:37 jj Exp $ * etrap.S: Preparing for entry into the kernel on Sparc V9. * * Copyright (C) 1996, 1997 David S. Miller (davem@caip.rutgers.edu) @@ -12,88 +12,121 @@ #include <asm/spitfire.h> #include <asm/head.h> - /* We assume that pstate, when entering this, has AG and - * IE bits set, MG and IG clear. - * - * We also guarentee for caller that AG %g4 and %g5 will have - * their values preserved and left in %l4 and %l5 respectively - * for him (fault handling needs this). - */ +#define FPUREG_SZ ((64 * 4) + (2 * 8)) +#define TASK_REGOFF ((((PAGE_SIZE<<1)-FPUREG_SZ)&~(64-1)) - \ + TRACEREG_SZ-REGWIN_SZ) - .text - .align 32 - .globl etrap, etrap_irq, etraptl1 -etrap: - rdpr %pil, %g2 -etrap_irq: - rdpr %tstate, %g1 - sllx %g2, 20, %g2 - or %g1, %g2, %g1 - andcc %g1, TSTATE_PRIV, %g0 - bne,a,pn %xcc, 1f - sub %sp, REGWIN_SZ + TRACEREG_SZ - STACK_BIAS, %g2 - rd %pic, %g3 + .text + .align 32 + .globl etrap, etrap_irq, etraptl1 - sethi %hi((PAGE_SIZE<<1)-TRACEREG_SZ-REGWIN_SZ), %g2 - or %g2, %lo((PAGE_SIZE<<1)-TRACEREG_SZ-REGWIN_SZ), %g2 - add %g3, %g2, %g2 -1: stx %g1, [%g2 + REGWIN_SZ + PT_V9_TSTATE] - rdpr %tpc, %g1 - rdpr %tnpc, %g3 - stx %g1, [%g2 + REGWIN_SZ + PT_V9_TPC] - rd %y, %g1 +etrap: rdpr %pil, %g2 +etrap_irq: rdpr %tstate, %g1 + sllx %g2, 20, %g2 + or %g1, %g2, %g1 + andcc %g1, TSTATE_PRIV, %g0 + bne,pn %xcc, etrap_maybe_fpu + sub %sp, REGWIN_SZ + TRACEREG_SZ - STACK_BIAS, %g2 + sethi %hi(TASK_REGOFF), %g2 - stx %g3, [%g2 + REGWIN_SZ + PT_V9_TNPC] - stx %g1, [%g2 + REGWIN_SZ + PT_V9_Y] - save %g2, -STACK_BIAS, %sp ! The ordering of these two instructions - rdpr %pstate, %g1 ! is critical, see winfixup.S for details - bne,pn %xcc, 2f - rdpr %canrestore, %g3 - rdpr %wstate, %g6 - wrpr %g0, 7, %cleanwin + or %g2, %lo(TASK_REGOFF), %g2 + add %g6, %g2, %g2 +etrap_maybe_fpu:rd %fprs, %g3 + brnz,pn %g3, etrap_save_fpu + st %g0, [%g2 + REGWIN_SZ + PT_V9_FPRS] +etrap_after_fpu:rdpr %tpc, %g3 + stx %g1, [%g2 + REGWIN_SZ + PT_V9_TSTATE] + rdpr %tnpc, %g1 - wrpr %g0, 0, %canrestore - sll %g6, 3, %g6 - wrpr %g3, 0, %otherwin - wrpr %g6, %wstate - sethi %uhi(KERNBASE), %g3 - sllx %g3, 32, %g3 - mov PRIMARY_CONTEXT, %g2 - stxa %g0, [%g2] ASI_DMMU + stx %g3, [%g2 + REGWIN_SZ + PT_V9_TPC] + rd %y, %g3 + stx %g1, [%g2 + REGWIN_SZ + PT_V9_TNPC] + st %g3, [%g2 + REGWIN_SZ + PT_V9_Y] + save %g2, -STACK_BIAS, %sp ! The ordering here is + rdpr %pstate, %g1 ! critical, see winfixup + bne,pn %xcc, 2f + rdpr %canrestore, %g3 - flush %g3 -2: wrpr %g0, 0x0, %tl - mov %g1, %l1 - mov %g4, %l4 - mov %g5, %l5 - mov %g7, %l2 - wrpr %l1, PSTATE_AG, %pstate - stx %g1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G1] + rdpr %wstate, %g2 + wrpr %g0, 7, %cleanwin + wrpr %g0, 0, %canrestore + sll %g2, 3, %g2 + wrpr %g3, 0, %otherwin + wrpr %g2, 0, %wstate + wr %g0, ASI_DMMU, %asi + ldxa [%g0 + PRIMARY_CONTEXT] %asi, %g2 - stx %g2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G2] - stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G3] - stx %g4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G4] - stx %g5, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G5] - stx %g6, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G6] - stx %g7, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G7] - stx %i0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] - stx %i1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1] + stxa %g0, [%g0 + PRIMARY_CONTEXT] %asi + stxa %g2, [%g0 + SECONDARY_CONTEXT] %asi + flush %g6 +2: wrpr %g0, 0x0, %tl + or %g1, 0, %l1 + add %g4, 0, %l4 + or %g5, 0, %l5 + add %g7, 0, %l2 - stx %i2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2] - stx %i3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I3] - stx %i4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I4] - stx %i5, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I5] - stx %i6, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I6] - stx %i7, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I7] - wrpr %l1, (PSTATE_IE | PSTATE_AG), %pstate - sethi %uhi(KERNBASE), %g4 + or %g6, 0, %l6 + wrpr %l1, (PSTATE_AG|PSTATE_RMO), %pstate + stx %g1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G1] + stx %g2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G2] + stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G3] + stx %g4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G4] + stx %g5, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G5] + stx %g6, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G6] - rd %pic, %g6 - jmpl %l2 + 0x4, %g0 - sllx %g4, 32, %g4 -etraptl1: - rdpr %tstate, %g1 - sub %sp, REGWIN_SZ + TRACEREG_SZ - STACK_BIAS, %g2 - ba,pt %xcc, 1b - andcc %g1, TSTATE_PRIV, %g0 - nop + stx %g7, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G7] + stx %i0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0] + stx %i1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1] + stx %i2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2] + stx %i3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I3] + stx %i4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I4] + sethi %uhi(PAGE_OFFSET), %g4 + stx %i5, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I5] + + stx %i6, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I6] + sllx %g4, 32, %g4 + stx %i7, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I7] + wrpr %l1, (PSTATE_IE|PSTATE_AG|PSTATE_RMO), %pstate + jmpl %l2 + 0x4, %g0 + mov %l6, %g6 +etrap_save_fpu: and %g3, FPRS_FEF, %g3 + brz,pn %g3, 2f + + nop + be,a,pt %xcc, 3f + add %g2, (TRACEREG_SZ + REGWIN_SZ), %g2 + wr %g0, ASI_BLK_P, %asi + add %g2, ((TRACEREG_SZ+REGWIN_SZ)-FPUREG_SZ), %g2 + andn %g2, (64 - 1), %g2 +1: st %g3, [%g2 - 0x4 /*REGWIN_SZ + PT_V9_FPRS*/] + rd %gsr, %g3 + + stx %fsr, [%g2 + 0x100] + stx %g3, [%g2 + 0x108] + membar #StoreStore | #LoadStore + stda %f0, [%g2 + 0x000] %asi + stda %f16, [%g2 + 0x040] %asi + stda %f32, [%g2 + 0x080] %asi + stda %f48, [%g2 + 0x0c0] %asi + membar #Sync + + sub %g2, (TRACEREG_SZ + REGWIN_SZ), %g2 +2: b,pt %xcc, etrap_after_fpu + wr %g0, 0, %fprs +3: /* Because Ultra lacks ASI_BLK_NUCLEUS a hack has to take place. */ + mov SECONDARY_CONTEXT, %g3 + stxa %g0, [%g3] ASI_DMMU + flush %g2 + wr %g0, ASI_BLK_S, %asi + nop + + b,pt %xcc, 1b + mov FPRS_FEF, %g3 + nop +etraptl1: rdpr %tstate, %g1 + sub %sp, REGWIN_SZ + TRACEREG_SZ - STACK_BIAS, %g2 + ba,pt %xcc, etrap_maybe_fpu + andcc %g1, TSTATE_PRIV, %g0 + nop +#undef TASK_REGOFF +#undef FPUREG_SZ diff --git a/arch/sparc64/kernel/hack.S b/arch/sparc64/kernel/hack.S deleted file mode 100644 index 843221395..000000000 --- a/arch/sparc64/kernel/hack.S +++ /dev/null @@ -1,170 +0,0 @@ -/* <hack> - This is just a huge ugly hack to get things compiled. - Hopefully will disappear quickly, once we get everything - to compile... */ - .text - .align 8 - .globl breakpoint -breakpoint: retl;nop - .globl do_cee -do_cee: retl;nop - .globl do_cee_tl1 -do_cee_tl1: retl;nop - .globl do_dae_tl1 -do_dae_tl1: retl;nop - .globl do_div0_tl1 -do_div0_tl1: retl;nop - .globl do_fpdis_tl1 -do_fpdis_tl1: retl;nop - .globl do_fpieee_tl1 -do_fpieee_tl1: retl;nop - .globl do_fpother_tl1 -do_fpother_tl1: retl;nop - .globl do_iae_tl1 -do_iae_tl1: retl;nop - .globl do_ill_tl1 -do_ill_tl1: retl;nop - .globl do_irq_tl1 -do_irq_tl1: retl;nop - .globl do_lddfmna -do_lddfmna: retl;nop - .globl do_lddfmna_tl1 -do_lddfmna_tl1: retl;nop - .globl do_paw -do_paw: retl;nop - .globl do_paw_tl1 -do_paw_tl1: retl;nop - .globl do_stdfmna -do_stdfmna: retl;nop - .globl do_stdfmna_tl1 -do_stdfmna_tl1: retl;nop - .globl do_tof_tl1 -do_tof_tl1: retl;nop - .globl do_vaw -do_vaw: retl;nop - .globl do_vaw_tl1 -do_vaw_tl1: retl;nop - .globl floppy_hardint -floppy_hardint: retl;nop - .globl get_cpuid -get_cpuid: retl;mov 0, %o0 - .globl getcc -getcc: retl;nop - .globl halt -halt: retl;nop - .globl indirect_syscall -indirect_syscall: retl;nop - .globl install_linux_ticker -install_linux_ticker: retl;nop - .globl install_obp_ticker -install_obp_ticker: retl;nop - .globl linux_dbvec -linux_dbvec: retl;nop - .globl linux_num_cpus -linux_num_cpus: retl;nop - .globl netbsd_syscall -netbsd_syscall: retl;nop - .globl setcc -setcc: retl;nop - .globl solaris_syscall -solaris_syscall: retl;nop - .globl sunos_mmap -sunos_mmap: retl;nop - .globl sunos_syscall -sunos_syscall: retl;nop - .globl svr4_getcontext -svr4_getcontext: retl;nop - .globl svr4_setcontext -svr4_setcontext: retl;nop - .globl sunos_accept -sunos_accept: retl;nop - .globl sunos_audit -sunos_audit: retl;nop - .globl sunos_brk -sunos_brk: retl;nop - .globl sunos_execv -sunos_execv: retl;nop - .globl sunos_fpathconf -sunos_fpathconf: retl;nop - .globl sunos_getdents -sunos_getdents: retl;nop - .globl sunos_getdirentries -sunos_getdirentries: retl;nop - .globl sunos_getdomainname -sunos_getdomainname: retl;nop - .globl sunos_getdtablesize -sunos_getdtablesize: retl;nop - .globl sunos_getgid -sunos_getgid: retl;nop - .globl sunos_gethostid -sunos_gethostid: retl;nop - .globl sunos_getpid -sunos_getpid: retl;nop - .globl sunos_getsockopt -sunos_getsockopt: retl;nop - .globl sunos_getuid -sunos_getuid: retl;nop - .globl sunos_indir -sunos_indir: retl;nop - .globl sunos_ioctl -sunos_ioctl: retl;nop - .globl sunos_killpg -sunos_killpg: retl;nop - .globl sunos_madvise -sunos_madvise: retl;nop - .globl sunos_mctl -sunos_mctl: retl;nop - .globl sunos_mincore -sunos_mincore: retl;nop - .globl sunos_mount -sunos_mount: retl;nop - .globl sunos_nop -sunos_nop: retl;nop - .globl sunos_nosys -sunos_nosys: retl;nop - .globl sunos_open -sunos_open: retl;nop - .globl sunos_pathconf -sunos_pathconf: retl;nop - .globl sunos_poll -sunos_poll: retl;nop - .globl sunos_read -sunos_read: retl;nop - .globl sunos_readv -sunos_readv: retl;nop - .globl sunos_recv -sunos_recv: retl;nop - .globl sunos_sbrk -sunos_sbrk: retl;nop - .globl sunos_select -sunos_select: retl;nop - .globl sunos_semsys -sunos_semsys: retl;nop - .globl sunos_send -sunos_send: retl;nop - .globl sunos_setpgrp -sunos_setpgrp: retl;nop - .globl sunos_setsockopt -sunos_setsockopt: retl;nop - .globl sunos_shmsys -sunos_shmsys: retl;nop - .globl sunos_sigaction -sunos_sigaction: retl;nop - .globl sunos_sigblock -sunos_sigblock: retl;nop - .globl sunos_sigsetmask -sunos_sigsetmask: retl;nop - .globl sunos_sstk -sunos_sstk: retl;nop - .globl sunos_sysconf -sunos_sysconf: retl;nop - .globl sunos_uname -sunos_uname: retl;nop - .globl sunos_vadvise -sunos_vadvise: retl;nop - .globl sunos_wait4 -sunos_wait4: retl;nop - .globl sunos_write -sunos_write: retl;nop - .globl sunos_writev -sunos_writev: retl;nop diff --git a/arch/sparc64/kernel/head.S b/arch/sparc64/kernel/head.S index 3844c24c3..0ed975aff 100644 --- a/arch/sparc64/kernel/head.S +++ b/arch/sparc64/kernel/head.S @@ -1,4 +1,4 @@ -/* $Id: head.S,v 1.31 1997/05/30 22:35:28 davem Exp $ +/* $Id: head.S,v 1.43 1997/07/07 03:05:25 davem Exp $ * head.S: Initial boot code for the Sparc64 port of Linux. * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) @@ -8,20 +8,25 @@ */ #include <linux/version.h> +#include <linux/errno.h> +#include <asm/asm_offsets.h> +#include <asm/asi.h> #include <asm/pstate.h> #include <asm/ptrace.h> #include <asm/spitfire.h> #include <asm/page.h> #include <asm/pgtable.h> #include <asm/errno.h> +#include <asm/signal.h> +#include <asm/processor.h> #include <asm/lsu.h> #include <asm/head.h> /* This section from from _start to sparc64_boot_end should fit into - * 0xffff.f800.0000.4000 to 0xffff.f800.0000.8000 and will be sharing space - * with bootup_user_stack, which is from 0xffff.f800.0000.4000 to - * 0xffff.f800.0000.6000 and bootup_kernel_stack, which is from - * 0xffff.f800.0000.6000 to 0xffff.f800.0000.8000. + * 0x0000.0000.0040.4000 to 0x0000.0000.0040.8000 and will be sharing space + * with bootup_user_stack, which is from 0x0000.0000.0040.4000 to + * 0x0000.0000.0040.6000 and bootup_kernel_stack, which is from + * 0x0000.0000.0040.6000 to 0x0000.0000.0040.8000. */ .text @@ -31,7 +36,7 @@ start: _stext: stext: bootup_user_stack: -! 0xfffff80000004000 +! 0x0000000000404000 b sparc64_boot flushw /* Flush register file. */ @@ -41,10 +46,11 @@ bootup_user_stack: */ .global root_flags, ram_flags, root_dev .global ramdisk_image, ramdisk_size + .globl silo_args .ascii "HdrS" .word LINUX_VERSION_CODE - .half 0x0201 /* HdrS version */ + .half 0x0202 /* HdrS version */ root_flags: .half 1 root_dev: @@ -55,7 +61,8 @@ ramdisk_image: .word 0 ramdisk_size: .word 0 - .xword reboot_command + .xword reboot_command + .xword bootstr_len /* We must be careful, 32-bit OpenBOOT will get confused if it * tries to save away a register window to a 64-bit kernel @@ -80,26 +87,7 @@ sparc64_boot: * Again, typically PROM has left %pil at 13 or similar, and * (PSTATE_PRIV | PSTATE_PEF | PSTATE_IE) in %pstate. */ - wrpr %g0, 0xf, %pil /* Interrupts off. */ - wrpr %g0, (PSTATE_PRIV|PSTATE_PEF), %pstate - - /* Check if we are mapped where we expect to be in virtual - * memory. The Solaris /boot elf format bootloader - * will peek into our elf header and load us where - * we want to be, otherwise we have to re-map. - */ -current_pc: - rd %pc, %g3 - sethi %uhi(KERNBASE), %g4 - sllx %g4, 32, %g4 - - /* Check the run time program counter. */ - - set current_pc, %g5 - add %g5, %g4, %g5 - cmp %g3, %g5 - be %xcc, sun4u_init - nop + wrpr %g0, (PSTATE_PRIV|PSTATE_PEF|PSTATE_IE), %pstate create_mappings: /* %g5 holds the tlb data */ @@ -136,15 +124,10 @@ create_mappings: cmp %g1, %g2 be,a,pn %xcc, got_tlbentry ldxa [%l0] ASI_ITLB_DATA_ACCESS, %g1 - cmp %l1, (63 << 3) + cmp %l0, (63 << 3) blu,pt %xcc, 1b add %l0, (1 << 3), %l0 -boot_failed: - /* Debugging 8-) */ - set 0xdeadbeef, %g1 - t 0x11 - got_tlbentry: /* Nops here again, perhaps Cheetah/Blackbird are better behaved... */ nop @@ -159,33 +142,73 @@ got_tlbentry: or %g5, %g1, %g5 /* Or it into TAG being built. */ + clr %l0 /* TLB entry walker. */ + sethi %hi(KERNBASE), %g3 /* 4M lower limit */ + sethi %hi(KERNBASE<<1), %g7 /* 8M upper limit */ + mov TLB_TAG_ACCESS, %l7 +1: + /* Yes, the nops seem to be necessary for now, don't ask me why. -DaveM */ + ldxa [%l0] ASI_ITLB_TAG_READ, %g1 + nop + nop + nop + andn %g1, %l2, %g1 /* Get vaddr */ + cmp %g1, %g3 + blu,pn %xcc, 2f + cmp %g1, %g7 + bgeu,pn %xcc, 2f + nop + stxa %g0, [%l7] ASI_IMMU + stxa %g0, [%l0] ASI_ITLB_DATA_ACCESS +2: + cmp %l0, (63 << 3) + blu,pt %xcc, 1b + add %l0, (1 << 3), %l0 + + nop; nop; nop + + clr %l0 /* TLB entry walker. */ +1: + /* Yes, the nops seem to be necessary for now, don't ask me why. -DaveM */ + ldxa [%l0] ASI_DTLB_TAG_READ, %g1 + nop + nop + nop + andn %g1, %l2, %g1 /* Get vaddr */ + cmp %g1, %g3 + blu,pn %xcc, 2f + cmp %g1, %g7 + bgeu,pn %xcc, 2f + nop + stxa %g0, [%l7] ASI_DMMU + stxa %g0, [%l0] ASI_DTLB_DATA_ACCESS +2: + cmp %l0, (63 << 3) + blu,pt %xcc, 1b + add %l0, (1 << 3), %l0 + + nop; nop; nop + + /* PROM never puts any TLB entries into the MMU with the lock bit - * set. So we gladly use tlb entry 63 for KERNBASE, 62 for - * boot time locked PROM CIF handler page, we remove the locked - * bit for the CIF page in paging_init(). + * set. So we gladly use tlb entry 63 for KERNBASE. */ - mov TLB_TAG_ACCESS, %g3 - mov (63 << 3), %g7 - stxa %g4, [%g3] ASI_IMMU /* KERNBASE into TLB TAG */ - stxa %g5, [%g7] ASI_ITLB_DATA_ACCESS /* TTE into TLB DATA */ - membar #Sync - /* Same for DTLB */ - stxa %g4, [%g3] ASI_DMMU /* KERNBASE into TLB TAG */ + sethi %hi(KERNBASE), %g3 + mov (63 << 3), %g7 + stxa %g3, [%l7] ASI_DMMU /* KERNBASE into TLB TAG */ stxa %g5, [%g7] ASI_DTLB_DATA_ACCESS /* TTE into TLB DATA */ membar #Sync - - /* Kill instruction prefetch queues. */ - flush %g4 + stxa %g3, [%l7] ASI_IMMU /* KERNBASE into TLB TAG */ + stxa %g5, [%g7] ASI_ITLB_DATA_ACCESS /* TTE into TLB DATA */ membar #Sync - - ba,pt %xcc, go_to_highmem + flush %g3 + membar #Sync + ba,pt %xcc, 1f nop - -go_to_highmem: - /* Now do a non-relative jump so that PC is in high-memory */ +1: set sun4u_init, %g2 - jmpl %g2 + %g4, %g0 + jmpl %g2 + %g0, %g0 nop sun4u_init: @@ -198,42 +221,16 @@ sun4u_init: stxa %g0, [%g7] ASI_DMMU membar #Sync - /* The lock bit has to be removed from this page later on, - * but before firing up init we will use PROM a lot, so we - * lock it there now... - */ - - /* Compute PROM CIF interface page TTE. */ - sethi %hi(__p1275_loc), %g7 - or %g7, (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W | _PAGE_L), %g7 - sethi %uhi(_PAGE_VALID), %g5 - sethi %hi(0x8000), %g3 - sllx %g5, 32, %g5 - mov TLB_TAG_ACCESS, %g6 - or %g5, %g7, %g5 - add %g5, %g1, %g5 /* Add in physbase. */ - - mov (62 << 3), %g7 /* TLB entry 62 */ - stxa %g3, [%g6] ASI_IMMU /* CIF page into TLB TAG */ - stxa %g5, [%g7] ASI_ITLB_DATA_ACCESS /* TTE into TLB DATA */ - membar #Sync - - /* Same for DTLB */ - stxa %g3, [%g6] ASI_DMMU /* CIF page into TLB TAG */ - stxa %g5, [%g7] ASI_DTLB_DATA_ACCESS /* TTE into TLB DATA */ - membar #Sync - - /* Kill instruction prefetch queues. */ - flush %g3 - membar #Sync + sethi %uhi(PAGE_OFFSET), %g4 + sllx %g4, 32, %g4 /* We are now safely (we hope) in Nucleus context (0), rewrite * the KERNBASE TTE's so they no longer have the global bit set. * Don't forget to setup TAG_ACCESS first 8-) */ mov TLB_TAG_ACCESS, %g2 - stxa %g4, [%g2] ASI_IMMU - stxa %g4, [%g2] ASI_DMMU + stxa %g3, [%g2] ASI_IMMU + stxa %g3, [%g2] ASI_DMMU mov (63 << 3), %g7 ldxa [%g7] ASI_ITLB_DATA_ACCESS, %g1 @@ -247,30 +244,22 @@ sun4u_init: membar #Sync /* Kill instruction prefetch queues. */ - flush %g4 + flush %g3 membar #Sync - /* Compute the number of windows in this machine - * store this in nwindows and nwindowsm1 - */ - rdpr %ver, %g1 /* Get VERSION register. */ - sethi %hi(nwindows), %g2 - and %g1, VERS_MAXWIN, %g5 - or %g2,%lo(nwindows),%g2 - add %g5, 1, %g6 - add %g2, (nwindows - nwindowsm1), %g3 - stx %g6, [%g2 + %g4] - stx %g5, [%g3 + %g4] - sethi %hi(init_task_union), %g6 or %g6, %lo(init_task_union), %g6 - add %g6, %g4, %g6 ! g6 usage is fixed as well mov %sp, %l6 mov %o4, %l7 +#if 0 /* We don't do it like this anymore, but for historical hack value + * I leave this snippet here to show how crazy we can be sometimes. 8-) + */ + /* Setup "Linux Current Register", thanks Sun 8-) */ wr %g0, 0x1, %pcr wr %g6, 0x0, %pic +#endif mov 1, %g5 sllx %g5, (PAGE_SHIFT + 1), %g5 @@ -291,23 +280,19 @@ sun4u_init: add %l1, %l2, %l1 andn %l1, %l2, %l1 add %l2, 1, %l2 - add %l0, %g4, %o0 + add %l0, %g0, %o0 1: - clr %o1 - sethi %hi(PAGE_SIZE), %o2 - or %o2, %lo(PAGE_SIZE), %o2 - call __memset + mov %l2, %o1 + call __bzero add %l0, %l2, %l0 cmp %l0, %l1 blu,pt %xcc, 1b - add %l0, %g4, %o0 + add %l0, %g0, %o0 /* Now clear empty_zero_page */ - clr %o1 - sethi %hi(PAGE_SIZE), %o2 - or %o2, %lo(PAGE_SIZE), %o2 - call __memset - mov %g4, %o0 + mov %l2, %o1 + call __bzero + mov %g3, %o0 mov %l6, %o1 ! OpenPROM stack call prom_init @@ -320,36 +305,45 @@ sun4u_init: .globl setup_tba setup_tba: + save %sp, -160, %sp + + rdpr %tba, %g7 + sethi %hi(prom_tba), %o1 + or %o1, %lo(prom_tba), %o1 + stx %g7, [%o1] + + /* Setup "Linux" globals 8-) */ + rdpr %pstate, %o1 + mov %g6, %o2 + wrpr %o1, (PSTATE_AG|PSTATE_IE), %pstate sethi %hi(sparc64_ttable_tl0), %g5 - add %g5, %g4, %g5 wrpr %g5, %tba + mov %o2, %g6 /* Set up MMU globals */ - rdpr %pstate, %o1 - wrpr %o1, PSTATE_MG, %pstate + wrpr %o1, (PSTATE_MG|PSTATE_IE), %pstate /* PGD/PMD offset mask, used by TLB miss handlers. */ sethi %hi(0x1ff8), %g2 or %g2, %lo(0x1ff8), %g2 /* Kernel PGDIR used by TLB miss handlers. */ - mov %o0, %g6 + mov %i0, %g6 /* To catch bootup bugs, this is user PGDIR for TLB miss handlers. */ clr %g7 /* Setup Interrupt globals */ - wrpr %o1, PSTATE_IG, %pstate - sethi %uhi(ivector_to_mask), %g4 - or %g4, %ulo(ivector_to_mask), %g4 + wrpr %o1, (PSTATE_IG|PSTATE_IE), %pstate sethi %hi(ivector_to_mask), %g5 - or %g5, %lo(ivector_to_mask), %g5 - or %g5, %g4, %g1 /* IVECTOR table */ + or %g5, %lo(ivector_to_mask), %g1 /* IVECTOR table */ mov 0x40, %g2 /* INTR data 0 register */ - andn %o1, PSTATE_IE, %o1 + /* Ok, we're done setting up all the state our trap mechanims needs, + * now get back into normal globals and let the PROM know what it up. + */ wrpr %g0, %g0, %wstate - wrpr %o1, %g0, %pstate + wrpr %o1, PSTATE_IE, %pstate /* Zap TSB BASE to zero with TSB_size==1. */ mov TSB_REG, %o4 @@ -359,8 +353,16 @@ setup_tba: membar #Sync - retl - nop + sethi %hi(sparc64_ttable_tl0), %g5 + call prom_set_trap_table + mov %g5, %o0 + + rdpr %pstate, %o1 + or %o1, PSTATE_IE, %o1 + wrpr %o1, 0, %pstate + + ret + restore sparc64_boot_end: .skip 0x2000 + _start - sparc64_boot_end @@ -369,18 +371,21 @@ bootup_user_stack_end: bootup_kernel_stack: .skip 0x2000 -! 0xfffff80000008000 +! 0x0000000000408000 #include "ttable.S" +#include "etrap.S" +#include "rtrap.S" +#include "winfixup.S" +#include "entry.S" /* This is just anal retentiveness on my part... */ .align 16384 .data .align 8 - .globl nwindows, nwindowsm1 -nwindows: .xword 0 -nwindowsm1: .xword 0 + .globl prom_tba +prom_tba: .xword 0 .section ".fixup",#alloc,#execinstr .globl __ret_efault __ret_efault: diff --git a/arch/sparc64/kernel/ioctl32.c b/arch/sparc64/kernel/ioctl32.c index d3792dec6..b7a0f312d 100644 --- a/arch/sparc64/kernel/ioctl32.c +++ b/arch/sparc64/kernel/ioctl32.c @@ -1,4 +1,4 @@ -/* $Id: ioctl32.c,v 1.8 1997/06/04 13:05:15 jj Exp $ +/* $Id: ioctl32.c,v 1.13 1997/07/17 02:20:38 davem Exp $ * ioctl32.c: Conversion between 32bit and 64bit native ioctls. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -20,9 +20,16 @@ #include <linux/kd.h> #include <linux/route.h> #include <linux/netlink.h> +#include <linux/vt.h> +#include <linux/fs.h> +#include <linux/fd.h> #include <asm/types.h> #include <asm/uaccess.h> +#include <asm/fbio.h> +#include <asm/kbio.h> +#include <asm/vuid_event.h> +#include <asm/rtc.h> /* As gcc will warn about casting u32 to some ptr, we have to cast it to * unsigned long first, and that's what is A() for. @@ -370,14 +377,122 @@ static inline int hdio_getgeo(unsigned int fd, u32 arg) return err; } +struct fbcmap32 { + int index; /* first element (0 origin) */ + int count; + u32 red; + u32 green; + u32 blue; +}; + +#define FBIOPUTCMAP32 _IOW('F', 3, struct fbcmap32) +#define FBIOGETCMAP32 _IOW('F', 4, struct fbcmap32) + +static inline int fbiogetputcmap(unsigned int fd, unsigned int cmd, u32 arg) +{ + struct fbcmap f; + int ret; + char red[256], green[256], blue[256]; + u32 r, g, b; + unsigned long old_fs = get_fs(); + + if (get_user(f.index, &(((struct fbcmap32 *)A(arg))->index)) || + __get_user(f.count, &(((struct fbcmap32 *)A(arg))->count)) || + __get_user(r, &(((struct fbcmap32 *)A(arg))->red)) || + __get_user(g, &(((struct fbcmap32 *)A(arg))->green)) || + __get_user(b, &(((struct fbcmap32 *)A(arg))->blue))) + return -EFAULT; + if ((f.index < 0) || (f.index > 255)) return -EINVAL; + if (f.index + f.count > 256) + f.count = 256 - f.index; + if (cmd == FBIOPUTCMAP32) { + if (copy_from_user (red, (char *)A(r), f.count) || + copy_from_user (green, (char *)A(g), f.count) || + copy_from_user (blue, (char *)A(b), f.count)) + return -EFAULT; + } + f.red = red; f.green = green; f.blue = blue; + set_fs (KERNEL_DS); + ret = sys_ioctl (fd, (cmd == FBIOPUTCMAP32) ? FBIOPUTCMAP : FBIOGETCMAP, (long)&f); + set_fs (old_fs); + if (!ret && cmd == FBIOGETCMAP32) { + if (copy_to_user ((char *)A(r), red, f.count) || + copy_to_user ((char *)A(g), green, f.count) || + copy_to_user ((char *)A(b), blue, f.count)) + return -EFAULT; + } + return ret; +} + +struct fbcursor32 { + short set; /* what to set, choose from the list above */ + short enable; /* cursor on/off */ + struct fbcurpos pos; /* cursor position */ + struct fbcurpos hot; /* cursor hot spot */ + struct fbcmap32 cmap; /* color map info */ + struct fbcurpos size; /* cursor bit map size */ + u32 image; /* cursor image bits */ + u32 mask; /* cursor mask bits */ +}; + +#define FBIOSCURSOR32 _IOW('F', 24, struct fbcursor32) +#define FBIOGCURSOR32 _IOW('F', 25, struct fbcursor32) + +static inline int fbiogscursor(unsigned int fd, unsigned int cmd, u32 arg) +{ + struct fbcursor f; + int ret; + char red[2], green[2], blue[2]; + char image[128], mask[128]; + u32 r, g, b; + u32 m, i; + unsigned long old_fs = get_fs(); + + if (copy_from_user (&f, (struct fbcursor32 *)A(arg), 2 * sizeof (short) + 2 * sizeof(struct fbcurpos)) || + __get_user(f.size.fbx, &(((struct fbcursor32 *)A(arg))->size.fbx)) || + __get_user(f.size.fby, &(((struct fbcursor32 *)A(arg))->size.fby)) || + __get_user(f.cmap.index, &(((struct fbcursor32 *)A(arg))->cmap.index)) || + __get_user(f.cmap.count, &(((struct fbcursor32 *)A(arg))->cmap.count)) || + __get_user(r, &(((struct fbcursor32 *)A(arg))->cmap.red)) || + __get_user(g, &(((struct fbcursor32 *)A(arg))->cmap.green)) || + __get_user(b, &(((struct fbcursor32 *)A(arg))->cmap.blue)) || + __get_user(m, &(((struct fbcursor32 *)A(arg))->mask)) || + __get_user(i, &(((struct fbcursor32 *)A(arg))->image))) + return -EFAULT; + if (f.set & FB_CUR_SETCMAP) { + if ((uint) f.size.fby > 32) + return -EINVAL; + if (copy_from_user (mask, (char *)A(m), f.size.fby * 4) || + copy_from_user (image, (char *)A(i), f.size.fby * 4)) + return -EFAULT; + f.image = image; f.mask = mask; + } + if (f.set & FB_CUR_SETCMAP) { + if (copy_from_user (red, (char *)A(r), 2) || + copy_from_user (green, (char *)A(g), 2) || + copy_from_user (blue, (char *)A(b), 2)) + return -EFAULT; + f.cmap.red = red; f.cmap.green = green; f.cmap.blue = blue; + } + set_fs (KERNEL_DS); + ret = sys_ioctl (fd, FBIOSCURSOR, (long)&f); + set_fs (old_fs); + return ret; +} + asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) { struct file * filp; int error = -EBADF; lock_kernel(); - if (fd >= NR_OPEN || !(filp = current->files->fd[fd])) + if(fd >= NR_OPEN) + goto out; + + filp = current->files->fd[fd]; + if(!filp) goto out; + if (!filp->f_op || !filp->f_op->ioctl) { error = sys_ioctl (fd, cmd, (unsigned long)arg); goto out; @@ -431,6 +546,15 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) case BLKGETSIZE: error = w_long(fd, cmd, arg); goto out; + + case FBIOPUTCMAP32: + case FBIOGETCMAP32: + error = fbiogetputcmap(fd, cmd, arg); + goto out; + + case FBIOSCURSOR32: + error = fbiogscursor(fd, cmd, arg); + goto out; /* List here exlicitly which ioctl's are known to have * compatable types passed or none at all... @@ -471,6 +595,17 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) case TIOCSPGRP: case TIOCGPGRP: case TIOCSCTTY: + + /* Big F */ + case FBIOGTYPE: + case FBIOSATTR: + case FBIOGATTR: + case FBIOSVIDEO: + case FBIOGVIDEO: + case FBIOGCURSOR32: /* This is not implemented yet. Later it should be converted... */ + case FBIOSCURPOS: + case FBIOGCURPOS: + case FBIOGCURMAX: /* Little f */ case FIOCLEX: @@ -479,6 +614,19 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) case FIONBIO: case FIONREAD: /* This is also TIOCINQ */ + /* 0x00 */ + case FIBMAP: + case FIGETBSZ: + + /* 0x02 -- Floppy ioctls */ + case FDSETEMSGTRESH: + case FDFLUSH: + case FDSETMAXERRS: + case FDGETMAXERRS: + case FDGETDRVTYP: + case FDEJECT: + /* XXX The rest need struct floppy_* translations. */ + /* 0x12 */ case BLKRRPART: case BLKFLSBUF: @@ -495,6 +643,59 @@ asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, u32 arg) case KDSIGACCEPT: case KDGETKEYCODE: case KDSETKEYCODE: + case KIOCSOUND: + case KDMKTONE: + case KDGKBTYPE: + case KDSETMODE: + case KDGETMODE: + case KDSKBMODE: + case KDGKBMODE: + case KDSKBMETA: + case KDGKBMETA: + case KDGKBENT: + case KDSKBENT: + case KDGKBSENT: + case KDSKBSENT: + case KDGKBDIACR: + case KDSKBDIACR: + case KDGKBLED: + case KDSKBLED: + case KDGETLED: + case KDSETLED: + + /* Little k */ + case KIOCTYPE: + case KIOCLAYOUT: + case KIOCGTRANS: + case KIOCTRANS: + case KIOCCMD: + case KIOCSDIRECT: + case KIOCSLED: + case KIOCGLED: + case KIOCSRATE: + case KIOCGRATE: + + /* Big V */ + case VT_SETMODE: + case VT_GETMODE: + case VT_GETSTATE: + case VT_OPENQRY: + case VT_ACTIVATE: + case VT_WAITACTIVE: + case VT_RELDISP: + case VT_DISALLOCATE: + case VT_RESIZE: + case VT_RESIZEX: + case VT_LOCKSWITCH: + case VT_UNLOCKSWITCH: + + /* Little v */ + case VUIDSFORMAT: + case VUIDGFORMAT: + + /* Little p (/dev/rtc etc.) */ + case RTCGET: + case RTCSET: /* Socket level stuff */ case FIOSETOWN: diff --git a/arch/sparc64/kernel/ioport.c b/arch/sparc64/kernel/ioport.c index 2f94e9102..390c33517 100644 --- a/arch/sparc64/kernel/ioport.c +++ b/arch/sparc64/kernel/ioport.c @@ -1,4 +1,4 @@ -/* $Id: ioport.c,v 1.7 1997/04/10 05:13:01 davem Exp $ +/* $Id: ioport.c,v 1.10 1997/06/30 09:24:02 jj Exp $ * ioport.c: Simple io mapping allocator. * * Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu) @@ -64,13 +64,7 @@ void *sparc_alloc_io (u32 address, void *virtual, int len, char *name, /* Tell Linux resource manager about the mapping */ request_region ((vaddr | offset), len, name); } else { - vaddr = occupy_region(sparc_iobase_vaddr, IOBASE_END, - (offset + len + PAGE_SIZE-1) & PAGE_MASK, PAGE_SIZE, name); - if (vaddr == 0) { - /* Usually we cannot see printks in this case. */ - prom_printf("alloc_io: cannot occupy %d region\n", len); - prom_halt(); - } + return __va(addr); } base_address = vaddr; @@ -88,6 +82,9 @@ void sparc_free_io (void *virtual, int len) { unsigned long vaddr = (unsigned long) virtual & PAGE_MASK; unsigned long plen = (((unsigned long)virtual & ~PAGE_MASK) + len + PAGE_SIZE-1) & PAGE_MASK; + + if (virtual >= PAGE_OFFSET + 0x10000000000UL) + return; release_region(vaddr, plen); diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index 3c9b1a89e..c4e7f0e74 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.13 1997/05/27 07:54:28 davem Exp $ +/* $Id: irq.c,v 1.16 1997/07/11 03:03:08 davem Exp $ * irq.c: UltraSparc IRQ handling/init/registry. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -47,7 +47,8 @@ unsigned long ivector_to_mask[NUM_IVECS]; static struct irqaction static_irqaction[MAX_STATIC_ALLOC]; static int static_irq_count = 0; -static struct irqaction *irq_action[NR_IRQS+1] = { +/* XXX Must be exported so that fast IRQ handlers can get at it... -DaveM */ +struct irqaction *irq_action[NR_IRQS+1] = { NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL }; @@ -279,13 +280,13 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *) /* If this is flagged as statically allocated then we use our * private struct which is never freed. */ - if(irqflags & SA_STATIC_ALLOC) + if(irqflags & SA_STATIC_ALLOC) { if(static_irq_count < MAX_STATIC_ALLOC) action = &static_irqaction[static_irq_count++]; else printk("Request for IRQ%d (%s) SA_STATIC_ALLOC failed " "using kmalloc\n", irq, name); - + } if(action == NULL) action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL); @@ -464,14 +465,101 @@ void sparc_floppy_irq(int irq, void *dev_cookie, struct pt_regs *regs) } #endif -/* XXX This needs to be written for floppy driver, and soon will be necessary - * XXX for serial driver as well. +/* The following assumes that the branch lies before the place we + * are branching to. This is the case for a trap vector... + * You have been warned. */ +#define SPARC_BRANCH(dest_addr, inst_addr) \ + (0x10800000 | ((((dest_addr)-(inst_addr))>>2)&0x3fffff)) + +#define SPARC_NOP (0x01000000) + +static void install_fast_irq(unsigned int cpu_irq, + void (*handler)(int, void *, struct pt_regs *)) +{ + extern unsigned long sparc64_ttable_tl0; + unsigned long ttent = (unsigned long) &sparc64_ttable_tl0; + unsigned int *insns; + + ttent += 0x820; + ttent += (cpu_irq - 1) << 5; + insns = (unsigned int *) ttent; + insns[0] = SPARC_BRANCH(((unsigned long) handler), + ((unsigned long)&insns[0])); + insns[1] = SPARC_NOP; + __asm__ __volatile__("flush %0" : : "r" (ttent)); +} + int request_fast_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long irqflags, const char *name) { - return -1; + struct irqaction *action; + unsigned long flags; + unsigned int cpu_irq, *imap, *iclr; + + /* XXX This really is not the way to do it, the "right way" + * XXX is to have drivers set SA_SBUS or something like that + * XXX in irqflags and we base our decision here on whether + * XXX that flag bit is set or not. + * + * In this case nobody can have a fast interrupt at the level + * where TICK interrupts live. + */ + if(irq == 14) + return -EINVAL; + cpu_irq = ino_to_pil[irq]; + + if(!handler) + return -EINVAL; + imap = irq_to_imap(irq); + action = *(cpu_irq + irq_action); + if(action) { + if(action->flags & SA_SHIRQ) + panic("Trying to register fast irq when already shared.\n"); + if(irqflags & SA_SHIRQ) + panic("Trying to register fast irq as shared.\n"); + printk("request_fast_irq: Trying to register yet already owned.\n"); + return -EBUSY; + } + save_and_cli(flags); + if(irqflags & SA_STATIC_ALLOC) { + if(static_irq_count < MAX_STATIC_ALLOC) + action = &static_irqaction[static_irq_count++]; + else + printk("Request for IRQ%d (%s) SA_STATIC_ALLOC failed " + "using kmalloc\n", irq, name); + } + if(action == NULL) + action = (struct irqaction *)kmalloc(sizeof(struct irqaction), + GFP_KERNEL); + if(!action) { + restore_flags(flags); + return -ENOMEM; + } + install_fast_irq(cpu_irq, handler); + + if(imap) { + int ivindex = (*imap & (SYSIO_IMAP_IGN | SYSIO_IMAP_INO)); + + ivector_to_mask[ivindex] = (1 << cpu_irq); + iclr = imap_to_iclr(imap); + action->mask = (unsigned long) iclr; + irqflags |= SA_SYSIO_MASKED; + } else + action->mask = 0; + + action->handler = handler; + action->flags = irqflags; + action->dev_id = NULL; + action->name = name; + action->next = NULL; + + *(cpu_irq + irq_action) = action; + + enable_irq(irq); + restore_flags(flags); + return 0; } /* We really don't need these at all on the Sparc. We only have @@ -496,27 +584,31 @@ static unsigned long tick_offset; /* XXX This doesn't belong here, just do this cruft in the timer.c handler code. */ static void timer_handler(int irq, void *dev_id, struct pt_regs *regs) { - extern void timer_interrupt(int, void *, struct pt_regs *); - unsigned long compare; - if (!(get_softint () & 1)) { /* Just to be sure... */ clear_softint(1 << 14); printk("Spurious level14 at %016lx\n", regs->tpc); return; - } + } else { + unsigned long compare, tick; + + do { + extern void timer_interrupt(int, void *, struct pt_regs *); - timer_interrupt(irq, dev_id, regs); + timer_interrupt(irq, dev_id, regs); - /* Acknowledge INT_TIMER */ - clear_softint(1 << 0); + /* Acknowledge INT_TIMER */ + clear_softint(1 << 0); - /* Set up for next timer tick. */ - __asm__ __volatile__("rd %%tick_cmpr, %0\n\t" - "add %0, %1, %0\n\t" - "wr %0, 0x0, %%tick_cmpr" - : "=r" (compare) - : "r" (tick_offset)); + /* Set up for next timer tick. */ + __asm__ __volatile__("rd %%tick_cmpr, %0\n\t" + "add %0, %2, %0\n\t" + "wr %0, 0x0, %%tick_cmpr\n\t" + "rd %%tick, %1" + : "=&r" (compare), "=r" (tick) + : "r" (tick_offset)); + } while(tick >= compare); + } } /* This is called from time_init() to get the jiffies timer going. */ @@ -558,6 +650,8 @@ struct sun5_timer { volatile u32 limit1, _unused3; } *prom_timers; +static u32 prom_limit0, prom_limit1; + static void map_prom_timers(void) { unsigned int addr[3]; @@ -582,7 +676,7 @@ static void map_prom_timers(void) prom_timers = (struct sun5_timer *) 0; return; } - prom_timers = (struct sun5_timer *) addr[0]; + prom_timers = (struct sun5_timer *) ((unsigned long)addr[0]); } static void kill_prom_timer(void) @@ -590,24 +684,39 @@ static void kill_prom_timer(void) if(!prom_timers) return; + /* Save them away for later. */ + prom_limit0 = prom_timers->limit0; + prom_limit1 = prom_timers->limit1; + /* Just as in sun4c/sun4m PROM uses timer which ticks at IRQ 14. * We turn both off here just to be paranoid. */ prom_timers->limit0 = 0; prom_timers->limit1 = 0; + + /* Wheee, eat the interrupt packet too... */ + __asm__ __volatile__(" + mov 0x40, %%g2 + ldxa [%%g0] %0, %%g1 + ldxa [%%g2] %1, %%g1 + stxa %%g0, [%%g0] %0 + membar #Sync +" : /* no outputs */ + : "i" (ASI_INTR_RECEIVE), "i" (ASI_UDB_INTR_R) + : "g1", "g2"); } -#if 0 /* Unused at this time. -DaveM */ -static void enable_prom_timer(void) +void enable_prom_timer(void) { if(!prom_timers) return; - /* Set it to fire off every 10ms. */ - prom_timers->limit1 = 0xa000270f; + /* Set it to whatever was there before. */ + prom_timers->limit1 = prom_limit1; prom_timers->count1 = 0; + prom_timers->limit0 = prom_limit0; + prom_timers->count0 = 0; } -#endif __initfunc(void init_IRQ(void)) { diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c index cc8183618..89f63f78f 100644 --- a/arch/sparc64/kernel/process.c +++ b/arch/sparc64/kernel/process.c @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.17 1997/06/02 06:33:32 davem Exp $ +/* $Id: process.c,v 1.29 1997/07/17 02:20:40 davem Exp $ * arch/sparc64/kernel/process.c * * Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu) @@ -17,6 +17,8 @@ #include <linux/sched.h> #include <linux/kernel.h> #include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> @@ -137,7 +139,7 @@ void machine_restart(char * cmd) prom_reboot(cmd); if (*reboot_command) prom_reboot(reboot_command); - prom_feval ("reset"); + prom_reboot(""); panic("Reboot failed!"); } @@ -146,8 +148,55 @@ void machine_power_off(void) machine_halt(); } -void show_regwindow(struct reg_window *rw) +static void show_regwindow32(struct pt_regs *regs) { + struct reg_window32 *rw; + struct reg_window32 r_w; + unsigned long old_fs; + + __asm__ __volatile__ ("flushw"); + rw = (struct reg_window32 *)((long)(unsigned)regs->u_regs[14]); + old_fs = get_fs(); + set_fs (USER_DS); + if (copy_from_user (&r_w, rw, sizeof(r_w))) { + set_fs (old_fs); + return; + } + rw = &r_w; + set_fs (old_fs); + printk("l0: %016x l1: %016x l2: %016x l3: %016x\n" + "l4: %016x l5: %016x l6: %016x l7: %016x\n", + rw->locals[0], rw->locals[1], rw->locals[2], rw->locals[3], + rw->locals[4], rw->locals[5], rw->locals[6], rw->locals[7]); + printk("i0: %016x i1: %016x i2: %016x i3: %016x\n" + "i4: %016x i5: %016x i6: %016x i7: %016x\n", + rw->ins[0], rw->ins[1], rw->ins[2], rw->ins[3], + rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]); +} + +static void show_regwindow(struct pt_regs *regs) +{ + struct reg_window *rw; + struct reg_window r_w; + unsigned long old_fs; + + if ((regs->tstate & TSTATE_PRIV) || !(current->tss.flags & SPARC_FLAG_32BIT)) { + __asm__ __volatile__ ("flushw"); + rw = (struct reg_window *)(regs->u_regs[14] + STACK_BIAS); + if (!(regs->tstate & TSTATE_PRIV)) { + old_fs = get_fs(); + set_fs (USER_DS); + if (copy_from_user (&r_w, rw, sizeof(r_w))) { + set_fs (old_fs); + return; + } + rw = &r_w; + set_fs (old_fs); + } + } else { + show_regwindow32(regs); + return; + } printk("l0: %016lx l1: %016lx l2: %016lx l3: %016lx\n", rw->locals[0], rw->locals[1], rw->locals[2], rw->locals[3]); printk("l4: %016lx l5: %016lx l6: %016lx l7: %016lx\n", @@ -158,18 +207,6 @@ void show_regwindow(struct reg_window *rw) rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]); } -void show_regwindow32(struct reg_window32 *rw) -{ - printk("l0: %08x l1: %08x l2: %08x l3: %08x\n" - "l4: %08x l5: %08x l6: %08x l7: %08x\n", - rw->locals[0], rw->locals[1], rw->locals[2], rw->locals[3], - rw->locals[4], rw->locals[5], rw->locals[6], rw->locals[7]); - printk("i0: %08x i1: %08x i2: %08x i3: %08x\n" - "i4: %08x i5: %08x i6: %08x i7: %08x\n", - rw->ins[0], rw->ins[1], rw->ins[2], rw->ins[3], - rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]); -} - void show_stackframe(struct sparc_stackf *sf) { unsigned long size; @@ -228,10 +265,7 @@ void show_stackframe32(struct sparc_stackf32 *sf) void show_regs(struct pt_regs * regs) { -#if __MPP__ - printk("CID: %d\n",mpp_cid()); -#endif - printk("TSTATE: %016lx TPC: %016lx TNPC: %016lx Y: %016lx\n", regs->tstate, + printk("TSTATE: %016lx TPC: %016lx TNPC: %016lx Y: %08x\n", regs->tstate, regs->tpc, regs->tnpc, regs->y); printk("g0: %016lx g1: %016lx g2: %016lx g3: %016lx\n", regs->u_regs[0], regs->u_regs[1], regs->u_regs[2], @@ -245,16 +279,11 @@ void show_regs(struct pt_regs * regs) printk("o4: %016lx o5: %016lx sp: %016lx ret_pc: %016lx\n", regs->u_regs[12], regs->u_regs[13], regs->u_regs[14], regs->u_regs[15]); -#if 0 - show_regwindow((struct reg_window *)(regs->u_regs[14] + STACK_BIAS)); -#endif + show_regwindow(regs); } void show_regs32(struct pt_regs32 *regs) { -#if __MPP__ - printk("CID: %d\n",mpp_cid()); -#endif printk("PSR: %08x PC: %08x NPC: %08x Y: %08x\n", regs->psr, regs->pc, regs->npc, regs->y); printk("g0: %08x g1: %08x g2: %08x g3: %08x\n", @@ -269,7 +298,6 @@ void show_regs32(struct pt_regs32 *regs) printk("o4: %08x o5: %08x sp: %08x ret_pc: %08x\n", regs->u_regs[12], regs->u_regs[13], regs->u_regs[14], regs->u_regs[15]); - show_regwindow32((struct reg_window32 *)((unsigned long)regs->u_regs[14])); } void show_thread(struct thread_struct *tss) @@ -290,46 +318,22 @@ void show_thread(struct thread_struct *tss) continue; printk("reg_window[%d]:\n", i); printk("stack ptr: 0x%016lx\n", tss->rwbuf_stkptrs[i]); - show_regwindow(&tss->reg_window[i]); } printk("w_saved: 0x%08lx\n", tss->w_saved); - /* XXX missing: float_regs */ - printk("fsr: 0x%016lx\n", tss->fsr); - printk("sstk_info.stack: 0x%016lx\n", (unsigned long)tss->sstk_info.the_stack); printk("sstk_info.status: 0x%016lx\n", (unsigned long)tss->sstk_info.cur_status); - printk("flags: 0x%016lx\n", tss->flags); - printk("current_ds: 0x%016x\n", tss->current_ds); + printk("flags: 0x%08x\n", tss->flags); + printk("current_ds: 0x%016lx\n", tss->current_ds); /* XXX missing: core_exec */ } -/* - * Free current thread data structures etc.. - */ +/* Free current thread data structures etc.. */ void exit_thread(void) { -#ifndef __SMP__ - if(last_task_used_math == current) { -#else - if(current->flags & PF_USEDFPU) { -#endif - fprs_write(FPRS_FEF); - if(current->tss.flags & SPARC_FLAG_32BIT) - fpsave32((unsigned long *)¤t->tss.float_regs[0], - ¤t->tss.fsr); - else - fpsave((unsigned long *)¤t->tss.float_regs[0], - ¤t->tss.fsr); -#ifndef __SMP__ - last_task_used_math = NULL; -#else - current->flags &= ~PF_USEDFPU; -#endif - } } void flush_thread(void) @@ -338,28 +342,12 @@ void flush_thread(void) current->tss.sstk_info.cur_status = 0; current->tss.sstk_info.the_stack = 0; - /* No new signal delivery by default */ + /* No new signal delivery by default. */ current->tss.new_signal = 0; -#ifndef __SMP__ - if(last_task_used_math == current) { -#else - if(current->flags & PF_USEDFPU) { -#endif - fprs_write(FPRS_FEF); - if(current->tss.flags & SPARC_FLAG_32BIT) - fpsave32((unsigned long *)¤t->tss.float_regs[0], - ¤t->tss.fsr); - else - fpsave((unsigned long *)¤t->tss.float_regs[0], - ¤t->tss.fsr); -#ifndef __SMP__ - last_task_used_math = NULL; -#else - current->flags &= ~PF_USEDFPU; -#endif - } + current->flags &= ~PF_USEDFPU; /* Now, this task is no longer a kernel thread. */ + current->tss.current_ds = USER_DS; if(current->tss.flags & SPARC_FLAG_KTHREAD) { current->tss.flags &= ~SPARC_FLAG_KTHREAD; @@ -368,78 +356,38 @@ void flush_thread(void) */ get_mmu_context(current); } - current->tss.current_ds = USER_DS; - spitfire_set_secondary_context (current->mm->context); + current->tss.ctx = current->mm->context & 0x1fff; + spitfire_set_secondary_context (current->tss.ctx); + __asm__ __volatile__("flush %g6"); } -static __inline__ void copy_regs(struct pt_regs *dst, struct pt_regs *src) +/* It's a bit more tricky when 64-bit tasks are involved... */ +static unsigned long clone_stackframe(unsigned long csp, unsigned long psp) { - __asm__ __volatile__("ldd\t[%1 + 0x00], %%g2\n\t" - "ldd\t[%1 + 0x08], %%g4\n\t" - "ldd\t[%1 + 0x10], %%o4\n\t" - "std\t%%g2, [%0 + 0x00]\n\t" - "std\t%%g4, [%0 + 0x08]\n\t" - "std\t%%o4, [%0 + 0x10]\n\t" - "ldd\t[%1 + 0x18], %%g2\n\t" - "ldd\t[%1 + 0x20], %%g4\n\t" - "ldd\t[%1 + 0x28], %%o4\n\t" - "std\t%%g2, [%0 + 0x18]\n\t" - "std\t%%g4, [%0 + 0x20]\n\t" - "std\t%%o4, [%0 + 0x28]\n\t" - "ldd\t[%1 + 0x30], %%g2\n\t" - "ldd\t[%1 + 0x38], %%g4\n\t" - "ldd\t[%1 + 0x40], %%o4\n\t" - "std\t%%g2, [%0 + 0x30]\n\t" - "std\t%%g4, [%0 + 0x38]\n\t" - "ldd\t[%1 + 0x48], %%g2\n\t" - "std\t%%o4, [%0 + 0x40]\n\t" - "std\t%%g2, [%0 + 0x48]\n\t" : : - "r" (dst), "r" (src) : - "g2", "g3", "g4", "g5", "o4", "o5"); -} - -static __inline__ void copy_regwin(struct reg_window *dst, struct reg_window *src) -{ - __asm__ __volatile__("ldd\t[%1 + 0x00], %%g2\n\t" - "ldd\t[%1 + 0x08], %%g4\n\t" - "ldd\t[%1 + 0x10], %%o4\n\t" - "std\t%%g2, [%0 + 0x00]\n\t" - "std\t%%g4, [%0 + 0x08]\n\t" - "std\t%%o4, [%0 + 0x10]\n\t" - "ldd\t[%1 + 0x18], %%g2\n\t" - "ldd\t[%1 + 0x20], %%g4\n\t" - "ldd\t[%1 + 0x28], %%o4\n\t" - "std\t%%g2, [%0 + 0x18]\n\t" - "std\t%%g4, [%0 + 0x20]\n\t" - "std\t%%o4, [%0 + 0x28]\n\t" - "ldd\t[%1 + 0x30], %%g2\n\t" - "ldd\t[%1 + 0x38], %%g4\n\t" - "std\t%%g2, [%0 + 0x30]\n\t" - "std\t%%g4, [%0 + 0x38]\n\t" : : - "r" (dst), "r" (src) : - "g2", "g3", "g4", "g5", "o4", "o5"); -} - -static __inline__ struct sparc_stackf * -clone_stackframe(struct sparc_stackf *dst, struct sparc_stackf *src) -{ - struct sparc_stackf *sp; - -#if 0 - unsigned long size; - size = ((unsigned long)src->fp) - ((unsigned long)src); - sp = (struct sparc_stackf *)(((unsigned long)dst) - size); - - if (copy_to_user(sp, src, size)) - return 0; - if (put_user(dst, &sp->fp)) + unsigned long fp, distance, rval; + + if(!(current->tss.flags & SPARC_FLAG_32BIT)) { + csp += STACK_BIAS; + psp += STACK_BIAS; + __get_user(fp, &(((struct reg_window *)psp)->ins[6])); + } else + __get_user(fp, &(((struct reg_window32 *)psp)->ins[6])); + distance = fp - psp; + rval = (csp - distance); + if(copy_in_user(rval, psp, distance)) return 0; -#endif - return sp; + if(current->tss.flags & SPARC_FLAG_32BIT) { + if(put_user(((u32)csp), &(((struct reg_window32 *)rval)->ins[6]))) + return 0; + return rval; + } else { + if(put_user(((u64)csp - STACK_BIAS), + &(((struct reg_window *)rval)->ins[6]))) + return 0; + return rval - STACK_BIAS; + } } -/* #define DEBUG_WINFIXUPS */ - /* Standard stuff. */ static inline void shift_window_buffer(int first_win, int last_win, struct thread_struct *tp) @@ -461,16 +409,16 @@ void synchronize_user_stack(void) flush_user_windows(); if((window = tp->w_saved) != 0) { int winsize = REGWIN_SZ; + int bias = 0; -#ifdef DEBUG_WINFIXUPS - printk("sus(%d", (int)window); -#endif if(tp->flags & SPARC_FLAG_32BIT) winsize = REGWIN32_SZ; + else + bias = STACK_BIAS; window -= 1; do { - unsigned long sp = tp->rwbuf_stkptrs[window]; + unsigned long sp = (tp->rwbuf_stkptrs[window] + bias); struct reg_window *rwin = &tp->reg_window[window]; if(!copy_to_user((char *)sp, rwin, winsize)) { @@ -478,9 +426,6 @@ void synchronize_user_stack(void) tp->w_saved--; } } while(window--); -#ifdef DEBUG_WINFIXUPS - printk(")"); -#endif } } @@ -489,18 +434,18 @@ void fault_in_user_windows(struct pt_regs *regs) struct thread_struct *tp = ¤t->tss; unsigned long window; int winsize = REGWIN_SZ; + int bias = 0; if(tp->flags & SPARC_FLAG_32BIT) winsize = REGWIN32_SZ; + else + bias = STACK_BIAS; flush_user_windows(); window = tp->w_saved; -#ifdef DEBUG_WINFIXUPS - printk("fiuw(%d", (int)window); -#endif if(window != 0) { window -= 1; do { - unsigned long sp = tp->rwbuf_stkptrs[window]; + unsigned long sp = (tp->rwbuf_stkptrs[window] + bias); struct reg_window *rwin = &tp->reg_window[window]; if(copy_to_user((char *)sp, rwin, winsize)) @@ -508,9 +453,6 @@ void fault_in_user_windows(struct pt_regs *regs) } while(window--); } current->tss.w_saved = 0; -#ifdef DEBUG_WINFIXUPS - printk(")"); -#endif } /* Copy a Sparc thread. The fork() return value conventions @@ -530,97 +472,49 @@ extern void ret_from_syscall(void); int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, struct task_struct *p, struct pt_regs *regs) { - struct pt_regs *childregs; - struct reg_window *new_stack, *old_stack; unsigned long stack_offset; - -#ifndef __SMP__ - if(last_task_used_math == current) { -#else - if(current->flags & PF_USEDFPU) { -#endif - fprs_write(FPRS_FEF); - fpsave((unsigned long *)&p->tss.float_regs[0], &p->tss.fsr); -#ifdef __SMP__ - current->flags &= ~PF_USEDFPU; -#endif - } + char *child_trap_frame; + int tframe_size; /* Calculate offset to stack_frame & pt_regs */ - stack_offset = ((PAGE_SIZE<<1) - TRACEREG_SZ); - - if(regs->tstate & TSTATE_PRIV) - stack_offset -= REGWIN_SZ; - - childregs = ((struct pt_regs *) (((unsigned long)p) + stack_offset)); - *childregs = *regs; - new_stack = (((struct reg_window *) childregs) - 1); - old_stack = (((struct reg_window *) regs) - 1); - *new_stack = *old_stack; - - p->tss.ksp = ((unsigned long) new_stack) - STACK_BIAS; + stack_offset = (((PAGE_SIZE << 1) - + ((sizeof(unsigned int)*64) + (2*sizeof(unsigned long)))) & + ~(64 - 1)) - (TRACEREG_SZ+REGWIN_SZ); + tframe_size = (TRACEREG_SZ + REGWIN_SZ) + + (sizeof(unsigned int) * 64) + (2 * sizeof(unsigned long)); + child_trap_frame = ((char *)p) + stack_offset; + memcpy(child_trap_frame, (((struct reg_window *)regs)-1), tframe_size); + p->tss.ksp = ((unsigned long) child_trap_frame) - STACK_BIAS; p->tss.kpc = ((unsigned long) ret_from_syscall) - 0x8; - p->tss.kregs = childregs; - - /* Don't look... */ + p->tss.kregs = (struct pt_regs *)(child_trap_frame+sizeof(struct reg_window)); p->tss.cwp = regs->u_regs[UREG_G0]; - - /* tss.wstate was copied by do_fork() */ - if(regs->tstate & TSTATE_PRIV) { - childregs->u_regs[UREG_FP] = p->tss.ksp; + p->tss.kregs->u_regs[UREG_FP] = p->tss.ksp; p->tss.flags |= SPARC_FLAG_KTHREAD; p->tss.current_ds = KERNEL_DS; - childregs->u_regs[UREG_G6] = (unsigned long) p; + p->tss.ctx = 0; + p->tss.kregs->u_regs[UREG_G6] = (unsigned long) p; } else { - childregs->u_regs[UREG_FP] = sp; + p->tss.kregs->u_regs[UREG_FP] = sp; p->tss.flags &= ~SPARC_FLAG_KTHREAD; p->tss.current_ds = USER_DS; - -#if 0 + p->tss.ctx = (p->mm->context & 0x1fff); if (sp != regs->u_regs[UREG_FP]) { - struct sparc_stackf *childstack; - struct sparc_stackf *parentstack; - - /* - * This is a clone() call with supplied user stack. - * Set some valid stack frames to give to the child. - */ - childstack = (struct sparc_stackf *)sp; - parentstack = (struct sparc_stackf *)regs->u_regs[UREG_FP]; + unsigned long csp; -#if 0 - printk("clone: parent stack:\n"); - show_stackframe(parentstack); -#endif - - childstack = clone_stackframe(childstack, parentstack); - if (!childstack) + csp = clone_stackframe(sp, regs->u_regs[UREG_FP]); + if(!csp) return -EFAULT; - -#if 0 - printk("clone: child stack:\n"); - show_stackframe(childstack); -#endif - - childregs->u_regs[UREG_FP] = (unsigned long)childstack; + p->tss.kregs->u_regs[UREG_FP] = csp; } -#endif } /* Set the return value for the child. */ - childregs->u_regs[UREG_I0] = current->pid; - childregs->u_regs[UREG_I1] = 1; + p->tss.kregs->u_regs[UREG_I0] = current->pid; + p->tss.kregs->u_regs[UREG_I1] = 1; - /* Set the return value for the parent. */ + /* Set the second return value for the parent. */ regs->u_regs[UREG_I1] = 0; -#if 0 - printk("CHILD register dump\n"); - show_regs(childregs); - show_regwindow(new_stack); - while(1) - barrier(); -#endif return 0; } @@ -676,11 +570,19 @@ asmlinkage int sparc_execve(struct pt_regs *regs) if(regs->u_regs[UREG_G1] == 0) base = 1; - error = getname((char *) regs->u_regs[base + UREG_I0], &filename); - if(error) - return error; + lock_kernel(); + filename = getname((char *)regs->u_regs[base + UREG_I0]); + error = PTR_ERR(filename); + if(IS_ERR(filename)) + goto out; error = do_execve(filename, (char **) regs->u_regs[base + UREG_I1], (char **) regs->u_regs[base + UREG_I2], regs); putname(filename); + if(!error) { + fprs_write(0); + regs->fprs = 0; + } +out: + unlock_kernel(); return error; } diff --git a/arch/sparc64/kernel/ptrace.c b/arch/sparc64/kernel/ptrace.c index 24fe052cd..ac91df894 100644 --- a/arch/sparc64/kernel/ptrace.c +++ b/arch/sparc64/kernel/ptrace.c @@ -100,7 +100,16 @@ static inline void put_long(struct task_struct * tsk, struct vm_area_struct * vm /* this is a hack for non-kernel-mapped video buffers and similar */ flush_cache_page(vma, addr); if (MAP_NR(page) < max_mapnr) { - *(unsigned long *) (page + (addr & ~PAGE_MASK)) = data; + unsigned long pgaddr; + + pgaddr = page + (addr & ~PAGE_MASK); + *(unsigned long *) (pgaddr) = data; + + __asm__ __volatile__(" + membar #StoreStore + flush %0 +" : : "r" (pgaddr & ~7) : "memory"); + flush_page_to_ram(page); } /* we're bypassing pagetables, so we have to set the dirty bit ourselves */ @@ -138,7 +147,16 @@ static inline void put_int(struct task_struct * tsk, struct vm_area_struct * vma /* this is a hack for non-kernel-mapped video buffers and similar */ flush_cache_page(vma, addr); if (MAP_NR(page) < max_mapnr) { - *(unsigned int *) (page + (addr & ~PAGE_MASK)) = data; + unsigned long pgaddr; + + pgaddr = page + (addr & ~PAGE_MASK); + *(unsigned int *) (pgaddr) = data; + + __asm__ __volatile__(" + membar #StoreStore + flush %0 +" : : "r" (pgaddr & ~7) : "memory"); + flush_page_to_ram(page); } /* we're bypassing pagetables, so we have to set the dirty bit ourselves */ @@ -570,6 +588,21 @@ asmlinkage void do_ptrace(struct pt_regs *regs) pt_error_return(regs, ESRCH); goto out; } + + if(!(child->tss.flags & SPARC_FLAG_32BIT) && + ((request == PTRACE_READDATA64) || + (request == PTRACE_WRITEDATA64) || + (request == PTRACE_READTEXT64) || + (request == PTRACE_WRITETEXT64) || + (request == PTRACE_PEEKTEXT64) || + (request == PTRACE_POKETEXT64) || + (request == PTRACE_PEEKDATA64) || + (request == PTRACE_POKEDATA64))) { + addr = regs->u_regs[UREG_G2]; + addr2 = regs->u_regs[UREG_G3]; + request -= 30; /* wheee... */ + } + switch(request) { case PTRACE_PEEKTEXT: /* read word at location addr. */ case PTRACE_PEEKDATA: { @@ -641,195 +674,207 @@ asmlinkage void do_ptrace(struct pt_regs *regs) goto out; } - case PTRACE_GETREGS: - if (current->tss.flags & SPARC_FLAG_32BIT) { - struct pt_regs32 *pregs = (struct pt_regs32 *) addr; - struct pt_regs *cregs = child->tss.kregs; - int rval; - - if (__put_user(tstate_to_psr(cregs->tstate), (&pregs->psr)) || - __put_user(cregs->tpc, (&pregs->pc)) || - __put_user(cregs->tnpc, (&pregs->npc)) || - __put_user(cregs->y, (&pregs->y))) { + case PTRACE_GETREGS: { + struct pt_regs32 *pregs = (struct pt_regs32 *) addr; + struct pt_regs *cregs = child->tss.kregs; + int rval; + + if (__put_user(tstate_to_psr(cregs->tstate), (&pregs->psr)) || + __put_user(cregs->tpc, (&pregs->pc)) || + __put_user(cregs->tnpc, (&pregs->npc)) || + __put_user(cregs->y, (&pregs->y))) { + pt_error_return(regs, EFAULT); + goto out; + } + for(rval = 1; rval < 16; rval++) + if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) { pt_error_return(regs, EFAULT); goto out; } - for(rval = 1; rval < 16; rval++) - if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) { - pt_error_return(regs, EFAULT); - goto out; - } - pt_succ_return(regs, 0); + pt_succ_return(regs, 0); #ifdef DEBUG_PTRACE - printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]); + printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]); #endif + goto out; + } + + case PTRACE_GETREGS64: { + struct pt_regs *pregs = (struct pt_regs *) addr; + struct pt_regs *cregs = child->tss.kregs; + int rval; + + if (__put_user(cregs->tstate, (&pregs->tstate)) || + __put_user(cregs->tpc, (&pregs->tpc)) || + __put_user(cregs->tnpc, (&pregs->tnpc)) || + __put_user(cregs->y, (&pregs->y))) { + pt_error_return(regs, EFAULT); goto out; - } else { - struct pt_regs *pregs = (struct pt_regs *) addr; - struct pt_regs *cregs = child->tss.kregs; - int rval; - - if (__put_user(cregs->tstate, (&pregs->tstate)) || - __put_user(cregs->tpc, (&pregs->tpc)) || - __put_user(cregs->tnpc, (&pregs->tnpc)) || - __put_user(cregs->y, (&pregs->y))) { + } + for(rval = 1; rval < 16; rval++) + if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) { pt_error_return(regs, EFAULT); goto out; } - for(rval = 1; rval < 16; rval++) - if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) { - pt_error_return(regs, EFAULT); - goto out; - } - pt_succ_return(regs, 0); + pt_succ_return(regs, 0); #ifdef DEBUG_PTRACE - printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]); + printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]); #endif + goto out; + } + + case PTRACE_SETREGS: { + struct pt_regs32 *pregs = (struct pt_regs32 *) addr; + struct pt_regs *cregs = child->tss.kregs; + unsigned int psr, pc, npc, y; + int i; + + /* Must be careful, tracing process can only set certain + * bits in the psr. + */ + if (__get_user(psr, (&pregs->psr)) || + __get_user(pc, (&pregs->pc)) || + __get_user(npc, (&pregs->npc)) || + __get_user(y, (&pregs->y))) { + pt_error_return(regs, EFAULT); goto out; } - - case PTRACE_SETREGS: - if (current->tss.flags & SPARC_FLAG_32BIT) { - struct pt_regs32 *pregs = (struct pt_regs32 *) addr; - struct pt_regs *cregs = child->tss.kregs; - unsigned int psr, pc, npc, y; - int i; - - /* Must be careful, tracing process can only set certain - * bits in the psr. - */ - if (__get_user(psr, (&pregs->psr)) || - __get_user(pc, (&pregs->pc)) || - __get_user(npc, (&pregs->npc)) || - __get_user(y, (&pregs->y))) { + cregs->tstate &= ~(TSTATE_ICC); + cregs->tstate |= psr_to_tstate_icc(psr); + if(!((pc | npc) & 3)) { + cregs->tpc = pc; + cregs->tnpc = npc; + } + cregs->y = y; + for(i = 1; i < 16; i++) + if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) { pt_error_return(regs, EFAULT); goto out; } - cregs->tstate &= ~(TSTATE_ICC); - cregs->tstate |= psr_to_tstate_icc(psr); - if(!((pc | npc) & 3)) { - cregs->tpc = pc; - cregs->tpc = npc; - } - cregs->y = y; - for(i = 1; i < 16; i++) - if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) { - pt_error_return(regs, EFAULT); - goto out; - } - pt_succ_return(regs, 0); - goto out; - } else { - struct pt_regs *pregs = (struct pt_regs *) addr; - struct pt_regs *cregs = child->tss.kregs; - unsigned long tstate, tpc, tnpc, y; - int i; + pt_succ_return(regs, 0); + goto out; + } - /* Must be careful, tracing process can only set certain - * bits in the psr. - */ - if (__get_user(tstate, (&pregs->tstate)) || - __get_user(tpc, (&pregs->tpc)) || - __get_user(tnpc, (&pregs->tnpc)) || - __get_user(y, (&pregs->y))) { - pt_error_return(regs, EFAULT); - goto out; - } - tstate &= (TSTATE_ICC | TSTATE_XCC); - cregs->tstate &= ~(TSTATE_ICC | TSTATE_XCC); - cregs->tstate |= tstate; - if(!((tpc | tnpc) & 3)) { - cregs->tpc = tpc; - cregs->tnpc = tnpc; - } - cregs->y = y; - for(i = 1; i < 16; i++) - if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) { - pt_error_return(regs, EFAULT); - goto out; - } - pt_succ_return(regs, 0); + case PTRACE_SETREGS64: { + struct pt_regs *pregs = (struct pt_regs *) addr; + struct pt_regs *cregs = child->tss.kregs; + unsigned long tstate, tpc, tnpc, y; + int i; + + /* Must be careful, tracing process can only set certain + * bits in the psr. + */ + if (__get_user(tstate, (&pregs->tstate)) || + __get_user(tpc, (&pregs->tpc)) || + __get_user(tnpc, (&pregs->tnpc)) || + __get_user(y, (&pregs->y))) { + pt_error_return(regs, EFAULT); goto out; } - - case PTRACE_GETFPREGS: - if (current->tss.flags & SPARC_FLAG_32BIT) { - struct fps { - unsigned int regs[32]; - unsigned int fsr; - unsigned int flags; - unsigned int extra; - unsigned int fpqd; - struct fq { - unsigned int insnaddr; - unsigned int insn; - } fpq[16]; - } *fps = (struct fps *) addr; - - if (copy_to_user(&fps->regs[0], &child->tss.float_regs[0], (32 * sizeof(unsigned int))) || - __put_user(child->tss.fsr, (&fps->fsr)) || - __put_user(0, (&fps->fpqd)) || - __put_user(0, (&fps->flags)) || - __put_user(0, (&fps->extra)) || - clear_user(&fps->fpq[0], 32 * sizeof(unsigned int))) { + tstate &= (TSTATE_ICC | TSTATE_XCC); + cregs->tstate &= ~(TSTATE_ICC | TSTATE_XCC); + cregs->tstate |= tstate; + if(!((tpc | tnpc) & 3)) { + cregs->tpc = tpc; + cregs->tnpc = tnpc; + } + cregs->y = y; + for(i = 1; i < 16; i++) + if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) { pt_error_return(regs, EFAULT); goto out; } - pt_succ_return(regs, 0); + pt_succ_return(regs, 0); + goto out; + } + + case PTRACE_GETFPREGS: { + struct fps { + unsigned int regs[32]; + unsigned int fsr; + unsigned int flags; + unsigned int extra; + unsigned int fpqd; + struct fq { + unsigned int insnaddr; + unsigned int insn; + } fpq[16]; + } *fps = (struct fps *) addr; + unsigned long *fpregs = (unsigned long *)(child->tss.kregs+1); + + if (copy_to_user(&fps->regs[0], fpregs, + (32 * sizeof(unsigned int))) || + __put_user(((unsigned int)fpregs[32]), (&fps->fsr)) || + __put_user(0, (&fps->fpqd)) || + __put_user(0, (&fps->flags)) || + __put_user(0, (&fps->extra)) || + clear_user(&fps->fpq[0], 32 * sizeof(unsigned int))) { + pt_error_return(regs, EFAULT); goto out; - } else { - struct fps { - unsigned int regs[64]; - unsigned long fsr; - } *fps = (struct fps *) addr; + } + pt_succ_return(regs, 0); + goto out; + } - if (copy_to_user(&fps->regs[0], &child->tss.float_regs[0], (64 * sizeof(unsigned int))) || - __put_user(child->tss.fsr, (&fps->fsr))) { - pt_error_return(regs, EFAULT); - goto out; - } - pt_succ_return(regs, 0); + case PTRACE_GETFPREGS64: { + struct fps { + unsigned int regs[64]; + unsigned long fsr; + } *fps = (struct fps *) addr; + unsigned long *fpregs = (unsigned long *)(child->tss.kregs+1); + + if (copy_to_user(&fps->regs[0], fpregs, + (64 * sizeof(unsigned int))) || + __put_user(fpregs[32], (&fps->fsr))) { + pt_error_return(regs, EFAULT); goto out; } + pt_succ_return(regs, 0); + goto out; + } - case PTRACE_SETFPREGS: - if (current->tss.flags & SPARC_FLAG_32BIT) { - struct fps { - unsigned int regs[32]; - unsigned int fsr; - unsigned int flags; - unsigned int extra; - unsigned int fpqd; - struct fq { - unsigned int insnaddr; - unsigned int insn; - } fpq[16]; - } *fps = (struct fps *) addr; - unsigned fsr; - - if (copy_from_user(&child->tss.float_regs[0], &fps->regs[0], (32 * sizeof(unsigned int))) || - __get_user(fsr, (&fps->fsr))) { - pt_error_return(regs, EFAULT); - goto out; - } - child->tss.fsr &= 0xffffffff00000000UL; - child->tss.fsr |= fsr; - pt_succ_return(regs, 0); + case PTRACE_SETFPREGS: { + struct fps { + unsigned int regs[32]; + unsigned int fsr; + unsigned int flags; + unsigned int extra; + unsigned int fpqd; + struct fq { + unsigned int insnaddr; + unsigned int insn; + } fpq[16]; + } *fps = (struct fps *) addr; + unsigned long *fpregs = (unsigned long *)(child->tss.kregs+1); + unsigned fsr; + + if (copy_from_user(fpregs, &fps->regs[0], + (32 * sizeof(unsigned int))) || + __get_user(fsr, (&fps->fsr))) { + pt_error_return(regs, EFAULT); goto out; - } else { - struct fps { - unsigned int regs[64]; - unsigned long fsr; - } *fps = (struct fps *) addr; + } + fpregs[32] &= 0xffffffff00000000UL; + fpregs[32] |= fsr; + pt_succ_return(regs, 0); + goto out; + } - if (copy_from_user(&child->tss.float_regs[0], &fps->regs[0], (64 * sizeof(unsigned int))) || - __get_user(child->tss.fsr, (&fps->fsr))) { - pt_error_return(regs, EFAULT); - goto out; - } - pt_succ_return(regs, 0); + case PTRACE_SETFPREGS64: { + struct fps { + unsigned int regs[64]; + unsigned long fsr; + } *fps = (struct fps *) addr; + unsigned long *fpregs = (unsigned long *)(child->tss.kregs+1); + + if (copy_from_user(fpregs, &fps->regs[0], + (64 * sizeof(unsigned int))) || + __get_user(fpregs[32], (&fps->fsr))) { + pt_error_return(regs, EFAULT); goto out; } + pt_succ_return(regs, 0); + goto out; + } case PTRACE_READTEXT: case PTRACE_READDATA: { @@ -1022,7 +1067,10 @@ asmlinkage void syscall_trace(void) current->pid, current->exit_code); #endif if (current->exit_code) { - set_bit(current->exit_code + 31, ¤t->signal); + /* spin_lock_irq(¤t->sigmask_lock); */ + current->signal |= (1 << (current->exit_code - 1)); + /* spin_unlock_irq(¤t->sigmask_lock); */ } + current->exit_code = 0; } diff --git a/arch/sparc64/kernel/rtrap.S b/arch/sparc64/kernel/rtrap.S index 165b17ef0..9f087a969 100644 --- a/arch/sparc64/kernel/rtrap.S +++ b/arch/sparc64/kernel/rtrap.S @@ -1,4 +1,4 @@ -/* $Id: rtrap.S,v 1.21 1997/06/02 07:26:54 davem Exp $ +/* $Id: rtrap.S,v 1.28 1997/06/30 10:31:39 jj Exp $ * rtrap.S: Preparing for return from trap on Sparc V9. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -11,121 +11,124 @@ #include <asm/spitfire.h> #include <asm/head.h> - /* We assume here that this is entered with AG, MG and IG bits - * in pstate clear. - */ + .text + .align 32 + .globl rtrap_clr_l6, rtrap +#define PTREGS_OFF (STACK_BIAS + REGWIN_SZ) +rtrap_clr_l6: ba,pt %xcc, rtrap + clr %l6 +rtrap: sethi %hi(bh_active), %l2 + sethi %hi(bh_mask), %l1 + ldx [%l2 + %lo(bh_active)], %l4 + ldx [%l1 + %lo(bh_mask)], %l7 - .text - .align 32 - .globl rtrap_clr_l6, rtrap -rtrap_clr_l6: - ba,pt %xcc, rtrap - clr %l6 -rtrap: sethi %hi(bh_active), %l2 - or %l2, %lo(bh_active), %l2 - sethi %hi(bh_mask), %l1 - or %l1, %lo(bh_mask), %l1 - ldx [%l2 + %g4], %l3 - ldx [%l1 + %g4], %l4 + andcc %l4, %l7, %g0 + be,pt %xcc, 2f + nop + call do_bottom_half + nop +2: ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1 + sethi %hi(0xf << 20), %l4 + andcc %l1, TSTATE_PRIV, %l3 - andcc %l3, %l4, %g0 - be,pt %xcc, 2f - nop - call do_bottom_half - nop -2: ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE], %l1 - sethi %hi(0xf << 20), %l4 - andcc %l1, TSTATE_PRIV, %l3 + and %l1, %l4, %l4 + rdpr %pstate, %l7 + andn %l1, %l4, %l1 + be,pt %icc, to_user + andn %l7, PSTATE_IE, %l7 +rt_continue: ld [%sp + PTREGS_OFF + PT_V9_FPRS], %l2 + ld [%g6 + AOFF_task_tss + AOFF_thread_ctx], %l0 + ldx [%sp + PTREGS_OFF + PT_V9_G1], %g1 - and %l1, %l4, %l4 - rdpr %pstate, %l7 - andn %l1, %l4, %l1 - be,pt %icc, to_user - andn %l7, PSTATE_IE, %l7 -3: ldx [%g6 + AOFF_task_tss + AOFF_thread_ctx], %l0 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G1], %g1 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G2], %g2 + brnz,pn %l2, rt_fpu_restore + ldx [%sp + PTREGS_OFF + PT_V9_G2], %g2 +rt_after_fpu: ldx [%sp + PTREGS_OFF + PT_V9_G3], %g3 + mov %g6, %l6 + ldx [%sp + PTREGS_OFF + PT_V9_G4], %g4 + ldx [%sp + PTREGS_OFF + PT_V9_G5], %g5 + ldx [%sp + PTREGS_OFF + PT_V9_G6], %g6 + ldx [%sp + PTREGS_OFF + PT_V9_G7], %g7 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G3], %g3 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G4], %g4 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G5], %g5 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G6], %g6 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G7], %g7 - wrpr %l7, PSTATE_AG, %pstate - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0], %i0 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1], %i1 + wrpr %l7, PSTATE_AG, %pstate + ldx [%sp + PTREGS_OFF + PT_V9_I0], %i0 + ldx [%sp + PTREGS_OFF + PT_V9_I1], %i1 + ldx [%sp + PTREGS_OFF + PT_V9_I2], %i2 + ldx [%sp + PTREGS_OFF + PT_V9_I3], %i3 + ldx [%sp + PTREGS_OFF + PT_V9_I4], %i4 + ldx [%sp + PTREGS_OFF + PT_V9_I5], %i5 + ldx [%sp + PTREGS_OFF + PT_V9_I6], %i6 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2], %i2 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I3], %i3 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I4], %i4 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I5], %i5 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I6], %i6 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I7], %i7 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_Y], %o3 - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC], %l2 + ldx [%sp + PTREGS_OFF + PT_V9_I7], %i7 + ld [%sp + PTREGS_OFF + PT_V9_Y], %o3 + ldx [%sp + PTREGS_OFF + PT_V9_TPC], %l2 + ldx [%sp + PTREGS_OFF + PT_V9_TNPC], %o2 + wr %o3, %g0, %y + srl %l4, 20, %l4 + wrpr %l4, 0x0, %pil + wrpr %g0, 0x1, %tl - ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %o2 - wr %o3, %g0, %y - srl %l4, 20, %l4 - wrpr %l4, 0x0, %pil - wrpr %g0, 0x1, %tl - wrpr %l1, %g0, %tstate - wrpr %l2, %g0, %tpc - mov PRIMARY_CONTEXT, %l7 + wrpr %l1, %g0, %tstate + wrpr %l2, %g0, %tpc + mov PRIMARY_CONTEXT, %l7 + brnz,pn %l3, kern_rtt + wrpr %o2, %g0, %tnpc + stxa %l0, [%l7] ASI_DMMU + flush %l6 + rdpr %wstate, %l1 - wrpr %o2, %g0, %tnpc - brnz,a,pn %l3, 1f - restore - sethi %uhi(KERNBASE), %l5 - sllx %l5, 32, %l5 - stxa %l0, [%l7] ASI_DMMU - flush %l5 - rdpr %wstate, %l1 + rdpr %otherwin, %l2 + srl %l1, 3, %l1 + wrpr %l2, %g0, %canrestore + wrpr %l1, %g0, %wstate + wrpr %g0, %g0, %otherwin + restore + rdpr %canrestore, %g1 + wrpr %g1, 0x0, %cleanwin - rdpr %otherwin, %l2 - srl %l1, 3, %l1 - wrpr %l2, %g0, %canrestore - wrpr %l1, %g0, %wstate - wrpr %g0, %g0, %otherwin - restore - rdpr %canrestore, %g1 - wrpr %g1, 0x0, %cleanwin + retry +kern_rtt: restore + retry +to_user: sethi %hi(need_resched), %l0 + ld [%l0 + %lo(need_resched)], %l0 + wrpr %l7, PSTATE_IE, %pstate + brz,pt %l0, check_signal + ldx [%g6 + AOFF_task_signal], %l0 -1: retry -to_user: - sethi %hi(need_resched), %l0 - or %l0, %lo(need_resched), %l0 - ld [%l0 + %g4], %l0 - wrpr %l7, PSTATE_IE, %pstate - brz,pt %l0, check_signal - ldx [%g6 + AOFF_task_signal], %l0 - nop + call schedule + nop + ldx [%g6 + AOFF_task_signal], %l0 + nop +check_signal: ldx [%g6 + AOFF_task_blocked], %o0 + andncc %l0, %o0, %g0 + be,pt %xcc, check_user_wins + ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2 - call schedule - nop - ba,pt %xcc, check_signal - ldx [%g6 + AOFF_task_signal], %l0 -check_signal: - ldx [%g6 + AOFF_task_blocked], %o0 - andncc %l0, %o0, %g0 - be,a,pt %xcc, check_user_wins - ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2 - - mov %l5, %o2 - mov %l6, %o3 - call do_signal - add %sp, STACK_BIAS + REGWIN_SZ, %o1 - ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2 - clr %l6 + mov %l5, %o2 + mov %l6, %o3 + call do_signal + add %sp, STACK_BIAS + REGWIN_SZ, %o1 + ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2 + clr %l6 check_user_wins: - brz,pt %o2, 3b - nop + brz,pt %o2, rt_continue + nop + + call fault_in_user_windows + add %sp, STACK_BIAS + REGWIN_SZ, %o0 + ba,a,pt %xcc, rt_continue +rt_fpu_restore: wr %g0, FPRS_FEF, %fprs + add %sp, PTREGS_OFF + TRACEREG_SZ, %g4 + wr %g0, ASI_BLK_P, %asi + + membar #StoreLoad | #LoadLoad + ldda [%g4 + 0x000] %asi, %f0 + ldda [%g4 + 0x040] %asi, %f16 + ldda [%g4 + 0x080] %asi, %f32 + ldda [%g4 + 0x0c0] %asi, %f48 + ldx [%g4 + 0x100], %fsr + ldx [%g4 + 0x108], %g3 + membar #Sync - call fault_in_user_windows - add %sp, STACK_BIAS + REGWIN_SZ, %o0 - ba,a,pt %xcc, 3b - nop - nop - nop - nop - nop + b,pt %xcc, rt_after_fpu + wr %g3, 0, %gsr +#undef PTREGS_OFF diff --git a/arch/sparc64/kernel/setup.c b/arch/sparc64/kernel/setup.c index 832d3b97f..680baf7de 100644 --- a/arch/sparc64/kernel/setup.c +++ b/arch/sparc64/kernel/setup.c @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.7 1997/05/20 07:58:56 jj Exp $ +/* $Id: setup.c,v 1.10 1997/07/08 11:07:47 jj Exp $ * linux/arch/sparc64/kernel/setup.c * * Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu) @@ -35,6 +35,7 @@ #include <asm/page.h> #include <asm/pgtable.h> #include <asm/idprom.h> +#include <asm/head.h> struct screen_info screen_info = { 0, 0, /* orig-x, orig-y */ @@ -62,7 +63,6 @@ unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end) */ extern unsigned long sparc64_ttable_tl0; -extern void breakpoint(void); #if CONFIG_SUN_CONSOLE extern void console_restore_palette(void); #endif @@ -108,23 +108,13 @@ static int console_fb = 0; #endif static unsigned long memory_size = 0; +/* XXX Implement this at some point... */ void kernel_enter_debugger(void) { -#if 0 - if (boot_flags & BOOTME_KGDB) { - printk("KGDB: Entered\n"); - breakpoint(); - } -#endif } int obp_system_intr(void) { - if (boot_flags & BOOTME_KGDB) { - printk("KGDB: system interrupted\n"); - breakpoint(); - return 1; - } if (boot_flags & BOOTME_DEBUG) { printk("OBP: system interrupted\n"); prom_halt(); @@ -148,7 +138,7 @@ __initfunc(static void process_switch(char c)) break; case 'h': prom_printf("boot_flags_init: Halt!\n"); - halt(); + prom_halt(); break; default: printk("Unknown boot switch (-%c)\n", c); @@ -266,23 +256,9 @@ __initfunc(void setup_arch(char **cmdline_p, *cmdline_p = prom_getbootargs(); strcpy(saved_command_line, *cmdline_p); - prom_printf("BOOT: args[%s] saved[%s]\n", *cmdline_p, saved_command_line); - printk("ARCH: SUN4U\n"); boot_flags_init(*cmdline_p); -#if 0 - if((boot_flags&BOOTME_DEBUG) && (linux_dbvec!=0) && - ((*(short *)linux_dbvec) != -1)) { - printk("Booted under KADB. Syncing trap table.\n"); - (*(linux_dbvec->teach_debugger))(); - } - if((boot_flags & BOOTME_KGDB)) { - set_debug_traps(); - prom_printf ("Breakpoint!\n"); - breakpoint(); - } -#endif idprom_init(); total = prom_probe_memory(); @@ -313,7 +289,7 @@ __initfunc(void setup_arch(char **cmdline_p, *memory_start_p = PAGE_ALIGN(((unsigned long) &end)); *memory_end_p = (end_of_phys_memory + PAGE_OFFSET); -#ifndef NO_DAVEM_DEBUGGING +#ifdef DAVEM_DEBUGGING prom_printf("phys_base[%016lx] memory_start[%016lx] memory_end[%016lx]\n", phys_base, *memory_start_p, *memory_end_p); #endif @@ -328,8 +304,11 @@ __initfunc(void setup_arch(char **cmdline_p, #endif #ifdef CONFIG_BLK_DEV_INITRD if (ramdisk_image) { - initrd_start = ramdisk_image; - if (initrd_start < PAGE_OFFSET) initrd_start += PAGE_OFFSET; + unsigned long start = 0; + + if (ramdisk_image >= (unsigned long)&end - 2 * PAGE_SIZE) + ramdisk_image -= KERNBASE; + initrd_start = ramdisk_image + phys_base + PAGE_OFFSET; initrd_end = initrd_start + ramdisk_size; if (initrd_end > *memory_end_p) { printk(KERN_CRIT "initrd extends beyond end of memory " @@ -337,9 +316,11 @@ __initfunc(void setup_arch(char **cmdline_p, initrd_end,*memory_end_p); initrd_start = 0; } - if (initrd_start >= *memory_start_p && initrd_start < *memory_start_p + 2 * PAGE_SIZE) { + if (initrd_start) + start = ramdisk_image + KERNBASE; + if (start >= *memory_start_p && start < *memory_start_p + 2 * PAGE_SIZE) { initrd_below_start_ok = 1; - *memory_start_p = PAGE_ALIGN (initrd_end); + *memory_start_p = PAGE_ALIGN (start + ramdisk_size); } } #endif @@ -424,7 +405,11 @@ extern char *mmu_info(void); int get_cpuinfo(char *buffer) { - int cpuid=get_cpuid(); +#ifndef __SMP__ + int cpuid=0; +#else +#error SMP not supported on sparc64 yet +#endif return sprintf(buffer, "cpu\t\t: %s\n" "fpu\t\t: %s\n" diff --git a/arch/sparc64/kernel/signal.c b/arch/sparc64/kernel/signal.c index fe4615a6b..cfc55fc2e 100644 --- a/arch/sparc64/kernel/signal.c +++ b/arch/sparc64/kernel/signal.c @@ -1,4 +1,4 @@ -/* $Id: signal.c,v 1.6 1997/05/29 12:44:48 jj Exp $ +/* $Id: signal.c,v 1.20 1997/07/14 03:10:28 davem Exp $ * arch/sparc64/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -8,6 +8,7 @@ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ +#include <linux/config.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/signal.h> @@ -23,6 +24,7 @@ #include <asm/svr4.h> #include <asm/pgtable.h> #include <asm/fpumacro.h> +#include <asm/uctx.h> #include <asm/smp_lock.h> #define _S(nr) (1<<((nr)-1)) @@ -38,6 +40,124 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs, /* This turned off for production... */ /* #define DEBUG_SIGNALS 1 */ +/* {set, get}context() needed for 64-bit SparcLinux userland. */ +asmlinkage void sparc64_set_context(struct pt_regs *regs) +{ + struct ucontext *ucp = (struct ucontext *) regs->u_regs[UREG_I0]; + struct thread_struct *tp = ¤t->tss; + mc_gregset_t *grp; + unsigned long pc, npc, tstate; + unsigned long fp, i7; + unsigned char fenab; + + __asm__ __volatile__("flushw"); + if(tp->w_saved || + (((unsigned long)ucp) & (sizeof(unsigned long)-1)) || + (!__access_ok((unsigned long)ucp, sizeof(*ucp)))) + do_exit(SIGSEGV); + grp = &ucp->uc_mcontext.mc_gregs; + __get_user(pc, &((*grp)[MC_PC])); + __get_user(npc, &((*grp)[MC_NPC])); + if((pc | npc) & 3) + do_exit(SIGSEGV); + if(regs->u_regs[UREG_I1]) { + __get_user(current->blocked, &ucp->uc_sigmask); + current->blocked &= _BLOCKABLE; + } + regs->tpc = pc; + regs->tnpc = npc; + __get_user(regs->y, &((*grp)[MC_Y])); + __get_user(tstate, &((*grp)[MC_Y])); + regs->tstate &= ~(TSTATE_ICC | TSTATE_XCC); + regs->tstate |= (tstate & (TSTATE_ICC | TSTATE_XCC)); + __get_user(regs->u_regs[UREG_G1], (&(*grp)[MC_G1])); + __get_user(regs->u_regs[UREG_G2], (&(*grp)[MC_G2])); + __get_user(regs->u_regs[UREG_G3], (&(*grp)[MC_G3])); + __get_user(regs->u_regs[UREG_G4], (&(*grp)[MC_G4])); + __get_user(regs->u_regs[UREG_G5], (&(*grp)[MC_G5])); + __get_user(regs->u_regs[UREG_G6], (&(*grp)[MC_G6])); + __get_user(regs->u_regs[UREG_G7], (&(*grp)[MC_G7])); + __get_user(regs->u_regs[UREG_I0], (&(*grp)[MC_O0])); + __get_user(regs->u_regs[UREG_I1], (&(*grp)[MC_O1])); + __get_user(regs->u_regs[UREG_I2], (&(*grp)[MC_O2])); + __get_user(regs->u_regs[UREG_I3], (&(*grp)[MC_O3])); + __get_user(regs->u_regs[UREG_I4], (&(*grp)[MC_O4])); + __get_user(regs->u_regs[UREG_I5], (&(*grp)[MC_O5])); + __get_user(regs->u_regs[UREG_I6], (&(*grp)[MC_O6])); + __get_user(regs->u_regs[UREG_I7], (&(*grp)[MC_O7])); + + __get_user(fp, &(ucp->uc_mcontext.mc_fp)); + __get_user(i7, &(ucp->uc_mcontext.mc_i7)); + __put_user(fp, (&(((struct reg_window *)(STACK_BIAS+regs->u_regs[UREG_I6]))->ins[6]))); + __put_user(i7, (&(((struct reg_window *)(STACK_BIAS+regs->u_regs[UREG_I6]))->ins[7]))); + + __get_user(fenab, &(ucp->uc_mcontext.mc_fpregs.mcfpu_enab)); + if(fenab) { + unsigned long *fpregs = (unsigned long *)(regs+1); + copy_from_user(fpregs, &(ucp->uc_mcontext.mc_fpregs.mcfpu_fregs), + (sizeof(unsigned long) * 32)); + __get_user(fpregs[32], &(ucp->uc_mcontext.mc_fpregs.mcfpu_fsr)); + __get_user(fpregs[33], &(ucp->uc_mcontext.mc_fpregs.mcfpu_gsr)); + regs->fprs = FPRS_FEF; + } +} + +asmlinkage void sparc64_get_context(struct pt_regs *regs) +{ + struct ucontext *ucp = (struct ucontext *) regs->u_regs[UREG_I0]; + struct thread_struct *tp = ¤t->tss; + mc_gregset_t *grp; + mcontext_t *mcp; + unsigned long fp, i7; + unsigned char fenab = (current->flags & PF_USEDFPU); + + synchronize_user_stack(); + if(tp->w_saved || clear_user(ucp, sizeof(*ucp))) + do_exit(SIGSEGV); + mcp = &ucp->uc_mcontext; + grp = &mcp->mc_gregs; + + /* Skip over the trap instruction, first. */ + regs->tpc = regs->tnpc; + regs->tnpc += 4; + + __put_user(current->blocked, &ucp->uc_sigmask); + __put_user(regs->tstate, &((*grp)[MC_TSTATE])); + __put_user(regs->tpc, &((*grp)[MC_PC])); + __put_user(regs->tnpc, &((*grp)[MC_NPC])); + __put_user(regs->y, &((*grp)[MC_Y])); + __put_user(regs->u_regs[UREG_G1], &((*grp)[MC_G1])); + __put_user(regs->u_regs[UREG_G2], &((*grp)[MC_G2])); + __put_user(regs->u_regs[UREG_G3], &((*grp)[MC_G3])); + __put_user(regs->u_regs[UREG_G4], &((*grp)[MC_G4])); + __put_user(regs->u_regs[UREG_G5], &((*grp)[MC_G5])); + __put_user(regs->u_regs[UREG_G6], &((*grp)[MC_G6])); + __put_user(regs->u_regs[UREG_G6], &((*grp)[MC_G7])); + __put_user(regs->u_regs[UREG_I0], &((*grp)[MC_O0])); + __put_user(regs->u_regs[UREG_I1], &((*grp)[MC_O1])); + __put_user(regs->u_regs[UREG_I2], &((*grp)[MC_O2])); + __put_user(regs->u_regs[UREG_I3], &((*grp)[MC_O3])); + __put_user(regs->u_regs[UREG_I4], &((*grp)[MC_O4])); + __put_user(regs->u_regs[UREG_I5], &((*grp)[MC_O5])); + __put_user(regs->u_regs[UREG_I6], &((*grp)[MC_O6])); + __put_user(regs->u_regs[UREG_I7], &((*grp)[MC_O7])); + + __get_user(fp, (&(((struct reg_window *)(STACK_BIAS+regs->u_regs[UREG_I6]))->ins[6]))); + __get_user(i7, (&(((struct reg_window *)(STACK_BIAS+regs->u_regs[UREG_I6]))->ins[7]))); + __put_user(fp, &(mcp->mc_fp)); + __put_user(i7, &(mcp->mc_i7)); + + __put_user(fenab, &(mcp->mc_fpregs.mcfpu_enab)); + if(fenab) { + unsigned long *fpregs = (unsigned long *)(regs+1); + copy_to_user(&(mcp->mc_fpregs.mcfpu_fregs), fpregs, + (sizeof(unsigned long) * 32)); + __put_user(fpregs[32], &(mcp->mc_fpregs.mcfpu_fsr)); + __put_user(fpregs[33], &(mcp->mc_fpregs.mcfpu_gsr)); + __put_user(FPRS_FEF, &(mcp->mc_fpregs.mcfpu_fprs)); + } +} + /* * The new signal frame, intended to be used for Linux applications only * (we have enough in there to work with clone). @@ -65,7 +185,8 @@ asmlinkage void _sigpause_common(unsigned int set, struct pt_regs *regs) #ifdef CONFIG_SPARC32_COMPAT if (current->tss.flags & SPARC_FLAG_32BIT) { - extern asmlinkage void _sigpause32_common(unsigned int, struct pt_regs *); + extern asmlinkage void _sigpause32_common(unsigned int, + struct pt_regs *); _sigpause32_common(set, regs); return; } @@ -111,22 +232,12 @@ asmlinkage void do_sigsuspend(struct pt_regs *regs) static inline void restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t *fpu) { -#ifdef __SMP__ - if (current->flags & PF_USEDFPU) - regs->tstate &= ~(TSTATE_PEF); -#else - if (current == last_task_used_math) { - last_task_used_math = 0; - regs->tstate &= ~(TSTATE_PEF); - } -#endif - current->used_math = 1; - current->flags &= ~PF_USEDFPU; - - copy_from_user(¤t->tss.float_regs[0], - &fpu->si_float_regs[0], + unsigned long *fpregs = (unsigned long *)(regs+1); + copy_from_user(fpregs, &fpu->si_float_regs[0], (sizeof(unsigned int) * 64)); - __get_user(current->tss.fsr, &fpu->si_fsr); + __get_user(fpregs[32], &fpu->si_fsr); + __get_user(fpregs[33], &fpu->si_gsr); + regs->fprs = FPRS_FEF; } void do_sigreturn(struct pt_regs *regs) @@ -139,24 +250,25 @@ void do_sigreturn(struct pt_regs *regs) #ifdef CONFIG_SPARC32_COMPAT if (current->tss.flags & SPARC_FLAG_32BIT) { extern asmlinkage void do_sigreturn32(struct pt_regs *); - do_sigreturn32(regs); - return; + return do_sigreturn32(regs); } #endif synchronize_user_stack (); - sf = (struct new_signal_frame *) regs->u_regs [UREG_FP]; + sf = (struct new_signal_frame *) + (regs->u_regs [UREG_FP] + STACK_BIAS); + /* 1. Make sure we are not getting garbage from the user */ - if (verify_area (VERIFY_READ, sf, sizeof (*sf))){ + if (verify_area (VERIFY_READ, sf, sizeof (*sf))) goto segv; - } - if (((unsigned long) sf) & 3){ + + if (((unsigned long) sf) & 3) goto segv; - } + get_user(tpc, &sf->info.si_regs.tpc); __get_user(tnpc, &sf->info.si_regs.tnpc); - if ((tpc | tnpc) & 3){ + if ((tpc | tnpc) & 3) goto segv; - } + regs->tpc = tpc; regs->tnpc = tnpc; @@ -165,9 +277,9 @@ void do_sigreturn(struct pt_regs *regs) __get_user(tstate, &sf->info.si_regs.tstate); copy_from_user(regs->u_regs, sf->info.si_regs.u_regs, sizeof(regs->u_regs)); - /* User can only change condition codes and FPU enabling in %tstate. */ - regs->tstate &= ~(TSTATE_ICC | TSTATE_PEF); - regs->tstate |= (tstate & (TSTATE_ICC | TSTATE_PEF)); + /* User can only change condition codes in %tstate. */ + regs->tstate &= ~(TSTATE_ICC); + regs->tstate |= (tstate & TSTATE_ICC); __get_user(fpu_save, &sf->fpu_save); if (fpu_save) @@ -191,27 +303,12 @@ static int invalid_frame_pointer(void *fp, int fplen) static inline void save_fpu_state(struct pt_regs *regs, __siginfo_fpu_t *fpu) { -#ifdef __SMP__ - if (current->flags & PF_USEDFPU) { - fprs_write(FPRS_FEF); - fpsave((unsigned long *)¤t->tss.float_regs[0], - ¤t->tss.fsr); - regs->tstate &= ~(TSTATE_PEF); - current->flags &= ~(PF_USEDFPU); - } -#else - if (current == last_task_used_math) { - fprs_write(FPRS_FEF); - fpsave((unsigned long *)¤t->tss.float_regs[0], - ¤t->tss.fsr); - last_task_used_math = 0; - regs->tstate &= ~(TSTATE_PEF); - } -#endif - copy_to_user(&fpu->si_float_regs[0], ¤t->tss.float_regs[0], + unsigned long *fpregs = (unsigned long *)(regs+1); + copy_to_user(&fpu->si_float_regs[0], fpregs, (sizeof(unsigned int) * 64)); - __put_user(current->tss.fsr, &fpu->si_fsr); - current->used_math = 0; + __put_user(fpregs[32], &fpu->si_fsr); + __put_user(fpregs[33], &fpu->si_gsr); + regs->fprs = 0; } static inline void @@ -220,33 +317,29 @@ new_setup_frame(struct sigaction *sa, struct pt_regs *regs, { struct new_signal_frame *sf; int sigframe_size; - unsigned long tmp; - int i; /* 1. Make sure everything is clean */ synchronize_user_stack(); sigframe_size = NF_ALIGNEDSZ; - if (!current->used_math) + if (!(current->flags & PF_USEDFPU)) sigframe_size -= sizeof(__siginfo_fpu_t); - sf = (struct new_signal_frame *)(regs->u_regs[UREG_FP] - sigframe_size); + sf = (struct new_signal_frame *) + (regs->u_regs[UREG_FP] + STACK_BIAS - sigframe_size); - if (invalid_frame_pointer (sf, sigframe_size)){ - lock_kernel (); - do_exit(SIGILL); - } + if (invalid_frame_pointer (sf, sigframe_size)) + goto sigill; - if (current->tss.w_saved != 0){ + if (current->tss.w_saved != 0) { printk ("%s[%d]: Invalid user stack frame for " "signal delivery.\n", current->comm, current->pid); - lock_kernel (); - do_exit (SIGILL); + goto sigill; } /* 2. Save the current process state */ copy_to_user(&sf->info.si_regs, regs, sizeof (*regs)); - if (current->used_math) { + if (current->flags & PF_USEDFPU) { save_fpu_state(regs, &sf->fpu_state); __put_user((u64)&sf->fpu_state, &sf->fpu_save); } else { @@ -254,17 +347,17 @@ new_setup_frame(struct sigaction *sa, struct pt_regs *regs, } __put_user(oldmask, &sf->info.si_mask); - for (i = 0; i < sizeof(struct reg_window)/8; i++) { - __get_user(tmp, (((u64 *)regs->u_regs[UREG_FP])+i)); - __put_user(tmp, (((u64 *)sf)+i)); - } + + copy_in_user((u64 *)sf, + (u64 *)(regs->u_regs[UREG_FP]+STACK_BIAS), + sizeof(struct reg_window)); /* 3. return to kernel instructions */ __put_user(0x821020d8, &sf->insns[0]); /* mov __NR_sigreturn, %g1 */ __put_user(0x91d02011, &sf->insns[1]); /* t 0x11 */ /* 4. signal handler back-trampoline and parameters */ - regs->u_regs[UREG_FP] = (unsigned long) sf; + regs->u_regs[UREG_FP] = ((unsigned long) sf) - STACK_BIAS; regs->u_regs[UREG_I0] = signo; regs->u_regs[UREG_I1] = (unsigned long) &sf->info; regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2); @@ -274,15 +367,27 @@ new_setup_frame(struct sigaction *sa, struct pt_regs *regs, regs->tnpc = (regs->tpc + 4); /* Flush instruction space. */ - __asm__ __volatile__(" - membar #StoreStore - stxa %%g0, [%0] %2 - stxa %%g0, [%1] %2 - flush %%g4 - " : /* no outputs */ - : "r" (((unsigned long)&(sf->insns[0])) & ~(PAGE_MASK)), - "r" ((((unsigned long)&(sf->insns[0])) & ~(PAGE_MASK)) + PAGE_SIZE), - "i" (ASI_IC_TAG)); + { + unsigned long address = ((unsigned long)&(sf->insns[0])); + pgd_t *pgdp = pgd_offset(current->mm, address); + pmd_t *pmdp = pmd_offset(pgdp, address); + pte_t *ptep = pte_offset(pmdp, address); + + if(pte_present(*ptep)) { + unsigned long page = pte_page(*ptep); + + __asm__ __volatile__(" + membar #StoreStore + flush %0 + %1" + : : "r" (page), "r" (address & (PAGE_SIZE - 1)) + : "memory"); + } + } + return; + +sigill: + lock_kernel(); + do_exit(SIGILL); } static inline void handle_signal(unsigned long signr, struct sigaction *sa, @@ -291,8 +396,11 @@ static inline void handle_signal(unsigned long signr, struct sigaction *sa, new_setup_frame(sa, regs, signr, oldmask); if(sa->sa_flags & SA_ONESHOT) sa->sa_handler = NULL; - if(!(sa->sa_flags & SA_NOMASK)) + if(!(sa->sa_flags & SA_NOMASK)) { + spin_lock_irq(¤t->sigmask_lock); current->blocked |= (sa->sa_mask | _S(signr)) & _BLOCKABLE; + spin_unlock_irq(¤t->sigmask_lock); + } } static inline void syscall_restart(unsigned long orig_i0, struct pt_regs *regs, @@ -334,7 +442,11 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs, #endif while ((signr = current->signal & mask) != 0) { signr = ffz(~signr); - clear_bit(signr + 32, ¤t->signal); + + spin_lock_irq(¤t->sigmask_lock); + current->signal &= ~(1 << signr); + spin_unlock_irq(¤t->sigmask_lock); + sa = current->sig->action + signr; signr++; if ((current->flags & PF_PTRACED) && signr != SIGKILL) { @@ -348,7 +460,9 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs, if (signr == SIGSTOP) continue; if (_S(signr) & current->blocked) { + spin_lock_irq(¤t->sigmask_lock); current->signal |= _S(signr); + spin_unlock_irq(¤t->sigmask_lock); continue; } sa = current->sig->action + signr - 1; @@ -391,8 +505,10 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs, case SIGQUIT: case SIGILL: case SIGTRAP: case SIGABRT: case SIGFPE: case SIGSEGV: case SIGBUS: if(current->binfmt && current->binfmt->core_dump) { + lock_kernel(); if(current->binfmt->core_dump(signr, regs)) signr |= 0x80; + unlock_kernel(); } #ifdef DEBUG_SIGNALS /* Very useful to debug dynamic linker problems */ @@ -401,9 +517,15 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs, #endif /* fall through */ default: + spin_lock_irq(¤t->sigmask_lock); current->signal |= _S(signr & 0x7f); + spin_unlock_irq(¤t->sigmask_lock); + current->flags |= PF_SIGNALED; + + lock_kernel(); do_exit(signr); + unlock_kernel(); } } if(restart_syscall) diff --git a/arch/sparc64/kernel/signal32.c b/arch/sparc64/kernel/signal32.c index c0454658b..5135c2ae5 100644 --- a/arch/sparc64/kernel/signal32.c +++ b/arch/sparc64/kernel/signal32.c @@ -1,4 +1,4 @@ -/* $Id: signal32.c,v 1.13 1997/06/01 05:46:09 davem Exp $ +/* $Id: signal32.c,v 1.26 1997/07/14 03:10:31 davem Exp $ * arch/sparc64/kernel/signal32.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -67,13 +67,12 @@ struct signal_sframe32 { * (we have enough in there to work with clone). * All the interesting bits are in the info field. */ - struct new_signal_frame32 { struct sparc_stackf32 ss; __siginfo32_t info; /* __siginfo_fpu32_t * */ u32 fpu_save; unsigned int insns [2]; - __siginfo_fpu32_t fpu_state; + __siginfo_fpu_t fpu_state; }; /* Align macros */ @@ -115,25 +114,12 @@ asmlinkage void _sigpause32_common(unsigned int set, struct pt_regs *regs) } } -static inline void -restore_fpu_state32(struct pt_regs *regs, __siginfo_fpu32_t *fpu) +static inline void restore_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t *fpu) { -#ifdef __SMP__ - if (current->flags & PF_USEDFPU) - regs->tstate &= ~(TSTATE_PEF); -#else - if (current == last_task_used_math) { - last_task_used_math = 0; - regs->tstate &= ~(TSTATE_PEF); - } -#endif - current->used_math = 1; - current->flags &= ~PF_USEDFPU; - - copy_from_user(¤t->tss.float_regs[0], - &fpu->si_float_regs[0], - (sizeof(unsigned int) * 32)); - __get_user(current->tss.fsr, &fpu->si_fsr); + unsigned long *fpregs = (unsigned long *)(regs + 1); + copy_from_user(fpregs, &fpu->si_float_regs[0], (sizeof(unsigned int) * 64)); + __get_user(fpregs[32], &fpu->si_fsr); + __get_user(fpregs[33], &fpu->si_gsr); } void do_new_sigreturn32(struct pt_regs *regs) @@ -142,6 +128,7 @@ void do_new_sigreturn32(struct pt_regs *regs) unsigned int psr; unsigned pc, npc, fpu_save, mask; + regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL; sf = (struct new_signal_frame32 *) regs->u_regs [UREG_FP]; /* 1. Make sure we are not getting garbage from the user */ @@ -178,12 +165,12 @@ void do_new_sigreturn32(struct pt_regs *regs) __get_user(regs->u_regs[UREG_I6], &sf->info.si_regs.u_regs[UREG_I6]); __get_user(regs->u_regs[UREG_I7], &sf->info.si_regs.u_regs[UREG_I7]); - /* User can only change condition codes and FPU enabling in %tstate. */ - regs->tstate &= ~(TSTATE_ICC | TSTATE_PEF); + /* User can only change condition codes in %tstate. */ + regs->tstate &= ~(TSTATE_ICC); regs->tstate |= psr_to_tstate_icc(psr); if (psr & PSR_EF) - regs->tstate |= TSTATE_PEF; + regs->fprs = FPRS_FEF; __get_user(fpu_save, &sf->fpu_save); if (fpu_save) @@ -206,7 +193,8 @@ asmlinkage void do_sigreturn32(struct pt_regs *regs) if (current->tss.new_signal) return do_new_sigreturn32(regs); - scptr = (struct sigcontext32 *) regs->u_regs[UREG_I0]; + scptr = (struct sigcontext32 *) + (regs->u_regs[UREG_I0] & 0x00000000ffffffffUL); /* Check sanity of the user arg. */ if(verify_area(VERIFY_READ, scptr, sizeof(struct sigcontext32)) || (((unsigned long) scptr) & 3)) @@ -257,9 +245,9 @@ setup_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, #endif int old_status = current->tss.sstk_info.cur_status; unsigned psr; - int i; synchronize_user_stack(); + regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL; sframep = (struct signal_sframe32 *) regs->u_regs[UREG_FP]; sframep = (struct signal_sframe32 *) (((unsigned long) sframep)-SF_ALIGNEDSZ); if (invalid_frame_pointer (sframep, sizeof(*sframep))){ @@ -285,6 +273,8 @@ setup_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, __put_user(pc, &sc->sigc_pc); __put_user(npc, &sc->sigc_npc); psr = tstate_to_psr (regs->tstate); + if(current->flags & PF_USEDFPU) + psr |= PSR_EF; __put_user(psr, &sc->sigc_psr); __put_user(regs->u_regs[UREG_G1], &sc->sigc_g1); __put_user(regs->u_regs[UREG_I0], &sc->sigc_o0); @@ -301,13 +291,9 @@ setup_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, } else #endif - /* XXX Perhaps we need a copy_in_user()? -DaveM */ - for (i = 0; i < 16; i++) { - u32 temp; - - get_user (temp, (((u32 *)(regs->u_regs[UREG_FP]))+i)); - put_user (temp, (((u32 *)sframep)+i)); - } + copy_in_user((u32 *)sframep, + (u32 *)(regs->u_regs[UREG_FP]), + sizeof(struct reg_window32)); current->tss.w_saved = 0; /* So process is allowed to execute. */ __put_user(signr, &sframep->sig_num); @@ -329,35 +315,17 @@ setup_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, } -static inline void -save_fpu_state32(struct pt_regs *regs, __siginfo_fpu32_t *fpu) +static inline void save_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t *fpu) { -#ifdef __SMP__ - if (current->flags & PF_USEDFPU) { - fprs_write(FPRS_FEF); - fpsave32((unsigned long *)¤t->tss.float_regs[0], - ¤t->tss.fsr); - regs->tstate &= ~(TSTATE_PEF); - current->flags &= ~(PF_USEDFPU); - } -#else - if (current == last_task_used_math) { - fprs_write(FPRS_FEF); - fpsave32((unsigned long *)¤t->tss.float_regs[0], - ¤t->tss.fsr); - last_task_used_math = 0; - regs->tstate &= ~(TSTATE_PEF); - } -#endif - copy_to_user(&fpu->si_float_regs[0], ¤t->tss.float_regs[0], - (sizeof(unsigned int) * 32)); - __put_user(current->tss.fsr, &fpu->si_fsr); - current->used_math = 0; + unsigned long *fpregs = (unsigned long *)(regs+1); + copy_to_user(&fpu->si_float_regs[0], fpregs, (sizeof(unsigned int) * 64)); + __put_user(fpregs[32], &fpu->si_fsr); + __put_user(fpregs[33], &fpu->si_gsr); + regs->fprs = 0; } -static inline void -new_setup_frame32(struct sigaction *sa, struct pt_regs *regs, - int signo, unsigned long oldmask) +static inline void new_setup_frame32(struct sigaction *sa, struct pt_regs *regs, + int signo, unsigned long oldmask) { struct new_signal_frame32 *sf; int sigframe_size; @@ -367,21 +335,26 @@ new_setup_frame32(struct sigaction *sa, struct pt_regs *regs, /* 1. Make sure everything is clean */ synchronize_user_stack(); sigframe_size = NF_ALIGNEDSZ; - if (!current->used_math) - sigframe_size -= sizeof(__siginfo_fpu32_t); + if (!(current->flags & PF_USEDFPU)) + sigframe_size -= sizeof(__siginfo_fpu_t); + regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL; sf = (struct new_signal_frame32 *)(regs->u_regs[UREG_FP] - sigframe_size); if (invalid_frame_pointer (sf, sigframe_size)) { - lock_kernel (); - do_exit(SIGILL); +#ifdef DEBUG_SIGNALS + printk("new_setup_frame32(%s:%d): invalid_frame_pointer(%p, %d)\n", + current->comm, current->pid, sf, sigframe_size); +#endif + goto sigill; } if (current->tss.w_saved != 0) { +#ifdef DEBUG_SIGNALS printk ("%s[%d]: Invalid user stack frame for " "signal delivery.\n", current->comm, current->pid); - lock_kernel (); - do_exit (SIGILL); +#endif + goto sigill; } /* 2. Save the current process state */ @@ -389,11 +362,13 @@ new_setup_frame32(struct sigaction *sa, struct pt_regs *regs, __put_user(regs->tnpc, &sf->info.si_regs.npc); __put_user(regs->y, &sf->info.si_regs.y); psr = tstate_to_psr (regs->tstate); + if(current->flags & PF_USEDFPU) + psr |= PSR_EF; __put_user(psr, &sf->info.si_regs.psr); for (i = 0; i < 16; i++) __put_user(regs->u_regs[i], &sf->info.si_regs.u_regs[i]); - if (current->used_math) { + if (psr & PSR_EF) { save_fpu_state32(regs, &sf->fpu_state); __put_user((u64)&sf->fpu_state, &sf->fpu_save); } else { @@ -402,13 +377,9 @@ new_setup_frame32(struct sigaction *sa, struct pt_regs *regs, __put_user(oldmask, &sf->info.si_mask); - /* XXX Perhaps we need a copy_in_user()? -DaveM */ - for (i = 0; i < sizeof(struct reg_window32)/4; i++) { - u32 tmp; - - __get_user(tmp, (((u32 *)regs->u_regs[UREG_FP])+i)); - __put_user(tmp, (((u32 *)sf)+i)); - } + copy_in_user((u32 *)sf, + (u32 *)(regs->u_regs[UREG_FP]), + sizeof(struct reg_window32)); /* 3. return to kernel instructions */ __put_user(0x821020d8, &sf->insns[0]); /* mov __NR_sigreturn, %g1 */ @@ -425,15 +396,27 @@ new_setup_frame32(struct sigaction *sa, struct pt_regs *regs, regs->tnpc = (regs->tpc + 4); /* Flush instruction space. */ - __asm__ __volatile__(" - membar #StoreStore - stxa %%g0, [%0] %2 - stxa %%g0, [%1] %2 - flush %%g4 - " : /* no outputs */ - : "r" (((unsigned long)&(sf->insns[0])) & ~(PAGE_MASK)), - "r" ((((unsigned long)&(sf->insns[0])) & ~(PAGE_MASK)) + PAGE_SIZE), - "i" (ASI_IC_TAG)); + { + unsigned long address = ((unsigned long)&(sf->insns[0])); + pgd_t *pgdp = pgd_offset(current->mm, address); + pmd_t *pmdp = pmd_offset(pgdp, address); + pte_t *ptep = pte_offset(pmdp, address); + + if(pte_present(*ptep)) { + unsigned long page = pte_page(*ptep); + + __asm__ __volatile__(" + membar #StoreStore + flush %0 + %1" + : : "r" (page), "r" (address & (PAGE_SIZE - 1)) + : "memory"); + } + } + return; + +sigill: + lock_kernel(); + do_exit(SIGILL); } /* Setup a Solaris stack frame */ @@ -454,6 +437,7 @@ setup_svr4_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, int i; synchronize_user_stack(); + regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL; sfp = (svr4_signal_frame_t *) regs->u_regs[UREG_FP] - REGWIN_SZ; sfp = (svr4_signal_frame_t *) (((unsigned long) sfp)-SVR4_SF_ALIGNED); @@ -485,6 +469,8 @@ setup_svr4_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, __put_user(regs->tpc, &((*gr) [SVR4_PC])); __put_user(regs->tnpc, &((*gr) [SVR4_NPC])); psr = tstate_to_psr (regs->tstate); + if(current->flags & PF_USEDFPU) + psr |= PSR_EF; __put_user(psr, &((*gr) [SVR4_PSR])); __put_user(regs->y, &((*gr) [SVR4_Y])); @@ -546,7 +532,8 @@ setup_svr4_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, #endif /* Arguments passed to signal handler */ if (regs->u_regs [14]){ - struct reg_window32 *rw = (struct reg_window32 *) regs->u_regs [14]; + struct reg_window32 *rw = (struct reg_window32 *) + (regs->u_regs [14] & 0x00000000ffffffffUL); __put_user(signr, &rw->ins [0]); __put_user((u64)si, &rw->ins [1]); @@ -583,7 +570,9 @@ svr4_getcontext32(svr4_ucontext_t *uc, struct pt_regs *regs) /* Store registers */ __put_user(regs->tpc, &uc->mcontext.greg [SVR4_PC]); __put_user(regs->tnpc, &uc->mcontext.greg [SVR4_NPC]); - __put_user(tstate_to_psr(regs->tstate), &uc->mcontext.greg [SVR4_PSR]); + __put_user((tstate_to_psr(regs->tstate) | + ((current->flags & PF_USEDFPU) ? PSR_EF : 0)), + &uc->mcontext.greg [SVR4_PSR]); __put_user(regs->y, &uc->mcontext.greg [SVR4_Y]); /* Copy g [1..7] and o [0..7] registers */ @@ -619,16 +608,16 @@ asmlinkage int svr4_setcontext32(svr4_ucontext_t *c, struct pt_regs *regs) if (tp->w_saved){ printk ("Uh oh, w_saved is: 0x%lx\n", tp->w_saved); - do_exit(SIGSEGV); + goto sigsegv; } if (((unsigned long) c) & 3){ printk ("Unaligned structure passed\n"); - do_exit (SIGSEGV); + goto sigsegv; } if(!__access_ok((unsigned long)c, sizeof(*c))) { /* Miguel, add nice debugging msg _here_. ;-) */ - do_exit(SIGSEGV); + goto sigsegv; } /* Check for valid PC and nPC */ @@ -637,7 +626,7 @@ asmlinkage int svr4_setcontext32(svr4_ucontext_t *c, struct pt_regs *regs) __get_user(npc, &((*gr)[SVR4_NPC])); if((pc | npc) & 3) { printk ("setcontext, PC or nPC were bogus\n"); - do_exit (SIGSEGV); + goto sigsegv; } /* Retrieve information from passed ucontext */ /* note that nPC is ored a 1, this is used to inform entry.S */ @@ -650,14 +639,19 @@ asmlinkage int svr4_setcontext32(svr4_ucontext_t *c, struct pt_regs *regs) __get_user(psr, &((*gr) [SVR4_PSR])); regs->tstate &= ~(TSTATE_ICC); regs->tstate |= psr_to_tstate_icc(psr); + if(psr & PSR_EF) + regs->fprs = FPRS_FEF; /* Restore g[1..7] and o[0..7] registers */ for (i = 0; i < 7; i++) - __put_user(regs->u_regs[UREG_G1+i], (&(*gr)[SVR4_G1])+i); + __get_user(regs->u_regs[UREG_G1+i], (&(*gr)[SVR4_G1])+i); for (i = 0; i < 8; i++) - __put_user(regs->u_regs[UREG_I0+i], (&(*gr)[SVR4_O0])+i); + __get_user(regs->u_regs[UREG_I0+i], (&(*gr)[SVR4_O0])+i); return -EINTR; +sigsegv: + lock_kernel(); + do_exit(SIGSEGV); } static inline void handle_signal32(unsigned long signr, struct sigaction *sa, @@ -674,8 +668,11 @@ static inline void handle_signal32(unsigned long signr, struct sigaction *sa, } if(sa->sa_flags & SA_ONESHOT) sa->sa_handler = NULL; - if(!(sa->sa_flags & SA_NOMASK)) + if(!(sa->sa_flags & SA_NOMASK)) { + spin_lock_irq(¤t->sigmask_lock); current->blocked |= (sa->sa_mask | _S(signr)) & _BLOCKABLE; + spin_unlock_irq(¤t->sigmask_lock); + } } static inline void syscall_restart32(unsigned long orig_i0, struct pt_regs *regs, @@ -711,7 +708,11 @@ asmlinkage int do_signal32(unsigned long oldmask, struct pt_regs * regs, while ((signr = current->signal & mask) != 0) { signr = ffz(~signr); - clear_bit(signr + 32, ¤t->signal); + + spin_lock_irq(¤t->sigmask_lock); + current->signal &= ~(1 << signr); + spin_unlock_irq(¤t->sigmask_lock); + sa = current->sig->action + signr; signr++; if ((current->flags & PF_PTRACED) && signr != SIGKILL) { @@ -725,7 +726,9 @@ asmlinkage int do_signal32(unsigned long oldmask, struct pt_regs * regs, if (signr == SIGSTOP) continue; if (_S(signr) & current->blocked) { + spin_lock_irq(¤t->sigmask_lock); current->signal |= _S(signr); + spin_unlock_irq(¤t->sigmask_lock); continue; } sa = current->sig->action + signr - 1; @@ -768,8 +771,10 @@ asmlinkage int do_signal32(unsigned long oldmask, struct pt_regs * regs, case SIGQUIT: case SIGILL: case SIGTRAP: case SIGABRT: case SIGFPE: case SIGSEGV: case SIGBUS: if(current->binfmt && current->binfmt->core_dump) { + lock_kernel(); if(current->binfmt->core_dump(signr, regs)) signr |= 0x80; + unlock_kernel(); } #ifdef DEBUG_SIGNALS /* Very useful to debug dynamic linker problems */ @@ -778,9 +783,15 @@ asmlinkage int do_signal32(unsigned long oldmask, struct pt_regs * regs, #endif /* fall through */ default: + spin_lock_irq(¤t->sigmask_lock); current->signal |= _S(signr & 0x7f); + spin_unlock_irq(¤t->sigmask_lock); + current->flags |= PF_SIGNALED; + + lock_kernel(); do_exit(signr); + unlock_kernel(); } } if(restart_syscall) @@ -805,9 +816,10 @@ struct sigstack32 { int cur_status; }; -asmlinkage int -sys32_sigstack(struct sigstack32 *ssptr, struct sigstack32 *ossptr) +asmlinkage int sys32_sigstack(u32 u_ssptr, u32 u_ossptr) { + struct sigstack32 *ssptr = (struct sigstack32 *)((unsigned long)(u_ssptr)); + struct sigstack32 *ossptr = (struct sigstack32 *)((unsigned long)(u_ossptr)); int ret = -EFAULT; lock_kernel(); diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c new file mode 100644 index 000000000..88d7a8ecf --- /dev/null +++ b/arch/sparc64/kernel/smp.c @@ -0,0 +1,347 @@ +/* smp.c: Sparc64 SMP support. + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + */ + +#define __KERNEL_SYSCALLS__ +#include <linux/unistd.h> + +extern int linux_num_cpus; +extern void calibrate_delay(void); + +volatile int smp_processors_ready = 0; +unsigned long cpu_present_map = 0; +int smp_num_cpus = 1; +int smp_threads_ready = 0; + +struct cpuinfo_sparc64 cpu_data[NR_CPUS]; +static unsigned char boot_cpu_id = 0; +static int smp_activated = 0; + +volatile int cpu_number_map[NR_CPUS]; +volatile int cpu_logical_map[NR_CPUS]; + +struct klock_info klock_info = { KLOCK_CLEAR, 0 }; + +static volatile int smp_commenced = 0; + +void smp_setup(char *str, int *ints) +{ + /* XXX implement me XXX */ +} + +static char smp_buf[512]; + +char *smp_info(void) +{ + /* XXX not SMP safe and need to support up to 64 penguins */ + sprintf(smp_buf, +" CPU0\t\tCPU1\t\tCPU2\t\tCPU3\n" +"State: %s\t\t%s\t\t%s\t\t%s\n", +(cpu_present_map & 1) ? ((klock_info.akp == 0) ? "akp" : "online") : "offline", +(cpu_present_map & 2) ? ((klock_info.akp == 1) ? "akp" : "online") : "offline", +(cpu_present_map & 4) ? ((klock_info.akp == 2) ? "akp" : "online") : "offline", +(cpu_present_map & 8) ? ((klock_info.akp == 3) ? "akp" : "online") : "offline"); + return smp_buf; +} + +void smp_store_cpu_info(int id) +{ + cpu_data[id].udelay_val = loops_per_sec; +} + +void smp_commence(void) +{ + local_flush_cache_all(); + local_flush_tlb_all(); + smp_commenced = 1; + local_flush_cache_all(); + local_flush_tlb_all(); +} + +static void smp_setup_percpu_timer(void); + +static volatile unsigned long callin_flag = 0; + +void smp_callin(void) +{ + int cpuid = hard_smp_processor_id(); + + local_flush_cache_all(); + local_flush_tlb_all(); + + smp_setup_percpu_timer(); + + calibrate_delay(); + smp_store_cpu_info(cpuid); + callin_flag = 1; + __asm__ __volatile__("membar #Sync\n\t" + "flush %g6" : : : "memory"); + + while(!task[cpuid]) + barrier(); + current = task[cpuid]; + + while(!smp_commenced) + barrier(); + + __sti(); +} + +extern int cpu_idle(void *unused); +extern void init_IRQ(void); + +void initialize_secondary(void) +{ +} + +int start_secondary(void *unused) +{ + trap_init(); + init_IRQ(); + smp_callin(); + return cpu_idle(NULL); +} + +extern struct prom_cpuinfo linux_cpus[NR_CPUS]; + +void smp_boot_cpus(void) +{ + int cpucount = 0, i, first, prev; + + printk("Entering UltraSMPenguin Mode...\n"); + __sti(); + cpu_present_map = 0; + for(i = 0; i < linux_num_cpus; i++) + cpu_present_map |= (1 << i); + for(i = 0; i < NR_CPUS; i++) { + cpu_number_map[i] = -1; + cpu_logical_map[i] = -1; + } + cpu_number_map[boot_cpu_id] = 0; + cpu_logical_map[0] = boot_cpu_id; + klock_info.akp = boot_cpu_id; + current->processor = boot_cpu_id; + smp_store_cpu_info(boot_cpu_id); + smp_setup_percpu_timer(); + + if(linux_num_cpus == 1) + return; + + for(i = 0; i < NR_CPUS; i++) { + if(i == boot_cpu_id) + continue; + + if(cpu_present_map & (1 << i)) { + extern unsigned long sparc64_cpu_startup; + unsigned long entry = (unsigned long)&sparc_cpu_startup; + struct task_struct *p; + int timeout; + + kernel_thread(start_secondary, NULL, CLONE_PID); + p = task[++cpucount]; + p->processor = i; + prom_startcpu(linux_cpus[i].prom_node, entry, i); + for(timeout = 0; timeout < 5000000; timeout++) { + if(cpu_callin_map[i]) + break; + udelay(100); + } + if(cpu_callin_map[i]) { + /* XXX fix this */ + cpu_number_map[i] = i; + cpu_logical_map[i] = i; + } else { + cpucount--; + printk("Processor %d is stuck.\n", i); + } + } + if(!(cpu_callin_map[i])) { + cpu_present_map &= ~(1 << i); + cpu_number_map[i] = -1; + } + } + if(cpucount == 0) { + printk("Error: only one processor found.\n"); + cpu_present_map = (1 << smp_processor_id()); + } else { + unsigned long bogosum = 0; + + for(i = 0; i < NR_CPUS; i++) { + if(cpu_present_map & (1 << i)) + bogosum += cpu_data[i].udelay_val; + } + printk("Total of %d processors activated (%lu.%02lu BogoMIPS).\n", + cpucount + 1, + (bogosum + 2500)/500000, + ((bogosum + 2500)/5000)%100); + smp_activated = 1; + smp_num_cpus = cpucount + 1; + } + smp_processors_ready = 1; +} + +/* XXX deprecated interface... */ +void smp_message_pass(int target, int msg, unsigned long data, int wait) +{ + printk("smp_message_pass() called, this is bad, spinning.\n"); + __sti(); + while(1) + barrier(); +} + +/* XXX Make it fast later. */ +void smp_cross_call(unsigned long *func, u32 ctx, u64 data1, u64 data2) +{ + if(smp_processors_ready) { + unsigned long mask; + u64 data0 = (((unsigned long)ctx)<<32 | + (((unsigned long)func) & 0xffffffff)); + u64 pstate; + int i, ncpus = smp_num_cpus; + + __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate)); + mask = (cpu_present_map & ~(1 << smp_processor_id())); + for(i = 0; i < ncpus; i++) { + if(mask & (1 << i)) { + u64 target = mid<<14 | 0x70; + u64 result; + + __asm__ __volatile__(" + wrpr %0, %1, %%pstate + wrpr %%g0, %2, %%asi + stxa %3, [0x40] %%asi + stxa %4, [0x50] %%asi + stxa %5, [0x60] %%asi + stxa %%g0, [%6] %7 + membar #Sync" + : /* No outputs */ + : "r" (pstate), "i" (PSTATE_IE), "i" (ASI_UDB_INTR_W), + "r" (data0), "r" (data1), "r" (data2), + "r" (target), "i" (ASI_UDB_INTR_W)); + + /* NOTE: PSTATE_IE is still clear. */ + do { + __asm__ __volatile__("ldxa [%%g0] %1, %0", + : "=r" (result) + : "i" (ASI_INTR_DISPATCH_STAT)); + } while(result & 0x1); + __asm__ __volatile__("wrpr %0, 0x0, %%pstate" + : : "r" (pstate)); + if(result & 0x2) + panic("Penguin NACK's master!"); + } + } + + /* NOTE: Caller runs local copy on master. */ + } +} + +extern unsigned long xcall_flush_tlb_page; +extern unsigned long xcall_flush_tlb_mm; +extern unsigned long xcall_flush_tlb_range; +extern unsigned long xcall_flush_tlb_all; +extern unsigned long xcall_flush_cache_all; + +void smp_flush_cache_all(void) +{ + smp_cross_call(&xcall_flush_cache_all, 0, 0, 0); +} + +void smp_flush_tlb_all(void) +{ + smp_cross_call(&xcall_flush_tlb_all, 0, 0, 0); +} + +void smp_flush_tlb_mm(struct mm_struct *mm) +{ + u32 ctx = mm->context & 0x1fff; + if(mm->cpu_vm_mask != (1 << smp_processor_id())) + smp_cross_call(&xcall_flush_tlb_mm, ctx, 0, 0); + __flush_tlb_mm(ctx); +} + +void smp_flush_tlb_range(struct mm_struct *mm, unsigned long start, + unsigned long end) +{ + u32 ctx = mm->context & 0x1fff; + if(mm->cpu_vm_mask != (1 << smp_processor_id())) + smp_cross_call(&xcall_flush_tlb_range, ctx, start, end); + __flush_tlb_range(ctx, start, end); +} + +void smp_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) +{ + struct mm_struct *mm = vma->vm_mm; + u32 ctx = mm->context & 0x1fff; + + if(mm->cpu_vm_mask != (1 << smp_processor_id())) + smp_cross_call(&xcall_flush_tlb_page, ctx, page, 0); + __flush_tlb_page(ctx, page); +} + +static spinlock_t ticker_lock = SPIN_LOCK_UNLOCKED; + +static inline void sparc64_do_profile(unsigned long pc) +{ + if(prof_buffer && current->pid) { + extern int _stext; + + pc -= (unsigned long) &_stext; + pc >>= prof_shift; + + spin_lock(&ticker_lock); + if(pc < prof_len) + prof_buffer[pc]++; + else + prof_buffer[prof_len - 1]++; + spin_unlock(&ticker_lock); + } +} + +unsigned int prof_multiplier[NR_CPUS]; +unsigned int prof_counter[NR_CPUS]; + +extern void update_one_process(struct task_struct *p, unsigned long ticks, + unsigned long user, unsigned long system); + +void smp_percpu_timer_interrupt(struct pt_regs *regs) +{ + int cpu = smp_processor_id(); + + clear_profile_irq(cpu); + if(!user_mode(regs)) + sparc_do_profile(regs->pc); + if(!--prof_counter[cpu]) { + int user = user_mode(regs); + if(current->pid) { + update_one_process(current, 1, user, !user); + if(--current->counter < 0) { + current->counter = 0; + need_resched = 1; + } + + spin_lock(&ticker_lock); + if(user) { + if(current->priority < DEF_PRIORITY) + kstat.cpu_nice++; + else + kstat.cpu_user++; + } else { + kstat.cpu_system++; + } + spin_unlock(&ticker_lock); + } + prof_counter[cpu] = prof_multiplier[cpu]; + } +} + +static void smp_setup_percpu_timer(void) +{ + /* XXX implement me */ +} + +int setup_profiling_timer(unsigned int multiplier) +{ + /* XXX implement me */ +} diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index 91426c814..990202bac 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c @@ -1,5 +1,5 @@ -/* $Id: sparc64_ksyms.c,v 1.4 1997/04/14 17:04:43 jj Exp $ - * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support. +/* $Id: sparc64_ksyms.c,v 1.11 1997/07/14 23:58:20 davem Exp $ + * arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) @@ -18,6 +18,8 @@ #include <asm/pgtable.h> #include <asm/io.h> #include <asm/irq.h> +#include <asm/softirq.h> +#include <asm/hardirq.h> #include <asm/idprom.h> #include <asm/svr4.h> #include <asm/head.h> @@ -38,19 +40,17 @@ struct poll { short revents; }; -extern int svr4_getcontext (svr4_ucontext_t *, struct pt_regs *); -extern int svr4_setcontext (svr4_ucontext_t *, struct pt_regs *); extern unsigned long sunos_mmap(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); void _sigpause_common (unsigned int set, struct pt_regs *); -extern void __copy_1page(void *, const void *); -extern void *bzero_1page(void *); +extern void *__bzero_1page(void *); extern void *__bzero(void *, size_t); extern void *__memscan_zero(void *, size_t); extern void *__memscan_generic(void *, int, size_t); extern int __memcmp(const void *, const void *, __kernel_size_t); extern int __strncmp(const char *, const char *, __kernel_size_t); extern unsigned int __csum_partial_copy_sparc_generic (const char *, char *); +extern char saved_command_line[]; extern void bcopy (const char *, char *, int); extern int __ashrdi3(int, int); @@ -75,32 +75,24 @@ EXPORT_SYMBOL(klock_info); EXPORT_SYMBOL_PRIVATE(_lock_kernel); EXPORT_SYMBOL_PRIVATE(_unlock_kernel); +EXPORT_SYMBOL_PRIVATE(flushw_user); + EXPORT_SYMBOL(mstk48t02_regs); EXPORT_SYMBOL(request_fast_irq); EXPORT_SYMBOL(sparc_alloc_io); EXPORT_SYMBOL(sparc_free_io); -#if 0 -EXPORT_SYMBOL(io_remap_page_range); -EXPORT_SYMBOL(mmu_unlockarea); -EXPORT_SYMBOL(mmu_lockarea); +EXPORT_SYMBOL(local_irq_count); +EXPORT_SYMBOL(__sparc64_bh_counter); +EXPORT_SYMBOL(sparc_ultra_unmapioaddr); EXPORT_SYMBOL(mmu_get_scsi_sgl); EXPORT_SYMBOL(mmu_get_scsi_one); -EXPORT_SYMBOL(mmu_release_scsi_sgl); -EXPORT_SYMBOL(mmu_release_scsi_one); -#endif EXPORT_SYMBOL(sparc_dvma_malloc); -#if 0 -EXPORT_SYMBOL(sun4c_unmapioaddr); -EXPORT_SYMBOL(srmmu_unmapioaddr); -#endif #if CONFIG_SBUS EXPORT_SYMBOL(SBus_chain); EXPORT_SYMBOL(dma_chain); #endif /* Solaris/SunOS binary compatibility */ -EXPORT_SYMBOL(svr4_setcontext); -EXPORT_SYMBOL(svr4_getcontext); EXPORT_SYMBOL(_sigpause_common); EXPORT_SYMBOL(sunos_mmap); @@ -119,7 +111,7 @@ EXPORT_SYMBOL(prom_getproplen); EXPORT_SYMBOL(prom_getproperty); EXPORT_SYMBOL(prom_node_has_property); EXPORT_SYMBOL(prom_setprop); -EXPORT_SYMBOL(prom_getbootargs); +EXPORT_SYMBOL(saved_command_line); EXPORT_SYMBOL(prom_getname); EXPORT_SYMBOL(prom_feval); EXPORT_SYMBOL(prom_getstring); @@ -148,10 +140,9 @@ EXPORT_SYMBOL(strstr); EXPORT_SYMBOL(strspn); /* Special internal versions of library functions. */ -EXPORT_SYMBOL(__copy_1page); EXPORT_SYMBOL(__memcpy); EXPORT_SYMBOL(__memset); -EXPORT_SYMBOL(bzero_1page); +EXPORT_SYMBOL(__bzero_1page); EXPORT_SYMBOL(__bzero); EXPORT_SYMBOL(__memscan_zero); EXPORT_SYMBOL(__memscan_generic); diff --git a/arch/sparc64/kernel/sunos_ioctl32.c b/arch/sparc64/kernel/sunos_ioctl32.c new file mode 100644 index 000000000..311110d3c --- /dev/null +++ b/arch/sparc64/kernel/sunos_ioctl32.c @@ -0,0 +1,281 @@ +/* $Id: sunos_ioctl32.c,v 1.1 1997/07/18 06:26:42 ralf Exp $ + * sunos_ioctl32.c: SunOS ioctl compatability on sparc64. + * + * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx) + * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu) + */ + +#include <asm/uaccess.h> + +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/termios.h> +#include <linux/ioctl.h> +#include <linux/route.h> +#include <linux/sockios.h> +#include <linux/if.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <asm/kbio.h> + +#define A(x) ((unsigned long)x) + +#define SUNOS_NR_OPEN 256 + +struct rtentry32 { + u32 rt_pad1; + struct sockaddr rt_dst; /* target address */ + struct sockaddr rt_gateway; /* gateway addr (RTF_GATEWAY) */ + struct sockaddr rt_genmask; /* target network mask (IP) */ + unsigned short rt_flags; + short rt_pad2; + u32 rt_pad3; + unsigned char rt_tos; + unsigned char rt_class; + short rt_pad4; + short rt_metric; /* +1 for binary compatibility! */ + /* char * */ u32 rt_dev; /* forcing the device at add */ + u32 rt_mtu; /* per route MTU/Window */ + u32 rt_window; /* Window clamping */ + unsigned short rt_irtt; /* Initial RTT */ + +}; + +struct ifmap32 { + u32 mem_start; + u32 mem_end; + unsigned short base_addr; + unsigned char irq; + unsigned char dma; + unsigned char port; +}; + +struct ifreq32 { +#define IFHWADDRLEN 6 +#define IFNAMSIZ 16 + union { + char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + } ifr_ifrn; + union { + struct sockaddr ifru_addr; + struct sockaddr ifru_dstaddr; + struct sockaddr ifru_broadaddr; + struct sockaddr ifru_netmask; + struct sockaddr ifru_hwaddr; + short ifru_flags; + int ifru_ivalue; + int ifru_mtu; + struct ifmap32 ifru_map; + char ifru_slave[IFNAMSIZ]; /* Just fits the size */ + __kernel_caddr_t32 ifru_data; + } ifr_ifru; +}; + +struct ifconf32 { + int ifc_len; /* size of buffer */ + __kernel_caddr_t32 ifcbuf; +}; + +extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); + +extern asmlinkage int sys32_ioctl(unsigned int, unsigned int, u32); +extern asmlinkage int sys_setsid(void); + +asmlinkage int sunos_ioctl (int fd, u32 cmd, u32 arg) +{ + struct file *filp; + int ret = -EBADF; + + lock_kernel(); + if(fd >= SUNOS_NR_OPEN) + goto out; + + filp = current->files->fd[fd]; + if(!filp) + goto out; + + if(cmd == TIOCSETD) { + unsigned long old_fs = get_fs(); + int *p, ntty = N_TTY; + int tmp; + + p = (int *)A(arg); + ret = -EFAULT; + if(get_user(tmp, p)) + goto out; + if(tmp == 2) { + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, cmd, (unsigned long) &ntty); + set_fs(old_fs); + ret = (ret == -EINVAL ? -EOPNOTSUPP : ret); + goto out; + } + } + if(cmd == TIOCNOTTY) { + ret = sys_setsid(); + goto out; + } + switch(cmd) { + case _IOW('r', 10, struct rtentry32): + ret = sys32_ioctl(fd, SIOCADDRT, arg); + goto out; + case _IOW('r', 11, struct rtentry32): + ret = sys32_ioctl(fd, SIOCDELRT, arg); + goto out; + + case _IOW('i', 12, struct ifreq32): + ret = sys32_ioctl(fd, SIOCSIFADDR, arg); + goto out; + case _IOWR('i', 13, struct ifreq32): + ret = sys32_ioctl(fd, SIOCGIFADDR, arg); + goto out; + case _IOW('i', 14, struct ifreq32): + ret = sys32_ioctl(fd, SIOCSIFDSTADDR, arg); + goto out; + case _IOWR('i', 15, struct ifreq32): + ret = sys32_ioctl(fd, SIOCGIFDSTADDR, arg); + goto out; + case _IOW('i', 16, struct ifreq32): + ret = sys32_ioctl(fd, SIOCSIFFLAGS, arg); + goto out; + case _IOWR('i', 17, struct ifreq32): + ret = sys32_ioctl(fd, SIOCGIFFLAGS, arg); + goto out; + case _IOW('i', 18, struct ifreq32): + ret = sys32_ioctl(fd, SIOCSIFMEM, arg); + goto out; + case _IOWR('i', 19, struct ifreq32): + ret = sys32_ioctl(fd, SIOCGIFMEM, arg); + goto out; + + case _IOWR('i', 20, struct ifconf32): + ret = sys32_ioctl(fd, SIOCGIFCONF, arg); + goto out; + + case _IOW('i', 21, struct ifreq): /* SIOCSIFMTU */ + ret = sys_ioctl(fd, SIOCSIFMTU, arg); + goto out; + case _IOWR('i', 22, struct ifreq): /* SIOCGIFMTU */ + ret = sys_ioctl(fd, SIOCGIFMTU, arg); + goto out; + + case _IOWR('i', 23, struct ifreq32): + ret = sys32_ioctl(fd, SIOCGIFBRDADDR, arg); + goto out; + case _IOW('i', 24, struct ifreq32): + ret = sys32_ioctl(fd, SIOCGIFBRDADDR, arg); + goto out; + case _IOWR('i', 25, struct ifreq32): + ret = sys32_ioctl(fd, SIOCGIFNETMASK, arg); + goto out; + case _IOW('i', 26, struct ifreq32): + ret = sys32_ioctl(fd, SIOCSIFNETMASK, arg); + goto out; + case _IOWR('i', 27, struct ifreq32): + ret = sys32_ioctl(fd, SIOCGIFMETRIC, arg); + goto out; + case _IOW('i', 28, struct ifreq32): + ret = sys32_ioctl(fd, SIOCSIFMETRIC, arg); + goto out; + + case _IOW('i', 30, struct arpreq): + ret = sys32_ioctl(fd, SIOCSARP, arg); + goto out; + case _IOWR('i', 31, struct arpreq): + ret = sys32_ioctl(fd, SIOCGARP, arg); + goto out; + case _IOW('i', 32, struct arpreq): + ret = sys32_ioctl(fd, SIOCDARP, arg); + goto out; + + case _IOW('i', 40, struct ifreq32): /* SIOCUPPER */ + case _IOW('i', 41, struct ifreq32): /* SIOCLOWER */ + case _IOW('i', 44, struct ifreq32): /* SIOCSETSYNC */ + case _IOW('i', 45, struct ifreq32): /* SIOCGETSYNC */ + case _IOW('i', 46, struct ifreq32): /* SIOCSSDSTATS */ + case _IOW('i', 47, struct ifreq32): /* SIOCSSESTATS */ + case _IOW('i', 48, struct ifreq32): /* SIOCSPROMISC */ + ret = -EOPNOTSUPP; + goto out; + + case _IOW('i', 49, struct ifreq32): + ret = sys32_ioctl(fd, SIOCADDMULTI, arg); + goto out; + case _IOW('i', 50, struct ifreq32): + ret = sys32_ioctl(fd, SIOCDELMULTI, arg); + goto out; + + /* FDDI interface ioctls, unsupported. */ + + case _IOW('i', 51, struct ifreq32): /* SIOCFDRESET */ + case _IOW('i', 52, struct ifreq32): /* SIOCFDSLEEP */ + case _IOW('i', 53, struct ifreq32): /* SIOCSTRTFMWAR */ + case _IOW('i', 54, struct ifreq32): /* SIOCLDNSTRTFW */ + case _IOW('i', 55, struct ifreq32): /* SIOCGETFDSTAT */ + case _IOW('i', 56, struct ifreq32): /* SIOCFDNMIINT */ + case _IOW('i', 57, struct ifreq32): /* SIOCFDEXUSER */ + case _IOW('i', 58, struct ifreq32): /* SIOCFDGNETMAP */ + case _IOW('i', 59, struct ifreq32): /* SIOCFDGIOCTL */ + printk("FDDI ioctl, returning EOPNOTSUPP\n"); + ret = -EOPNOTSUPP; + goto out; + + case _IOW('t', 125, int): + /* More stupid tty sunos ioctls, just + * say it worked. + */ + ret = 0; + goto out; + + /* Non posix grp */ + case _IOW('t', 118, int): { + int oldval, newval, *ptr; + + cmd = TIOCSPGRP; + ptr = (int *) A(arg); + ret = -EFAULT; + if(get_user(oldval, ptr)) + goto out; + ret = sys32_ioctl(fd, cmd, arg); + __get_user(newval, ptr); + if(newval == -1) { + __put_user(oldval, ptr); + ret = -EIO; + } + if(ret == -ENOTTY) + ret = -EIO; + goto out; + } + + case _IOR('t', 119, int): { + int oldval, newval, *ptr; + + cmd = TIOCGPGRP; + ptr = (int *) A(arg); + ret = -EFAULT; + if(get_user(oldval, ptr)) + goto out; + ret = sys32_ioctl(fd, cmd, arg); + __get_user(newval, ptr); + if(newval == -1) { + __put_user(oldval, ptr); + ret = -EIO; + } + if(ret == -ENOTTY) + ret = -EIO; + goto out; + } + }; + + ret = sys32_ioctl(fd, cmd, arg); + /* so stupid... */ + ret = (ret == -EINVAL ? -EOPNOTSUPP : ret); +out: + unlock_kernel(); + return ret; +} diff --git a/arch/sparc64/kernel/sys32.S b/arch/sparc64/kernel/sys32.S new file mode 100644 index 000000000..ddd726069 --- /dev/null +++ b/arch/sparc64/kernel/sys32.S @@ -0,0 +1,427 @@ +/* $Id: sys32.S,v 1.1 1997/07/18 06:26:42 ralf Exp $ + * sys32.S: I-cache tricks for 32-bit compatability layer simple + * conversions. + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + */ + + .text + + .align 32 + .globl sys32_mmap, sys32_mprotect, sys32_munmap, sys32_msync + .globl sys32_mlock, sys32_munlock, sys32_mremap, sparc32_brk +sys32_mmap: + srl %o0, 0, %o0 ! IEU0 Group + sethi %hi(0xffffffff), %g2 ! IEU1 + srl %o1, 0, %o1 ! IEU0 Group + or %g2, %lo(0xffffffff), %g2 ! IEU1 + srl %o2, 0, %o2 ! IEU0 Group + mov %o7, %g1 ! IEU1 + and %o3, %g2, %o3 ! IEU0 Group + and %o4, %g2, %o4 ! IEU1 + and %o5, %g2, %o5 ! IEU0 Group + call sys_mmap ! CTI Group brk forced + mov %g1, %o7 ! IEU0 Group (regdep) +sys32_mprotect: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + srl %o2, 0, %o2 + call sys_mprotect + mov %g1, %o7 +sys32_munmap: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + call sys_munmap + mov %g1, %o7 +sparc32_brk: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_brk + mov %g1, %o7 +sys32_msync: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + call sys_msync + mov %g1, %o7 +sys32_mlock: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + call sys_mlock + mov %g1, %o7 +sys32_munlock: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + call sys_munlock + mov %g1, %o7 +sys32_mremap: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + srl %o2, 0, %o2 + srl %o3, 0, %o3 + call sys_mremap + mov %g1, %o7 + + .align 32 + .globl sys32_read, sys32_write, sys32_open, sys32_access + .globl sys32_chdir, sys32_lseek, sys32_llseek, sys32_poll + .globl sys32_readlink, sys32_unlink, sys32_rmdir, sys32_symlink + .globl sys32_link, sys32_rename, sys32_truncate, sys32_ftruncate + .globl sys32_chroot, sys32_chmod, sys32_chown, sys32_creat + .globl sys32_mkdir, sys32_mknod, sys32_utimes, sys32_ustat +sys32_read: + srl %o1, 0, %o1 + mov %o7, %g1 + srl %o2, 0, %o2 + call sys_read + mov %g1, %o7 +sys32_write: + srl %o1, 0, %o1 + mov %o7, %g1 + srl %o2, 0, %o2 + call sys_write + mov %g1, %o7 +sys32_open: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_open + mov %g1, %o7 +sys32_access: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_access + mov %g1, %o7 +sys32_chdir: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_chdir + mov %g1, %o7 +sys32_lseek: + sra %o1, 0, %o1 + mov %o7, %g1 + call sys_lseek + mov %g1, %o7 +sys32_llseek: + srl %o1, 0, %o1 + mov %o7, %g1 + srl %o2, 0, %o2 + srl %o3, 0, %o3 + call sys_llseek + mov %g1, %o7 +sys32_poll: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_poll + mov %g1, %o7 +sys32_readlink: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + call sys_readlink + mov %g1, %o7 +sys32_unlink: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_unlink + mov %g1, %o7 +sys32_rmdir: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_rmdir + mov %g1, %o7 +sys32_symlink: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + call sys_symlink + mov %g1, %o7 +sys32_link: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + call sys_link + mov %g1, %o7 +sys32_rename: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + call sys_rename + mov %g1, %o7 + nop +sys32_truncate: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + call sys_truncate + mov %g1, %o7 +sys32_ftruncate: + srl %o1, 0, %o1 + mov %o7, %g1 + call sys_ftruncate + mov %g1, %o7 +sys32_chroot: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_chroot + mov %g1, %o7 +sys32_chmod: + sll %o1, 16, %o1 + mov %o7, %g1 + srl %o0, 0, %o0 + srl %o1, 16, %o1 + call sys_chmod + mov %g1, %o7 +sys32_chown: + sll %o1, 16, %o1 + mov %o7, %g1 + sll %o2, 16, %o2 + srl %o0, 0, %o0 + srl %o1, 16, %o1 + srl %o2, 16, %o2 + call sys_chown + mov %g1, %o7 +sys32_creat: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_creat + mov %g1, %o7 +sys32_mkdir: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_mkdir + mov %g1, %o7 +sys32_mknod: + sll %o2, 16, %o2 + mov %o7, %g1 + srl %o0, 0, %o0 + srl %o2, 16, %o2 + call sys_mknod + mov %g1, %o7 +sys32_utimes: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + call sys_utimes + mov %g1, %o7 +sys32_ustat: + srl %o1, 0, %o1 + mov %o7, %g1 + call sys_ustat + mov %g1, %o7 + + .align 32 + .globl sys32_bind, sys32_accept, sys32_connect, sys32_getsockname + .globl sys32_getpeername, sys32_send, sys32_sendto, sys32_recv + .globl sys32_recvfrom, sys32_setsockopt, sys32_getsockopt +sys32_bind: + srl %o1, 0, %o1 + mov %o7, %g1 + call sys_bind + mov %g1, %o7 +sys32_accept: + srl %o1, 0, %o1 + mov %o7, %g1 + srl %o2, 0, %o2 + call sys_accept + mov %g1, %o7 +sys32_connect: + srl %o1, 0, %o1 + mov %o7, %g1 + call sys_connect + mov %g1, %o7 +sys32_getsockname: + srl %o1, 0, %o1 + mov %o7, %g1 + srl %o2, 0, %o2 + call sys_getsockname + mov %g1, %o7 +sys32_getpeername: + srl %o1, 0, %o1 + mov %o7, %g1 + srl %o2, 0, %o2 + call sys_getpeername + mov %g1, %o7 +sys32_send: + srl %o1, 0, %o1 + mov %o7, %g1 + srl %o2, 0, %o2 + call sys_send + mov %g1, %o7 +sys32_sendto: + srl %o1, 0, %o1 + mov %o7, %g1 + srl %o2, 0, %o2 + srl %o4, 0, %o4 + call sys_sendto + mov %g1, %o7 +sys32_recv: + srl %o1, 0, %o1 + mov %o7, %g1 + srl %o2, 0, %o2 + call sys_recv + mov %g1, %o7 +sys32_recvfrom: + srl %o1, 0, %o1 + mov %o7, %g1 + srl %o2, 0, %o2 + srl %o4, 0, %o4 + srl %o5, 0, %o5 + call sys_recvfrom + mov %g1, %o7 +sys32_setsockopt: + srl %o3, 0, %o3 + mov %o7, %g1 + call sys_setsockopt + mov %g1, %o7 +sys32_getsockopt: + srl %o3, 0, %o3 + mov %o7, %g1 + srl %o4, 0, %o4 + call sys_setsockopt + mov %g1, %o7 + + .align 32 + .globl sys32_gettimeofday, sys32_settimeofday +sys32_gettimeofday: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + call sys_gettimeofday + mov %g1, %o7 +sys32_settimeofday: + srl %o0, 0, %o0 + mov %o7, %g1 + srl %o1, 0, %o1 + call sys_settimeofday + mov %g1, %o7 + + .globl sys32_bdflush, sys32_uselib, sys32_umount, sys32_syslog + .globl sys32_personality, sys32_waitpid, sys32_getitimer + .globl sys32_setitimer, sys32_sched_setscheduler + .globl sys32_sched_setparam, sys32_sched_getparam, sys32_signal + .globl sys32_reboot, sys32_acct, sys32_newuname, sys32_olduname + .globl sys32_sethostname, sys32_gethostname, sys32_setdomainname + .globl sys32_time, sys32_swapoff, sys32_swapon, sys32_nfsservctl + .globl sys32_create_module, sys32_init_module, sys32_delete_module +sys32_bdflush: + sra %o1, 0, %o1 + mov %o7, %g1 + call sys_bdflush + mov %g1, %o7 +sys32_uselib: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_uselib + mov %g1, %o7 +sys32_umount: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_umount + mov %g1, %o7 +sys32_syslog: + srl %o1, 0, %o1 + mov %o7, %g1 + call sys_syslog + mov %g1, %o7 +sys32_personality: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_personality + mov %g1, %o7 +sys32_waitpid: + srl %o1, 0, %o1 + mov %o7, %g1 + call sys_waitpid + mov %g1, %o7 +sys32_getitimer: + srl %o1, 0, %o1 + mov %o7, %g1 + call sys_getitimer + mov %g1, %o7 +sys32_setitimer: + srl %o1, 0, %o1 + mov %o7, %g1 + srl %o2, 0, %o2 + call sys_setitimer + mov %g1, %o7 +sys32_sched_setscheduler: + srl %o2, 0, %o2 + mov %o7, %g1 + call sys_sched_setscheduler + mov %g1, %o7 +sys32_sched_setparam: + srl %o1, 0, %o1 + mov %o7, %g1 + call sys_sched_setparam + mov %g1, %o7 +sys32_sched_getparam: + srl %o1, 0, %o1 + mov %o7, %g1 + call sys_sched_getparam + mov %g1, %o7 +sys32_signal: + srl %o1, 0, %o1 + mov %o7, %g1 + call sys_signal + mov %g1, %o7 +sys32_reboot: + srl %o3, 0, %o3 + mov %o7, %g1 + call sys_reboot + mov %g1, %o7 +sys32_acct: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_acct + mov %g1, %o7 +sys32_newuname: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_newuname + mov %g1, %o7 +sys32_olduname: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_olduname + mov %g1, %o7 +sys32_sethostname: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_sethostname + mov %g1, %o7 +sys32_gethostname: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_gethostname + mov %g1, %o7 +sys32_setdomainname: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_setdomainname + mov %g1, %o7 +sys32_time: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_time + mov %g1, %o7 +sys32_swapoff: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_swapoff + mov %g1, %o7 +sys32_swapon: + srl %o0, 0, %o0 + mov %o7, %g1 + call sys_swapon + mov %g1, %o7 +sys32_nfsservctl: + srl %o1, 0, %o1 + mov %o7, %g1 + srl %o2, 0, %o2 + call sys_nfsservctl + mov %g1, %o7 diff --git a/arch/sparc64/kernel/sys_sparc.c b/arch/sparc64/kernel/sys_sparc.c index 851d1550c..c827df7a1 100644 --- a/arch/sparc64/kernel/sys_sparc.c +++ b/arch/sparc64/kernel/sys_sparc.c @@ -1,4 +1,4 @@ -/* $Id: sys_sparc.c,v 1.1 1997/04/09 08:25:18 jj Exp $ +/* $Id: sys_sparc.c,v 1.2 1997/07/05 09:52:34 davem Exp $ * linux/arch/sparc64/kernel/sys_sparc.c * * This file contains various random system calls that @@ -9,7 +9,6 @@ #include <linux/errno.h> #include <linux/types.h> #include <linux/sched.h> -#include <linux/config.h> #include <linux/fs.h> #include <linux/mm.h> #include <linux/sem.h> diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index 59815b7a8..1f607da98 100644 --- a/arch/sparc64/kernel/sys_sparc32.c +++ b/arch/sparc64/kernel/sys_sparc32.c @@ -1,4 +1,4 @@ -/* $Id: sys_sparc32.c,v 1.26 1997/06/04 13:05:21 jj Exp $ +/* $Id: sys_sparc32.c,v 1.43 1997/07/17 02:20:45 davem Exp $ * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -8,6 +8,7 @@ * environment. */ +#include <linux/config.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/signal.h> @@ -30,11 +31,13 @@ #include <linux/ncp_fs.h> #include <linux/quota.h> #include <linux/file.h> +#include <linux/module.h> #include <asm/types.h> #include <asm/poll.h> #include <asm/ipc.h> #include <asm/uaccess.h> +#include <asm/fpumacro.h> /* As gcc will warn about casting u32 to some ptr, we have to cast it to * unsigned long first, and that's what is A() for. @@ -372,11 +375,12 @@ asmlinkage int sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u switch (version) { case 0: default: { unsigned long raddr; + u32 *uptr = (u32 *) A(((u32)third)); err = sys_shmat (first, (char *)A(ptr), second, &raddr); if (err) goto out; err = -EFAULT; - if(put_user (raddr, ((u32 *)A(third)))) + if(put_user (raddr, uptr)) goto out; err = 0; goto out; @@ -469,32 +473,6 @@ out: return err; } -extern asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, - unsigned long prot, unsigned long flags, - unsigned long fd, unsigned long off); - -asmlinkage unsigned long sys32_mmap(u32 addr, u32 len, u32 prot, - u32 flags, u32 fd, u32 off) -{ - return sys_mmap((unsigned long)addr, (unsigned long)len, - (unsigned long)prot, (unsigned long)flags, - (unsigned long)fd, (unsigned long)off); -} - -extern asmlinkage int sys_bdflush(int func, long data); - -asmlinkage int sys32_bdflush(int func, s32 data) -{ - return sys_bdflush(func, (long)data); -} - -extern asmlinkage int sys_uselib(const char * library); - -asmlinkage int sys32_uselib(u32 library) -{ - return sys_uselib((const char *)A(library)); -} - static inline int get_flock(struct flock *kfl, struct flock32 *ufl) { if(get_user(kfl->l_type, &ufl->l_type) || @@ -544,55 +522,6 @@ asmlinkage long sys32_fcntl(unsigned int fd, unsigned int cmd, u32 arg) } } -extern asmlinkage int sys_mknod(const char * filename, int mode, dev_t dev); - -asmlinkage int sys32_mknod(u32 filename, int mode, __kernel_dev_t32 dev) -{ - return sys_mknod((const char *)A(filename), mode, dev); -} - -extern asmlinkage int sys_mkdir(const char * pathname, int mode); - -asmlinkage int sys32_mkdir(u32 pathname, int mode) -{ - return sys_mkdir((const char *)A(pathname), mode); -} - -extern asmlinkage int sys_rmdir(const char * pathname); - -asmlinkage int sys32_rmdir(u32 pathname) -{ - return sys_rmdir((const char *)A(pathname)); -} - -extern asmlinkage int sys_unlink(const char * pathname); - -asmlinkage int sys32_unlink(u32 pathname) -{ - return sys_unlink((const char *)A(pathname)); -} - -extern asmlinkage int sys_symlink(const char * oldname, const char * newname); - -asmlinkage int sys32_symlink(u32 oldname, u32 newname) -{ - return sys_symlink((const char *)A(oldname), (const char *)A(newname)); -} - -extern asmlinkage int sys_link(const char * oldname, const char * newname); - -asmlinkage int sys32_link(u32 oldname, u32 newname) -{ - return sys_link((const char *)A(oldname), (const char *)A(newname)); -} - -extern asmlinkage int sys_rename(const char * oldname, const char * newname); - -asmlinkage int sys32_rename(u32 oldname, u32 newname) -{ - return sys_rename((const char *)A(oldname), (const char *)A(newname)); -} - struct dqblk32 { __u32 dqb_bhardlimit; __u32 dqb_bsoftlimit; @@ -701,20 +630,6 @@ asmlinkage int sys32_fstatfs(unsigned int fd, u32 buf) return ret; } -extern asmlinkage int sys_truncate(const char * path, unsigned long length); - -asmlinkage int sys32_truncate(u32 path, u32 length) -{ - return sys_truncate((const char *)A(path), (unsigned long)length); -} - -extern asmlinkage int sys_ftruncate(unsigned int fd, unsigned long length); - -asmlinkage int sys32_ftruncate(unsigned int fd, u32 length) -{ - return sys_ftruncate(fd, (unsigned long)length); -} - extern asmlinkage int sys_utime(char * filename, struct utimbuf * times); asmlinkage int sys32_utime(u32 filename, u32 times) @@ -741,96 +656,6 @@ asmlinkage int sys32_utime(u32 filename, u32 times) return ret; } -extern asmlinkage int sys_utimes(char * filename, struct timeval * utimes); - -asmlinkage int sys32_utimes(u32 filename, u32 utimes) -{ - /* struct timeval is the same :)) */ - return sys_utimes((char *)A(filename), (struct timeval *)A(utimes)); -} - -extern asmlinkage int sys_access(const char * filename, int mode); - -asmlinkage int sys32_access(u32 filename, int mode) -{ - return sys_access((const char *)A(filename), mode); -} - -extern asmlinkage int sys_chdir(const char * filename); - -asmlinkage int sys32_chdir(u32 filename) -{ - return sys_chdir((const char *)A(filename)); -} - -extern asmlinkage int sys_chroot(const char * filename); - -asmlinkage int sys32_chroot(u32 filename) -{ - return sys_chroot((const char *)A(filename)); -} - -extern asmlinkage int sys_chmod(const char * filename, mode_t mode); - -asmlinkage int sys32_chmod(u32 filename, __kernel_mode_t32 mode) -{ - return sys_chmod((const char *)A(filename), mode); -} - -extern asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group); - -asmlinkage int sys32_chown(u32 filename, __kernel_uid_t32 user, __kernel_gid_t32 group) -{ - return sys_chown((const char *)A(filename), user, group); -} - -extern asmlinkage int sys_open(const char * filename,int flags,int mode); - -asmlinkage int sys32_open(u32 filename, int flags, int mode) -{ - return sys_open((const char *)A(filename), flags, mode); -} - -extern asmlinkage int sys_creat(const char * pathname, int mode); - -asmlinkage int sys32_creat(u32 pathname, int mode) -{ - return sys_creat((const char *)A(pathname), mode); -} - -extern asmlinkage long sys_lseek(unsigned int fd, off_t offset, unsigned int origin); - -asmlinkage long sys32_lseek(unsigned int fd, s32 offset, unsigned int origin) -{ - return sys_lseek(fd, (off_t)offset, origin); -} - -extern asmlinkage int sys_llseek(unsigned int fd, unsigned long offset_high, - unsigned long offset_low, - loff_t *result, unsigned int origin); - -asmlinkage int sys32_llseek(unsigned int fd, u32 offset_high, - u32 offset_low, u32 result, unsigned int origin) -{ - /* loff_t is the same :)) */ - return sys_llseek(fd, (unsigned long)offset_high, (unsigned long)offset_low, - (loff_t *)A(result), origin); -} - -extern asmlinkage long sys_read(unsigned int fd, char * buf, unsigned long count); - -asmlinkage long sys32_read(unsigned int fd, u32 buf, u32 count) -{ - return sys_read(fd, (char *)A(buf), (unsigned long)count); -} - -extern asmlinkage long sys_write(unsigned int fd, const char * buf, unsigned long count); - -asmlinkage long sys32_write(unsigned int fd, u32 buf, u32 count) -{ - return sys_write(fd, (const char *)A(buf), (unsigned long)count); -} - struct iovec32 { u32 iov_base; __kernel_size_t32 iov_len; }; typedef long (*IO_fn_t)(struct inode *, struct file *, char *, unsigned long); @@ -934,14 +759,29 @@ static long do_readv_writev32(int type, struct inode *inode, struct file *file, asmlinkage long sys32_readv(int fd, u32 vector, u32 count) { struct file *file; + struct dentry *dentry; struct inode *inode; long err = -EBADF; lock_kernel(); - if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || !(inode=file->f_inode)) + if(fd >= NR_OPEN) + goto out; + + file = current->files->fd[fd]; + if(!file) + goto out; + + if(!(file->f_mode & 1)) + goto out; + + dentry = file->f_dentry; + if(!dentry) goto out; - if (!(file->f_mode & 1)) + + inode = dentry->d_inode; + if(!inode) goto out; + err = do_readv_writev32(VERIFY_WRITE, inode, file, (struct iovec32 *)A(vector), count); out: @@ -953,13 +793,28 @@ asmlinkage long sys32_writev(int fd, u32 vector, u32 count) { int error = -EBADF; struct file *file; + struct dentry *dentry; struct inode *inode; lock_kernel(); - if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || !(inode=file->f_inode)) + if(fd >= NR_OPEN) + goto out; + + file = current->files->fd[fd]; + if(!file) + goto out; + + if(!(file->f_mode & 2)) + goto out; + + dentry = file->f_dentry; + if(!dentry) goto out; - if (!(file->f_mode & 2)) + + inode = dentry->d_inode; + if(!inode) goto out; + down(&inode->i_sem); error = do_readv_writev32(VERIFY_READ, inode, file, (struct iovec32 *)A(vector), count); @@ -1008,21 +863,34 @@ asmlinkage int old32_readdir(unsigned int fd, u32 dirent, unsigned int count) { int error = -EBADF; struct file * file; + struct dentry * dentry; + struct inode * inode; struct readdir_callback32 buf; lock_kernel(); - if (fd >= NR_OPEN || !(file = current->files->fd[fd])) + if(fd >= NR_OPEN) goto out; - error = -ENOTDIR; - if (!file->f_op || !file->f_op->readdir) + + file = current->files->fd[fd]; + if(!file) goto out; - error = verify_area(VERIFY_WRITE, (void *)A(dirent), - sizeof(struct old_linux_dirent32)); - if (error) + + dentry = file->f_dentry; + if(!dentry) + goto out; + + inode = dentry->d_inode; + if(!inode) goto out; + buf.count = 0; buf.dirent = (struct old_linux_dirent32 *)A(dirent); - error = file->f_op->readdir(file->f_inode, file, &buf, fillonedir); + + error = -ENOTDIR; + if (!file->f_op || !file->f_op->readdir) + goto out; + + error = file->f_op->readdir(inode, file, &buf, fillonedir); if (error < 0) goto out; error = buf.count; @@ -1072,30 +940,43 @@ static int filldir(void * __buf, const char * name, int namlen, off_t offset, in asmlinkage int sys32_getdents(unsigned int fd, u32 dirent, unsigned int count) { struct file * file; + struct dentry * dentry; + struct inode *inode; struct linux_dirent32 * lastdirent; struct getdents_callback32 buf; int error = -EBADF; lock_kernel(); - if (fd >= NR_OPEN || !(file = current->files->fd[fd])) + if(fd >= NR_OPEN) goto out; - error = -ENOTDIR; - if (!file->f_op || !file->f_op->readdir) + + file = current->files->fd[fd]; + if(!file) + goto out; + + dentry = file->f_dentry; + if(!dentry) goto out; - error = verify_area(VERIFY_WRITE, (void *)A(dirent), count); - if (error) + + inode = dentry->d_inode; + if(!inode) goto out; + buf.current_dir = (struct linux_dirent32 *) A(dirent); buf.previous = NULL; buf.count = count; buf.error = 0; - error = file->f_op->readdir(file->f_inode, file, &buf, filldir); + + error = -ENOTDIR; + if (!file->f_op || !file->f_op->readdir) + goto out; + + error = file->f_op->readdir(inode, file, &buf, filldir); if (error < 0) goto out; lastdirent = buf.previous; - if (!lastdirent) { - error = buf.error; - } else { + error = buf.error; + if(lastdirent) { put_user(file->f_pos, &lastdirent->d_off); error = count - buf.count; } @@ -1196,13 +1077,6 @@ out: return ret; } -extern asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, int timeout); - -asmlinkage int sys32_poll(u32 ufds, unsigned int nfds, int timeout) -{ - return sys_poll((struct pollfd *)A(ufds), nfds, timeout); -} - static inline int putstat(struct stat32 *ubuf, struct stat *kbuf) { if (put_user (kbuf->st_dev, &ubuf->st_dev) || @@ -1280,13 +1154,6 @@ asmlinkage int sys32_newfstat(unsigned int fd, u32 statbuf) return ret; } -extern asmlinkage int sys_readlink(const char * path, char * buf, int bufsiz); - -asmlinkage int sys32_readlink(u32 path, u32 buf, int bufsiz) -{ - return sys_readlink((const char *)A(path), (char *)A(buf), bufsiz); -} - extern asmlinkage int sys_sysfs(int option, ...); asmlinkage int sys32_sysfs(int option, ...) @@ -1312,42 +1179,162 @@ asmlinkage int sys32_sysfs(int option, ...) return ret; } -extern asmlinkage int sys_ustat(dev_t dev, struct ustat * ubuf); +struct ncp_mount_data32 { + int version; + unsigned int ncp_fd; + __kernel_uid_t32 mounted_uid; + __kernel_pid_t32 wdog_pid; + unsigned char mounted_vol[NCP_VOLNAME_LEN + 1]; + unsigned int time_out; + unsigned int retry_count; + unsigned int flags; + __kernel_uid_t32 uid; + __kernel_gid_t32 gid; + __kernel_mode_t32 file_mode; + __kernel_mode_t32 dir_mode; +}; -asmlinkage int sys32_ustat(dev_t dev, u32 ubuf) +static void *do_ncp_super_data_conv(void *raw_data) { - /* ustat is the same :)) */ - return sys_ustat(dev, (struct ustat *)A(ubuf)); + struct ncp_mount_data *n = (struct ncp_mount_data *)raw_data; + struct ncp_mount_data32 *n32 = (struct ncp_mount_data32 *)raw_data; + + n->dir_mode = n32->dir_mode; + n->file_mode = n32->file_mode; + n->gid = n32->gid; + n->uid = n32->uid; + memmove (n->mounted_vol, n32->mounted_vol, (sizeof (n32->mounted_vol) + 3 * sizeof (unsigned int))); + n->wdog_pid = n32->wdog_pid; + n->mounted_uid = n32->mounted_uid; + return raw_data; } -extern asmlinkage int sys_umount(char * name); +struct smb_mount_data32 { + int version; + unsigned int fd; + __kernel_uid_t32 mounted_uid; + struct sockaddr_in addr; + char server_name[17]; + char client_name[17]; + char service[64]; + char root_path[64]; + char username[64]; + char password[64]; + char domain[64]; + unsigned short max_xmit; + __kernel_uid_t32 uid; + __kernel_gid_t32 gid; + __kernel_mode_t32 file_mode; + __kernel_mode_t32 dir_mode; +}; -asmlinkage int sys32_umount(u32 name) +static void *do_smb_super_data_conv(void *raw_data) { - return sys_umount((char *)A(name)); -} + struct smb_mount_data *s = (struct smb_mount_data *)raw_data; + struct smb_mount_data32 *s32 = (struct smb_mount_data32 *)raw_data; -extern asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type, - unsigned long new_flags, void *data); + s->dir_mode = s32->dir_mode; + s->file_mode = s32->file_mode; + s->gid = s32->gid; + s->uid = s32->uid; + memmove (&s->addr, &s32->addr, (((long)&s->uid) - ((long)&s->addr))); + s->mounted_uid = s32->mounted_uid; + return raw_data; +} -asmlinkage int sys32_mount(u32 dev_name, u32 dir_name, u32 type, u32 new_flags, u32 data) +static int copy_mount_stuff_to_kernel(const void *user, unsigned long *kernel) { - return sys_mount((char *)A(dev_name), (char *)A(dir_name), (char *)A(type), - (unsigned long)new_flags, (void *)A(data)); + int i; + unsigned long page; + struct vm_area_struct *vma; + + *kernel = 0; + if(!user) + return 0; + vma = find_vma(current->mm, (unsigned long)user); + if(!vma || (unsigned long)user < vma->vm_start) + return -EFAULT; + if(!(vma->vm_flags & VM_READ)) + return -EFAULT; + i = vma->vm_end - (unsigned long) user; + if(PAGE_SIZE <= (unsigned long) i) + i = PAGE_SIZE - 1; + if(!(page = __get_free_page(GFP_KERNEL))) + return -ENOMEM; + if(copy_from_user((void *) page, user, i)) { + free_page(page); + return -EFAULT; + } + *kernel = page; + return 0; } -extern asmlinkage int sys_syslog(int type, char * bug, int count); +extern asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type, + unsigned long new_flags, void *data); + +#define SMBFS_NAME "smbfs" +#define NCPFS_NAME "ncpfs" -asmlinkage int sys32_syslog(int type, u32 bug, int count) +asmlinkage int sys32_mount(u32 dev_name, u32 dir_name, u32 type, u32 new_flags, u32 data) { - return sys_syslog(type, (char *)A(bug), count); -} + unsigned long type_page; + int err, is_smb, is_ncp; -extern asmlinkage int sys_personality(unsigned long personality); + if(!suser()) + return -EPERM; + is_smb = is_ncp = 0; + err = copy_mount_stuff_to_kernel((const void *)A(type), &type_page); + if(err) + return err; + if(type_page) { + is_smb = !strcmp((char *)type_page, SMBFS_NAME); + is_ncp = !strcmp((char *)type_page, NCPFS_NAME); + } + if(!is_smb && !is_ncp) { + if(type_page) + free_page(type_page); + return sys_mount((char *)A(dev_name), (char *)A(dir_name), + (char *)A(type), (unsigned long)new_flags, + (void *)A(data)); + } else { + unsigned long dev_page, dir_page, data_page; + int old_fs; -asmlinkage int sys32_personality(u32 personality) -{ - return sys_personality((unsigned long)personality); + err = copy_mount_stuff_to_kernel((const void *)A(dev_name), &dev_page); + if(err) + goto out; + err = copy_mount_stuff_to_kernel((const void *)A(dir_name), &dir_page); + if(err) + goto dev_out; + err = copy_mount_stuff_to_kernel((const void *)A(data), &data_page); + if(err) + goto dir_out; + if(is_ncp) + do_ncp_super_data_conv((void *)data_page); + else if(is_smb) + do_smb_super_data_conv((void *)data_page); + else + panic("Tell DaveM he fucked up..."); + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_mount((char *)dev_page, (char *)dir_page, + (char *)type_page, (unsigned long)new_flags, + (void *)data_page); + set_fs(old_fs); + + if(data_page) + free_page(data_page); + dir_out: + if(dir_page) + free_page(dir_page); + dev_out: + if(dev_page) + free_page(dev_page); + out: + if(type_page) + free_page(type_page); + return err; + } } struct rusage32 { @@ -1416,13 +1403,6 @@ asmlinkage int sys32_wait4(__kernel_pid_t32 pid, u32 stat_addr, int options, u32 } } -extern asmlinkage int sys_waitpid(pid_t pid,unsigned int * stat_addr, int options); - -asmlinkage int sys32_waitpid(__kernel_pid_t32 pid, u32 stat_addr, int options) -{ - return sys_waitpid(pid, (unsigned int *)A(stat_addr), options); -} - struct sysinfo32 { s32 uptime; u32 loads[3]; @@ -1462,46 +1442,6 @@ asmlinkage int sys32_sysinfo(u32 info) return ret; } -extern asmlinkage int sys_getitimer(int which, struct itimerval *value); - -asmlinkage int sys32_getitimer(int which, u32 value) -{ - /* itimerval is the same :)) */ - return sys_getitimer(which, (struct itimerval *)A(value)); -} - -extern asmlinkage int sys_setitimer(int which, struct itimerval *value, - struct itimerval *ovalue); - -asmlinkage int sys32_setitimer(int which, u32 value, u32 ovalue) -{ - return sys_setitimer(which, (struct itimerval *)A(value), - (struct itimerval *)A(ovalue)); -} - -extern asmlinkage int sys_sched_setscheduler(pid_t pid, int policy, - struct sched_param *param); - -asmlinkage int sys32_sched_setscheduler(__kernel_pid_t32 pid, int policy, u32 param) -{ - /* sched_param is the same :)) */ - return sys_sched_setscheduler(pid, policy, (struct sched_param *)A(param)); -} - -extern asmlinkage int sys_sched_setparam(pid_t pid, struct sched_param *param); - -asmlinkage int sys32_sched_setparam(__kernel_pid_t32 pid, u32 param) -{ - return sys_sched_setparam(pid, (struct sched_param *)A(param)); -} - -extern asmlinkage int sys_sched_getparam(pid_t pid, struct sched_param *param); - -asmlinkage int sys32_sched_getparam(__kernel_pid_t32 pid, u32 param) -{ - return sys_sched_getparam(pid, (struct sched_param *)A(param)); -} - struct timespec32 { s32 tv_sec; s32 tv_nsec; @@ -1577,25 +1517,29 @@ asmlinkage int sys32_sigpending(u32 set) return ret; } -extern asmlinkage unsigned long sys_signal(int signum, __sighandler_t handler); +extern asmlinkage int sys_setreuid(uid_t ruid, uid_t euid); -asmlinkage unsigned long sys32_signal(int signum, u32 handler) +asmlinkage int sys32_setreuid(__kernel_uid_t32 ruid, __kernel_uid_t32 euid) { - return sys_signal(signum, (__sighandler_t)A(handler)); -} - -extern asmlinkage int sys_reboot(int magic1, int magic2, int cmd, void * arg); + uid_t sruid, seuid; -asmlinkage int sys32_reboot(int magic1, int magic2, int cmd, u32 arg) -{ - return sys_reboot(magic1, magic2, cmd, (void *)A(arg)); + sruid = (ruid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)ruid); + seuid = (euid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)euid); + return sys_setreuid(sruid, seuid); } -extern asmlinkage int sys_acct(const char *name); +extern asmlinkage int sys_setresuid(uid_t ruid, uid_t euid, uid_t suid); -asmlinkage int sys32_acct(u32 name) +asmlinkage int sys32_setresuid(__kernel_uid_t32 ruid, + __kernel_uid_t32 euid, + __kernel_uid_t32 suid) { - return sys_acct((const char *)A(name)); + uid_t sruid, seuid, ssuid; + + sruid = (ruid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)ruid); + seuid = (euid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)euid); + ssuid = (suid == (__kernel_uid_t32)-1) ? ((uid_t)-1) : ((uid_t)suid); + return sys_setresuid(sruid, seuid, ssuid); } extern asmlinkage int sys_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); @@ -1654,7 +1598,7 @@ asmlinkage int sys32_getgroups(int gidsetsize, u32 grouplist) set_fs (KERNEL_DS); ret = sys_getgroups(gidsetsize, gl); set_fs (old_fs); - if (ret > 0 && ret <= NGROUPS) + if (gidsetsize && ret > 0 && ret <= NGROUPS) for (i = 0; i < ret; i++, grouplist += sizeof(__kernel_gid_t32)) if (__put_user (gl[i], (__kernel_gid_t32 *)A(grouplist))) return -EFAULT; @@ -1680,41 +1624,8 @@ asmlinkage int sys32_setgroups(int gidsetsize, u32 grouplist) return ret; } -extern asmlinkage int sys_newuname(struct new_utsname * name); - -asmlinkage int sys32_newuname(u32 name) -{ - /* utsname is the same :)) */ - return sys_newuname((struct new_utsname *)A(name)); -} - -extern asmlinkage int sys_olduname(struct oldold_utsname * name); - -asmlinkage int sys32_olduname(u32 name) -{ - return sys_olduname((struct oldold_utsname *)A(name)); -} - -extern asmlinkage int sys_sethostname(char *name, int len); - -asmlinkage int sys32_sethostname(u32 name, int len) -{ - return sys_sethostname((char *)A(name), len); -} - -extern asmlinkage int sys_gethostname(char *name, int len); - -asmlinkage int sys32_gethostname(u32 name, int len) -{ - return sys_gethostname((char *)A(name), len); -} - -extern asmlinkage int sys_setdomainname(char *name, int len); - -asmlinkage int sys32_setdomainname(u32 name, int len) -{ - return sys_setdomainname((char *)A(name), len); -} +#define RLIM_INFINITY32 0x7fffffff +#define RESOURCE32(x) ((x > RLIM_INFINITY32) ? RLIM_INFINITY32 : x) struct rlimit32 { s32 rlim_cur; @@ -1733,8 +1644,8 @@ asmlinkage int sys32_getrlimit(unsigned int resource, u32 rlim) ret = sys_getrlimit(resource, &r); set_fs (old_fs); if (!ret && ( - put_user (r.rlim_cur, &(((struct rlimit32 *)A(rlim))->rlim_cur)) || - __put_user (r.rlim_max, &(((struct rlimit32 *)A(rlim))->rlim_max)))) + put_user (RESOURCE32(r.rlim_cur), &(((struct rlimit32 *)A(rlim))->rlim_cur)) || + __put_user (RESOURCE32(r.rlim_max), &(((struct rlimit32 *)A(rlim))->rlim_max)))) return -EFAULT; return ret; } @@ -1751,6 +1662,10 @@ asmlinkage int sys32_setrlimit(unsigned int resource, u32 rlim) if (get_user (r.rlim_cur, &(((struct rlimit32 *)A(rlim))->rlim_cur)) || __get_user (r.rlim_max, &(((struct rlimit32 *)A(rlim))->rlim_max))) return -EFAULT; + if (r.rlim_cur == RLIM_INFINITY32) + r.rlim_cur = RLIM_INFINITY; + if (r.rlim_max == RLIM_INFINITY32) + r.rlim_max = RLIM_INFINITY; set_fs (KERNEL_DS); ret = sys_setrlimit(resource, &r); set_fs (old_fs); @@ -1772,28 +1687,6 @@ asmlinkage int sys32_getrusage(int who, u32 ru) return ret; } -extern asmlinkage int sys_time(int * tloc); - -asmlinkage int sys32_time(u32 tloc) -{ - return sys_time((int *)A(tloc)); -} - -extern asmlinkage int sys_gettimeofday(struct timeval *tv, struct timezone *tz); - -asmlinkage int sys32_gettimeofday(u32 tv, u32 tz) -{ - /* both timeval and timezone are ok :)) */ - return sys_gettimeofday((struct timeval *)A(tv), (struct timezone *)A(tz)); -} - -extern asmlinkage int sys_settimeofday(struct timeval *tv, struct timezone *tz); - -asmlinkage int sys32_settimeofday(u32 tv, u32 tz) -{ - return sys_settimeofday((struct timeval *)A(tv), (struct timezone *)A(tz)); -} - struct timex32 { unsigned int modes; s32 offset; @@ -1865,170 +1758,6 @@ asmlinkage int sys32_adjtimex(u32 txc_p) return ret; } -extern asmlinkage int sys_msync(unsigned long start, size_t len, int flags); - -asmlinkage int sys32_msync(u32 start, __kernel_size_t32 len, int flags) -{ - return sys_msync((unsigned long)start, (size_t)len, flags); -} - -extern asmlinkage int sys_mlock(unsigned long start, size_t len); - -asmlinkage int sys32_mlock(u32 start, __kernel_size_t32 len) -{ - return sys_mlock((unsigned long)start, (size_t)len); -} - -extern asmlinkage int sys_munlock(unsigned long start, size_t len); - -asmlinkage int sys32_munlock(u32 start, __kernel_size_t32 len) -{ - return sys_munlock((unsigned long)start, (size_t)len); -} - -extern asmlinkage unsigned long sys_brk(unsigned long brk); - -asmlinkage unsigned long sparc32_brk(u32 brk) -{ - return sys_brk((unsigned long)brk); -} - -extern asmlinkage int sys_munmap(unsigned long addr, size_t len); - -asmlinkage int sys32_munmap(u32 addr, __kernel_size_t32 len) -{ - return sys_munmap((unsigned long)addr, (size_t)len); -} - -extern asmlinkage int sys_mprotect(unsigned long start, size_t len, unsigned long prot); - -asmlinkage int sys32_mprotect(u32 start, __kernel_size_t32 len, u32 prot) -{ - return sys_mprotect((unsigned long)start, (size_t)len, (unsigned long)prot); -} - -extern asmlinkage unsigned long sys_mremap(unsigned long addr, unsigned long old_len, - unsigned long new_len, unsigned long flags); - -asmlinkage unsigned long sys32_mremap(u32 addr, u32 old_len, u32 new_len, u32 flags) -{ - return sys_mremap((unsigned long)addr, (unsigned long)old_len, - (unsigned long)new_len, (unsigned long)flags); -} - -extern asmlinkage int sys_swapoff(const char * specialfile); - -asmlinkage int sys32_swapoff(u32 specialfile) -{ - return sys_swapoff((const char *)A(specialfile)); -} - -extern asmlinkage int sys_swapon(const char * specialfile, int swap_flags); - -asmlinkage int sys32_swapon(u32 specialfile, int swap_flags) -{ - return sys_swapon((const char *)A(specialfile), swap_flags); -} - -extern asmlinkage int sys_bind(int fd, struct sockaddr *umyaddr, int addrlen); - -asmlinkage inline int sys32_bind(int fd, u32 umyaddr, int addrlen) -{ - /* sockaddr is the same :)) */ - return sys_bind(fd, (struct sockaddr *)A(umyaddr), addrlen); -} - -extern asmlinkage int sys_accept(int fd, struct sockaddr *upeer_sockaddr, - int *upeer_addrlen); - -asmlinkage inline int sys32_accept(int fd, u32 upeer_sockaddr, u32 upeer_addrlen) -{ - return sys_accept(fd, (struct sockaddr *)A(upeer_sockaddr), - (int *)A(upeer_addrlen)); -} - -extern asmlinkage int sys_connect(int fd, struct sockaddr *uservaddr, int addrlen); - -asmlinkage inline int sys32_connect(int fd, u32 uservaddr, int addrlen) -{ - return sys_connect(fd, (struct sockaddr *)A(uservaddr), addrlen); -} - -extern asmlinkage int sys_getsockname(int fd, struct sockaddr *usockaddr, - int *usockaddr_len); - -asmlinkage int sys32_getsockname(int fd, u32 usockaddr, u32 usockaddr_len) -{ - return sys_getsockname(fd, (struct sockaddr *)A(usockaddr), - (int *)A(usockaddr_len)); -} - -extern asmlinkage int sys_getpeername(int fd, struct sockaddr *usockaddr, - int *usockaddr_len); - -asmlinkage int sys32_getpeername(int fd, u32 usockaddr, u32 usockaddr_len) -{ - return sys_getpeername(fd, (struct sockaddr *)A(usockaddr), - (int *)A(usockaddr_len)); -} - -extern asmlinkage int sys_send(int fd, void * buff, size_t len, unsigned flags); - -asmlinkage inline int sys32_send(int fd, u32 buff, - __kernel_size_t32 len, unsigned flags) -{ - return sys_send(fd, (void *)A(buff), (size_t)len, flags); -} - -extern asmlinkage int sys_sendto(int fd, void * buff, size_t len, unsigned flags, - struct sockaddr *addr, int addr_len); - -asmlinkage inline int sys32_sendto(int fd, u32 buff, __kernel_size_t32 len, - unsigned flags, u32 addr, int addr_len) -{ - return sys_sendto(fd, (void *)A(buff), (size_t)len, flags, - (struct sockaddr *)A(addr), addr_len); -} - -extern asmlinkage int sys_recv(int fd, void * ubuf, size_t size, unsigned flags); - -asmlinkage inline int sys32_recv(int fd, u32 ubuf, - __kernel_size_t32 size, unsigned flags) -{ - return sys_recv(fd, (void *)A(ubuf), (size_t)size, flags); -} - -extern asmlinkage int sys_recvfrom(int fd, void * ubuf, size_t size, unsigned flags, - struct sockaddr *addr, int *addr_len); - -asmlinkage inline int sys32_recvfrom(int fd, u32 ubuf, __kernel_size_t32 size, - unsigned flags, u32 addr, u32 addr_len) -{ - return sys_recvfrom(fd, (void *)A(ubuf), (size_t)size, flags, - (struct sockaddr *)A(addr), (int *)A(addr_len)); -} - -extern asmlinkage int sys_setsockopt(int fd, int level, int optname, - char *optval, int optlen); - -asmlinkage inline int sys32_setsockopt(int fd, int level, int optname, - u32 optval, int optlen) -{ - /* XXX handle ip_fw32->ip_fw conversion for IP firewalling and accounting. - Do it using some macro in ip_sockglue.c - Other optval arguments are mostly just ints or 32<->64bit transparent */ - return sys_setsockopt(fd, level, optname, (char *)A(optval), optlen); -} - -extern asmlinkage int sys_getsockopt(int fd, int level, int optname, - char *optval, int *optlen); - -asmlinkage inline int sys32_getsockopt(int fd, int level, int optname, - u32 optval, u32 optlen) -{ - return sys_getsockopt(fd, level, optname, (char *)A(optval), (int *)A(optlen)); -} - /* XXX This really belongs in some header file... -DaveM */ #define MAX_SOCK_ADDR 128 /* 108 for Unix domain - 16 for IP, 16 for IPX, @@ -2052,11 +1781,11 @@ extern __inline__ struct socket *sockfd_lookup(int fd, int *err) return NULL; } - inode = file->f_inode; + inode = file->f_dentry->d_inode; if (!inode || !inode->i_sock || !socki_lookup(inode)) { *err = -ENOTSOCK; - fput(file,inode); + fput(file); return NULL; } @@ -2065,7 +1794,7 @@ extern __inline__ struct socket *sockfd_lookup(int fd, int *err) extern __inline__ void sockfd_put(struct socket *sock) { - fput(sock->file,sock->inode); + fput(sock->file); } struct msghdr32 { @@ -2293,6 +2022,24 @@ static unsigned char nargs[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)}; #undef AL +extern asmlinkage int sys32_bind(int fd, u32 umyaddr, int addrlen); +extern asmlinkage int sys32_connect(int fd, u32 uservaddr, int addrlen); +extern asmlinkage int sys32_accept(int fd, u32 upeer_sockaddr, u32 upeer_addrlen); +extern asmlinkage int sys32_getsockname(int fd, u32 usockaddr, u32 usockaddr_len); +extern asmlinkage int sys32_getpeername(int fd, u32 usockaddr, u32 usockaddr_len); +extern asmlinkage int sys32_send(int fd, u32 buff, __kernel_size_t32 len, + unsigned flags); +extern asmlinkage int sys32_sendto(int fd, u32 buff, __kernel_size_t32 len, + unsigned flags, u32 addr, int addr_len); +extern asmlinkage int sys32_recv(int fd, u32 ubuf, __kernel_size_t32 size, + unsigned flags); +extern asmlinkage int sys32_recvfrom(int fd, u32 ubuf, __kernel_size_t32 size, + unsigned flags, u32 addr, u32 addr_len); +extern asmlinkage int sys32_setsockopt(int fd, int level, int optname, + u32 optval, int optlen); +extern asmlinkage int sys32_getsockopt(int fd, int level, int optname, + u32 optval, u32 optlen); + extern asmlinkage int sys_socket(int family, int type, int protocol); extern asmlinkage int sys_socketpair(int family, int type, int protocol, int usockvec[2]); @@ -2389,7 +2136,7 @@ asmlinkage int sparc32_sigaction (int signum, u32 action, u32 oldaction) old_sa.sa_mask = (sigset_t32)(p->sa_mask); old_sa.sa_flags = (unsigned)(p->sa_flags); old_sa.sa_restorer = (unsigned)(u64)(p->sa_restorer); - if (copy_to_user(A(oldaction), p, sizeof(struct sigaction32))) + if (copy_to_user(A(oldaction), &old_sa, sizeof(struct sigaction32))) goto out; } @@ -2407,14 +2154,6 @@ out: return err; } -extern asmlinkage int sys_nfsservctl(int cmd, void *argp, void *resp); - -asmlinkage int sys32_nfsservctl(int cmd, u32 argp, u32 resp) -{ - /* XXX handle argp and resp args */ - return sys_nfsservctl(cmd, (void *)A(argp), (void *)A(resp)); -} - /* * count32() counts the number of arguments/envelopes */ @@ -2485,25 +2224,33 @@ static inline int do_execve32(char * filename, u32 * argv, u32 * envp, struct pt_regs * regs) { struct linux_binprm bprm; + struct dentry * dentry; int retval; int i; bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */ bprm.page[i] = 0; - retval = open_namei(filename, 0, 0, &bprm.inode, NULL); - if (retval) + + dentry = open_namei(filename, 0, 0); + retval = PTR_ERR(dentry); + if (IS_ERR(dentry)) return retval; + + bprm.dentry = dentry; bprm.filename = filename; bprm.sh_bang = 0; bprm.java = 0; bprm.loader = 0; bprm.exec = 0; - bprm.dont_iput = 0; - if ((bprm.argc = count32(argv)) < 0) + if ((bprm.argc = count32(argv)) < 0) { + dput(dentry); return bprm.argc; - if ((bprm.envc = count32(envp)) < 0) + } + if ((bprm.envc = count32(envp)) < 0) { + dput(dentry); return bprm.envc; + } retval = prepare_binprm(&bprm); @@ -2523,8 +2270,9 @@ do_execve32(char * filename, u32 * argv, u32 * envp, struct pt_regs * regs) return retval; /* Something went wrong, return the inode and free the argument pages*/ - if(!bprm.dont_iput) - iput(bprm.inode); + if(bprm.dentry) + dput(bprm.dentry); + for (i=0 ; i<MAX_ARG_PAGES ; i++) free_page(bprm.page[i]); return(retval); @@ -2543,81 +2291,231 @@ asmlinkage int sparc32_execve(struct pt_regs *regs) if((u32)regs->u_regs[UREG_G1] == 0) base = 1; - error = getname((char *)(unsigned long)(u32)regs->u_regs[base + UREG_I0], &filename); - if(error) - return error; + lock_kernel(); + filename = getname((char *)(unsigned long)(u32)regs->u_regs[base + UREG_I0]); + error = PTR_ERR(filename); + if(IS_ERR(filename)) + goto out; error = do_execve32(filename, (u32 *)A((u32)regs->u_regs[base + UREG_I1]), (u32 *)A((u32)regs->u_regs[base + UREG_I2]), regs); putname(filename); + + if(!error) { + fprs_write(0); + regs->fprs = 0; + } +out: + unlock_kernel(); return error; } -/* Modules will be supported with 64bit modutils only */ -asmlinkage int sys32_no_modules(void) +#ifdef CONFIG_MODULES + +extern asmlinkage unsigned long sys_create_module(const char *name_user, size_t size); + +asmlinkage unsigned long sys32_create_module(u32 name_user, __kernel_size_t32 size) { - return -ENOSYS; + return sys_create_module((const char *)A(name_user), (size_t)size); } -struct ncp_mount_data32 { - int version; - unsigned int ncp_fd; - __kernel_uid_t32 mounted_uid; - __kernel_pid_t32 wdog_pid; - unsigned char mounted_vol[NCP_VOLNAME_LEN + 1]; - unsigned int time_out; - unsigned int retry_count; - unsigned int flags; - __kernel_uid_t32 uid; - __kernel_gid_t32 gid; - __kernel_mode_t32 file_mode; - __kernel_mode_t32 dir_mode; -}; +extern asmlinkage int sys_init_module(const char *name_user, struct module *mod_user); -void *do_ncp_super_data_conv(void *raw_data) +/* Hey, when you're trying to init module, take time and prepare us a nice 64bit + * module structure, even if from 32bit modutils... Why to pollute kernel... :)) + */ +asmlinkage int sys32_init_module(u32 nameuser, u32 mod_user) { - struct ncp_mount_data *n = (struct ncp_mount_data *)raw_data; - struct ncp_mount_data32 *n32 = (struct ncp_mount_data32 *)raw_data; + return sys_init_module((const char *)A(nameuser), (struct module *)A(mod_user)); +} - n->dir_mode = n32->dir_mode; - n->file_mode = n32->file_mode; - n->gid = n32->gid; - n->uid = n32->uid; - memmove (n->mounted_vol, n32->mounted_vol, (sizeof (n32->mounted_vol) + 3 * sizeof (unsigned int))); - n->wdog_pid = n32->wdog_pid; - n->mounted_uid = n32->mounted_uid; - return raw_data; +extern asmlinkage int sys_delete_module(const char *name_user); + +asmlinkage int sys32_delete_module(u32 name_user) +{ + return sys_delete_module((const char *)A(name_user)); } -struct smb_mount_data32 { - int version; - unsigned int fd; - __kernel_uid_t32 mounted_uid; - struct sockaddr_in addr; - char server_name[17]; - char client_name[17]; - char service[64]; - char root_path[64]; - char username[64]; - char password[64]; - char domain[64]; - unsigned short max_xmit; - __kernel_uid_t32 uid; - __kernel_gid_t32 gid; - __kernel_mode_t32 file_mode; - __kernel_mode_t32 dir_mode; +struct module_info32 { + u32 addr; + u32 size; + u32 flags; + s32 usecount; }; -void *do_smb_super_data_conv(void *raw_data) +extern asmlinkage int sys_query_module(const char *name_user, int which, char *buf, size_t bufsize, size_t *ret); + +asmlinkage int sys32_query_module(u32 name_user, int which, u32 buf, __kernel_size_t32 bufsize, u32 retv) { - struct smb_mount_data *s = (struct smb_mount_data *)raw_data; - struct smb_mount_data32 *s32 = (struct smb_mount_data32 *)raw_data; + char *buff; + unsigned long old_fs = get_fs(); + size_t val; + int ret, i, j; + unsigned long *p; + char *usernam = NULL; + int bufsiz = bufsize; + struct module_info mi; + + switch (which) { + case 0: return sys_query_module ((const char *)A(name_user), which, (char *)A(buf), (size_t)bufsize, (size_t *)A(retv)); + case QM_SYMBOLS: + bufsiz <<= 1; + case QM_MODULES: + case QM_REFS: + case QM_DEPS: + if (name_user && (ret = getname32 (name_user, &usernam))) + return ret; + buff = kmalloc (bufsiz, GFP_KERNEL); + if (!buff) { + if (name_user) putname32 (usernam); + return -ENOMEM; + } +qmsym_toshort: + set_fs (KERNEL_DS); + ret = sys_query_module (usernam, which, buff, bufsiz, &val); + set_fs (old_fs); + if (which != QM_SYMBOLS) { + if (ret == -ENOSPC || !ret) { + if (put_user (val, (__kernel_size_t32 *)A(retv))) + ret = -EFAULT; + } + if (!ret) { + if (copy_to_user ((char *)A(buf), buff, bufsize)) + ret = -EFAULT; + } + } else { + if (ret == -ENOSPC) { + if (put_user (2 * val, (__kernel_size_t32 *)A(retv))) + ret = -EFAULT; + } + p = (unsigned long *)buff; + if (!ret) { + if (put_user (val, (__kernel_size_t32 *)A(retv))) + ret = -EFAULT; + } + if (!ret) { + j = val * 8; + for (i = 0; i < val; i++, p += 2) { + if (bufsize < (2 * sizeof (u32))) { + bufsiz = 0; + goto qmsym_toshort; + } + if (put_user (p[0], (u32 *)A(buf)) || + __put_user (p[1] - j, (((u32 *)A(buf))+1))) { + ret = -EFAULT; + break; + } + bufsize -= (2 * sizeof (u32)); + buf += (2 * sizeof (u32)); + } + } + if (!ret && val) { + char *strings = buff + ((unsigned long *)buff)[1]; + j = *(p - 1) - ((unsigned long *)buff)[1]; + j = j + strlen (buff + j) + 1; + if (bufsize < j) { + bufsiz = 0; + goto qmsym_toshort; + } + if (copy_to_user ((char *)A(buf), strings, j)) + ret = -EFAULT; + } + } + kfree (buff); + if (name_user) putname32 (usernam); + return ret; + case QM_INFO: + if (name_user && (ret = getname32 (name_user, &usernam))) + return ret; + set_fs (KERNEL_DS); + ret = sys_query_module (usernam, which, (char *)&mi, sizeof (mi), &val); + set_fs (old_fs); + if (!ret) { + if (put_user (sizeof (struct module_info32), (__kernel_size_t32 *)A(retv))) + ret = -EFAULT; + else if (bufsize < sizeof (struct module_info32)) + ret = -ENOSPC; + } + if (!ret) { + if (put_user (mi.addr, &(((struct module_info32 *)A(buf))->addr)) || + __put_user (mi.size, &(((struct module_info32 *)A(buf))->size)) || + __put_user (mi.flags, &(((struct module_info32 *)A(buf))->flags)) || + __put_user (mi.usecount, &(((struct module_info32 *)A(buf))->usecount))) + ret = -EFAULT; + } + if (name_user) putname32 (usernam); + return ret; + default: + return -EINVAL; + } +} - s->dir_mode = s32->dir_mode; - s->file_mode = s32->file_mode; - s->gid = s32->gid; - s->uid = s32->uid; - memmove (&s->addr, &s32->addr, (((long)&s->uid) - ((long)&s->addr))); - s->mounted_uid = s32->mounted_uid; - return raw_data; +struct kernel_sym32 { + u32 value; + char name[60]; +}; + +extern asmlinkage int sys_get_kernel_syms(struct kernel_sym *table); + +asmlinkage int sys32_get_kernel_syms(u32 table) +{ + int len, i; + struct kernel_sym *tbl; + unsigned long old_fs; + + len = sys_get_kernel_syms(NULL); + if (!table) return len; + tbl = kmalloc (len * sizeof (struct kernel_sym), GFP_KERNEL); + if (!tbl) return -ENOMEM; + old_fs = get_fs(); + set_fs (KERNEL_DS); + sys_get_kernel_syms(tbl); + set_fs (old_fs); + for (i = 0; i < len; i++, table += sizeof (struct kernel_sym32)) { + if (put_user (tbl[i].value, &(((struct kernel_sym32 *)A(table))->value)) || + copy_to_user (((struct kernel_sym32 *)A(table))->name, tbl[i].name, 60)) + break; + } + kfree (tbl); + return i; +} + +#else /* CONFIG_MODULES */ + +asmlinkage unsigned long +sys_create_module(const char *name_user, size_t size) +{ + return -ENOSYS; +} + +asmlinkage int +sys_init_module(const char *name_user, struct module *mod_user) +{ + return -ENOSYS; } + +asmlinkage int +sys_delete_module(const char *name_user) +{ + return -ENOSYS; +} + +asmlinkage int +sys_query_module(const char *name_user, int which, char *buf, size_t bufsize, + size_t *ret) +{ + /* Let the program know about the new interface. Not that + it'll do them much good. */ + if (which == 0) + return 0; + + return -ENOSYS; +} + +asmlinkage int +sys_get_kernel_syms(struct kernel_sym *table) +{ + return -ENOSYS; +} + +#endif /* CONFIG_MODULES */ diff --git a/arch/sparc64/kernel/sys_sunos32.c b/arch/sparc64/kernel/sys_sunos32.c new file mode 100644 index 000000000..3dfdad5a7 --- /dev/null +++ b/arch/sparc64/kernel/sys_sunos32.c @@ -0,0 +1,1511 @@ +/* $Id: sys_sunos32.c,v 1.1 1997/07/18 06:26:43 ralf Exp $ + * sys_sunos32.c: SunOS binary compatability layer on sparc64. + * + * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx) + * + * Based upon preliminary work which is: + * + * Copyright (C) 1995 Adrian M. Rodriguez (adrian@remus.rutgers.edu) + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/mman.h> +#include <linux/mm.h> +#include <linux/swap.h> +#include <linux/fs.h> +#include <linux/resource.h> +#include <linux/ipc.h> +#include <linux/shm.h> +#include <linux/msg.h> +#include <linux/sem.h> +#include <linux/signal.h> +#include <linux/uio.h> +#include <linux/utsname.h> +#include <linux/fs.h> +#include <linux/major.h> +#include <linux/stat.h> +#include <linux/malloc.h> +#include <linux/pagemap.h> +#include <linux/errno.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> + +#include <asm/uaccess.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/pconf.h> +#include <asm/idprom.h> /* for gethostid() */ +#include <asm/unistd.h> +#include <asm/system.h> + +/* For the nfs mount emulation */ +#include <linux/socket.h> +#include <linux/in.h> +#include <linux/nfs.h> +#include <linux/nfs_mount.h> + +/* for sunos_select */ +#include <linux/time.h> +#include <linux/personality.h> + +#define A(x) ((unsigned long)x) + +#define SUNOS_NR_OPEN 256 + +extern unsigned long get_unmapped_area(unsigned long addr, unsigned long len); + +asmlinkage u32 sunos_mmap(u32 addr, u32 len, u32 prot, u32 flags, u32 fd, u32 off) +{ + struct file *file = NULL; + unsigned long retval, ret_type; + + lock_kernel(); + current->personality |= PER_BSD; + if(flags & MAP_NORESERVE) { + printk("%s: unimplemented SunOS MAP_NORESERVE mmap() flag\n", + current->comm); + flags &= ~MAP_NORESERVE; + } + retval = -EBADF; + if(!(flags & MAP_ANONYMOUS)) + if(fd >= SUNOS_NR_OPEN || !(file = current->files->fd[fd])) + goto out; + retval = -ENOMEM; + if(!(flags & MAP_FIXED) && !addr) { + unsigned long attempt = get_unmapped_area(addr, len); + if(!attempt || (attempt >= 0xf0000000UL)) + goto out; + addr = (u32) attempt; + } + if(file->f_dentry && file->f_dentry->d_inode) { + if(MAJOR(file->f_dentry->d_inode->i_rdev) == MEM_MAJOR && + MINOR(file->f_dentry->d_inode->i_rdev) == 5) { + flags |= MAP_ANONYMOUS; + file = 0; + } + } + if(!(flags & MAP_FIXED)) + addr = 0; + ret_type = flags & _MAP_NEW; + flags &= ~_MAP_NEW; + + retval = do_mmap(file, + (unsigned long) addr, (unsigned long) len, + (unsigned long) prot, (unsigned long) flags, + (unsigned long) off); + if(!ret_type) + retval = ((retval < 0xf0000000) ? 0 : retval); +out: + unlock_kernel(); + return (u32) retval; +} + +asmlinkage int sunos_mctl(u32 addr, u32 len, int function, u32 arg) +{ + return 0; +} + +asmlinkage int sunos_brk(u32 baddr) +{ + int freepages, retval = -ENOMEM; + unsigned long rlim; + unsigned long newbrk, oldbrk, brk = (unsigned long) baddr; + + lock_kernel(); + if (brk < current->mm->end_code) + goto out; + newbrk = PAGE_ALIGN(brk); + oldbrk = PAGE_ALIGN(current->mm->brk); + retval = 0; + if (oldbrk == newbrk) { + current->mm->brk = brk; + goto out; + } + /* Always allow shrinking brk. */ + if (brk <= current->mm->brk) { + current->mm->brk = brk; + do_munmap(newbrk, oldbrk-newbrk); + goto out; + } + /* Check against rlimit and stack.. */ + retval = -ENOMEM; + rlim = current->rlim[RLIMIT_DATA].rlim_cur; + if (rlim >= RLIM_INFINITY) + rlim = ~0; + if (brk - current->mm->end_code > rlim) + goto out; + /* Check against existing mmap mappings. */ + if (find_vma_intersection(current->mm, oldbrk, newbrk+PAGE_SIZE)) + goto out; + /* stupid algorithm to decide if we have enough memory: while + * simple, it hopefully works in most obvious cases.. Easy to + * fool it, but this should catch most mistakes. + */ + freepages = buffermem >> PAGE_SHIFT; + freepages += page_cache_size; + freepages >>= 1; + freepages += nr_free_pages; + freepages += nr_swap_pages; + freepages -= num_physpages >> 4; + freepages -= (newbrk-oldbrk) >> PAGE_SHIFT; + if (freepages < 0) + goto out; + /* Ok, we have probably got enough memory - let it rip. */ + current->mm->brk = brk; + do_mmap(NULL, oldbrk, newbrk-oldbrk, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_FIXED|MAP_PRIVATE, 0); + retval = 0; +out: + unlock_kernel(); + return retval; +} + +asmlinkage u32 sunos_sbrk(int increment) +{ + int error, oldbrk; + + /* This should do it hopefully... */ + lock_kernel(); + oldbrk = (int)current->mm->brk; + error = sunos_brk(((int) current->mm->brk) + increment); + if(!error) + error = oldbrk; + unlock_kernel(); + return error; +} + +asmlinkage u32 sunos_sstk(int increment) +{ + lock_kernel(); + printk("%s: Call to sunos_sstk(increment<%d>) is unsupported\n", + current->comm, increment); + unlock_kernel(); + return (u32)-1; +} + +/* Give hints to the kernel as to what paging strategy to use... + * Completely bogus, don't remind me. + */ +#define VA_NORMAL 0 /* Normal vm usage expected */ +#define VA_ABNORMAL 1 /* Abnormal/random vm usage probable */ +#define VA_SEQUENTIAL 2 /* Accesses will be of a sequential nature */ +#define VA_INVALIDATE 3 /* Page table entries should be flushed ??? */ +static char *vstrings[] = { + "VA_NORMAL", + "VA_ABNORMAL", + "VA_SEQUENTIAL", + "VA_INVALIDATE", +}; + +asmlinkage void sunos_vadvise(u32 strategy) +{ + /* I wanna see who uses this... */ + lock_kernel(); + printk("%s: Advises us to use %s paging strategy\n", + current->comm, + strategy <= 3 ? vstrings[strategy] : "BOGUS"); + unlock_kernel(); +} + +/* Same as vadvise, and just as bogus, but for a range of virtual + * process address space. + */ +#define MADV_NORMAL 0 /* Nothing special... */ +#define MADV_RANDOM 1 /* I am emacs... */ +#define MADV_SEQUENTIAL 2 /* I am researcher code... */ +#define MADV_WILLNEED 3 /* Pages in this range will be needed */ +#define MADV_DONTNEED 4 /* Pages in this range won't be needed */ + +static char *mstrings[] = { + "MADV_NORMAL", + "MADV_RANDOM", + "MADV_SEQUENTIAL", + "MADV_WILLNEED", + "MADV_DONTNEED", +}; + +asmlinkage void sunos_madvise(u32 address, u32 len, u32 strategy) +{ + /* I wanna see who uses this... */ + lock_kernel(); + printk("%s: Advises us to use %s paging strategy for addr<%08x> len<%08x>\n", + current->comm, strategy <= 4 ? mstrings[strategy] : "BOGUS", + address, len); + unlock_kernel(); +} + +/* Places into character array, the status of all the pages in the passed + * range from 'addr' to 'addr + len'. -1 on failure, 0 on success... + * The encoding in each character is: + * low-bit is zero == Page is not in physical ram right now + * low-bit is one == Page is currently residing in core + * All other bits are undefined within the character so there... + * Also, if you try to get stats on an area outside of the user vm area + * *or* the passed base address is not aligned on a page boundary you + * get an error. + */ +asmlinkage int sunos_mincore(u32 addr, u32 len, u32 u_array) +{ + pgd_t *pgdp; + pmd_t *pmdp; + pte_t *ptep; + unsigned long limit; + int num_pages, pnum, retval = -EINVAL; + char *array = (char *)A(u_array); + + lock_kernel(); + if(addr & ~(4096)) + goto out; + num_pages = (len / 4096); + retval = -EFAULT; + if(verify_area(VERIFY_WRITE, array, num_pages)) + goto out; + retval = -ENOMEM; + if((addr >= 0xf0000000) || ((addr + len) > 0xf0000000)) + goto out; /* I'm sure you're curious about kernel mappings.. */ + /* Wheee, go through pte's */ + pnum = 0; + for(limit = addr + len; addr < limit; addr += 4096, pnum++) { + pgdp = pgd_offset(current->mm, addr); + if(pgd_none(*pgdp)) + goto out; /* As per SunOS manpage */ + pmdp = pmd_offset(pgdp, addr); + if(pmd_none(*pmdp)) + goto out; /* As per SunOS manpage */ + ptep = pte_offset(pmdp, addr); + if(pte_none(*ptep)) + goto out; /* As per SunOS manpage */ + /* Page in core or Swapped page? */ + __put_user((pte_present(*ptep) ? 1 : 0), &array[pnum]); + } + retval = 0; /* Success... I think... */ +out: + unlock_kernel(); + return retval; +} + +/* This just wants the soft limit (ie. rlim_cur element) of the RLIMIT_NOFILE + * resource limit and is for backwards compatibility with older sunos + * revs. + */ +asmlinkage int sunos_getdtablesize(void) +{ + return SUNOS_NR_OPEN; +} + +#define _S(nr) (1<<((nr)-1)) + +#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) + +asmlinkage u32 sunos_sigblock(u32 blk_mask) +{ + unsigned long flags; + u32 old; + + lock_kernel(); + save_and_cli(flags); + old = (u32) current->blocked; + current->blocked |= (blk_mask & _BLOCKABLE); + restore_flags(flags); + unlock_kernel(); + return old; +} + +asmlinkage u32 sunos_sigsetmask(u32 newmask) +{ + unsigned long flags; + u32 retval; + + lock_kernel(); + save_and_cli(flags); + retval = (u32) current->blocked; + current->blocked = (newmask & _BLOCKABLE); + restore_flags(flags); + unlock_kernel(); + return retval; +} + +/* SunOS getdents is very similar to the newer Linux (iBCS2 compliant) */ +/* getdents system call, the format of the structure just has a different */ +/* layout (d_off+d_ino instead of d_ino+d_off) */ +struct sunos_dirent { + s32 d_off; + u32 d_ino; + u16 d_reclen; + u16 d_namlen; + char d_name[1]; +}; + +struct sunos_dirent_callback { + struct sunos_dirent *curr; + struct sunos_dirent *previous; + int count; + int error; +}; + +#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) +#define ROUND_UP(x) (((x)+sizeof(s32)-1) & ~(sizeof(s32)-1)) + +static int sunos_filldir(void * __buf, const char * name, int namlen, + off_t offset, ino_t ino) +{ + struct sunos_dirent * dirent; + struct sunos_dirent_callback * buf = (struct sunos_dirent_callback *) __buf; + int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1); + + buf->error = -EINVAL; /* only used if we fail.. */ + if (reclen > buf->count) + return -EINVAL; + dirent = buf->previous; + if (dirent) + put_user(offset, &dirent->d_off); + dirent = buf->curr; + buf->previous = dirent; + put_user(ino, &dirent->d_ino); + put_user(namlen, &dirent->d_namlen); + put_user(reclen, &dirent->d_reclen); + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); + ((char *) dirent) += reclen; + buf->curr = dirent; + buf->count -= reclen; + return 0; +} + +asmlinkage int sunos_getdents(unsigned int fd, u32 u_dirent, int cnt) +{ + struct file * file; + struct dentry * dentry; + struct inode * inode; + struct sunos_dirent * lastdirent; + struct sunos_dirent_callback buf; + int error = -EBADF; + void *dirent = (void *)A(u_dirent); + + lock_kernel(); + if(fd >= SUNOS_NR_OPEN) + goto out; + + file = current->files->fd[fd]; + if(!file) + goto out; + + dentry = file->f_dentry; + if(!dentry) + goto out; + + inode = dentry->d_inode; + if(!inode) + goto out; + + error = -ENOTDIR; + if (!file->f_op || !file->f_op->readdir) + goto out; + + error = -EINVAL; + if(cnt < (sizeof(struct sunos_dirent) + 255)) + goto out; + + buf.curr = (struct sunos_dirent *) dirent; + buf.previous = NULL; + buf.count = cnt; + buf.error = 0; + + error = file->f_op->readdir(inode, file, &buf, sunos_filldir); + if (error < 0) + goto out; + lastdirent = buf.previous; + error = buf.error; + if (lastdirent) { + put_user(file->f_pos, &lastdirent->d_off); + error = cnt - buf.count; + } +out: + unlock_kernel(); + return error; +} + +/* Old sunos getdirentries, severely broken compatibility stuff here. */ +struct sunos_direntry { + u32 d_ino; + u16 d_reclen; + u16 d_namlen; + char d_name[1]; +}; + +struct sunos_direntry_callback { + struct sunos_direntry *curr; + struct sunos_direntry *previous; + int count; + int error; +}; + +static int sunos_filldirentry(void * __buf, const char * name, int namlen, + off_t offset, ino_t ino) +{ + struct sunos_direntry * dirent; + struct sunos_direntry_callback * buf = (struct sunos_direntry_callback *) __buf; + int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1); + + buf->error = -EINVAL; /* only used if we fail.. */ + if (reclen > buf->count) + return -EINVAL; + dirent = buf->previous; + dirent = buf->curr; + buf->previous = dirent; + put_user(ino, &dirent->d_ino); + put_user(namlen, &dirent->d_namlen); + put_user(reclen, &dirent->d_reclen); + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); + ((char *) dirent) += reclen; + buf->curr = dirent; + buf->count -= reclen; + return 0; +} + +asmlinkage int sunos_getdirentries(unsigned int fd, u32 u_dirent, + int cnt, u32 u_basep) +{ + struct file * file; + struct dentry * dentry; + struct inode * inode; + struct sunos_direntry * lastdirent; + struct sunos_direntry_callback buf; + int error = -EBADF; + void *dirent = (void *) A(u_dirent); + unsigned int *basep = (unsigned int *)A(u_basep); + + lock_kernel(); + if(fd >= SUNOS_NR_OPEN) + goto out; + + file = current->files->fd[fd]; + if(!file) + goto out; + + dentry = file->f_dentry; + if(!dentry) + goto out; + + inode = dentry->d_inode; + if(!inode) + goto out; + + error = -ENOTDIR; + if (!file->f_op || !file->f_op->readdir) + goto out; + + error = -EINVAL; + if(cnt < (sizeof(struct sunos_direntry) + 255)) + goto out; + + buf.curr = (struct sunos_direntry *) dirent; + buf.previous = NULL; + buf.count = cnt; + buf.error = 0; + + error = file->f_op->readdir(inode, file, &buf, sunos_filldirentry); + if (error < 0) + goto out; + lastdirent = buf.previous; + error = buf.error; + if (lastdirent) { + put_user(file->f_pos, basep); + error = cnt - buf.count; + } +out: + unlock_kernel(); + return error; +} + +asmlinkage int sunos_getdomainname(u32 u_name, int len) +{ + int nlen = strlen(system_utsname.domainname); + int ret = -EFAULT; + char *name = (char *)A(u_name); + + lock_kernel(); + if (nlen < len) + len = nlen; + + if(len > __NEW_UTS_LEN) + goto out; + if(copy_to_user(name, system_utsname.domainname, len)) + goto out; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +struct sunos_utsname { + char sname[9]; + char nname[9]; + char nnext[56]; + char rel[9]; + char ver[9]; + char mach[9]; +}; + +asmlinkage int sunos_uname(u32 u_name) +{ + struct sunos_utsname *name = (struct sunos_utsname *)A(u_name); + int ret = -EFAULT; + + lock_kernel(); + if(!name) + goto out; + if(copy_to_user(&name->sname[0], + &system_utsname.sysname[0], + sizeof(name->sname) - 1)) + goto out; + copy_to_user(&name->nname[0], + &system_utsname.nodename[0], + sizeof(name->nname) - 1); + put_user('\0', &name->nname[8]); + copy_to_user(&name->rel[0], &system_utsname.release[0], sizeof(name->rel) - 1); + copy_to_user(&name->ver[0], &system_utsname.version[0], sizeof(name->ver) - 1); + copy_to_user(&name->mach[0], &system_utsname.machine[0], sizeof(name->mach) - 1); + ret = 0; +out: + unlock_kernel(); + return ret; +} + +asmlinkage int sunos_nosys(void) +{ + struct pt_regs *regs; + + lock_kernel(); + regs = current->tss.kregs; + current->tss.sig_address = regs->tpc; + current->tss.sig_desc = regs->u_regs[UREG_G1]; + send_sig(SIGSYS, current, 1); + printk("Process makes ni_syscall number %d, register dump:\n", + (int) regs->u_regs[UREG_G1]); + show_regs(regs); + unlock_kernel(); + return -ENOSYS; +} + +/* This is not a real and complete implementation yet, just to keep + * the easy SunOS binaries happy. + */ +asmlinkage int sunos_fpathconf(int fd, int name) +{ + int ret; + + lock_kernel(); + switch(name) { + case _PCONF_LINK: + ret = LINK_MAX; + break; + case _PCONF_CANON: + ret = MAX_CANON; + break; + case _PCONF_INPUT: + ret = MAX_INPUT; + break; + case _PCONF_NAME: + ret = NAME_MAX; + break; + case _PCONF_PATH: + ret = PATH_MAX; + break; + case _PCONF_PIPE: + ret = PIPE_BUF; + break; + case _PCONF_CHRESTRICT: /* XXX Investigate XXX */ + ret = 1; + break; + case _PCONF_NOTRUNC: /* XXX Investigate XXX */ + case _PCONF_VDISABLE: + ret = 0; + break; + default: + ret = -EINVAL; + break; + } + unlock_kernel(); + return ret; +} + +asmlinkage int sunos_pathconf(u32 u_path, int name) +{ + int ret; + + lock_kernel(); + ret = sunos_fpathconf(0, name); /* XXX cheese XXX */ + unlock_kernel(); + return ret; +} + +/* SunOS mount system call emulation */ +extern asmlinkage int +sys32_select(int n, u32 inp, u32 outp, u32 exp, u32 tvp); + +asmlinkage int sunos_select(int width, u32 inp, u32 outp, u32 exp, u32 tvp) +{ + int ret; + + /* SunOS binaries expect that select won't change the tvp contents */ + lock_kernel(); + current->personality |= STICKY_TIMEOUTS; + ret = sys32_select (width, inp, outp, exp, tvp); + unlock_kernel(); + return ret; +} + +asmlinkage void sunos_nop(void) +{ + return; +} + +/* XXXXXXXXXX SunOS mount/umount. XXXXXXXXXXX */ +#define SMNT_RDONLY 1 +#define SMNT_NOSUID 2 +#define SMNT_NEWTYPE 4 +#define SMNT_GRPID 8 +#define SMNT_REMOUNT 16 +#define SMNT_NOSUB 32 +#define SMNT_MULTI 64 +#define SMNT_SYS5 128 + +struct sunos_fh_t { + char fh_data [NFS_FHSIZE]; +}; + +struct sunos_nfs_mount_args { + struct sockaddr_in *addr; /* file server address */ + struct nfs_fh *fh; /* File handle to be mounted */ + int flags; /* flags */ + int wsize; /* write size in bytes */ + int rsize; /* read size in bytes */ + int timeo; /* initial timeout in .1 secs */ + int retrans; /* times to retry send */ + char *hostname; /* server's hostname */ + int acregmin; /* attr cache file min secs */ + int acregmax; /* attr cache file max secs */ + int acdirmin; /* attr cache dir min secs */ + int acdirmax; /* attr cache dir max secs */ + char *netname; /* server's netname */ +}; + +extern int do_mount(kdev_t, const char *, const char *, char *, int, void *); +extern dev_t get_unnamed_dev(void); +extern void put_unnamed_dev(dev_t); +extern asmlinkage int sys_mount(char *, char *, char *, unsigned long, void *); +extern asmlinkage int sys_connect(int fd, struct sockaddr *uservaddr, int addrlen); +extern asmlinkage int sys_socket(int family, int type, int protocol); +extern asmlinkage int sys_bind(int fd, struct sockaddr *umyaddr, int addrlen); + + +/* Bind the socket on a local reserved port and connect it to the + * remote server. This on Linux/i386 is done by the mount program, + * not by the kernel. + */ +/* XXXXXXXXXXXXXXXXXXXX */ +static int +sunos_nfs_get_server_fd (int fd, struct sockaddr_in *addr) +{ + struct sockaddr_in local; + struct sockaddr_in server; + int try_port; + int ret; + struct socket *socket; + struct dentry *dentry; + struct inode *inode; + struct file *file; + + file = current->files->fd [fd]; + if(!file) + return 0; + + dentry = file->f_dentry; + if(!dentry) + return 0; + + inode = dentry->d_inode; + if(!inode) + return 0; + + socket = &inode->u.socket_i; + local.sin_family = AF_INET; + local.sin_addr.s_addr = INADDR_ANY; + + /* IPPORT_RESERVED = 1024, can't find the definition in the kernel */ + try_port = 1024; + do { + local.sin_port = htons (--try_port); + ret = socket->ops->bind(socket, (struct sockaddr*)&local, + sizeof(local)); + } while (ret && try_port > (1024 / 2)); + + if (ret) + return 0; + + server.sin_family = AF_INET; + server.sin_addr = addr->sin_addr; + server.sin_port = NFS_PORT; + + /* Call sys_connect */ + ret = socket->ops->connect (socket, (struct sockaddr *) &server, + sizeof (server), file->f_flags); + if (ret < 0) + return 0; + return 1; +} + +/* XXXXXXXXXXXXXXXXXXXX */ +static int get_default (int value, int def_value) +{ + if (value) + return value; + else + return def_value; +} + +/* XXXXXXXXXXXXXXXXXXXX */ +asmlinkage int sunos_nfs_mount(char *dir_name, int linux_flags, void *data) +{ + int ret = -ENODEV; + int server_fd; + char *the_name; + struct nfs_mount_data linux_nfs_mount; + struct sunos_nfs_mount_args *sunos_mount = data; + dev_t dev; + + /* Ok, here comes the fun part: Linux's nfs mount needs a + * socket connection to the server, but SunOS mount does not + * require this, so we use the information on the destination + * address to create a socket and bind it to a reserved + * port on this system + */ + server_fd = sys_socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (server_fd < 0) + return -ENXIO; + + if (!sunos_nfs_get_server_fd (server_fd, sunos_mount->addr)){ + sys_close (server_fd); + return -ENXIO; + } + + /* Now, bind it to a locally reserved port */ + linux_nfs_mount.version = NFS_MOUNT_VERSION; + linux_nfs_mount.flags = sunos_mount->flags; + linux_nfs_mount.addr = *sunos_mount->addr; + linux_nfs_mount.root = *sunos_mount->fh; + linux_nfs_mount.fd = server_fd; + + linux_nfs_mount.rsize = get_default (sunos_mount->rsize, 8192); + linux_nfs_mount.wsize = get_default (sunos_mount->wsize, 8192); + linux_nfs_mount.timeo = get_default (sunos_mount->timeo, 10); + linux_nfs_mount.retrans = sunos_mount->retrans; + + linux_nfs_mount.acregmin = sunos_mount->acregmin; + linux_nfs_mount.acregmax = sunos_mount->acregmax; + linux_nfs_mount.acdirmin = sunos_mount->acdirmin; + linux_nfs_mount.acdirmax = sunos_mount->acdirmax; + + the_name = getname(sunos_mount->hostname); + if(IS_ERR(the_name)) + return -EFAULT; + + strncpy (linux_nfs_mount.hostname, the_name, 254); + linux_nfs_mount.hostname [255] = 0; + putname (the_name); + + dev = get_unnamed_dev (); + + ret = do_mount (dev, "", dir_name, "nfs", linux_flags, &linux_nfs_mount); + if (ret) + put_unnamed_dev(dev); + + return ret; +} + +/* XXXXXXXXXXXXXXXXXXXX */ +asmlinkage int +sunos_mount(char *type, char *dir, int flags, void *data) +{ + int linux_flags = MS_MGC_MSK; /* new semantics */ + int ret = -EINVAL; + char *dev_fname = 0; + + lock_kernel(); + /* We don't handle the integer fs type */ + if ((flags & SMNT_NEWTYPE) == 0) + goto out; + + /* Do not allow for those flags we don't support */ + if (flags & (SMNT_GRPID|SMNT_NOSUB|SMNT_MULTI|SMNT_SYS5)) + goto out; + + if(flags & SMNT_REMOUNT) + linux_flags |= MS_REMOUNT; + if(flags & SMNT_RDONLY) + linux_flags |= MS_RDONLY; + if(flags & SMNT_NOSUID) + linux_flags |= MS_NOSUID; + if(strcmp(type, "ext2") == 0) { + dev_fname = (char *) data; + } else if(strcmp(type, "iso9660") == 0) { + dev_fname = (char *) data; + } else if(strcmp(type, "minix") == 0) { + dev_fname = (char *) data; + } else if(strcmp(type, "nfs") == 0) { + ret = sunos_nfs_mount (dir, flags, data); + goto out; + } else if(strcmp(type, "ufs") == 0) { + printk("Warning: UFS filesystem mounts unsupported.\n"); + ret = -ENODEV; + goto out; + } else if(strcmp(type, "proc")) { + ret = -ENODEV; + goto out; + } + ret = sys_mount(dev_fname, dir, type, linux_flags, NULL); +out: + unlock_kernel(); + return ret; +} + +extern asmlinkage int sys_setsid(void); +extern asmlinkage int sys_setpgid(pid_t, pid_t); + +asmlinkage int sunos_setpgrp(pid_t pid, pid_t pgid) +{ + int ret; + + /* So stupid... */ + lock_kernel(); + if((!pid || pid == current->pid) && + !pgid) { + sys_setsid(); + ret = 0; + } else { + ret = sys_setpgid(pid, pgid); + } + unlock_kernel(); + return ret; +} + +/* So stupid... */ +extern asmlinkage int sys32_wait4(__kernel_pid_t32 pid, + u32 stat_addr, int options, u32 ru); + +asmlinkage int sunos_wait4(__kernel_pid_t32 pid, u32 stat_addr, int options, u32 ru) +{ + int ret; + + lock_kernel(); + ret = sys32_wait4((pid ? pid : ((__kernel_pid_t32)-1)), + stat_addr, options, ru); + unlock_kernel(); + return ret; +} + +extern int kill_pg(int, int, int); +asmlinkage int sunos_killpg(int pgrp, int sig) +{ + int ret; + + lock_kernel(); + ret = kill_pg(pgrp, sig, 0); + unlock_kernel(); + return ret; +} + +asmlinkage int sunos_audit(void) +{ + lock_kernel(); + printk ("sys_audit\n"); + unlock_kernel(); + return -1; +} + +extern asmlinkage u32 sunos_gethostid(void) +{ + u32 ret; + + lock_kernel(); + ret = (((u32)idprom->id_machtype << 24) | ((u32)idprom->id_sernum)); + unlock_kernel(); + return ret; +} + +/* sysconf options, for SunOS compatibility */ +#define _SC_ARG_MAX 1 +#define _SC_CHILD_MAX 2 +#define _SC_CLK_TCK 3 +#define _SC_NGROUPS_MAX 4 +#define _SC_OPEN_MAX 5 +#define _SC_JOB_CONTROL 6 +#define _SC_SAVED_IDS 7 +#define _SC_VERSION 8 + +extern asmlinkage s32 sunos_sysconf (int name) +{ + s32 ret; + + lock_kernel(); + switch (name){ + case _SC_ARG_MAX: + ret = ARG_MAX; + break; + case _SC_CHILD_MAX: + ret = CHILD_MAX; + break; + case _SC_CLK_TCK: + ret = HZ; + break; + case _SC_NGROUPS_MAX: + ret = NGROUPS_MAX; + break; + case _SC_OPEN_MAX: + ret = OPEN_MAX; + break; + case _SC_JOB_CONTROL: + ret = 1; /* yes, we do support job control */ + break; + case _SC_SAVED_IDS: + ret = 1; /* yes, we do support saved uids */ + break; + case _SC_VERSION: + /* mhm, POSIX_VERSION is in /usr/include/unistd.h + * should it go on /usr/include/linux? + */ + ret = 199009; + break; + default: + ret = -1; + break; + }; + unlock_kernel(); + return ret; +} + +extern asmlinkage int sys_semctl (int semid, int semnum, int cmd, union semun arg); +extern asmlinkage int sys_semget (key_t key, int nsems, int semflg); +extern asmlinkage int sys_semop (int semid, struct sembuf *tsops, unsigned nsops); + +asmlinkage int sunos_semsys(int op, u32 arg1, u32 arg2, u32 arg3, u32 ptr) +{ + union semun arg4; + int ret; + + lock_kernel(); + switch (op) { + case 0: + /* Most arguments match on a 1:1 basis but cmd doesn't */ + switch(arg3) { + case 4: + arg3=GETPID; break; + case 5: + arg3=GETVAL; break; + case 6: + arg3=GETALL; break; + case 3: + arg3=GETNCNT; break; + case 7: + arg3=GETZCNT; break; + case 8: + arg3=SETVAL; break; + case 9: + arg3=SETALL; break; + } + /* sys_semctl(): */ + arg4.__pad=(void *)A(ptr); /* value to modify semaphore to */ + ret = sys_semctl((int)arg1, (int)arg2, (int)arg3, arg4); + break; + case 1: + /* sys_semget(): */ + ret = sys_semget((key_t)arg1, (int)arg2, (int)arg3); + break; + case 2: + /* sys_semop(): */ + ret = sys_semop((int)arg1, (struct sembuf *)A(arg2), (unsigned)arg3); + break; + default: + ret = -EINVAL; + break; + }; + unlock_kernel(); + return ret; +} + +struct msgbuf32 { + s32 mtype; + char mtext[1]; +}; + +struct ipc_perm32 +{ + key_t key; + __kernel_uid_t32 uid; + __kernel_gid_t32 gid; + __kernel_uid_t32 cuid; + __kernel_gid_t32 cgid; + __kernel_mode_t32 mode; + unsigned short seq; +}; + +struct msqid_ds32 +{ + struct ipc_perm32 msg_perm; + u32 msg_first; + u32 msg_last; + __kernel_time_t32 msg_stime; + __kernel_time_t32 msg_rtime; + __kernel_time_t32 msg_ctime; + u32 wwait; + u32 rwait; + unsigned short msg_cbytes; + unsigned short msg_qnum; + unsigned short msg_qbytes; + __kernel_ipc_pid_t32 msg_lspid; + __kernel_ipc_pid_t32 msg_lrpid; +}; + +static inline int sunos_msqid_get(struct msqid_ds32 *user, + struct msqid_ds *kern) +{ + if(get_user(kern->msg_perm.key, &user->msg_perm.key) || + __get_user(kern->msg_perm.uid, &user->msg_perm.uid) || + __get_user(kern->msg_perm.gid, &user->msg_perm.gid) || + __get_user(kern->msg_perm.cuid, &user->msg_perm.cuid) || + __get_user(kern->msg_perm.cgid, &user->msg_perm.cgid) || + __get_user(kern->msg_stime, &user->msg_stime) || + __get_user(kern->msg_rtime, &user->msg_rtime) || + __get_user(kern->msg_ctime, &user->msg_ctime) || + __get_user(kern->msg_ctime, &user->msg_cbytes) || + __get_user(kern->msg_ctime, &user->msg_qnum) || + __get_user(kern->msg_ctime, &user->msg_qbytes) || + __get_user(kern->msg_ctime, &user->msg_lspid) || + __get_user(kern->msg_ctime, &user->msg_lrpid)) + return -EFAULT; + return 0; +} + +static inline int sunos_msqid_put(struct msqid_ds32 *user, + struct msqid_ds *kern) +{ + if(put_user(kern->msg_perm.key, &user->msg_perm.key) || + __put_user(kern->msg_perm.uid, &user->msg_perm.uid) || + __put_user(kern->msg_perm.gid, &user->msg_perm.gid) || + __put_user(kern->msg_perm.cuid, &user->msg_perm.cuid) || + __put_user(kern->msg_perm.cgid, &user->msg_perm.cgid) || + __put_user(kern->msg_stime, &user->msg_stime) || + __put_user(kern->msg_rtime, &user->msg_rtime) || + __put_user(kern->msg_ctime, &user->msg_ctime) || + __put_user(kern->msg_ctime, &user->msg_cbytes) || + __put_user(kern->msg_ctime, &user->msg_qnum) || + __put_user(kern->msg_ctime, &user->msg_qbytes) || + __put_user(kern->msg_ctime, &user->msg_lspid) || + __put_user(kern->msg_ctime, &user->msg_lrpid)) + return -EFAULT; + return 0; +} + +static inline int sunos_msgbuf_get(struct msgbuf32 *user, struct msgbuf *kern, int len) +{ + if(get_user(kern->mtype, &user->mtype) || + __copy_from_user(kern->mtext, &user->mtext, len)) + return -EFAULT; + return 0; +} + +static inline int sunos_msgbuf_put(struct msgbuf32 *user, struct msgbuf *kern, int len) +{ + if(put_user(kern->mtype, &user->mtype) || + __copy_to_user(user->mtext, kern->mtext, len)) + return -EFAULT; + return 0; +} + +extern asmlinkage int sys_msgget (key_t key, int msgflg); +extern asmlinkage int sys_msgrcv (int msqid, struct msgbuf *msgp, + size_t msgsz, long msgtyp, int msgflg); +extern asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp, + size_t msgsz, int msgflg); +extern asmlinkage int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf); + +asmlinkage int sunos_msgsys(int op, u32 arg1, u32 arg2, u32 arg3, u32 arg4) +{ + struct sparc_stackf32 *sp; + struct msqid_ds kds; + struct msgbuf *kmbuf; + unsigned long old_fs = get_fs(); + u32 arg5; + int rval; + + lock_kernel(); + switch(op) { + case 0: + rval = sys_msgget((key_t)arg1, (int)arg2); + break; + case 1: + if(!sunos_msqid_get((struct msqid_ds32 *)A(arg3), &kds)) { + set_fs(KERNEL_DS); + rval = sys_msgctl((int)arg1, (int)arg2, + (struct msqid_ds *)A(arg3)); + set_fs(old_fs); + if(!rval) + rval = sunos_msqid_put((struct msqid_ds32 *)A(arg3), + &kds); + } else + rval = -EFAULT; + break; + case 2: + rval = -EFAULT; + kmbuf = (struct msgbuf *)kmalloc(sizeof(struct msgbuf) + arg3, + GFP_KERNEL); + if(!kmbuf) + break; + sp = (struct sparc_stackf32 *) + (current->tss.kregs->u_regs[UREG_FP] & 0xffffffffUL); + if(get_user(arg5, &sp->xxargs[0])) { + rval = -EFAULT; + break; + } + set_fs(KERNEL_DS); + rval = sys_msgrcv((int)arg1, kmbuf, (size_t)arg3, + (long)arg4, (int)arg5); + set_fs(old_fs); + if(!rval) + rval = sunos_msgbuf_put((struct msgbuf32 *)A(arg2), + kmbuf, arg3); + kfree(kmbuf); + break; + case 3: + rval = -EFAULT; + kmbuf = (struct msgbuf *)kmalloc(sizeof(struct msgbuf) + arg3, + GFP_KERNEL); + if(!kmbuf || sunos_msgbuf_get((struct msgbuf32 *)A(arg2), + kmbuf, arg3)) + break; + set_fs(KERNEL_DS); + rval = sys_msgsnd((int)arg1, kmbuf, (size_t)arg3, (int)arg4); + set_fs(old_fs); + kfree(kmbuf); + break; + default: + rval = -EINVAL; + break; + } + unlock_kernel(); + return rval; +} + +struct shmid_ds32 { + struct ipc_perm32 shm_perm; + int shm_segsz; + __kernel_time_t32 shm_atime; + __kernel_time_t32 shm_dtime; + __kernel_time_t32 shm_ctime; + __kernel_ipc_pid_t32 shm_cpid; + __kernel_ipc_pid_t32 shm_lpid; + unsigned short shm_nattch; + unsigned short shm_npages; + u32 shm_pages; + u32 attaches; +}; + +static inline int sunos_shmid_get(struct shmid_ds32 *user, + struct shmid_ds *kern) +{ + if(get_user(kern->shm_perm.key, &user->shm_perm.key) || + __get_user(kern->shm_perm.uid, &user->shm_perm.uid) || + __get_user(kern->shm_perm.gid, &user->shm_perm.gid) || + __get_user(kern->shm_perm.cuid, &user->shm_perm.cuid) || + __get_user(kern->shm_perm.cgid, &user->shm_perm.cgid) || + __get_user(kern->shm_segsz, &user->shm_segsz) || + __get_user(kern->shm_atime, &user->shm_atime) || + __get_user(kern->shm_dtime, &user->shm_dtime) || + __get_user(kern->shm_ctime, &user->shm_ctime) || + __get_user(kern->shm_cpid, &user->shm_cpid) || + __get_user(kern->shm_lpid, &user->shm_lpid) || + __get_user(kern->shm_nattch, &user->shm_nattch) || + __get_user(kern->shm_npages, &user->shm_npages)) + return -EFAULT; + return 0; +} + +static inline int sunos_shmid_put(struct shmid_ds32 *user, + struct shmid_ds *kern) +{ + if(put_user(kern->shm_perm.key, &user->shm_perm.key) || + __put_user(kern->shm_perm.uid, &user->shm_perm.uid) || + __put_user(kern->shm_perm.gid, &user->shm_perm.gid) || + __put_user(kern->shm_perm.cuid, &user->shm_perm.cuid) || + __put_user(kern->shm_perm.cgid, &user->shm_perm.cgid) || + __put_user(kern->shm_segsz, &user->shm_segsz) || + __put_user(kern->shm_atime, &user->shm_atime) || + __put_user(kern->shm_dtime, &user->shm_dtime) || + __put_user(kern->shm_ctime, &user->shm_ctime) || + __put_user(kern->shm_cpid, &user->shm_cpid) || + __put_user(kern->shm_lpid, &user->shm_lpid) || + __put_user(kern->shm_nattch, &user->shm_nattch) || + __put_user(kern->shm_npages, &user->shm_npages)) + return -EFAULT; + return 0; +} + +extern asmlinkage int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr); +extern asmlinkage int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf); +extern asmlinkage int sys_shmdt (char *shmaddr); +extern asmlinkage int sys_shmget (key_t key, int size, int shmflg); + +asmlinkage int sunos_shmsys(int op, u32 arg1, u32 arg2, u32 arg3) +{ + struct shmid_ds ksds; + unsigned long raddr, old_fs = get_fs(); + int rval; + + lock_kernel(); + switch(op) { + case 0: + /* sys_shmat(): attach a shared memory area */ + rval = sys_shmat((int)arg1,(char *)A(arg2),(int)arg3,&raddr); + if(!rval) + rval = (int) raddr; + break; + case 1: + /* sys_shmctl(): modify shared memory area attr. */ + if(!sunos_shmid_get((struct shmid_ds32 *)A(arg3), &ksds)) { + set_fs(KERNEL_DS); + rval = sys_shmctl((int)arg1,(int)arg2, &ksds); + set_fs(old_fs); + if(!rval) + rval = sunos_shmid_put((struct shmid_ds32 *)A(arg3), + &ksds); + } else + rval = -EFAULT; + break; + case 2: + /* sys_shmdt(): detach a shared memory area */ + rval = sys_shmdt((char *)A(arg1)); + break; + case 3: + /* sys_shmget(): get a shared memory area */ + rval = sys_shmget((key_t)arg1,(int)arg2,(int)arg3); + break; + default: + rval = -EINVAL; + break; + }; + unlock_kernel(); + return rval; +} + +asmlinkage int sunos_open(u32 filename, int flags, int mode) +{ + int ret; + + lock_kernel(); + current->personality |= PER_BSD; + ret = sys_open ((char *)A(filename), flags, mode); + unlock_kernel(); + return ret; +} + +#define SUNOS_EWOULDBLOCK 35 + +/* see the sunos man page read(2v) for an explanation + of this garbage. We use O_NDELAY to mark + file descriptors that have been set non-blocking + using 4.2BSD style calls. (tridge) */ + +static inline int check_nonblock(int ret, int fd) +{ + if (ret == -EAGAIN && (current->files->fd[fd]->f_flags & O_NDELAY)) + return -SUNOS_EWOULDBLOCK; + return ret; +} + +extern asmlinkage int sys32_read(unsigned int fd, u32 buf, int count); +extern asmlinkage int sys32_write(unsigned int fd, u32 buf,int count); +extern asmlinkage int sys32_recv(int fd, u32 ubuf, int size, unsigned flags); +extern asmlinkage int sys32_send(int fd, u32 buff, int len, unsigned flags); +extern asmlinkage int sys32_accept(int fd, u32 sa, u32 addrlen); +extern asmlinkage int sys32_readv(u32 fd, u32 vector, s32 count); +extern asmlinkage int sys32_writev(u32 fd, u32 vector, s32 count); + +asmlinkage int sunos_read(unsigned int fd, u32 buf, int count) +{ + int ret; + + lock_kernel(); + ret = check_nonblock(sys32_read(fd, buf, count), fd); + unlock_kernel(); + return ret; +} + +asmlinkage int sunos_readv(u32 fd, u32 vector, s32 count) +{ + int ret; + + lock_kernel(); + ret = check_nonblock(sys32_readv(fd, vector, count), fd); + lock_kernel(); + return ret; +} + +asmlinkage int sunos_write(unsigned int fd, u32 buf, int count) +{ + int ret; + + lock_kernel(); + ret = check_nonblock(sys32_write(fd, buf, count), fd); + unlock_kernel(); + return ret; +} + +asmlinkage int sunos_writev(u32 fd, u32 vector, s32 count) +{ + int ret; + + lock_kernel(); + ret = check_nonblock(sys32_writev(fd, vector, count), fd); + unlock_kernel(); + return ret; +} + +asmlinkage int sunos_recv(int fd, u32 ubuf, int size, unsigned flags) +{ + int ret; + + lock_kernel(); + ret = check_nonblock(sys32_recv(fd, ubuf, size, flags), fd); + unlock_kernel(); + return ret; +} + +asmlinkage int sunos_send(int fd, u32 buff, int len, unsigned flags) +{ + int ret; + + lock_kernel(); + ret = check_nonblock(sys32_send(fd, buff, len, flags), fd); + unlock_kernel(); + return ret; +} + +asmlinkage int sunos_accept(int fd, u32 sa, u32 addrlen) +{ + int ret; + + lock_kernel(); + ret = check_nonblock(sys32_accept(fd, sa, addrlen), fd); + unlock_kernel(); + return ret; +} + +#define SUNOS_SV_INTERRUPT 2 + +extern void check_pending(int signum); + +asmlinkage int sunos_sigaction(int signum, u32 action, u32 oldaction) +{ + struct sigaction32 new_sa, old_sa; + struct sigaction *p; + const int sigaction_size = sizeof (struct sigaction32) - sizeof (u32); + + current->personality |= PER_BSD; + if(signum < 1 || signum > 32) + return -EINVAL; + + p = signum - 1 + current->sig->action; + + if(action) { + if (signum==SIGKILL || signum==SIGSTOP) + return -EINVAL; + memset(&new_sa, 0, sizeof(struct sigaction32)); + if(copy_from_user(&new_sa, (struct sigaction32 *)A(action), + sigaction_size)) + return -EFAULT; + if (((__sighandler_t)A(new_sa.sa_handler) != SIG_DFL) && + (__sighandler_t)A(new_sa.sa_handler) != SIG_IGN) { + if(verify_area(VERIFY_READ, + (__sighandler_t)A(new_sa.sa_handler), 1)) + return -EFAULT; + } + new_sa.sa_flags ^= SUNOS_SV_INTERRUPT; + } + + if (oldaction) { + /* In the clone() case we could copy half consistant + * state to the user, however this could sleep and + * deadlock us if we held the signal lock on SMP. So for + * now I take the easy way out and do no locking. + * But then again we don't support SunOS lwp's anyways ;-) + */ + old_sa.sa_handler = (unsigned)(u64)(p->sa_handler); + old_sa.sa_mask = (sigset_t32)(p->sa_mask); + old_sa.sa_flags = (unsigned)(p->sa_flags); + + if (old_sa.sa_flags & SA_RESTART) + old_sa.sa_flags &= ~SA_RESTART; + else + old_sa.sa_flags |= SUNOS_SV_INTERRUPT; + if (copy_to_user((struct sigaction32 *)A(oldaction), + &old_sa, sigaction_size)) + return -EFAULT; + } + + if (action) { + spin_lock_irq(¤t->sig->siglock); + p->sa_handler = (__sighandler_t)A(new_sa.sa_handler); + p->sa_mask = (sigset_t)(new_sa.sa_mask); + p->sa_flags = new_sa.sa_flags; + p->sa_restorer = (void (*)(void))0; + check_pending(signum); + spin_unlock_irq(¤t->sig->siglock); + } + return 0; +} + + +extern asmlinkage int sys32_setsockopt(int fd, int level, int optname, + u32 optval, int optlen); +extern asmlinkage int sys32_getsockopt(int fd, int level, int optname, + u32 optval, u32 optlen); + +asmlinkage int sunos_setsockopt(int fd, int level, int optname, u32 optval, + int optlen) +{ + int tr_opt = optname; + int ret; + + lock_kernel(); + if (level == SOL_IP) { + /* Multicast socketopts (ttl, membership) */ + if (tr_opt >=2 && tr_opt <= 6) + tr_opt += 30; + } + ret = sys32_setsockopt(fd, level, tr_opt, optval, optlen); + unlock_kernel(); + return ret; +} + +asmlinkage int sunos_getsockopt(int fd, int level, int optname, + u32 optval, u32 optlen) +{ + int tr_opt = optname; + int ret; + + lock_kernel(); + if (level == SOL_IP) { + /* Multicast socketopts (ttl, membership) */ + if (tr_opt >=2 && tr_opt <= 6) + tr_opt += 30; + } + ret = sys32_getsockopt(fd, level, tr_opt, optval, optlen); + unlock_kernel(); + return ret; +} diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc64/kernel/systbls.S index a74d0ffbd..eda0ff326 100644 --- a/arch/sparc64/kernel/systbls.S +++ b/arch/sparc64/kernel/systbls.S @@ -1,4 +1,4 @@ -/* $Id: systbls.S,v 1.13 1997/06/04 13:05:29 jj Exp $ +/* $Id: systbls.S,v 1.21 1997/07/05 07:09:17 davem Exp $ * systbls.S: System call entry point tables for OS compatibility. * The native Linux system call table lives here also. * @@ -30,7 +30,7 @@ sys_call_table32: /*50*/ .xword sys_getegid, sys32_acct, sys_nis_syscall, sys_nis_syscall, sys32_ioctl .xword sys32_reboot, sys_nis_syscall, sys32_symlink, sys32_readlink, sys32_execve /*60*/ .xword sys_umask, sys32_chroot, sys32_newfstat, sys_nis_syscall, sys_getpagesize - .xword sys_nis_syscall, sys_vfork, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall + .xword sys32_msync, sys_vfork, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall /*70*/ .xword sys_nis_syscall, sys32_mmap, sys_nis_syscall, sys32_munmap, sys32_mprotect .xword sys_nis_syscall, sys_vhangup, sys_nis_syscall, sys_nis_syscall, sys32_getgroups /*80*/ .xword sys32_setgroups, sys_getpgrp, sys_nis_syscall, sys32_setitimer, sys_nis_syscall @@ -42,7 +42,7 @@ sys_call_table32: /*110*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall .xword sys_nis_syscall, sys32_gettimeofday, sys32_getrusage, sys_nis_syscall, sys_nis_syscall /*120*/ .xword sys32_readv, sys32_writev, sys32_settimeofday, sys_fchown, sys_fchmod - .xword sys_nis_syscall, sys_setreuid, sys_setregid, sys32_rename, sys32_truncate + .xword sys_nis_syscall, sys32_setreuid, sys_setregid, sys32_rename, sys32_truncate /*130*/ .xword sys32_ftruncate, sys_flock, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall .xword sys_nis_syscall, sys32_mkdir, sys32_rmdir, sys_nis_syscall, sys_nis_syscall /*140*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_getrlimit @@ -53,15 +53,15 @@ sys_call_table32: .xword sys32_quotactl, sys_nis_syscall, sys32_mount, sys32_ustat, sys_nis_syscall /*170*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_getdents .xword sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall -/*180*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_sigpending, sys32_no_modules +/*180*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_sigpending, sys32_query_module .xword sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_newuname -/*190*/ .xword sys32_no_modules, sys32_personality, sys_prof, sys_break, sys_lock +/*190*/ .xword sys32_init_module, sys32_personality, sys_prof, sys_break, sys_lock .xword sys_mpx, sys_ulimit, sys_getppid, sparc32_sigaction, sys_sgetmask /*200*/ .xword sys_ssetmask, sys_sigsuspend, sys32_newlstat, sys32_uselib, old32_readdir .xword sys_nis_syscall, sys32_socketcall, sys32_syslog, sys32_olduname, sys_nis_syscall /*210*/ .xword sys_idle, sys_nis_syscall, sys32_waitpid, sys32_swapoff, sys32_sysinfo .xword sys32_ipc, sys_sigreturn, sys_clone, sys_nis_syscall, sys32_adjtimex -/*220*/ .xword sys32_sigprocmask, sys32_no_modules, sys32_no_modules, sys32_no_modules, sys_getpgid +/*220*/ .xword sys32_sigprocmask, sys32_create_module, sys32_delete_module, sys32_get_kernel_syms, sys_getpgid .xword sys32_bdflush, sys32_sysfs, sys_nis_syscall, sys_setfsuid, sys_setfsgid /*230*/ .xword sys32_llseek, sys32_time, sys_nis_syscall, sys_stime, sys_nis_syscall .xword sys_nis_syscall, sys32_llseek, sys32_mlock, sys32_munlock, sys_mlockall @@ -94,29 +94,29 @@ sys_call_table: /*80*/ .xword sys_setgroups, sys_getpgrp, sys_nis_syscall, sys_setitimer, sys_nis_syscall .xword sys_swapon, sys_getitimer, sys_nis_syscall, sys_sethostname, sys_nis_syscall /*90*/ .xword sys_dup2, sys_nis_syscall, sys_fcntl, sys_select, sys_nis_syscall - .xword sys_fsync, sys_setpriority, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall -/*100*/ .xword sys_getpriority, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall - .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall -/*110*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall - .xword sys_nis_syscall, sys_gettimeofday, sys_getrusage, sys_nis_syscall, sys_nis_syscall + .xword sys_fsync, sys_setpriority, sys_socket, sys_connect, sys_accept +/*100*/ .xword sys_getpriority, sys_send, sys_recv, sys_nis_syscall, sys_bind + .xword sys_setsockopt, sys_listen, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall +/*110*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_recvmsg, sys_sendmsg + .xword sys_nis_syscall, sys_gettimeofday, sys_getrusage, sys_getsockopt, sys_nis_syscall /*120*/ .xword sys_readv, sys_writev, sys_settimeofday, sys_fchown, sys_fchmod - .xword sys_nis_syscall, sys_setreuid, sys_setregid, sys_rename, sys_truncate -/*130*/ .xword sys_ftruncate, sys_flock, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall - .xword sys_nis_syscall, sys_mkdir, sys_rmdir, sys_nis_syscall, sys_nis_syscall -/*140*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_getrlimit + .xword sys_recvfrom, sys_setreuid, sys_setregid, sys_rename, sys_truncate +/*130*/ .xword sys_ftruncate, sys_flock, sys_nis_syscall, sys_sendto, sys_shutdown + .xword sys_socketpair, sys_mkdir, sys_rmdir, sys_nis_syscall, sys_nis_syscall +/*140*/ .xword sys_nis_syscall, sys_getpeername, sys_nis_syscall, sys_nis_syscall, sys_getrlimit .xword sys_setrlimit, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall -/*150*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall +/*150*/ .xword sys_getsockname, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall .xword sys_nis_syscall, sys_nis_syscall, sys_statfs, sys_fstatfs, sys_umount /*160*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_setdomainname, sys_nis_syscall .xword sys_quotactl, sys_nis_syscall, sys_mount, sys_ustat, sys_nis_syscall /*170*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_getdents .xword sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall -/*180*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_sigpending, sys_nis_syscall +/*180*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_sigpending, sys_query_module .xword sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_newuname /*190*/ .xword sys_init_module, sys_personality, sys_prof, sys_break, sys_lock .xword sys_mpx, sys_ulimit, sys_getppid, sparc_sigaction, sys_sgetmask /*200*/ .xword sys_ssetmask, sys_sigsuspend, sys_newlstat, sys_uselib, sys_nis_syscall - .xword sys_nis_syscall, sys_socketcall, sys_syslog, sys_nis_syscall, sys_nis_syscall + .xword sys_nis_syscall, sys_nis_syscall, sys_syslog, sys_nis_syscall, sys_nis_syscall /*210*/ .xword sys_idle, sys_nis_syscall, sys_waitpid, sys_swapoff, sys_sysinfo .xword sys_ipc, sys_sigreturn, sys_clone, sys_nis_syscall, sys_adjtimex /*220*/ .xword sys_sigprocmask, sys_create_module, sys_delete_module, sys_get_kernel_syms, sys_getpgid @@ -130,72 +130,72 @@ sys_call_table: /* Now the 32-bit SunOS syscall table. */ - .align 4 + .align 8 .globl sunos_sys_table sunos_sys_table: /*0*/ .xword sunos_indir, sys_exit, sys_fork .xword sunos_read, sunos_write, sunos_open - .xword sys_close, sunos_wait4, sys_creat - .xword sys_link, sys_unlink, sunos_execv - .xword sys_chdir, sunos_nosys, sys_mknod - .xword sys_chmod, sys_chown, sunos_brk - .xword sunos_nosys, sys_lseek, sunos_getpid + .xword sys_close, sunos_wait4, sys32_creat + .xword sys32_link, sys32_unlink, sunos_execv + .xword sys32_chdir, sunos_nosys, sys32_mknod + .xword sys32_chmod, sys32_chown, sunos_brk + .xword sunos_nosys, sys32_lseek, sunos_getpid .xword sunos_nosys, sunos_nosys, sunos_nosys .xword sunos_getuid, sunos_nosys, sys_ptrace .xword sunos_nosys, sunos_nosys, sunos_nosys .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sys_access, sunos_nosys, sunos_nosys - .xword sys_sync, sys_kill, sys_newstat - .xword sunos_nosys, sys_newlstat, sys_dup + .xword sys32_access, sunos_nosys, sunos_nosys + .xword sys_sync, sys_kill, sys32_newstat + .xword sunos_nosys, sys32_newlstat, sys_dup .xword sys_pipe, sunos_nosys, sys_profil .xword sunos_nosys, sunos_nosys, sunos_getgid .xword sunos_nosys, sunos_nosys -/*50*/ .xword sunos_nosys, sys_acct, sunos_nosys - .xword sunos_mctl, sunos_ioctl, sys_reboot - .xword sunos_nosys, sys_symlink, sys_readlink - .xword sys32_execve, sys_umask, sys_chroot - .xword sys_newfstat, sunos_nosys, sys_getpagesize - .xword sys_msync, sys_vfork, sunos_nosys +/*50*/ .xword sunos_nosys, sys32_acct, sunos_nosys + .xword sunos_mctl, sunos_ioctl, sys32_reboot + .xword sunos_nosys, sys32_symlink, sys32_readlink + .xword sys32_execve, sys_umask, sys32_chroot + .xword sys32_newfstat, sunos_nosys, sys_getpagesize + .xword sys32_msync, sys_vfork, sunos_nosys .xword sunos_nosys, sunos_sbrk, sunos_sstk - .xword sunos_mmap, sunos_vadvise, sys_munmap - .xword sys_mprotect, sunos_madvise, sys_vhangup - .xword sunos_nosys, sunos_mincore, sys_getgroups - .xword sys_setgroups, sys_getpgrp, sunos_setpgrp - .xword sys_setitimer, sunos_nosys, sys_swapon - .xword sys_getitimer, sys_gethostname, sys_sethostname + .xword sunos_mmap, sunos_vadvise, sys32_munmap + .xword sys32_mprotect, sunos_madvise, sys_vhangup + .xword sunos_nosys, sunos_mincore, sys32_getgroups + .xword sys32_setgroups, sys_getpgrp, sunos_setpgrp + .xword sys32_setitimer, sunos_nosys, sys32_swapon + .xword sys32_getitimer, sys32_gethostname, sys32_sethostname .xword sunos_getdtablesize, sys_dup2, sunos_nop - .xword sys_fcntl, sunos_select, sunos_nop + .xword sys32_fcntl, sunos_select, sunos_nop .xword sys_fsync, sys_setpriority, sys_socket - .xword sys_connect, sunos_accept + .xword sys32_connect, sunos_accept /*100*/ .xword sys_getpriority, sunos_send, sunos_recv - .xword sunos_nosys, sys_bind, sunos_setsockopt + .xword sunos_nosys, sys32_bind, sunos_setsockopt .xword sys_listen, sunos_nosys, sunos_sigaction .xword sunos_sigblock, sunos_sigsetmask, sys_sigpause - .xword sys_sigstack, sys_recvmsg, sys_sendmsg - .xword sunos_nosys, sys_gettimeofday, sys_getrusage + .xword sys32_sigstack, sys32_recvmsg, sys32_sendmsg + .xword sunos_nosys, sys_gettimeofday, sys32_getrusage .xword sunos_getsockopt, sunos_nosys, sunos_readv .xword sunos_writev, sys_settimeofday, sys_fchown - .xword sys_fchmod, sys_recvfrom, sys_setreuid - .xword sys_setregid, sys_rename, sys_truncate - .xword sys_ftruncate, sys_flock, sunos_nosys - .xword sys_sendto, sys_shutdown, sys_socketpair - .xword sys_mkdir, sys_rmdir, sys_utimes - .xword sys_sigreturn, sunos_nosys, sys_getpeername - .xword sunos_gethostid, sunos_nosys, sys_getrlimit - .xword sys_setrlimit, sunos_killpg, sunos_nosys + .xword sys_fchmod, sys32_recvfrom, sys32_setreuid + .xword sys_setregid, sys32_rename, sys32_truncate + .xword sys32_ftruncate, sys_flock, sunos_nosys + .xword sys32_sendto, sys_shutdown, sys_socketpair + .xword sys32_mkdir, sys32_rmdir, sys32_utimes + .xword sys_sigreturn, sunos_nosys, sys32_getpeername + .xword sunos_gethostid, sunos_nosys, sys32_getrlimit + .xword sys32_setrlimit, sunos_killpg, sunos_nosys .xword sunos_nosys, sunos_nosys -/*150*/ .xword sys_getsockname, sunos_nosys, sunos_nosys - .xword sunos_poll, sunos_nosys, sunos_nosys - .xword sunos_getdirentries, sys_statfs, sys_fstatfs - .xword sys_umount, sunos_nosys, sunos_nosys - .xword sunos_getdomainname, sys_setdomainname - .xword sunos_nosys, sys_quotactl, sunos_nosys - .xword sunos_mount, sys_ustat, sunos_semsys +/*150*/ .xword sys32_getsockname, sunos_nosys, sunos_nosys + .xword sys32_poll, sunos_nosys, sunos_nosys + .xword sunos_getdirentries, sys32_statfs, sys32_fstatfs + .xword sys32_umount, sunos_nosys, sunos_nosys + .xword sunos_getdomainname, sys32_setdomainname + .xword sunos_nosys, sys32_quotactl, sunos_nosys + .xword sunos_mount, sys32_ustat, sunos_semsys .xword sunos_nosys, sunos_shmsys, sunos_audit .xword sunos_nosys, sunos_getdents, sys_setsid .xword sys_fchdir, sunos_nosys, sunos_nosys .xword sunos_nosys, sunos_nosys, sunos_nosys - .xword sunos_nosys, sys_sigpending, sunos_nosys + .xword sunos_nosys, sys32_sigpending, sunos_nosys .xword sys_setpgid, sunos_pathconf, sunos_fpathconf .xword sunos_sysconf, sunos_uname, sunos_nosys .xword sunos_nosys, sunos_nosys, sunos_nosys diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c index 3f15fcb54..ad40a5fb5 100644 --- a/arch/sparc64/kernel/time.c +++ b/arch/sparc64/kernel/time.c @@ -1,4 +1,4 @@ -/* $Id: time.c,v 1.2 1997/04/10 03:02:35 davem Exp $ +/* $Id: time.c,v 1.3 1997/06/17 13:25:29 jj Exp $ * time.c: UltraSparc timer and TOD clock support. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -146,9 +146,6 @@ static int has_low_battery(void) return (data1 == data2); /* Was the write blocked? */ } -/* XXX HACK HACK HACK, delete me soon */ -static struct linux_prom_ranges XXX_sbus_ranges[PROMREG_MAX]; -static int XXX_sbus_nranges; /* Probe for the real time clock chip. */ __initfunc(static void clock_probe(void)) @@ -157,6 +154,10 @@ __initfunc(static void clock_probe(void)) char model[128]; int node, sbusnd, err; + /* XXX HACK HACK HACK, delete me soon */ + struct linux_prom_ranges XXX_sbus_ranges[PROMREG_MAX]; + int XXX_sbus_nranges; + node = prom_getchild(prom_root_node); sbusnd = prom_searchsiblings(node, "sbus"); node = prom_getchild(sbusnd); diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c index 824a3ddb4..ac3e79958 100644 --- a/arch/sparc64/kernel/traps.c +++ b/arch/sparc64/kernel/traps.c @@ -1,14 +1,15 @@ -/* $Id: traps.c,v 1.19 1997/06/05 06:22:49 davem Exp $ - * arch/sparc/kernel/traps.c +/* $Id: traps.c,v 1.29 1997/07/05 09:52:38 davem Exp $ + * arch/sparc64/kernel/traps.c * * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ /* - * I hate traps on the sparc, grrr... + * I like traps on v9, :)))) */ +#include <linux/config.h> #include <linux/sched.h> /* for jiffies */ #include <linux/kernel.h> #include <linux/signal.h> @@ -123,6 +124,8 @@ void syscall_trace_entry(unsigned long g1, struct pt_regs *regs) int i; #endif + if(strcmp(current->comm, "bash.sunos")) + return; printk("SYS[%s:%d]: PC(%016lx) <%3d> ", current->comm, current->pid, regs->tpc, (int)g1); #ifdef VERBOSE_SYSCALL_TRACING @@ -153,53 +156,12 @@ void syscall_trace_entry(unsigned long g1, struct pt_regs *regs) unsigned long syscall_trace_exit(unsigned long retval, struct pt_regs *regs) { - printk("ret[%016lx]\n", retval); + if(!strcmp(current->comm, "bash.sunos")) + printk("ret[%016lx]\n", retval); return retval; } #endif /* SYSCALL_TRACING */ -#if 0 -void user_rtrap_report(struct pt_regs *regs) -{ - static int hits = 0; - - /* Bwahhhhrggg... */ - if(regs->tpc == 0x1f294UL && ++hits == 2) { - register unsigned long ctx asm("o4"); - register unsigned long paddr asm("o5"); - unsigned long cwp, wstate; - - printk("RT[%016lx:%016lx] ", regs->tpc, regs->u_regs[UREG_I6]); - __asm__ __volatile__("rdpr %%cwp, %0" : "=r" (cwp)); - __asm__ __volatile__("rdpr %%wstate, %0" : "=r" (wstate)); - printk("CWP[%d] WSTATE[%016lx]\n" - "TSS( ksp[%016lx] kpc[%016lx] wstate[%016lx] w_saved[%d] flgs[%x]" - " cur_ds[%d] )\n", cwp, wstate, - current->tss.ksp, current->tss.kpc, current->tss.wstate, - (int) current->tss.w_saved, current->tss.flags, - current->tss.current_ds); - __asm__ __volatile__(" - rdpr %%pstate, %%o3 - wrpr %%o3, %2, %%pstate - mov %%g7, %%o5 - mov 0x10, %%o4 - ldxa [%%o4] %3, %%o4 - wrpr %%o3, 0x0, %%pstate - " : "=r" (ctx), "=r" (paddr) - : "i" (PSTATE_MG|PSTATE_IE), "i" (ASI_DMMU)); - - printk("MMU[ppgd(%016lx)sctx(%d)] ", paddr, ctx); - printk("mm->context(%016lx) mm->pgd(%p)\n", - current->mm->context, current->mm->pgd); - printk("TASK: signal[%016lx] blocked[%016lx]\n", - current->signal, current->blocked); - show_regs(regs); - while(1) - barrier(); - } -} -#endif - void bad_trap (struct pt_regs *regs, long lvl) { lock_kernel (); @@ -221,168 +183,44 @@ void bad_trap_tl1 (struct pt_regs *regs, long lvl) { char buffer[24]; - lock_kernel (); + lock_kernel(); sprintf (buffer, "Bad trap %lx at tl>0", lvl); die_if_kernel (buffer, regs); + unlock_kernel(); } void data_access_exception (struct pt_regs *regs) { - lock_kernel (); - printk ("Unhandled data access exception "); - printk("sfsr %016lx sfar %016lx\n", spitfire_get_dsfsr(), spitfire_get_sfar()); - die_if_kernel("Data access exception", regs); + send_sig(SIGSEGV, current, 1); } void do_dae(struct pt_regs *regs) { - printk("DAE: at %016lx\n", regs->tpc); - while(1) - barrier(); + send_sig(SIGSEGV, current, 1); } void instruction_access_exception (struct pt_regs *regs) { - lock_kernel (); - printk ("Unhandled instruction access exception "); - printk("sfsr %016lx\n", spitfire_get_isfsr()); - die_if_kernel("Instruction access exception", regs); + send_sig(SIGSEGV, current, 1); } void do_iae(struct pt_regs *regs) { - printk("IAE at %016lx\n", regs->tpc); - while(1) - barrier(); -} - -static unsigned long init_fsr = 0x0UL; -static unsigned int init_fregs[64] __attribute__ ((aligned (64))) = - { ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, - ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, - ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, - ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, - ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, - ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, - ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, - ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U }; - -void do_fpdis(struct pt_regs *regs) -{ - lock_kernel(); - - regs->tstate |= TSTATE_PEF; - fprs_write(FPRS_FEF); - - /* This is allowed now because the V9 ABI varargs passes floating - * point args in floating point registers, so vsprintf() and sprintf() - * cause problems. Luckily we never actually pass floating point values - * to those routines in the kernel and the code generated just does - * stores of them to the stack. Therefore, for the moment this fix - * is sufficient. -DaveM - */ - if(regs->tstate & TSTATE_PRIV) - goto out; - -#ifndef __SMP__ - if(last_task_used_math == current) - goto out; - if(last_task_used_math) { - struct task_struct *fptask = last_task_used_math; - - if(fptask->tss.flags & SPARC_FLAG_32BIT) - fpsave32((unsigned long *)&fptask->tss.float_regs[0], - &fptask->tss.fsr); - else - fpsave((unsigned long *)&fptask->tss.float_regs[0], - &fptask->tss.fsr); - } - last_task_used_math = current; - if(current->used_math) { - if(current->tss.flags & SPARC_FLAG_32BIT) - fpload32(¤t->tss.float_regs[0], - ¤t->tss.fsr); - else - fpload(¤t->tss.float_regs[0], - ¤t->tss.fsr); - } else { - /* Set inital sane state. */ - fpload(&init_fregs[0], &init_fsr); - current->used_math = 1; - } -#else - if(!current->used_math) { - fpload(&init_fregs[0], &init_fsr); - current->used_math = 1; - } else { - if(current->tss.flags & SPARC_FLAG_32BIT) - fpload32(¤t->tss.float_regs[0], - ¤t->tss.fsr); - else - fpload(¤t->tss.float_regs[0], - ¤t->tss.fsr); - } - current->flags |= PF_USEDFPU; -#endif -#ifndef __SMP__ -out: -#endif - unlock_kernel(); + send_sig(SIGSEGV, current, 1); } -static unsigned long fake_regs[32] __attribute__ ((aligned (8))); -static unsigned long fake_fsr; - void do_fpe_common(struct pt_regs *regs) { - static int calls = 0; -#ifndef __SMP__ - struct task_struct *fpt = last_task_used_math; -#else - struct task_struct *fpt = current; -#endif - - lock_kernel(); - fprs_write(FPRS_FEF); - -#ifndef __SMP__ - if(!fpt) { -#else - if(!(fpt->flags & PF_USEDFPU)) { -#endif - fpsave(&fake_regs[0], &fake_fsr); - regs->tstate &= ~(TSTATE_PEF); - goto out; - } - if(fpt->tss.flags & SPARC_FLAG_32BIT) - fpsave32((unsigned long *)&fpt->tss.float_regs[0], &fpt->tss.fsr); - else - fpsave((unsigned long *)&fpt->tss.float_regs[0], &fpt->tss.fsr); - fpt->tss.sig_address = regs->tpc; - fpt->tss.sig_desc = SUBSIG_FPERROR; -#ifdef __SMP__ - fpt->flags &= ~PF_USEDFPU; -#endif if(regs->tstate & TSTATE_PRIV) { - printk("WARNING: FPU exception from kernel mode. at pc=%016lx\n", - regs->tpc); regs->tpc = regs->tnpc; regs->tnpc += 4; - calls++; - if(calls > 2) - die_if_kernel("Too many Penguin-FPU traps from kernel mode", - regs); - goto out; + } else { + lock_kernel(); + current->tss.sig_address = regs->tpc; + current->tss.sig_desc = SUBSIG_FPERROR; + send_sig(SIGFPE, current, 1); + unlock_kernel(); } - send_sig(SIGFPE, fpt, 1); -#ifndef __SMP__ - last_task_used_math = NULL; -#endif - regs->tstate &= ~TSTATE_PEF; - if(calls > 0) - calls = 0; -out: - unlock_kernel(); } void do_fpieee(struct pt_regs *regs) @@ -397,16 +235,16 @@ void do_fpother(struct pt_regs *regs) void do_tof(struct pt_regs *regs) { - printk("TOF: at %016lx\n", regs->tpc); - while(1) - barrier(); + if(regs->tstate & TSTATE_PRIV) + die_if_kernel("Penguin overflow trap from kernel mode", regs); + current->tss.sig_address = regs->tpc; + current->tss.sig_desc = SUBSIG_TAG; /* as good as any */ + send_sig(SIGEMT, current, 1); } void do_div0(struct pt_regs *regs) { - printk("DIV0: at %016lx\n", regs->tpc); - while(1) - barrier(); + send_sig(SIGILL, current, 1); } void instruction_dump (unsigned int *pc) @@ -426,7 +264,7 @@ void die_if_kernel(char *str, struct pt_regs *regs) /* Amuse the user. */ printk( " \\|/ ____ \\|/\n" -" \"@'/ .` \\`@\"\n" +" \"@'/ .. \\`@\"\n" " /_| \\__/ |_\\\n" " \\__U_/\n"); @@ -437,17 +275,15 @@ void die_if_kernel(char *str, struct pt_regs *regs) struct reg_window *rw = (struct reg_window *) (regs->u_regs[UREG_FP] + STACK_BIAS); - if(rw) { + /* Stop the back trace when we hit userland or we + * find some badly aligned kernel stack. + */ + while(rw && + (((unsigned long) rw) >= PAGE_OFFSET) && + !(((unsigned long) rw) & 0x7)) { printk("Caller[%016lx]\n", rw->ins[7]); rw = (struct reg_window *) (rw->ins[6] + STACK_BIAS); - if(rw) { - printk("Caller[%016lx]\n", rw->ins[7]); - rw = (struct reg_window *) - (rw->ins[6] + STACK_BIAS); - if(rw) - printk("Caller[%016lx]\n", rw->ins[7]); - } } } printk("Instruction DUMP:"); @@ -465,16 +301,6 @@ void do_illegal_instruction(struct pt_regs *regs) lock_kernel(); if(tstate & TSTATE_PRIV) die_if_kernel("Kernel illegal instruction", regs); -#if 1 - { - unsigned int insn; - - printk("Ill instr. at pc=%016lx ", pc); - get_user(insn, ((unsigned int *)pc)); - printk("insn=[%08x]\n", insn); - show_regs(regs); - } -#endif current->tss.sig_address = pc; current->tss.sig_desc = SUBSIG_ILLINST; send_sig(SIGILL, current, 1); @@ -483,13 +309,11 @@ void do_illegal_instruction(struct pt_regs *regs) void mem_address_unaligned(struct pt_regs *regs) { - printk("AIEEE: do_mna at %016lx\n", regs->tpc); - show_regs(regs); if(regs->tstate & TSTATE_PRIV) { - printk("MNA from kernel, spinning\n"); - sti(); - while(1) - barrier(); + extern void kernel_unaligned_trap(struct pt_regs *regs, + unsigned int insn); + + return kernel_unaligned_trap(regs, *((unsigned int *)regs->tpc)); } else { current->tss.sig_address = regs->tpc; current->tss.sig_desc = SUBSIG_PRIVINST; @@ -499,16 +323,17 @@ void mem_address_unaligned(struct pt_regs *regs) void do_privop(struct pt_regs *regs) { - printk("PRIVOP: at %016lx\n", regs->tpc); - while(1) - barrier(); + current->tss.sig_address = regs->tpc; + current->tss.sig_desc = SUBSIG_PRIVINST; + send_sig(SIGILL, current, 1); } void do_privact(struct pt_regs *regs) { - printk("PRIVACT: at %016lx\n", regs->tpc); - while(1) - barrier(); + current->tss.sig_address = regs->tpc; + current->tss.sig_desc = SUBSIG_PRIVINST; + send_sig(SIGILL, current, 1); + unlock_kernel(); } void do_priv_instruction(struct pt_regs *regs, unsigned long pc, unsigned long npc, @@ -537,11 +362,6 @@ void do_memaccess_unaligned(struct pt_regs *regs, unsigned long pc, unsigned lon } current->tss.sig_address = pc; current->tss.sig_desc = SUBSIG_PRIVINST; -#if 0 - show_regs (regs); - instruction_dump ((unsigned long *) regs->tpc); - printk ("do_MNA!\n"); -#endif send_sig(SIGBUS, current, 1); unlock_kernel(); } @@ -554,6 +374,134 @@ void handle_hw_divzero(struct pt_regs *regs, unsigned long pc, unsigned long npc unlock_kernel(); } +/* Trap level 1 stuff or other traps we should never see... */ +void do_cee(struct pt_regs *regs) +{ + die_if_kernel("TL0: Cache Error Exception", regs); +} + +void do_cee_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: Cache Error Exception", regs); +} + +void do_dae_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: Data Access Exception", regs); +} + +void do_iae_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: Instruction Access Exception", regs); +} + +void do_div0_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: DIV0 Exception", regs); +} + +void do_fpdis_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: FPU Disabled", regs); +} + +void do_fpieee_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: FPU IEEE Exception", regs); +} + +void do_fpother_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: FPU Other Exception", regs); +} + +void do_ill_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: Illegal Instruction Exception", regs); +} + +void do_irq_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: IRQ Exception", regs); +} + +void do_lddfmna(struct pt_regs *regs) +{ + die_if_kernel("TL0: LDDF Exception", regs); +} + +void do_lddfmna_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: LDDF Exception", regs); +} + +void do_stdfmna(struct pt_regs *regs) +{ + die_if_kernel("TL0: STDF Exception", regs); +} + +void do_stdfmna_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: STDF Exception", regs); +} + +void do_paw(struct pt_regs *regs) +{ + die_if_kernel("TL0: Phys Watchpoint Exception", regs); +} + +void do_paw_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: Phys Watchpoint Exception", regs); +} + +void do_vaw(struct pt_regs *regs) +{ + die_if_kernel("TL0: Virt Watchpoint Exception", regs); +} + +void do_vaw_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: Virt Watchpoint Exception", regs); +} + +void do_tof_tl1(struct pt_regs *regs) +{ + die_if_kernel("TL1: Tag Overflow Exception", regs); +} + +#ifdef CONFIG_EC_FLUSH_TRAP +void cache_flush_trap(struct pt_regs *regs) +{ +#ifndef __SMP__ + unsigned node = linux_cpus[get_cpuid()].prom_node; +#else +#error SMP not supported on sparc64 yet +#endif + int size = prom_getintdefault(node, "ecache-size", 512*1024); + int i, j; + unsigned long addr, page_nr; + + regs->tpc = regs->tnpc; + regs->tnpc = regs->tnpc + 4; + if (!suser()) return; + size >>= PAGE_SHIFT; + addr = PAGE_OFFSET - PAGE_SIZE; + for (i = 0; i < size; i++) { + do { + addr += PAGE_SIZE; + page_nr = MAP_NR(addr); + if (page_nr >= max_mapnr) { + return; + } + } while (!PageReserved (mem_map + page_nr)); + /* E-Cache line size is 64B. Let us pollute it :)) */ + for (j = 0; j < PAGE_SIZE; j += 64) + __asm__ __volatile__ ("ldx [%0 + %1], %%g1" : : "r" (j), "r" (addr) : "g1"); + } +} +#endif + void trap_init(void) { } diff --git a/arch/sparc64/kernel/ttable.S b/arch/sparc64/kernel/ttable.S index 8db708f07..73bda96d9 100644 --- a/arch/sparc64/kernel/ttable.S +++ b/arch/sparc64/kernel/ttable.S @@ -1,9 +1,11 @@ -/* $Id: ttable.S,v 1.13 1997/06/02 06:33:34 davem Exp $ +/* $Id: ttable.S,v 1.18 1997/07/05 09:52:41 davem Exp $ * ttable.S: Sparc V9 Trap Table(s) with SpitFire extensions. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) */ +#include <linux/config.h> + .globl sparc64_ttable_tl0, sparc64_ttable_tl1 sparc64_ttable_tl0: @@ -18,7 +20,7 @@ tl0_privop: TRAP(do_privop) tl0_resv012: BTRAP(0x12) BTRAP(0x13) BTRAP(0x14) BTRAP(0x15) BTRAP(0x16) BTRAP(0x17) tl0_resv018: BTRAP(0x18) BTRAP(0x19) BTRAP(0x1a) BTRAP(0x1b) BTRAP(0x1c) BTRAP(0x1d) tl0_resv01e: BTRAP(0x1e) BTRAP(0x1f) -tl0_fpdis: TRAP(do_fpdis) +tl0_fpdis: TRAP_NOSAVE(do_fpdis) tl0_fpieee: TRAP(do_fpieee) tl0_fpother: TRAP(do_fpother) tl0_tof: TRAP(do_tof) @@ -124,7 +126,13 @@ tl0_resv15a: BTRAP(0x15a) BTRAP(0x15b) BTRAP(0x15c) BTRAP(0x15d) BTRAP(0x15e) tl0_resv15f: BTRAP(0x15f) BTRAP(0x160) BTRAP(0x161) BTRAP(0x162) BTRAP(0x163) tl0_resv164: BTRAP(0x164) BTRAP(0x165) BTRAP(0x166) BTRAP(0x167) BTRAP(0x168) tl0_resv169: BTRAP(0x169) BTRAP(0x16a) BTRAP(0x16b) BTRAP(0x16c) BTRAP(0x16d) -tl0_resv16e: BTRAP(0x16e) BTRAP(0x16f) BTRAP(0x170) BTRAP(0x171) BTRAP(0x172) +tl0_gsctx: TRAP(sparc64_get_context) TRAP(sparc64_set_context) +tl0_resv170: BTRAP(0x170) BTRAP(0x171) +#ifdef CONFIG_EC_FLUSH_TRAP + TRAP(cache_flush_trap) +#else + BTRAP(0x172) +#endif tl0_resv173: BTRAP(0x173) BTRAP(0x174) BTRAP(0x175) BTRAP(0x176) BTRAP(0x177) tl0_resv178: BTRAP(0x178) BTRAP(0x179) BTRAP(0x17a) BTRAP(0x17b) BTRAP(0x17c) tl0_resv17d: BTRAP(0x17d) BTRAP(0x17e) BTRAP(0x17f) @@ -151,7 +159,7 @@ tl1_resv012: BTRAPTL1(0x12) BTRAPTL1(0x13) BTRAPTL1(0x14) BTRAPTL1(0x15) tl1_resv016: BTRAPTL1(0x16) BTRAPTL1(0x17) BTRAPTL1(0x18) BTRAPTL1(0x19) tl1_resv01a: BTRAPTL1(0x1a) BTRAPTL1(0x1b) BTRAPTL1(0x1c) BTRAPTL1(0x1d) tl1_resv01e: BTRAPTL1(0x1e) BTRAPTL1(0x1f) -tl1_fpdis: TRAPTL1(do_fpdis_tl1) +tl1_fpdis: TRAP_NOSAVE(do_fpdis) tl1_fpieee: TRAPTL1(do_fpieee_tl1) tl1_fpother: TRAPTL1(do_fpother_tl1) tl1_tof: TRAPTL1(do_tof_tl1) diff --git a/arch/sparc64/kernel/unaligned.c b/arch/sparc64/kernel/unaligned.c new file mode 100644 index 000000000..f66889195 --- /dev/null +++ b/arch/sparc64/kernel/unaligned.c @@ -0,0 +1,517 @@ +/* $Id: unaligned.c,v 1.1 1997/07/18 06:26:45 ralf Exp $ + * unaligned.c: Unaligned load/store trap handling with special + * cases for the kernel to do them more quickly. + * + * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <asm/asi.h> +#include <asm/ptrace.h> +#include <asm/processor.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> + +/* #define DEBUG_MNA */ + +enum direction { + load, /* ld, ldd, ldh, ldsh */ + store, /* st, std, sth, stsh */ + both, /* Swap, ldstub, cas, ... */ + fpload, + fpstore, + invalid, +}; + +#ifdef DEBUG_MNA +static char *dirstrings[] = { + "load", "store", "both", "fpload", "fpstore", "invalid" +}; +#endif + +static inline enum direction decode_direction(unsigned int insn) +{ + unsigned long tmp = (insn >> 21) & 1; + + if(!tmp) + return load; + else { + switch ((insn>>19)&0xf) { + case 15: /* swap* */ + return both; + default: + return store; + } + } +} + +/* 16 = double-word, 8 = extra-word, 4 = word, 2 = half-word */ +static inline int decode_access_size(unsigned int insn) +{ + unsigned int tmp; + + if (((insn >> 19) & 0xf) == 14) + return 8; /* stx* */ + tmp = (insn >> 19) & 3; + if(!tmp) + return 4; + else if(tmp == 3) + return 16; /* ldd/std - Although it is actually 8 */ + else if(tmp == 2) + return 2; + else { + printk("Impossible unaligned trap. insn=%08x\n", insn); + die_if_kernel("Byte sized unaligned access?!?!", current->tss.kregs); + } +} + +static inline int decode_asi(unsigned int insn, struct pt_regs *regs) +{ + if (insn & 0x800000) { + if (insn & 0x2000) + return (unsigned char)(regs->tstate >> 24); /* %asi */ + else + return (unsigned char)(insn >> 5); /* imm_asi */ + } else + return ASI_P; +} + +/* 0x400000 = signed, 0 = unsigned */ +static inline int decode_signedness(unsigned int insn) +{ + return (insn & 0x400000); +} + +static inline void maybe_flush_windows(unsigned int rs1, unsigned int rs2, + unsigned int rd) +{ + if(rs2 >= 16 || rs1 >= 16 || rd >= 16) { + flushw_user(); + } +} + +static inline long sign_extend_imm13(long imm) +{ + return imm << 51 >> 51; +} + +static inline unsigned long fetch_reg(unsigned int reg, struct pt_regs *regs) +{ + struct reg_window *win; + + if(reg < 16) + return (!reg ? 0 : regs->u_regs[reg]); + + /* Ho hum, the slightly complicated case. */ + win = (struct reg_window *) regs->u_regs[UREG_FP]; + return win->locals[reg - 16]; /* yes, I know what this does... */ +} + +static inline unsigned long *fetch_reg_addr(unsigned int reg, struct pt_regs *regs) +{ + struct reg_window *win; + + if(reg < 16) + return ®s->u_regs[reg]; + win = (struct reg_window *) regs->u_regs[UREG_FP]; + return &win->locals[reg - 16]; +} + +static inline unsigned long compute_effective_address(struct pt_regs *regs, + unsigned int insn) +{ + unsigned int rs1 = (insn >> 14) & 0x1f; + unsigned int rs2 = insn & 0x1f; + unsigned int rd = (insn >> 25) & 0x1f; + + if(insn & 0x2000) { + maybe_flush_windows(rs1, 0, rd); + return (fetch_reg(rs1, regs) + sign_extend_imm13(insn)); + } else { + maybe_flush_windows(rs1, rs2, rd); + return (fetch_reg(rs1, regs) + fetch_reg(rs2, regs)); + } +} + +/* This is just to make gcc think panic does return... */ +static void unaligned_panic(char *str) +{ + panic(str); +} + +#define do_integer_load(dest_reg, size, saddr, is_signed, asi, errh) ({ \ +__asm__ __volatile__ ( \ + "wr %4, 0, %%asi\n\t" \ + "cmp %1, 8\n\t" \ + "bge,pn %%icc, 9f\n\t" \ + " cmp %1, 4\n\t" \ + "be,pt %%icc, 6f\n" \ +"4:\t" " lduba [%2] %%asi, %%l1\n" \ +"5:\t" "lduba [%2 + 1] %%asi, %%l2\n\t" \ + "sll %%l1, 8, %%l1\n\t" \ + "brz,pt %3, 3f\n\t" \ + " add %%l1, %%l2, %%l1\n\t" \ + "sllx %%l1, 48, %%l1\n\t" \ + "srax %%l1, 48, %%l1\n" \ +"3:\t" "ba,pt %%xcc, 0f\n\t" \ + " stx %%l1, [%0]\n" \ +"6:\t" "lduba [%2 + 1] %%asi, %%l2\n\t" \ + "sll %%l1, 24, %%l1\n" \ +"7:\t" "lduba [%2 + 2] %%asi, %%g7\n\t" \ + "sll %%l2, 16, %%l2\n" \ +"8:\t" "lduba [%2 + 3] %%asi, %%g1\n\t" \ + "sll %%g7, 8, %%g7\n\t" \ + "or %%l1, %%l2, %%l1\n\t" \ + "or %%g7, %%g1, %%g7\n\t" \ + "or %%l1, %%g7, %%l1\n\t" \ + "brnz,a,pt %3, 3f\n\t" \ + " sra %%l1, 0, %%l1\n" \ +"3:\t" "ba,pt %%xcc, 0f\n\t" \ + " stx %%l1, [%0]\n" \ +"9:\t" "lduba [%2] %%asi, %%l1\n" \ +"10:\t" "lduba [%2 + 1] %%asi, %%l2\n\t" \ + "sllx %%l1, 56, %%l1\n" \ +"11:\t" "lduba [%2 + 2] %%asi, %%g7\n\t" \ + "sllx %%l2, 48, %%l2\n" \ +"12:\t" "lduba [%2 + 3] %%asi, %%g1\n\t" \ + "sllx %%g7, 40, %%g7\n\t" \ + "sllx %%g1, 32, %%g1\n\t" \ + "or %%l1, %%l2, %%l1\n\t" \ + "or %%g7, %%g1, %%g7\n" \ +"13:\t" "lduba [%2 + 4] %%asi, %%l2\n\t" \ + "or %%l1, %%g7, %%g7\n" \ +"14:\t" "lduba [%2 + 5] %%asi, %%g1\n\t" \ + "sllx %%l2, 24, %%l2\n" \ +"15:\t" "lduba [%2 + 6] %%asi, %%l1\n\t" \ + "sllx %%g1, 16, %%g1\n\t" \ + "or %%g7, %%l2, %%g7\n" \ +"16:\t" "lduba [%2 + 7] %%asi, %%l2\n\t" \ + "sllx %%l1, 8, %%l1\n\t" \ + "or %%g7, %%g1, %%g7\n\t" \ + "or %%l1, %%l2, %%l1\n\t" \ + "or %%g7, %%l1, %%g7\n\t" \ + "cmp %1, 8\n\t" \ + "be,a,pt %%icc, 0f\n\t" \ + " stx %%g7, [%0]\n\t" \ + "srlx %%g7, 32, %%l1\n\t" \ + "sra %%g7, 0, %%g7\n\t" \ + "stx %%l1, [%0]\n\t" \ + "stx %%g7, [%0 + 8]\n" \ +"0:\n\n\t" \ + ".section __ex_table\n\t" \ + ".xword 4b, " #errh "\n\t" \ + ".xword 5b, " #errh "\n\t" \ + ".xword 6b, " #errh "\n\t" \ + ".xword 7b, " #errh "\n\t" \ + ".xword 8b, " #errh "\n\t" \ + ".xword 9b, " #errh "\n\t" \ + ".xword 10b, " #errh "\n\t" \ + ".xword 11b, " #errh "\n\t" \ + ".xword 12b, " #errh "\n\t" \ + ".xword 13b, " #errh "\n\t" \ + ".xword 14b, " #errh "\n\t" \ + ".xword 15b, " #errh "\n\t" \ + ".xword 16b, " #errh "\n\n\t" \ + ".previous\n\t" \ + : : "r" (dest_reg), "r" (size), "r" (saddr), "r" (is_signed), "r" (asi) \ + : "l1", "l2", "g7", "g1", "cc"); \ +}) + +#define store_common(dst_addr, size, src_val, asi, errh) ({ \ +__asm__ __volatile__ ( \ + "wr %3, 0, %%asi\n\t" \ + "ldx [%2], %%l1\n" \ + "cmp %1, 2\n\t" \ + "be,pn %%icc, 2f\n\t" \ + " cmp %1, 4\n\t" \ + "be,pt %%icc, 1f\n\t" \ + " srlx %%l1, 24, %%l2\n\t" \ + "srlx %%l1, 56, %%g1\n\t" \ + "srlx %%l1, 48, %%g7\n" \ +"4:\t" "stba %%g1, [%0] %%asi\n\t" \ + "srlx %%l1, 40, %%g1\n" \ +"5:\t" "stba %%g7, [%0 + 1] %%asi\n\t" \ + "srlx %%l1, 32, %%g7\n" \ +"6:\t" "stba %%g1, [%0 + 2] %%asi\n" \ +"7:\t" "stba %%g7, [%0 + 3] %%asi\n\t" \ + "srlx %%l1, 16, %%g1\n" \ +"8:\t" "stba %%l2, [%0 + 4] %%asi\n\t" \ + "srlx %%l1, 8, %%g7\n" \ +"9:\t" "stba %%g1, [%0 + 5] %%asi\n" \ +"10:\t" "stba %%g7, [%0 + 6] %%asi\n\t" \ + "ba,pt %%xcc, 0f\n" \ +"11:\t" " stba %%l1, [%0 + 7] %%asi\n" \ +"1:\t" "srl %%l1, 16, %%g7\n" \ +"12:\t" "stba %%l2, [%0] %%asi\n\t" \ + "srl %%l1, 8, %%l2\n" \ +"13:\t" "stba %%g7, [%0 + 1] %%asi\n" \ +"14:\t" "stba %%l2, [%0 + 2] %%asi\n\t" \ + "ba,pt %%xcc, 0f\n" \ +"15:\t" " stba %%l1, [%0 + 3] %%asi\n" \ +"2:\t" "srl %%l1, 8, %%l2\n" \ +"16:\t" "stba %%l2, [%0] %%asi\n" \ +"17:\t" "stba %%l1, [%0 + 1] %%asi\n" \ +"0:\n\n\t" \ + ".section __ex_table\n\t" \ + ".xword 4b, " #errh "\n\t" \ + ".xword 5b, " #errh "\n\t" \ + ".xword 6b, " #errh "\n\t" \ + ".xword 7b, " #errh "\n\t" \ + ".xword 8b, " #errh "\n\t" \ + ".xword 9b, " #errh "\n\t" \ + ".xword 10b, " #errh "\n\t" \ + ".xword 11b, " #errh "\n\t" \ + ".xword 12b, " #errh "\n\t" \ + ".xword 13b, " #errh "\n\t" \ + ".xword 14b, " #errh "\n\t" \ + ".xword 15b, " #errh "\n\t" \ + ".xword 16b, " #errh "\n\t" \ + ".xword 17b, " #errh "\n\n\t" \ + ".previous\n\t" \ + : : "r" (dst_addr), "r" (size), "r" (src_val), "r" (asi) \ + : "l1", "l2", "g7", "g1", "cc"); \ +}) + +#define do_integer_store(reg_num, size, dst_addr, regs, asi, errh) ({ \ + unsigned long zero = 0; \ + unsigned long *src_val = &zero; \ + \ + if (size == 16) { \ + size = 8; \ + zero = (((long)(reg_num ? \ + (unsigned)fetch_reg(reg_num, regs) : 0)) << 32) | \ + (unsigned)fetch_reg(reg_num + 1, regs); \ + } else if (reg_num) src_val = fetch_reg_addr(reg_num, regs); \ + store_common(dst_addr, size, src_val, asi, errh); \ +}) + +/* XXX Need to capture/release other cpu's for SMP around this. */ +#define do_atomic(srcdest_reg, mem, errh) ({ \ + unsigned long flags, tmp; \ + \ + save_and_cli(flags); \ + tmp = *srcdest_reg; \ + do_integer_load(srcdest_reg, 4, mem, 0, errh); \ + store_common(mem, 4, &tmp, errh); \ + restore_flags(flags); \ +}) + +static inline void advance(struct pt_regs *regs) +{ + regs->tpc = regs->tnpc; + regs->tnpc += 4; +} + +static inline int floating_point_load_or_store_p(unsigned int insn) +{ + return (insn >> 24) & 1; +} + +static inline int ok_for_kernel(unsigned int insn) +{ + return !floating_point_load_or_store_p(insn); +} + +void kernel_mna_trap_fault(struct pt_regs *regs, unsigned int insn) __asm__ ("kernel_mna_trap_fault"); + +void kernel_mna_trap_fault(struct pt_regs *regs, unsigned int insn) +{ + unsigned long g2 = regs->u_regs [UREG_G2]; + unsigned long fixup = search_exception_table (regs->tpc, &g2); + + if (!fixup) { + unsigned long address = compute_effective_address(regs, insn); + if(address < PAGE_SIZE) { + printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference in mna handler"); + } else + printk(KERN_ALERT "Unable to handle kernel paging request in mna handler"); + printk(KERN_ALERT " at virtual address %016lx\n",address); + printk(KERN_ALERT "current->mm->context = %016lx\n", + (unsigned long) current->mm->context); + printk(KERN_ALERT "current->mm->pgd = %016lx\n", + (unsigned long) current->mm->pgd); + die_if_kernel("Oops", regs); + /* Not reached */ + } + regs->tpc = fixup; + regs->tnpc = regs->tpc + 4; + regs->u_regs [UREG_G2] = g2; +} + +asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn) +{ + enum direction dir = decode_direction(insn); + int size = decode_access_size(insn); + + lock_kernel(); + if(!ok_for_kernel(insn) || dir == both) { + printk("Unsupported unaligned load/store trap for kernel at <%016lx>.\n", + regs->tpc); + unaligned_panic("Wheee. Kernel does fpu/atomic unaligned load/store."); + + __asm__ __volatile__ ("\n" +"kernel_unaligned_trap_fault:\n\t" + "mov %0, %%o0\n\t" + "call kernel_mna_trap_fault\n\t" + " mov %1, %%o1\n\t" + : + : "r" (regs), "r" (insn) + : "o0", "o1", "o2", "o3", "o4", "o5", "o7", + "g1", "g2", "g3", "g4", "g5", "g7", "cc"); + } else { + unsigned long addr = compute_effective_address(regs, insn); + +#ifdef DEBUG_MNA + printk("KMNA: pc=%016lx [dir=%s addr=%016lx size=%d] retpc[%016lx]\n", + regs->tpc, dirstrings[dir], addr, size, regs->u_regs[UREG_RETPC]); +#endif + switch(dir) { + case load: + do_integer_load(fetch_reg_addr(((insn>>25)&0x1f), regs), + size, (unsigned long *) addr, + decode_signedness(insn), decode_asi(insn, regs), + kernel_unaligned_trap_fault); + break; + + case store: + do_integer_store(((insn>>25)&0x1f), size, + (unsigned long *) addr, regs, + decode_asi(insn, regs), + kernel_unaligned_trap_fault); + break; +#if 0 /* unsupported */ + case both: + do_atomic(fetch_reg_addr(((insn>>25)&0x1f), regs), + (unsigned long *) addr, + kernel_unaligned_trap_fault); + break; +#endif + default: + panic("Impossible kernel unaligned trap."); + /* Not reached... */ + } + advance(regs); + } + unlock_kernel(); +} + +#if 0 /* XXX: Implement user mna some day */ +static inline int ok_for_user(struct pt_regs *regs, unsigned int insn, + enum direction dir) +{ + unsigned int reg; + int retval, check = (dir == load) ? VERIFY_READ : VERIFY_WRITE; + int size = ((insn >> 19) & 3) == 3 ? 8 : 4; + + if((regs->pc | regs->npc) & 3) + return 0; + + /* Must verify_area() in all the necessary places. */ +#define WINREG_ADDR(regnum) ((void *)(((unsigned long *)regs->u_regs[UREG_FP])+(regnum))) + retval = 0; + reg = (insn >> 25) & 0x1f; + if(reg >= 16) { + retval = verify_area(check, WINREG_ADDR(reg - 16), size); + if(retval) + return retval; + } + reg = (insn >> 14) & 0x1f; + if(reg >= 16) { + retval = verify_area(check, WINREG_ADDR(reg - 16), size); + if(retval) + return retval; + } + if(!(insn & 0x2000)) { + reg = (insn & 0x1f); + if(reg >= 16) { + retval = verify_area(check, WINREG_ADDR(reg - 16), size); + if(retval) + return retval; + } + } + return retval; +#undef WINREG_ADDR +} + +void user_mna_trap_fault(struct pt_regs *regs, unsigned int insn) __asm__ ("user_mna_trap_fault"); + +void user_mna_trap_fault(struct pt_regs *regs, unsigned int insn) +{ + current->tss.sig_address = regs->pc; + current->tss.sig_desc = SUBSIG_PRIVINST; + send_sig(SIGBUS, current, 1); +} + +asmlinkage void user_unaligned_trap(struct pt_regs *regs, unsigned int insn) +{ + enum direction dir; + + lock_kernel(); + if(!(current->tss.flags & SPARC_FLAG_UNALIGNED) || + (((insn >> 30) & 3) != 3)) + goto kill_user; + dir = decode_direction(insn); + if(!ok_for_user(regs, insn, dir)) { + goto kill_user; + } else { + int size = decode_access_size(insn); + unsigned long addr; + + if(floating_point_load_or_store_p(insn)) { + printk("User FPU load/store unaligned unsupported.\n"); + goto kill_user; + } + + addr = compute_effective_address(regs, insn); + switch(dir) { + case load: + do_integer_load(fetch_reg_addr(((insn>>25)&0x1f), regs), + size, (unsigned long *) addr, + decode_signedness(insn), + user_unaligned_trap_fault); + break; + + case store: + do_integer_store(((insn>>25)&0x1f), size, + (unsigned long *) addr, regs, + user_unaligned_trap_fault); + break; + + case both: + do_atomic(fetch_reg_addr(((insn>>25)&0x1f), regs), + (unsigned long *) addr, + user_unaligned_trap_fault); + break; + + default: + unaligned_panic("Impossible user unaligned trap."); + + __asm__ __volatile__ ("\n" +"user_unaligned_trap_fault:\n\t" + "mov %0, %%o0\n\t" + "call user_mna_trap_fault\n\t" + " mov %1, %%o1\n\t" + : + : "r" (regs), "r" (insn) + : "o0", "o1", "o2", "o3", "o4", "o5", "o7", + "g1", "g2", "g3", "g4", "g5", "g7", "cc"); + goto out; + } + advance(regs); + goto out; + } + +kill_user: + current->tss.sig_address = regs->pc; + current->tss.sig_desc = SUBSIG_PRIVINST; + send_sig(SIGBUS, current, 1); +out: + unlock_kernel(); +} +#endif diff --git a/arch/sparc64/kernel/winfixup.S b/arch/sparc64/kernel/winfixup.S index 2ac19a440..f2c714eae 100644 --- a/arch/sparc64/kernel/winfixup.S +++ b/arch/sparc64/kernel/winfixup.S @@ -1,4 +1,4 @@ -/* $Id: winfixup.S,v 1.8 1997/06/02 06:33:35 davem Exp $ +/* $Id: winfixup.S,v 1.16 1997/07/13 20:02:42 davem Exp $ * * winfixup.S: Handle cases where user stack pointer is found to be bogus. * @@ -31,6 +31,7 @@ fill_fixup: rdpr %tstate, %g1 andcc %g1, TSTATE_PRIV, %g0 + clr %g4 be,pt %xcc, window_scheisse_from_user_common and %g1, TSTATE_CWP, %g1 @@ -53,25 +54,26 @@ fill_fixup: rdpr %wstate, %g2 ! Grab user mode wstate. wrpr %g1, %cwp ! Get into the right window. sll %g2, 3, %g2 ! NORMAL-->OTHER - wrpr %g0, 0x0, %canrestore ! Standard etrap stuff. + wrpr %g0, 0x0, %canrestore ! Standard etrap stuff. + wr %g0, 0x0, %fprs ! zap FPU just in case... wrpr %g2, 0x0, %wstate ! This must be consistant. wrpr %g0, 0x0, %otherwin ! We know this. - sethi %uhi(KERNBASE), %g2 ! Set this up - sllx %g2, 32, %g2 ! for the iflush mov PRIMARY_CONTEXT, %g1 ! Change contexts... stxa %g0, [%g1] ASI_DMMU ! Back into the nucleus. - flush %g2 ! Flush instruction buffers + flush %g6 ! Flush instruction buffers rdpr %pstate, %l1 ! Prepare to change globals. - mov %g4, %o5 ! Setup args for - mov %g5, %o4 ! final call to do_sparc64_fault. + mov %g6, %o7 ! Get current. + mov %g5, %l5 ! Fault address + clr %l4 ! It was a load, not a store wrpr %g0, 0x0, %tl ! Out of trap levels. - wrpr %l1, (PSTATE_IE | PSTATE_AG), %pstate - sethi %uhi(KERNBASE), %g4 ! Restore med-any global reg. - rd %pic, %g6 ! Get current as well. + wrpr %l1, (PSTATE_IE | PSTATE_AG | PSTATE_RMO), %pstate + sethi %uhi(PAGE_OFFSET), %g4 ! Prepare page_offset global reg + mov %o7, %g6 b,pt %xcc, window_scheisse_merge ! And merge. - sllx %g4, 32, %g4 ! Finish med-any reg setup. + + sllx %g4, 32, %g4 ! and finish it... /* Be very careful about usage of the alternate globals here. * You cannot touch %g4/%g5 as that has the fault information @@ -82,17 +84,16 @@ fill_fixup: * do not touch %g7 or %g2 so we handle the two cases fine. */ spill_fixup: - rd %pic, %g1 - ldx [%g1 + AOFF_task_tss + AOFF_thread_flags], %g6 - andcc %g6, SPARC_FLAG_32BIT, %g0 - ldx [%g1 + AOFF_task_tss + AOFF_thread_w_saved], %g6 - sll %g6, 3, %g3 - add %g1, %g3, %g3 + ld [%g6 + AOFF_task_tss + AOFF_thread_flags], %g1 + andcc %g1, SPARC_FLAG_32BIT, %g0 + ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %g1 + sll %g1, 3, %g3 + add %g6, %g3, %g3 stx %sp, [%g3 + AOFF_task_tss + AOFF_thread_rwbuf_stkptrs] - sll %g6, 7, %g3 + sll %g1, 7, %g3 bne,pt %xcc, 1f - add %g1, %g3, %g3 + add %g6, %g3, %g3 stx %l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00] stx %l1, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08] stx %l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10] @@ -110,43 +111,45 @@ spill_fixup: stx %i5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x68] stx %i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x70] - stx %i7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x78] b,pt %xcc, 2f - add %g6, 1, %g6 -1: std %l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00] - std %l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08] - std %l4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10] - std %l6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x18] - - std %i0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x20] - std %i2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x28] - std %i4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x30] - std %i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x38] - add %g6, 1, %g6 -2: stx %g6, [%g1 + AOFF_task_tss + AOFF_thread_w_saved] + stx %i7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x78] +1: stw %l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00] + stw %l1, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x04] + stw %l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08] + stw %l3, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x0c] + stw %l4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10] + + stw %l5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x14] + stw %l6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x18] + stw %l7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x1c] + stw %i0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x20] + stw %i1, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x24] + stw %i2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x28] + stw %i3, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x2c] + stw %i4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x30] + + stw %i5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x34] + stw %i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x38] + stw %i7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x3c] +2: add %g1, 1, %g1 + stx %g1, [%g6 + AOFF_task_tss + AOFF_thread_w_saved] rdpr %tstate, %g1 - nop - andcc %g1, TSTATE_PRIV, %g0 saved + and %g1, TSTATE_CWP, %g1 be,a,pn %xcc, window_scheisse_from_user_common or %g4, 0x4, %g4 ! we know it was a write retry window_scheisse_from_user_common: - nop wrpr %g1, %cwp - ba,pt %xcc, etrap rd %pc, %g7 - mov %l5, %o4 - mov %l4, %o5 window_scheisse_merge: - srlx %o4, PAGE_SHIFT, %o3 - clr %o1 - sllx %o3, PAGE_SHIFT, %o3 - and %o5, 0x4, %o2 + srlx %l5, PAGE_SHIFT, %o1 + and %l4, 0x4, %o2 + sllx %o1, PAGE_SHIFT, %o1 call do_sparc64_fault add %sp, STACK_BIAS + REGWIN_SZ, %o0 ba,pt %xcc, rtrap @@ -154,6 +157,7 @@ window_scheisse_merge: winfix_trampoline: andn %g3, 0x7f, %g3 add %g3, 0x7c, %g3 + wrpr %g3, %tnpc done @@ -174,32 +178,31 @@ fill_fixup_mna: wrpr %g0, 0x0, %canrestore ! Standard etrap stuff. wrpr %g2, 0x0, %wstate ! This must be consistant. wrpr %g0, 0x0, %otherwin ! We know this. - sethi %uhi(KERNBASE), %g2 ! Set this up - sllx %g2, 32, %g2 ! for the iflush mov PRIMARY_CONTEXT, %g1 ! Change contexts... stxa %g0, [%g1] ASI_DMMU ! Back into the nucleus. - flush %g2 ! Flush instruction buffers + flush %g6 ! Flush instruction buffers rdpr %pstate, %l1 ! Prepare to change globals. mov %g4, %o5 ! Setup args for mov %g5, %o4 ! final call to do_sparc64_fault. + mov %g6, %o7 ! Stash away current. wrpr %g0, 0x0, %tl ! Out of trap levels. - wrpr %l1, (PSTATE_IE | PSTATE_AG), %pstate - sethi %uhi(KERNBASE), %g4 ! Restore med-any global reg. - rd %pic, %g6 ! Get current as well. + wrpr %l1, (PSTATE_IE | PSTATE_AG | PSTATE_RMO), %pstate + sethi %uhi(PAGE_OFFSET), %g4 ! Set page_offset global reg. + mov %o7, %g6 ! Get current back. b,pt %xcc, window_mna_merge ! And merge. - sllx %g4, 32, %g4 ! Finish med-any reg setup. + sllx %g4, 32, %g4 ! Finish it. + spill_fixup_mna: - rd %pic, %g1 - ldx [%g1 + AOFF_task_tss + AOFF_thread_flags], %g6 - andcc %g6, SPARC_FLAG_32BIT, %g0 - ldx [%g1 + AOFF_task_tss + AOFF_thread_w_saved], %g6 - sll %g6, 3, %g3 - add %g1, %g3, %g3 + ld [%g6 + AOFF_task_tss + AOFF_thread_flags], %g1 + andcc %g1, SPARC_FLAG_32BIT, %g0 + ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %g1 + sll %g1, 3, %g3 + add %g6, %g3, %g3 stx %sp, [%g3 + AOFF_task_tss + AOFF_thread_rwbuf_stkptrs] - sll %g6, 7, %g3 + sll %g1, 7, %g3 bne,pt %xcc, 1f - add %g1, %g3, %g3 + add %g6, %g3, %g3 stx %l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00] stx %l1, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08] stx %l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10] @@ -219,7 +222,7 @@ spill_fixup_mna: stx %i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x70] stx %i7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x78] b,pt %xcc, 2f - add %g6, 1, %g6 + add %g1, 1, %g1 1: std %l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00] std %l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08] std %l4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10] @@ -229,8 +232,8 @@ spill_fixup_mna: std %i2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x28] std %i4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x30] std %i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x38] - add %g6, 1, %g6 -2: stx %g6, [%g1 + AOFF_task_tss + AOFF_thread_w_saved] + add %g1, 1, %g1 +2: stx %g1, [%g6 + AOFF_task_tss + AOFF_thread_w_saved] rdpr %tstate, %g1 nop diff --git a/arch/sparc64/lib/Makefile b/arch/sparc64/lib/Makefile index 56c506507..3da21c606 100644 --- a/arch/sparc64/lib/Makefile +++ b/arch/sparc64/lib/Makefile @@ -1,55 +1,25 @@ -# $Id: Makefile,v 1.7 1997/04/07 18:57:05 jj Exp $ +# $Id: Makefile,v 1.13 1997/07/16 10:12:03 jj Exp $ # Makefile for Sparc library files.. # CFLAGS := $(CFLAGS) -ansi -OBJS = memset.o blockops.o locks.o memcpy.o strlen.o strncmp.o \ +OBJS = blockops.o locks.o strlen.o strncmp.o \ memscan.o strncpy_from_user.o strlen_user.o memcmp.o checksum.o \ - copy_to_user.o copy_from_user.o + VIScopy.o VISbzero.o VISmemset.o VIScsum.o lib.a: $(OBJS) $(AR) rcs lib.a $(OBJS) sync -blockops.o: blockops.S - $(CC) -ansi -c -o blockops.o blockops.S +VIScopy.o: VIScopy.S VIS.h +VISbzero.o: VISbzero.S VIS.h -memset.o: memset.S - $(CC) -D__ASSEMBLY__ -ansi -c -o memset.o memset.S +.S.s: + $(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s -copy_to_user.o: copy_to_user.S - $(CC) -D__ASSEMBLY__ -ansi -c -o copy_to_user.o copy_to_user.S - -copy_from_user.o: copy_from_user.S - $(CC) -D__ASSEMBLY__ -ansi -c -o copy_from_user.o copy_from_user.S - -memcpy.o: memcpy.S - $(CC) -D__ASSEMBLY__ -ansi -c -o memcpy.o memcpy.S - -strlen.o: strlen.S - $(CC) -D__ASSEMBLY__ -ansi -c -o strlen.o strlen.S - -strncmp.o: strncmp.S - $(CC) -D__ASSEMBLY__ -ansi -c -o strncmp.o strncmp.S - -memcmp.o: memcmp.S - $(CC) -D__ASSEMBLY__ -ansi -c -o memcmp.o memcmp.S - -locks.o: locks.S - $(CC) -D__ASSEMBLY__ -ansi -c -o locks.o locks.S - -checksum.o: checksum.S - $(CC) -D__ASSEMBLY__ -ansi -c -o checksum.o checksum.S - -memscan.o: memscan.S - $(CC) -D__ASSEMBLY__ -ansi -c -o memscan.o memscan.S - -strncpy_from_user.o: strncpy_from_user.S - $(CC) -D__ASSEMBLY__ -ansi -c -o strncpy_from_user.o strncpy_from_user.S - -strlen_user.o: strlen_user.S - $(CC) -D__ASSEMBLY__ -ansi -c -o strlen_user.o strlen_user.S +.S.o: + $(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o dep: diff --git a/arch/sparc64/lib/VIS.h b/arch/sparc64/lib/VIS.h new file mode 100644 index 000000000..45bc870a4 --- /dev/null +++ b/arch/sparc64/lib/VIS.h @@ -0,0 +1,113 @@ +/* $Id: VIS.h,v 1.1 1997/07/18 06:26:48 ralf Exp $ + * VIS.h: High speed copy/clear operations utilizing the UltraSparc + * Visual Instruction Set. + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1996, 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + + /* VIS code can be used for numerous copy/set operation variants. + * It can be made to work in the kernel, one single instance, + * for all of memcpy, copy_to_user, and copy_from_user by setting + * the ASI src/dest globals correctly. Furthermore it can + * be used for kernel-->kernel page copies as well, a hook label + * is put in here just for this purpose. + * + * For userland, compiling this without __KERNEL__ defined makes + * it work just fine as a generic libc bcopy and memcpy. + * If for userland it is compiled with a 32bit gcc (but you need + * -Wa,-Av9a), the code will just rely on lower 32bits of + * IEU registers, if you compile it with 64bit gcc (ie. define + * __sparc_v9__), the code will use full 64bit. + */ + +#ifndef __VIS_H +#define __VIS_H + +#ifdef __KERNEL__ +#include <asm/head.h> +#include <asm/asi.h> +#else +#define ASI_P 0x80 /* Primary, implicit */ +#define ASI_S 0x81 /* Secondary, implicit */ +#define ASI_BLK_COMMIT_P 0xe0 /* Primary, blk store commit */ +#define ASI_BLK_COMMIT_S 0xe1 /* Secondary, blk store commit */ +#define ASI_BLK_P 0xf0 /* Primary, blk ld/st */ +#define ASI_BLK_S 0xf1 /* Secondary, blk ld/st */ +#define FPRS_FEF 0x04 +#endif + + /* I'm telling you, they really did this chip right. + * Perhaps the SunSoft folks should visit some of the + * people in Sun Microelectronics and start some brain + * cell exchange program... + */ +#define ASI_BLK_XOR (ASI_P ^ ASI_BLK_P) + +#define asi_src %o3 +#define asi_dest %o4 + +#ifdef __KERNEL__ +#define ASI_SETSRC_BLK wr asi_src, 0, %asi; +#define ASI_SETSRC_NOBLK wr asi_src, ASI_BLK_XOR, %asi; +#define ASI_SETDST_BLK wr asi_dest, 0, %asi; +#define ASI_SETDST_NOBLK wr asi_dest, ASI_BLK_XOR, %asi; +#define ASIBLK %asi +#define ASINORMAL %asi +#define LDUB lduba +#define LDUH lduha +#define LDUW lduwa +#define LDX ldxa +#define LDD ldda +#define LDDF ldda +#define LDBLK ldda +#define STB stba +#define STH stha +#define STW stwa +#define STD stda +#define STX stxa +#define STDF stda +#define STBLK stda +#else +#define ASI_SETSRC_BLK +#define ASI_SETSRC_NOBLK +#define ASI_SETDST_BLK +#define ASI_SETDST_NOBLK +#define ASI_SETDST_SPECIAL +#define ASIBLK %asi +#define ASINORMAL +#define LDUB ldub +#define LDUH lduh +#define LDUW lduw +#define LDD ldd +#define LDX ldx +#define LDDF ldd +#define LDBLK ldda +#define STB stb +#define STH sth +#define STW stw +#define STD std +#define STX stx +#define STDF std +#define STBLK stda +#endif + +#ifdef __KERNEL__ + +#define REGS_64BIT + +#else + +#ifndef REGS_64BIT +#ifdef __sparc_v9__ +#define REGS_64BIT +#endif +#endif + +#endif + +#ifndef REGS_64BIT +#define xcc icc +#endif + +#endif diff --git a/arch/sparc64/lib/VISbzero.S b/arch/sparc64/lib/VISbzero.S new file mode 100644 index 000000000..3c86861fd --- /dev/null +++ b/arch/sparc64/lib/VISbzero.S @@ -0,0 +1,246 @@ +/* $Id: VISbzero.S,v 1.1 1997/07/18 06:26:48 ralf Exp $ + * VISbzero.S: High speed clear operations utilizing the UltraSparc + * Visual Instruction Set. + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1996, 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#include "VIS.h" + +#ifdef __KERNEL__ +#define EXN(x,y,a,b,z) \ +98: x,y; \ + .section .fixup; \ + .align 4; \ +99: ba VISbzerofixup_ret##z; \ + a, b, %o0; \ + .section __ex_table; \ + .align 8; \ + .xword 98b, 99b; \ + .text; \ + .align 4; +#define EXC(x,y,a,b,c...) \ +98: x,y; \ + .section .fixup; \ + .align 4; \ +99: c; \ + ba VISbzerofixup_ret0; \ + a, b, %o0; \ + .section __ex_table; \ + .align 8; \ + .xword 98b, 99b; \ + .text; \ + .align 4; +#define EXO1(x,y) \ +98: x,y; \ + .section __ex_table; \ + .align 8; \ + .xword 98b, VISbzerofixup_reto1; \ + .text; \ + .align 4; +#define EX(x,y,a,b) EXN(x,y,a,b,0) +#define EX1(x,y,a,b) EXN(x,y,a,b,1) +#define EX2(x,y,a,b) EXN(x,y,a,b,2) +#define EXT(start,end,handler) \ + .section __ex_table; \ + .align 8; \ + .xword start, 0, end, handler; \ + .text; \ + .align 4 +#else +#define EX(x,y,a,b) x,y +#define EX1(x,y,a,b) x,y +#define EX2(x,y,a,b) x,y +#define EXC(x,y,a,b,c...) x,y +#define EXO1(x,y) x,y +#define EXT(a,b,c) +#endif + +#define ZERO_BLOCKS(base, offset, source) \ + STX source, [base - offset - 0x38] ASINORMAL; \ + STX source, [base - offset - 0x30] ASINORMAL; \ + STX source, [base - offset - 0x28] ASINORMAL; \ + STX source, [base - offset - 0x20] ASINORMAL; \ + STX source, [base - offset - 0x18] ASINORMAL; \ + STX source, [base - offset - 0x10] ASINORMAL; \ + STX source, [base - offset - 0x08] ASINORMAL; \ + STX source, [base - offset - 0x00] ASINORMAL; + +#ifdef __KERNEL__ +#define RETL clr %o0 +#else +#define RETL mov %g3, %o0 +#endif + + /* Well, bzero is a lot easier to get right than bcopy... */ +#ifdef __KERNEL__ + .section __ex_table,#alloc + .section .fixup,#alloc,#execinstr +#endif + .text + .align 32 +#ifdef __KERNEL__ + .globl __bzero, __bzero_noasi +__bzero: + wr %g0, ASI_P, %asi ! LSU Group +__bzero_noasi: +#else + .globl bzero +bzero_private: +bzero: +#ifndef REGS_64BIT + srl %o1, 0, %o1 +#endif + mov %o0, %g3 +#endif + cmp %o1, 7 + bleu,pn %xcc, 17f + andcc %o0, 3, %o2 + be,a,pt %xcc, 4f + andcc %o0, 4, %g0 + cmp %o2, 3 + be,pn %xcc, 2f + EXO1(STB %g0, [%o0 + 0x00] ASINORMAL) + cmp %o2, 2 + be,pt %xcc, 2f + EX(STB %g0, [%o0 + 0x01] ASINORMAL, sub %o1, 1) + EX(STB %g0, [%o0 + 0x02] ASINORMAL, sub %o1, 2) +2: sub %o2, 4, %o2 + sub %o0, %o2, %o0 + add %o1, %o2, %o1 + andcc %o0, 4, %g0 +4: be,pt %xcc, 2f + cmp %o1, 128 + EXO1(STW %g0, [%o0] ASINORMAL) + sub %o1, 4, %o1 + add %o0, 4, %o0 +2: blu,pn %xcc, 9f + andcc %o0, 0x38, %o2 + be,pn %icc, 6f + mov 64, %o5 + andcc %o0, 8, %g0 + be,pn %icc, 1f + sub %o5, %o2, %o5 + EX(STX %g0, [%o0] ASINORMAL, sub %o1, 0) + add %o0, 8, %o0 +1: andcc %o5, 16, %g0 + be,pn %icc, 1f + sub %o1, %o5, %o1 + EX1(STX %g0, [%o0] ASINORMAL, add %g0, 0) + EX1(STX %g0, [%o0 + 8] ASINORMAL, sub %g0, 8) + add %o0, 16, %o0 +1: andcc %o5, 32, %g0 + be,pn %icc, 7f + andncc %o1, 0x3f, %o3 + EX(STX %g0, [%o0] ASINORMAL, add %o1, 32) + EX(STX %g0, [%o0 + 8] ASINORMAL, add %o1, 24) + EX(STX %g0, [%o0 + 16] ASINORMAL, add %o1, 16) + EX(STX %g0, [%o0 + 24] ASINORMAL, add %o1, 8) + add %o0, 32, %o0 +6: andncc %o1, 0x3f, %o3 +7: be,pn %xcc, 9f +#ifdef __KERNEL__ + rd %asi, %g7 + wr %g0, FPRS_FEF, %fprs + wr %g7, ASI_BLK_XOR, %asi +#else + wr %g0, ASI_BLK_P, %asi +#endif + membar #StoreStore | #LoadStore + fzero %f0 + andcc %o3, 0xc0, %o2 + and %o1, 0x3f, %o1 + fzero %f2 + andn %o3, 0xff, %o3 + faddd %f0, %f2, %f4 + fmuld %f0, %f2, %f6 + cmp %o2, 64 + faddd %f0, %f2, %f8 + fmuld %f0, %f2, %f10 + faddd %f0, %f2, %f12 + brz,pn %o2, 10f + fmuld %f0, %f2, %f14 + be,pn %icc, 2f + EXC(STBLK %f0, [%o0 + 0x00] ASIBLK, add %o3, %o2, add %o2, %o1, %o2) + cmp %o2, 128 + be,pn %icc, 2f + EXC(STBLK %f0, [%o0 + 0x40] ASIBLK, add %o3, %o2, add %o2, %o1, %o2; sub %o2, 64, %o2) + EXC(STBLK %f0, [%o0 + 0x80] ASIBLK, add %o3, %o2, add %o2, %o1, %o2; sub %o2, 128, %o2) +2: brz,pn %o3, 12f + add %o0, %o2, %o0 +10: EX(STBLK %f0, [%o0 + 0x00] ASIBLK, add %o3, %o1) + EXC(STBLK %f0, [%o0 + 0x40] ASIBLK, add %o3, %o1, sub %o1, 64, %o1) + EXC(STBLK %f0, [%o0 + 0x80] ASIBLK, add %o3, %o1, sub %o1, 128, %o1) + EXC(STBLK %f0, [%o0 + 0xc0] ASIBLK, add %o3, %o1, sub %o1, 192, %o1) +11: subcc %o3, 256, %o3 + bne,pt %xcc, 10b + add %o0, 256, %o0 +12: +#ifdef __KERNEL__ + wr %g0, 0, %fprs + wr %g7, 0x0, %asi +#endif + membar #Sync +9: andcc %o1, 0xf8, %o2 + be,pn %xcc, 13f + andcc %o1, 7, %o1 +14: rd %pc, %o4 + srl %o2, 1, %o3 + sub %o4, %o3, %o4 + jmpl %o4 + (13f - 14b), %g0 + add %o0, %o2, %o0 +12: ZERO_BLOCKS(%o0, 0xc8, %g0) + ZERO_BLOCKS(%o0, 0x88, %g0) + ZERO_BLOCKS(%o0, 0x48, %g0) + ZERO_BLOCKS(%o0, 0x08, %g0) + EXT(12b,13f,VISbzerofixup_zb) +13: be,pn %xcc, 8f + andcc %o1, 4, %g0 + be,pn %xcc, 1f + andcc %o1, 2, %g0 + EX(STW %g0, [%o0] ASINORMAL, and %o1, 7) + add %o0, 4, %o0 +1: be,pn %xcc, 1f + andcc %o1, 1, %g0 + EX(STH %g0, [%o0] ASINORMAL, and %o1, 3) + add %o0, 2, %o0 +1: bne,a,pn %xcc, 8f + EX(STB %g0, [%o0] ASINORMAL, add %g0, 1) +8: retl + RETL +17: be,pn %xcc, 13b + orcc %o1, 0, %g0 + be,pn %xcc, 0f +8: add %o0, 1, %o0 + subcc %o1, 1, %o1 + bne,pt %xcc, 8b + EX(STB %g0, [%o0 - 1] ASINORMAL, add %o1, 1) +0: retl + RETL + +#ifdef __KERNEL__ + .section .fixup + .align 4 +VISbzerofixup_reto1: + mov %o1, %o0 +VISbzerofixup_ret0: + retl + wr %g0, 0, %fprs +VISbzerofixup_ret1: + and %o5, 0x30, %o5 + add %o5, %o1, %o5 + ba,pt %xcc, VISbzerofixup_ret0 + add %o0, %o5, %o0 +VISbzerofixup_ret2: + and %o5, 0x20, %o5 + add %o5, %o1, %o5 + ba,pt %xcc, VISbzerofixup_ret0 + add %o0, %o5, %o0 +VISbzerofixup_zb: + andcc %o1, 7, %o1 + sll %g2, 3, %g2 + add %o1, 256, %o1 + ba,pt %xcc, VISbzerofixup_ret0 + sub %o1, %g2, %o0 +#endif diff --git a/arch/sparc64/lib/VIScopy.S b/arch/sparc64/lib/VIScopy.S new file mode 100644 index 000000000..1429f1658 --- /dev/null +++ b/arch/sparc64/lib/VIScopy.S @@ -0,0 +1,1060 @@ +/* $Id: VIScopy.S,v 1.1 1997/07/18 06:26:48 ralf Exp $ + * VIScopy.S: High speed copy operations utilizing the UltraSparc + * Visual Instruction Set. + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1996, 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#include "VIS.h" + + /* VIS code can be used for numerous copy/set operation variants. + * It can be made to work in the kernel, one single instance, + * for all of memcpy, copy_to_user, and copy_from_user by setting + * the ASI src/dest globals correctly. Furthermore it can + * be used for kernel-->kernel page copies as well, a hook label + * is put in here just for this purpose. + * + * For userland, compiling this without __KERNEL__ defined makes + * it work just fine as a generic libc bcopy and memcpy. + * If for userland it is compiled with a 32bit gcc (but you need + * -Wa,-Av9a for as), the code will just rely on lower 32bits of + * IEU registers, if you compile it with 64bit gcc (ie. define + * __sparc_v9__), the code will use full 64bit. + */ + +#ifdef __KERNEL__ +#define FPU_CLEAN_RETL \ + wr %g0, 0, %fprs; \ + retl; \ + clr %o0; +#define FPU_RETL \ + wr %g0, 0, %fprs; \ + retl; \ + clr %o0; +#define NORMAL_RETL \ + retl; \ + clr %o0; +#define EX(x,y,a,b) \ +98: x,y; \ + .section .fixup; \ + .align 4; \ +99: ba VIScopyfixup_ret; \ + a, b, %o0; \ + .section __ex_table; \ + .align 8; \ + .xword 98b, 99b; \ + .text; \ + .align 4; +#define EX2(x,y,c,d,e,a,b) \ +98: x,y; \ + .section .fixup; \ + .align 4; \ +99: c, d, e; \ + ba VIScopyfixup_ret; \ + a, b, %o0; \ + .section __ex_table; \ + .align 8; \ + .xword 98b, 99b; \ + .text; \ + .align 4; +#define EXO2(x,y) \ +98: x,y; \ + .section __ex_table; \ + .align 8; \ + .xword 98b, VIScopyfixup_reto2; \ + .text; \ + .align 4; +#define EXVISN(x,y,n) \ +98: x,y; \ + .section __ex_table; \ + .align 8; \ + .xword 98b, VIScopyfixup_vis##n; \ + .text; \ + .align 4; +#define EXT(start,end,handler) \ + .section __ex_table; \ + .align 8; \ + .xword start, 0, end, handler; \ + .text; \ + .align 4; +#else +#define FPU_CLEAN_RETL \ + retl; \ + mov %g6, %o0; +#define FPU_RETL \ + retl; \ + mov %g6, %o0; +#define NORMAL_RETL \ + retl; \ + mov %g6, %o0; +#define EX(x,y,a,b) x,y +#define EX2(x,y,c,d,e,a,b) x,y +#define EXO2(x,y) x,y +#define EXVISN(x,y,n) x,y +#define EXT(a,b,c) +#endif +#define EXVIS(x,y) EXVISN(x,y,0) +#define EXVIS1(x,y) EXVISN(x,y,1) +#define EXVIS2(x,y) EXVISN(x,y,2) +#define EXVIS3(x,y) EXVISN(x,y,3) +#define EXVIS4(x,y) EXVISN(x,y,4) +#define EXVIS5(x,y) EXVISN(x,y,5) + +#define FREG_FROB(f1, f2, f3, f4, f5, f6, f7, f8, f9) \ + faligndata %f1, %f2, %f48; \ + faligndata %f2, %f3, %f50; \ + faligndata %f3, %f4, %f52; \ + faligndata %f4, %f5, %f54; \ + faligndata %f5, %f6, %f56; \ + faligndata %f6, %f7, %f58; \ + faligndata %f7, %f8, %f60; \ + faligndata %f8, %f9, %f62; + +#define MAIN_LOOP_CHUNK(src, dest, fdest, fsrc, len, jmptgt) \ + EXVIS(LDBLK [%src] ASIBLK, %fdest); \ + add %src, 0x40, %src; \ + ASI_SETDST_BLK \ + add %dest, 0x40, %dest; \ + subcc %len, 0x40, %len; \ + be,pn %xcc, jmptgt; \ + EXVIS2(STBLK %fsrc, [%dest - 0x40] ASIBLK); \ + ASI_SETSRC_BLK + +#define LOOP_CHUNK1(src, dest, len, branch_dest) \ + MAIN_LOOP_CHUNK(src, dest, f0, f48, len, branch_dest) +#define LOOP_CHUNK2(src, dest, len, branch_dest) \ + MAIN_LOOP_CHUNK(src, dest, f16, f48, len, branch_dest) +#define LOOP_CHUNK3(src, dest, len, branch_dest) \ + MAIN_LOOP_CHUNK(src, dest, f32, f48, len, branch_dest) + +#define STORE_SYNC(dest, fsrc) \ + EXVIS(STBLK %fsrc, [%dest] ASIBLK); \ + add %dest, 0x40, %dest; + +#define STORE_JUMP(dest, fsrc, target) \ + EXVIS3(STBLK %fsrc, [%dest] ASIBLK); \ + add %dest, 0x40, %dest; \ + ba,pt %xcc, target; + +#ifndef __KERNEL__ +#define VISLOOP_PAD nop; nop; nop; nop; \ + nop; nop; nop; nop; \ + nop; nop; nop; nop; \ + nop; nop; nop; +#else +#define VISLOOP_PAD nop; nop; nop; nop; \ + nop; nop; nop; nop; \ + nop; +#endif + +#define FINISH_VISCHUNK(dest, f0, f1, left) \ + ASI_SETDST_NOBLK \ + subcc %left, 8, %left; \ + bl,pn %xcc, vis_out; \ + faligndata %f0, %f1, %f48; \ + EXVIS4(STDF %f48, [%dest] ASINORMAL); \ + add %dest, 8, %dest; + +#define UNEVEN_VISCHUNK(dest, f0, f1, left) \ + subcc %left, 8, %left; \ + bl,pn %xcc, vis_out; \ + fsrc1 %f0, %f1; \ + ba,a,pt %xcc, vis_slk; + + /* Macros for non-VIS memcpy code. */ +#ifdef REGS_64BIT + +#define MOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3) \ + ASI_SETSRC_NOBLK \ + LDX [%src + offset + 0x00] ASINORMAL, %t0; \ + LDX [%src + offset + 0x08] ASINORMAL, %t1; \ + LDX [%src + offset + 0x10] ASINORMAL, %t2; \ + LDX [%src + offset + 0x18] ASINORMAL, %t3; \ + ASI_SETDST_NOBLK \ + STW %t0, [%dst + offset + 0x04] ASINORMAL; \ + srlx %t0, 32, %t0; \ + STW %t0, [%dst + offset + 0x00] ASINORMAL; \ + STW %t1, [%dst + offset + 0x0c] ASINORMAL; \ + srlx %t1, 32, %t1; \ + STW %t1, [%dst + offset + 0x08] ASINORMAL; \ + STW %t2, [%dst + offset + 0x14] ASINORMAL; \ + srlx %t2, 32, %t2; \ + STW %t2, [%dst + offset + 0x10] ASINORMAL; \ + STW %t3, [%dst + offset + 0x1c] ASINORMAL; \ + srlx %t3, 32, %t3; \ + STW %t3, [%dst + offset + 0x18] ASINORMAL; + +#define MOVE_BIGALIGNCHUNK(src, dst, offset, t0, t1, t2, t3) \ + ASI_SETSRC_NOBLK \ + LDX [%src + offset + 0x00] ASINORMAL, %t0; \ + LDX [%src + offset + 0x08] ASINORMAL, %t1; \ + LDX [%src + offset + 0x10] ASINORMAL, %t2; \ + LDX [%src + offset + 0x18] ASINORMAL, %t3; \ + ASI_SETDST_NOBLK \ + STX %t0, [%dst + offset + 0x00] ASINORMAL; \ + STX %t1, [%dst + offset + 0x08] ASINORMAL; \ + STX %t2, [%dst + offset + 0x10] ASINORMAL; \ + STX %t3, [%dst + offset + 0x18] ASINORMAL; \ + ASI_SETSRC_NOBLK \ + LDX [%src + offset + 0x20] ASINORMAL, %t0; \ + LDX [%src + offset + 0x28] ASINORMAL, %t1; \ + LDX [%src + offset + 0x30] ASINORMAL, %t2; \ + LDX [%src + offset + 0x38] ASINORMAL, %t3; \ + ASI_SETDST_NOBLK \ + STX %t0, [%dst + offset + 0x20] ASINORMAL; \ + STX %t1, [%dst + offset + 0x28] ASINORMAL; \ + STX %t2, [%dst + offset + 0x30] ASINORMAL; \ + STX %t3, [%dst + offset + 0x38] ASINORMAL; + +#define MOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \ + ASI_SETSRC_NOBLK \ + LDX [%src - offset - 0x10] ASINORMAL, %t0; \ + LDX [%src - offset - 0x08] ASINORMAL, %t1; \ + ASI_SETDST_NOBLK \ + STW %t0, [%dst - offset - 0x0c] ASINORMAL; \ + srlx %t0, 32, %t2; \ + STW %t2, [%dst - offset - 0x10] ASINORMAL; \ + STW %t1, [%dst - offset - 0x04] ASINORMAL; \ + srlx %t1, 32, %t3; \ + STW %t3, [%dst - offset - 0x08] ASINORMAL; + +#define MOVE_LASTALIGNCHUNK(src, dst, offset, t0, t1) \ + ASI_SETSRC_NOBLK \ + LDX [%src - offset - 0x10] ASINORMAL, %t0; \ + LDX [%src - offset - 0x08] ASINORMAL, %t1; \ + ASI_SETDST_NOBLK \ + STX %t0, [%dst - offset - 0x10] ASINORMAL; \ + STX %t1, [%dst - offset - 0x08] ASINORMAL; + +#else /* !REGS_64BIT */ + +#define MOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3) \ + lduw [%src + offset + 0x00], %t0; \ + lduw [%src + offset + 0x04], %t1; \ + lduw [%src + offset + 0x08], %t2; \ + lduw [%src + offset + 0x0c], %t3; \ + stw %t0, [%dst + offset + 0x00]; \ + stw %t1, [%dst + offset + 0x04]; \ + stw %t2, [%dst + offset + 0x08]; \ + stw %t3, [%dst + offset + 0x0c]; \ + lduw [%src + offset + 0x10], %t0; \ + lduw [%src + offset + 0x14], %t1; \ + lduw [%src + offset + 0x18], %t2; \ + lduw [%src + offset + 0x1c], %t3; \ + stw %t0, [%dst + offset + 0x10]; \ + stw %t1, [%dst + offset + 0x14]; \ + stw %t2, [%dst + offset + 0x18]; \ + stw %t3, [%dst + offset + 0x1c]; + +#define MOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \ + lduw [%src - offset - 0x10], %t0; \ + lduw [%src - offset - 0x0c], %t1; \ + lduw [%src - offset - 0x08], %t2; \ + lduw [%src - offset - 0x04], %t3; \ + stw %t0, [%dst - offset - 0x10]; \ + stw %t1, [%dst - offset - 0x0c]; \ + stw %t2, [%dst - offset - 0x08]; \ + stw %t3, [%dst - offset - 0x04]; + +#endif /* !REGS_64BIT */ + +#ifdef __KERNEL__ + .section __ex_table,#alloc + .section .fixup,#alloc,#execinstr +#endif + + .text + .align 32 + .globl memcpy + .type memcpy,@function + + .globl bcopy + .type bcopy,@function + +#ifdef __KERNEL__ + .globl __memcpy + .type __memcpy,@function + + .globl __memcpy_384plus + .type __memcpy_384plus,@function + + .globl __memcpy_16plus + .type __memcpy_16plus,@function + + .globl __memcpy_short + .type __memcpy_short,@function + + .globl __memcpy_entry + .type __memcpy_entry,@function + + .globl copy_page + .type copy_page,@function + +memcpy_private: +__memcpy: +memcpy: mov ASI_BLK_P, asi_src ! IEU0 Group + brnz,pt %o2, __memcpy_entry ! CTI + mov ASI_BLK_P, asi_dest ! IEU1 + retl + clr %o0 + +copy_page: wr %g0, FPRS_FEF, %fprs ! FPU Group + sethi %hi(8192), %o2 ! IEU0 Group + mov ASI_BLK_P, asi_src ! IEU1 + b,pt %xcc, dest_is_64byte_aligned ! CTI + mov ASI_BLK_COMMIT_P, asi_dest ! IEU0 Group + + .align 32 + .globl __copy_from_user + .type __copy_from_user,@function +__copy_from_user:mov ASI_BLK_S, asi_src ! IEU0 Group + brnz,pt %o2, __memcpy_entry ! CTI + mov ASI_BLK_P, asi_dest ! IEU1 + + .globl __copy_to_user + .type __copy_to_user,@function +__copy_to_user: mov ASI_BLK_P, asi_src ! IEU0 Group + brnz,pt %o2, __memcpy_entry ! CTI + mov ASI_BLK_S, asi_dest ! IEU1 + retl ! CTI Group + clr %o0 ! IEU0 Group + + .globl __copy_in_user + .type __copy_in_user,@function +__copy_in_user: mov ASI_BLK_S, asi_src ! IEU0 Group + brnz,pt %o2, __memcpy_entry ! CTI + mov ASI_BLK_S, asi_dest ! IEU1 + retl ! CTI Group + clr %o0 ! IEU0 Group +#endif + +bcopy: or %o0, 0, %g3 ! IEU0 Group + addcc %o1, 0, %o0 ! IEU1 + brgez,pt %o2, memcpy_private ! CTI + or %g3, 0, %o1 ! IEU0 Group + retl ! CTI Group brk forced + clr %o0 ! IEU0 + + + .align 32 +#ifdef __KERNEL__ +__memcpy_384plus: + andcc %o0, 7, %g2 ! IEU1 Group +#endif +VIS_enter: + be,pt %xcc, dest_is_8byte_aligned ! CTI + andcc %o0, 0x38, %g5 ! IEU1 Group +do_dest_8byte_align: + mov 8, %g1 ! IEU0 + sub %g1, %g2, %g2 ! IEU0 Group + andcc %o0, 1, %g0 ! IEU1 + be,pt %icc, 2f ! CTI + sub %o2, %g2, %o2 ! IEU0 Group +1: ASI_SETSRC_NOBLK ! LSU Group + EX(LDUB [%o1] ASINORMAL, %o5, + add %o2, %g2) ! Load Group + add %o1, 1, %o1 ! IEU0 + add %o0, 1, %o0 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + subcc %g2, 1, %g2 ! IEU1 Group + be,pn %xcc, 3f ! CTI + EX2(STB %o5, [%o0 - 1] ASINORMAL, + add %g2, 1, %g2, + add %o2, %g2) ! Store +2: ASI_SETSRC_NOBLK ! LSU Group + EX(LDUB [%o1] ASINORMAL, %o5, + add %o2, %g2) ! Load Group + add %o0, 2, %o0 ! IEU0 + EX(LDUB [%o1 + 1] ASINORMAL, %g3, + add %o2, %g2) ! Load Group + ASI_SETDST_NOBLK ! LSU Group + subcc %g2, 2, %g2 ! IEU1 Group + EX2(STB %o5, [%o0 - 2] ASINORMAL, + add %g2, 2, %g2, + add %o2, %g2) ! Store + add %o1, 2, %o1 ! IEU0 + bne,pt %xcc, 2b ! CTI Group + EX2(STB %g3, [%o0 - 1] ASINORMAL, + add %g2, 1, %g2, + add %o2, %g2) ! Store +3: andcc %o0, 0x38, %g5 ! IEU1 Group +dest_is_8byte_aligned: + be,pt %icc, dest_is_64byte_aligned ! CTI +#ifdef __KERNEL__ + wr %g0, FPRS_FEF, %fprs ! FPU Group +do_dest_64byte_align: + mov 64, %g1 ! IEU0 Group +#else + mov 64, %g1 ! IEU0 Group +do_dest_64byte_align: +#endif + fmovd %f0, %f2 ! FPU + sub %g1, %g5, %g5 ! IEU0 Group + ASI_SETSRC_NOBLK ! LSU Group + alignaddr %o1, %g0, %g1 ! GRU Group + EXO2(LDDF [%g1] ASINORMAL, %f4) ! Load Group + sub %o2, %g5, %o2 ! IEU0 +1: EX(LDDF [%g1 + 0x8] ASINORMAL, %f6, + add %o2, %g5) ! Load Group + add %g1, 0x8, %g1 ! IEU0 Group + subcc %g5, 8, %g5 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + faligndata %f4, %f6, %f0 ! GRU Group + EX2(STDF %f0, [%o0] ASINORMAL, + add %g5, 8, %g5, + add %o2, %g5) ! Store + add %o1, 8, %o1 ! IEU0 Group + be,pn %xcc, dest_is_64byte_aligned ! CTI + add %o0, 8, %o0 ! IEU1 + ASI_SETSRC_NOBLK ! LSU Group + EX(LDDF [%g1 + 0x8] ASINORMAL, %f4, + add %o2, %g5) ! Load Group + add %g1, 8, %g1 ! IEU0 + subcc %g5, 8, %g5 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + faligndata %f6, %f4, %f0 ! GRU Group + EX2(STDF %f0, [%o0] ASINORMAL, + add %g5, 8, %g5, + add %o2, %g5) ! Store + add %o1, 8, %o1 ! IEU0 + ASI_SETSRC_NOBLK ! LSU Group + bne,pt %xcc, 1b ! CTI Group + add %o0, 8, %o0 ! IEU0 +dest_is_64byte_aligned: + membar #LoadStore | #StoreStore | #StoreLoad ! LSU Group +#ifndef __KERNEL__ + wr %g0, ASI_BLK_P, %asi ! LSU Group +#endif + subcc %o2, 0x40, %g7 ! IEU1 Group + mov %o1, %g1 ! IEU0 + andncc %g7, (0x40 - 1), %g7 ! IEU1 Group + srl %g1, 3, %g2 ! IEU0 + sub %o2, %g7, %g3 ! IEU0 Group + andn %o1, (0x40 - 1), %o1 ! IEU1 + and %g2, 7, %g2 ! IEU0 Group + andncc %g3, 0x7, %g3 ! IEU1 + fmovd %f0, %f2 ! FPU + sub %g3, 0x10, %g3 ! IEU0 Group + sub %o2, %g7, %o2 ! IEU1 + alignaddr %g1, %g0, %g0 ! GRU Group + add %g1, %g7, %g1 ! IEU0 Group + subcc %o2, %g3, %o2 ! IEU1 + ASI_SETSRC_BLK ! LSU Group + EXVIS1(LDBLK [%o1 + 0x00] ASIBLK, %f0) ! LSU Group + add %g1, %g3, %g1 ! IEU0 + EXVIS1(LDBLK [%o1 + 0x40] ASIBLK, %f16) ! LSU Group + sub %g7, 0x80, %g7 ! IEU0 + EXVIS(LDBLK [%o1 + 0x80] ASIBLK, %f32) ! LSU Group + ! Clk1 Group 8-( + ! Clk2 Group 8-( + ! Clk3 Group 8-( + ! Clk4 Group 8-( +vispc: rd %pc, %g5 ! PDU Group 8-( + addcc %g5, %lo(vis00 - vispc), %g5 ! IEU1 Group + sll %g2, 9, %g2 ! IEU0 + jmpl %g5 + %g2, %g0 ! CTI Group brk forced + addcc %o1, 0xc0, %o1 ! IEU1 Group + .align 512 /* OK, here comes the fun part... */ +vis00:FREG_FROB(f0, f2, f4, f6, f8, f10,f12,f14,f16) LOOP_CHUNK1(o1, o0, g7, vis01) + FREG_FROB(f16,f18,f20,f22,f24,f26,f28,f30,f32) LOOP_CHUNK2(o1, o0, g7, vis02) + FREG_FROB(f32,f34,f36,f38,f40,f42,f44,f46,f0) LOOP_CHUNK3(o1, o0, g7, vis03) + b,pt %xcc, vis00+4; faligndata %f0, %f2, %f48 +vis01:FREG_FROB(f16,f18,f20,f22,f24,f26,f28,f30,f32) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f32,f34,f36,f38,f40,f42,f44,f46,f0) STORE_JUMP(o0, f48, finish_f0) membar #Sync +vis02:FREG_FROB(f32,f34,f36,f38,f40,f42,f44,f46,f0) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f0, f2, f4, f6, f8, f10,f12,f14,f16) STORE_JUMP(o0, f48, check_finish_f16) add %o2, %g3, %g7 +vis03:FREG_FROB(f0, f2, f4, f6, f8, f10,f12,f14,f16) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f16,f18,f20,f22,f24,f26,f28,f30,f32) STORE_JUMP(o0, f48, finish_f32) membar #Sync + VISLOOP_PAD +vis10:FREG_FROB(f2, f4, f6, f8, f10,f12,f14,f16,f18) LOOP_CHUNK1(o1, o0, g7, vis11) + FREG_FROB(f18,f20,f22,f24,f26,f28,f30,f32,f34) LOOP_CHUNK2(o1, o0, g7, vis12) + FREG_FROB(f34,f36,f38,f40,f42,f44,f46,f0, f2) LOOP_CHUNK3(o1, o0, g7, vis13) + b,pt %xcc, vis10+4; faligndata %f2, %f4, %f48 +vis11:FREG_FROB(f18,f20,f22,f24,f26,f28,f30,f32,f34) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f34,f36,f38,f40,f42,f44,f46,f0, f2) STORE_JUMP(o0, f48, finish_f2) membar #Sync +vis12:FREG_FROB(f34,f36,f38,f40,f42,f44,f46,f0, f2) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f2, f4, f6, f8, f10,f12,f14,f16,f18) STORE_JUMP(o0, f48, finish_f18) membar #Sync +vis13:FREG_FROB(f2, f4, f6, f8, f10,f12,f14,f16,f18) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f18,f20,f22,f24,f26,f28,f30,f32,f34) STORE_JUMP(o0, f48, finish_f34) membar #Sync + VISLOOP_PAD +vis20:FREG_FROB(f4, f6, f8, f10,f12,f14,f16,f18,f20) LOOP_CHUNK1(o1, o0, g7, vis21) + FREG_FROB(f20,f22,f24,f26,f28,f30,f32,f34,f36) LOOP_CHUNK2(o1, o0, g7, vis22) + FREG_FROB(f36,f38,f40,f42,f44,f46,f0, f2, f4) LOOP_CHUNK3(o1, o0, g7, vis23) + b,pt %xcc, vis20+4; faligndata %f4, %f6, %f48 +vis21:FREG_FROB(f20,f22,f24,f26,f28,f30,f32,f34,f36) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f36,f38,f40,f42,f44,f46,f0, f2, f4) STORE_JUMP(o0, f48, finish_f4) membar #Sync +vis22:FREG_FROB(f36,f38,f40,f42,f44,f46,f0, f2, f4) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f4, f6, f8, f10,f12,f14,f16,f18,f20) STORE_JUMP(o0, f48, finish_f20) membar #Sync +vis23:FREG_FROB(f4, f6, f8, f10,f12,f14,f16,f18,f20) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f20,f22,f24,f26,f28,f30,f32,f34,f36) STORE_JUMP(o0, f48, finish_f36) membar #Sync + VISLOOP_PAD +vis30:FREG_FROB(f6, f8, f10,f12,f14,f16,f18,f20,f22) LOOP_CHUNK1(o1, o0, g7, vis31) + FREG_FROB(f22,f24,f26,f28,f30,f32,f34,f36,f38) LOOP_CHUNK2(o1, o0, g7, vis32) + FREG_FROB(f38,f40,f42,f44,f46,f0, f2, f4, f6) LOOP_CHUNK3(o1, o0, g7, vis33) + b,pt %xcc, vis30+4; faligndata %f6, %f8, %f48 +vis31:FREG_FROB(f22,f24,f26,f28,f30,f32,f34,f36,f38) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f38,f40,f42,f44,f46,f0, f2, f4, f6) STORE_JUMP(o0, f48, finish_f6) membar #Sync +vis32:FREG_FROB(f38,f40,f42,f44,f46,f0, f2, f4, f6) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f6, f8, f10,f12,f14,f16,f18,f20,f22) STORE_JUMP(o0, f48, finish_f22) membar #Sync +vis33:FREG_FROB(f6, f8, f10,f12,f14,f16,f18,f20,f22) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f22,f24,f26,f28,f30,f32,f34,f36,f38) STORE_JUMP(o0, f48, finish_f38) membar #Sync + VISLOOP_PAD +vis40:FREG_FROB(f8, f10,f12,f14,f16,f18,f20,f22,f24) LOOP_CHUNK1(o1, o0, g7, vis41) + FREG_FROB(f24,f26,f28,f30,f32,f34,f36,f38,f40) LOOP_CHUNK2(o1, o0, g7, vis42) + FREG_FROB(f40,f42,f44,f46,f0, f2, f4, f6, f8) LOOP_CHUNK3(o1, o0, g7, vis43) + b,pt %xcc, vis40+4; faligndata %f8, %f10, %f48 +vis41:FREG_FROB(f24,f26,f28,f30,f32,f34,f36,f38,f40) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f40,f42,f44,f46,f0, f2, f4, f6, f8) STORE_JUMP(o0, f48, finish_f8) membar #Sync +vis42:FREG_FROB(f40,f42,f44,f46,f0, f2, f4, f6, f8) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f8, f10,f12,f14,f16,f18,f20,f22,f24) STORE_JUMP(o0, f48, finish_f24) membar #Sync +vis43:FREG_FROB(f8, f10,f12,f14,f16,f18,f20,f22,f24) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f24,f26,f28,f30,f32,f34,f36,f38,f40) STORE_JUMP(o0, f48, finish_f40) membar #Sync + VISLOOP_PAD +vis50:FREG_FROB(f10,f12,f14,f16,f18,f20,f22,f24,f26) LOOP_CHUNK1(o1, o0, g7, vis51) + FREG_FROB(f26,f28,f30,f32,f34,f36,f38,f40,f42) LOOP_CHUNK2(o1, o0, g7, vis52) + FREG_FROB(f42,f44,f46,f0, f2, f4, f6, f8, f10) LOOP_CHUNK3(o1, o0, g7, vis53) + b,pt %xcc, vis50+4; faligndata %f10, %f12, %f48 +vis51:FREG_FROB(f26,f28,f30,f32,f34,f36,f38,f40,f42) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f42,f44,f46,f0, f2, f4, f6, f8, f10) STORE_JUMP(o0, f48, finish_f10) membar #Sync +vis52:FREG_FROB(f42,f44,f46,f0, f2, f4, f6, f8, f10) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f10,f12,f14,f16,f18,f20,f22,f24,f26) STORE_JUMP(o0, f48, finish_f26) membar #Sync +vis53:FREG_FROB(f10,f12,f14,f16,f18,f20,f22,f24,f26) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f26,f28,f30,f32,f34,f36,f38,f40,f42) STORE_JUMP(o0, f48, finish_f42) membar #Sync + VISLOOP_PAD +vis60:FREG_FROB(f12,f14,f16,f18,f20,f22,f24,f26,f28) LOOP_CHUNK1(o1, o0, g7, vis61) + FREG_FROB(f28,f30,f32,f34,f36,f38,f40,f42,f44) LOOP_CHUNK2(o1, o0, g7, vis62) + FREG_FROB(f44,f46,f0, f2, f4, f6, f8, f10,f12) LOOP_CHUNK3(o1, o0, g7, vis63) + b,pt %xcc, vis60+4; faligndata %f12, %f14, %f48 +vis61:FREG_FROB(f28,f30,f32,f34,f36,f38,f40,f42,f44) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f44,f46,f0, f2, f4, f6, f8, f10,f12) STORE_JUMP(o0, f48, finish_f12) membar #Sync +vis62:FREG_FROB(f44,f46,f0, f2, f4, f6, f8, f10,f12) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f12,f14,f16,f18,f20,f22,f24,f26,f28) STORE_JUMP(o0, f48, finish_f28) membar #Sync +vis63:FREG_FROB(f12,f14,f16,f18,f20,f22,f24,f26,f28) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f28,f30,f32,f34,f36,f38,f40,f42,f44) STORE_JUMP(o0, f48, finish_f44) membar #Sync + VISLOOP_PAD +vis70:FREG_FROB(f14,f16,f18,f20,f22,f24,f26,f28,f30) LOOP_CHUNK1(o1, o0, g7, vis71) + FREG_FROB(f30,f32,f34,f36,f38,f40,f42,f44,f46) LOOP_CHUNK2(o1, o0, g7, vis72) + FREG_FROB(f46,f0, f2, f4, f6, f8, f10,f12,f14) LOOP_CHUNK3(o1, o0, g7, vis73) + b,pt %xcc, vis70+4; faligndata %f14, %f16, %f48 +vis71:FREG_FROB(f30,f32,f34,f36,f38,f40,f42,f44,f46) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f46,f0, f2, f4, f6, f8, f10,f12,f14) STORE_JUMP(o0, f48, finish_f14) membar #Sync +vis72:FREG_FROB(f46,f0, f2, f4, f6, f8, f10,f12,f14) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f14,f16,f18,f20,f22,f24,f26,f28,f30) STORE_JUMP(o0, f48, finish_f30) membar #Sync +vis73:FREG_FROB(f14,f16,f18,f20,f22,f24,f26,f28,f30) STORE_SYNC(o0, f48) membar #Sync + FREG_FROB(f30,f32,f34,f36,f38,f40,f42,f44,f46) STORE_JUMP(o0, f48, finish_f46) membar #Sync + VISLOOP_PAD +finish_f0: FINISH_VISCHUNK(o0, f0, f2, g3) +finish_f2: FINISH_VISCHUNK(o0, f2, f4, g3) +finish_f4: FINISH_VISCHUNK(o0, f4, f6, g3) +finish_f6: FINISH_VISCHUNK(o0, f6, f8, g3) +finish_f8: FINISH_VISCHUNK(o0, f8, f10, g3) +finish_f10: FINISH_VISCHUNK(o0, f10, f12, g3) +finish_f12: FINISH_VISCHUNK(o0, f12, f14, g3) +finish_f14: UNEVEN_VISCHUNK(o0, f14, f0, g3) +/* This is a special hack to speed up 8K page copy */ +check_finish_f16: + andcc %g1, 7, %g0 + bne,pn %icc, finish_f16 + cmp %g7, 0x40 + bne,pn %icc, finish_f16 + FREG_FROB(f16,f18,f20,f22,f24,f26,f28,f30,f32) + membar #Sync + EXVIS1(STBLK %f48, [%o0] ASIBLK) + b,pt %xcc, vis_ret +finish_f16: membar #Sync + FINISH_VISCHUNK(o0, f16, f18, g3) +finish_f18: FINISH_VISCHUNK(o0, f18, f20, g3) +finish_f20: FINISH_VISCHUNK(o0, f20, f22, g3) +finish_f22: FINISH_VISCHUNK(o0, f22, f24, g3) +finish_f24: FINISH_VISCHUNK(o0, f24, f26, g3) +finish_f26: FINISH_VISCHUNK(o0, f26, f28, g3) +finish_f28: FINISH_VISCHUNK(o0, f28, f30, g3) +finish_f30: UNEVEN_VISCHUNK(o0, f30, f0, g3) +finish_f32: FINISH_VISCHUNK(o0, f32, f34, g3) +finish_f34: FINISH_VISCHUNK(o0, f34, f36, g3) +finish_f36: FINISH_VISCHUNK(o0, f36, f38, g3) +finish_f38: FINISH_VISCHUNK(o0, f38, f40, g3) +finish_f40: FINISH_VISCHUNK(o0, f40, f42, g3) +finish_f42: FINISH_VISCHUNK(o0, f42, f44, g3) +finish_f44: FINISH_VISCHUNK(o0, f44, f46, g3) +finish_f46: UNEVEN_VISCHUNK(o0, f46, f0, g3) +vis_slk:ASI_SETSRC_NOBLK ! LSU Group + EXVIS4(LDDF [%o1] ASINORMAL, %f2) ! Load Group + add %o1, 8, %o1 ! IEU0 + subcc %g3, 8, %g3 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + faligndata %f0, %f2, %f8 ! GRU Group + EXVIS5(STDF %f8, [%o0] ASINORMAL) ! Store + bl,pn %xcc, vis_out ! CTI + add %o0, 8, %o0 ! IEU0 Group + ASI_SETSRC_NOBLK ! LSU Group + EXVIS4(LDDF [%o1] ASINORMAL, %f0) ! Load Group + add %o1, 8, %o1 ! IEU0 + subcc %g3, 8, %g3 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + faligndata %f2, %f0, %f8 ! GRU Group + EXVIS5(STDF %f8, [%o0] ASINORMAL) ! Store + bge,pt %xcc, vis_slk ! CTI + add %o0, 8, %o0 ! IEU0 Group +vis_out:brz,pt %o2, vis_ret ! CTI Group + mov %g1, %o1 ! IEU0 +vis_slp:ASI_SETSRC_NOBLK ! LSU Group + EXO2(LDUB [%o1] ASINORMAL, %g5) ! LOAD + add %o1, 1, %o1 ! IEU0 + add %o0, 1, %o0 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + subcc %o2, 1, %o2 ! IEU1 + bne,pt %xcc, vis_slp ! CTI + EX(STB %g5, [%o0 - 1] ASINORMAL, + add %o2, 1) ! Store Group +vis_ret:membar #StoreLoad | #StoreStore ! LSU Group + FPU_CLEAN_RETL + + +__memcpy_short: + andcc %o2, 1, %g0 ! IEU1 Group + be,pt %icc, 2f ! CTI +1: ASI_SETSRC_NOBLK ! LSU Group + EXO2(LDUB [%o1] ASINORMAL, %g5) ! LOAD Group + add %o1, 1, %o1 ! IEU0 + add %o0, 1, %o0 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + subcc %o2, 1, %o2 ! IEU1 Group + be,pn %xcc, short_ret ! CTI + EX(STB %g5, [%o0 - 1] ASINORMAL, + add %o2, 1) ! Store +2: ASI_SETSRC_NOBLK ! LSU Group + EXO2(LDUB [%o1] ASINORMAL, %g5) ! LOAD Group + add %o0, 2, %o0 ! IEU0 + EXO2(LDUB [%o1 + 1] ASINORMAL, %o5) ! LOAD Group + add %o1, 2, %o1 ! IEU0 + ASI_SETDST_NOBLK ! LSU Group + subcc %o2, 2, %o2 ! IEU1 Group + EX(STB %g5, [%o0 - 2] ASINORMAL, + add %o2, 2) ! Store + bne,pt %xcc, 2b ! CTI + EX(STB %o5, [%o0 - 1] ASINORMAL, + add %o2, 1) ! Store +short_ret: + NORMAL_RETL + +#ifndef __KERNEL__ +memcpy_private: +memcpy: +#ifndef REGS_64BIT + srl %o2, 0, %o2 ! IEU1 Group +#endif + brz,pn %o2, short_ret ! CTI Group + mov %o0, %g6 ! IEU0 +#endif +__memcpy_entry: + cmp %o2, 15 ! IEU1 Group + bleu,pn %xcc, __memcpy_short ! CTI + cmp %o2, (64 * 6) ! IEU1 Group + bgeu,pn %xcc, VIS_enter ! CTI +#ifdef __KERNEL__ +__memcpy_16plus: +#endif + andcc %o0, 7, %g2 ! IEU1 Group + sub %o0, %o1, %g5 ! IEU0 + andcc %g5, 3, %o5 ! IEU1 Group + bne,pn %xcc, memcpy_noVIS_misaligned ! CTI + andcc %o1, 3, %g0 ! IEU1 Group +#ifdef REGS_64BIT + be,a,pt %xcc, 3f ! CTI + andcc %o1, 4, %g0 ! IEU1 Group + andcc %o1, 1, %g0 ! IEU1 Group +#else /* !REGS_64BIT */ + be,pt %xcc, 5f ! CTI + andcc %o1, 1, %g0 ! IEU1 Group +#endif /* !REGS_64BIT */ + be,pn %xcc, 4f ! CTI + andcc %o1, 2, %g0 ! IEU1 Group + ASI_SETSRC_NOBLK ! LSU Group + EXO2(LDUB [%o1] ASINORMAL, %g2) ! Load Group + add %o1, 1, %o1 ! IEU0 + add %o0, 1, %o0 ! IEU1 + sub %o2, 1, %o2 ! IEU0 Group + ASI_SETDST_NOBLK ! LSU Group + bne,pn %xcc, 5f ! CTI Group + EX(STB %g2, [%o0 - 1] ASINORMAL, + add %o2, 1) ! Store +4: ASI_SETSRC_NOBLK ! LSU Group + EXO2(LDUH [%o1] ASINORMAL, %g2) ! Load Group + add %o1, 2, %o1 ! IEU0 + add %o0, 2, %o0 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + sub %o2, 2, %o2 ! IEU0 + EX(STH %g2, [%o0 - 2] ASINORMAL, + add %o2, 2) ! Store Group + bubble +#ifdef REGS_64BIT +5: andcc %o1, 4, %g0 ! IEU1 +3: be,a,pn %xcc, 2f ! CTI + andcc %o2, -128, %g7 ! IEU1 Group + ASI_SETSRC_NOBLK ! LSU Group + EXO2(LDUW [%o1] ASINORMAL, %g5) ! Load Group + add %o1, 4, %o1 ! IEU0 + add %o0, 4, %o0 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + sub %o2, 4, %o2 ! IEU0 Group + EX(STW %g5, [%o0 - 4] ASINORMAL, + add %o2, 4) ! Store + andcc %o2, -128, %g7 ! IEU1 Group +2: be,pn %xcc, 3f ! CTI + andcc %o0, 4, %g0 ! IEU1 Group + be,pn %xcc, 82f + 4 ! CTI Group +#else /* !REGS_64BIT */ +5: andcc %o2, -128, %g7 ! IEU1 + be,a,pn %xcc, 41f ! CTI + andcc %o2, 0x70, %g7 ! IEU1 Group +#endif /* !REGS_64BIT */ +5: MOVE_BIGCHUNK(o1, o0, 0x00, g1, g3, g5, o5) + MOVE_BIGCHUNK(o1, o0, 0x20, g1, g3, g5, o5) + MOVE_BIGCHUNK(o1, o0, 0x40, g1, g3, g5, o5) + MOVE_BIGCHUNK(o1, o0, 0x60, g1, g3, g5, o5) + EXT(5b,35f,VIScopyfixup1) +35: subcc %g7, 128, %g7 ! IEU1 Group + add %o1, 128, %o1 ! IEU0 + bne,pt %xcc, 5b ! CTI + add %o0, 128, %o0 ! IEU0 Group +3: andcc %o2, 0x70, %g7 ! IEU1 Group +41: be,pn %xcc, 80f ! CTI + andcc %o2, 8, %g0 ! IEU1 Group + ! Clk1 8-( + ! Clk2 8-( + ! Clk3 8-( + ! Clk4 8-( +79: rd %pc, %o5 ! PDU Group +#ifdef __KERNEL__ + sll %g7, 1, %g5 ! IEU0 Group + add %o1, %g7, %o1 ! IEU1 + srl %g7, 1, %g2 ! IEU0 Group + sub %o5, %g5, %o5 ! IEU1 + sub %o5, %g2, %o5 ! IEU0 Group + jmpl %o5 + %lo(80f - 79b), %g0 ! CTI Group brk forced + add %o0, %g7, %o0 ! IEU0 Group +#else + sll %g7, 1, %g5 ! IEU0 Group + add %o1, %g7, %o1 ! IEU1 + sub %o5, %g5, %o5 ! IEU0 Group + jmpl %o5 + %lo(80f - 79b), %g0 ! CTI Group brk forced + add %o0, %g7, %o0 ! IEU0 Group +#endif +36: MOVE_LASTCHUNK(o1, o0, 0x60, g2, g3, g5, o5) + MOVE_LASTCHUNK(o1, o0, 0x50, g2, g3, g5, o5) + MOVE_LASTCHUNK(o1, o0, 0x40, g2, g3, g5, o5) + MOVE_LASTCHUNK(o1, o0, 0x30, g2, g3, g5, o5) + MOVE_LASTCHUNK(o1, o0, 0x20, g2, g3, g5, o5) + MOVE_LASTCHUNK(o1, o0, 0x10, g2, g3, g5, o5) + MOVE_LASTCHUNK(o1, o0, 0x00, g2, g3, g5, o5) + EXT(36b,80f,VIScopyfixup2) +80: be,pt %xcc, 81f ! CTI + andcc %o2, 4, %g0 ! IEU1 +#ifdef REGS_64BIT + ASI_SETSRC_NOBLK ! LSU Group + EX(LDX [%o1] ASINORMAL, %g2, + and %o2, 0xf) ! Load Group + add %o0, 8, %o0 ! IEU0 + ASI_SETDST_NOBLK ! LSU Group + EX(STW %g2, [%o0 - 0x4] ASINORMAL, + and %o2, 0xf) ! Store Group + add %o1, 8, %o1 ! IEU1 + srlx %g2, 32, %g2 ! IEU0 Group + EX2(STW %g2, [%o0 - 0x8] ASINORMAL, + and %o2, 0xf, %o2, + sub %o2, 4) ! Store +#else /* !REGS_64BIT */ + lduw [%o1], %g2 ! Load Group + add %o0, 8, %o0 ! IEU0 + lduw [%o1 + 0x4], %g3 ! Load Group + add %o1, 8, %o1 ! IEU0 + stw %g2, [%o0 - 0x8] ! Store Group + stw %g3, [%o0 - 0x4] ! Store Group +#endif /* !REGS_64BIT */ +81: be,pt %xcc, 1f ! CTI + andcc %o2, 2, %g0 ! IEU1 Group + ASI_SETSRC_NOBLK ! LSU Group + EX(LDUW [%o1] ASINORMAL, %g2, + and %o2, 0x7) ! Load Group + add %o1, 4, %o1 ! IEU0 + ASI_SETDST_NOBLK ! LSU Group + EX(STW %g2, [%o0] ASINORMAL, + and %o2, 0x7) ! Store Group + add %o0, 4, %o0 ! IEU0 +1: be,pt %xcc, 1f ! CTI + andcc %o2, 1, %g0 ! IEU1 Group + ASI_SETSRC_NOBLK ! LSU Group + EX(LDUH [%o1] ASINORMAL, %g2, + and %o2, 0x3) ! Load Group + add %o1, 2, %o1 ! IEU0 + ASI_SETDST_NOBLK ! LSU Group + EX(STH %g2, [%o0] ASINORMAL, + and %o2, 0x3) ! Store Group + add %o0, 2, %o0 ! IEU0 +1: be,pt %xcc, normal_retl ! CTI + nop ! IEU1 + ASI_SETSRC_NOBLK ! LSU Group + EX(LDUB [%o1] ASINORMAL, %g2, + add %g0, 1) ! Load Group + ASI_SETDST_NOBLK ! LSU Group + EX(STB %g2, [%o0] ASINORMAL, + add %g0, 1) ! Store Group + bubble +normal_retl: + NORMAL_RETL + +#ifdef REGS_64BIT +82: MOVE_BIGALIGNCHUNK(o1, o0, 0x00, g1, g3, g5, o5) + MOVE_BIGALIGNCHUNK(o1, o0, 0x40, g1, g3, g5, o5) + EXT(82b,37f,VIScopyfixup3) +37: subcc %g7, 128, %g7 ! IEU1 Group + add %o1, 128, %o1 ! IEU0 + bne,pt %xcc, 82b ! CTI + add %o0, 128, %o0 ! IEU0 Group + andcc %o2, 0x70, %g7 ! IEU1 + be,pn %xcc, 84f ! CTI + andcc %o2, 8, %g0 ! IEU1 Group + ! Clk1 8-( + ! Clk2 8-( + ! Clk3 8-( + ! Clk4 8-( +83: rd %pc, %o5 ! PDU Group +#ifdef __KERNEL__ + srl %g7, 1, %g5 ! IEU0 Group + add %g7, %g5, %g5 ! IEU0 Group + add %o1, %g7, %o1 ! IEU1 + sub %o5, %g5, %o5 ! IEU0 Group + jmpl %o5 + %lo(84f - 83b), %g0 ! CTI Group brk forced + add %o0, %g7, %o0 ! IEU0 Group +#else + add %o1, %g7, %o1 ! IEU0 Group + sub %o5, %g7, %o5 ! IEU1 + jmpl %o5 + %lo(84f - 83b), %g0 ! CTI Group brk forced + add %o0, %g7, %o0 ! IEU0 Group +#endif +38: MOVE_LASTALIGNCHUNK(o1, o0, 0x60, g2, g3) + MOVE_LASTALIGNCHUNK(o1, o0, 0x50, g2, g3) + MOVE_LASTALIGNCHUNK(o1, o0, 0x40, g2, g3) + MOVE_LASTALIGNCHUNK(o1, o0, 0x30, g2, g3) + MOVE_LASTALIGNCHUNK(o1, o0, 0x20, g2, g3) + MOVE_LASTALIGNCHUNK(o1, o0, 0x10, g2, g3) + MOVE_LASTALIGNCHUNK(o1, o0, 0x00, g2, g3) + EXT(38b,84f,VIScopyfixup4) +84: be,pt %xcc, 85f ! CTI Group + andcc %o2, 4, %g0 ! IEU1 + ASI_SETSRC_NOBLK ! LSU Group + EX(LDX [%o1] ASINORMAL, %g2, + and %o2, 0xf) ! Load Group + add %o1, 8, %o1 ! IEU0 + ASI_SETDST_NOBLK ! LSU Group + add %o0, 8, %o0 ! IEU0 Group + EX(STX %g2, [%o0 - 0x8] ASINORMAL, + and %o2, 0xf) ! Store +85: be,pt %xcc, 1f ! CTI + andcc %o2, 2, %g0 ! IEU1 Group + ASI_SETSRC_NOBLK ! LSU Group + EX(LDUW [%o1] ASINORMAL, %g2, + and %o2, 0x7) ! Load Group + add %o1, 4, %o1 ! IEU0 + ASI_SETDST_NOBLK ! LSU Group + add %o0, 4, %o0 ! IEU0 Group + EX(STW %g2, [%o0 - 0x4] ASINORMAL, + and %o2, 0x7) ! Store +1: be,pt %xcc, 1f ! CTI + andcc %o2, 1, %g0 ! IEU1 Group + ASI_SETSRC_NOBLK ! LSU Group + EX(LDUH [%o1] ASINORMAL, %g2, + and %o2, 0x3) ! Load Group + add %o1, 2, %o1 ! IEU0 + ASI_SETDST_NOBLK ! LSU Group + add %o0, 2, %o0 ! IEU0 Group + EX(STH %g2, [%o0 - 0x2] ASINORMAL, + and %o2, 0x3) ! Store +1: be,pt %xcc, 1f ! CTI + nop ! IEU0 Group + ASI_SETSRC_NOBLK ! LSU Group + EX(LDUB [%o1] ASINORMAL, %g2, + add %g0, 1) ! Load Group + ASI_SETDST_NOBLK ! LSU Group + EX(STB %g2, [%o0] ASINORMAL, + add %g0, 1) ! Store Group + bubble +1: NORMAL_RETL +#endif /* REGS_64BIT */ + +memcpy_noVIS_misaligned: + brz,pt %g2, 2f ! CTI Group + mov 8, %g1 ! IEU0 + sub %g1, %g2, %g2 ! IEU0 Group + sub %o2, %g2, %o2 ! IEU0 Group +1: ASI_SETSRC_NOBLK ! LSU Group + EX(LDUB [%o1] ASINORMAL, %g5, + add %o2, %g2) ! Load Group + add %o1, 1, %o1 ! IEU0 + add %o0, 1, %o0 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + subcc %g2, 1, %g2 ! IEU1 Group + bne,pt %xcc, 1b ! CTI + EX2(STB %g5, [%o0 - 1] ASINORMAL, + add %o2, %g2, %o2, + add %o2, 1) ! Store +2: +#ifdef __KERNEL__ + wr %g0, FPRS_FEF, %fprs ! FPU Group +#endif + andn %o2, 7, %g5 ! IEU0 Group + and %o2, 7, %o2 ! IEU1 + fmovd %f0, %f2 ! FPU + ASI_SETSRC_NOBLK ! LSU Group + alignaddr %o1, %g0, %g1 ! GRU Group + EXO2(LDDF [%g1] ASINORMAL, %f4) ! Load Group +1: EX(LDDF [%g1 + 0x8] ASINORMAL, %f6, + add %o2, %g5) ! Load Group + add %g1, 0x8, %g1 ! IEU0 Group + subcc %g5, 8, %g5 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + faligndata %f4, %f6, %f0 ! GRU Group + EX2(STDF %f0, [%o0] ASINORMAL, + add %o2, %g5, %o2, + add %o2, 8) ! Store + add %o1, 8, %o1 ! IEU0 Group + be,pn %xcc, end_cruft ! CTI + add %o0, 8, %o0 ! IEU1 + ASI_SETSRC_NOBLK ! LSU Group + EX(LDDF [%g1 + 0x8] ASINORMAL, %f4, + add %o2, %g5) ! Load Group + add %g1, 8, %g1 ! IEU0 + subcc %g5, 8, %g5 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + faligndata %f6, %f4, %f0 ! GRU Group + EX2(STDF %f0, [%o0] ASINORMAL, + add %o2, %g5, %o2, + add %o2, 8) ! Store + add %o1, 8, %o1 ! IEU0 + ASI_SETSRC_NOBLK ! LSU Group + bne,pn %xcc, 1b ! CTI Group + add %o0, 8, %o0 ! IEU0 +end_cruft: + brz,pn %o2, fpu_retl ! CTI Group +#ifndef __KERNEL__ + nop ! IEU0 +#else + ASI_SETSRC_NOBLK ! LSU Group +#endif + EXO2(LDUB [%o1] ASINORMAL, %g5) ! LOAD + add %o1, 1, %o1 ! IEU0 + add %o0, 1, %o0 ! IEU1 + ASI_SETDST_NOBLK ! LSU Group + subcc %o2, 1, %o2 ! IEU1 + bne,pt %xcc, vis_slp ! CTI + EX(STB %g5, [%o0 - 1] ASINORMAL, + add %o2, 1) ! Store Group +fpu_retl: + FPU_RETL + +#ifdef __KERNEL__ + .section .fixup + .align 4 +VIScopyfixup_reto2: + mov %o2, %o0 +VIScopyfixup_ret: + retl + wr %g0, 0, %fprs +VIScopyfixup1: subcc %g2, 18, %g2 + bgeu,a,pt %icc, VIScopyfixup1 + sub %g7, 32, %g7 + rd %pc, %g5 + add %g2, 18, %g2 + add %g2, 20, %g2 + ldub [%g5 + %g2], %g2 + ba,a,pt %xcc, 2f +.byte 0, 0, 0, 0, 0, 0, 0, 4, 4, 8, 12, 12, 16, 20, 20, 24, 28, 28 + .align 4 +VIScopyfixup2: mov (7 * 16), %g7 +1: subcc %g2, 10, %g2 + bgeu,a,pt %icc, 1b + sub %g7, 16, %g7 + rd %pc, %g5 + add %g2, 10, %g2 + add %g2, 20, %g2 + ldub [%g5 + %g2], %g2 + ba,a,pt %xcc, 4f +.byte 0, 0, 0, 0, 0, 4, 4, 8, 12, 12 + .align 4 +VIScopyfixup3: subcc %g2, 10, %g2 + bgeu,a,pt %icc, VIScopyfixup3 + sub %g7, 32, %g7 + rd %pc, %g5 + add %g2, 10, %g2 + add %g2, 20, %g2 + ldub [%g5 + %g2], %g2 + ba,a,pt %xcc, 2f +.byte 0, 0, 0, 0, 0, 0, 0, 8, 16, 24 + .align 4 +2: and %g1, 0x7f, %g1 + sub %g7, %g2, %g7 + ba,pt %xcc, VIScopyfixup_ret + add %g7, %g1, %o0 +VIScopyfixup4: mov (7 * 16), %g7 +3: subcc %g2, 6, %g2 + bgeu,a,pt %icc, 3b + sub %g7, 16, %g7 + rd %pc, %g5 + add %g2, 6, %g2 + add %g2, 20, %g2 + ldub [%g5 + %g2], %g2 + ba,a,pt %xcc, 4f +.byte 0, 0, 0, 0, 0, 8 + .align 4 +4: and %g1, 7, %g1 + ba,pt %xcc, VIScopyfixup_ret + add %g7, %g1, %o0 +VIScopyfixup_vis3: + sub %o2, 0x80, %o2 +VIScopyfixup_vis2: + add %o2, 0x40, %o2 +VIScopyfixup_vis0: + add %o2, 0x80, %o2 +VIScopyfixup_vis1: + add %g7, %g3, %g7 + ba,pt %xcc, VIScopyfixup_ret + add %o2, %g7, %o0 +VIScopyfixup_vis5: + add %g3, 8, %g3 +VIScopyfixup_vis4: + add %g3, 8, %g3 + ba,pt %xcc, VIScopyfixup_ret + add %o2, %g3, %o0 +#endif + +#ifdef __KERNEL__ + .text + .align 32 + + .globl __memmove + .type __memmove,@function + + .globl memmove + .type memmove,@function + +memmove: +__memmove: cmp %o0, %o1 + blu,pt %xcc, memcpy_private + sub %o0, %o1, %g5 + add %o1, %o2, %g3 + cmp %g3, %o0 + bleu,pt %xcc, memcpy_private + add %o1, %o2, %g5 + add %o0, %o2, %o5 + + sub %g5, 1, %o1 + sub %o5, 1, %o0 +1: ldub [%o1], %g5 + subcc %o2, 1, %o2 + sub %o1, 1, %o1 + stb %g5, [%o0] + bne,pt %icc, 1b + sub %o0, 1, %o0 + + retl + clr %o0 +#endif diff --git a/arch/sparc64/lib/VIScsum.S b/arch/sparc64/lib/VIScsum.S new file mode 100644 index 000000000..1ccb98759 --- /dev/null +++ b/arch/sparc64/lib/VIScsum.S @@ -0,0 +1,436 @@ +/* $Id: VIScsum.S,v 1.1 1997/07/18 06:26:49 ralf Exp $ + * VIScsum.S: High bandwidth IP checksumming utilizing the UltraSparc + * Visual Instruction Set. + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * + * Based on older sparc32/sparc64 checksum.S, which is: + * + * Copyright(C) 1995 Linus Torvalds + * Copyright(C) 1995 Miguel de Icaza + * Copyright(C) 1996,1997 David S. Miller + * derived from: + * Linux/Alpha checksum c-code + * Linux/ix86 inline checksum assembly + * RFC1071 Computing the Internet Checksum (esp. Jacobsons m68k code) + * David Mosberger-Tang for optimized reference c-code + * BSD4.4 portable checksum routine + */ + +#ifdef __sparc_v9__ +#define STACKOFF 2175 +#else +#define STACKOFF 64 +#endif + +#ifdef __KERNEL__ +#include <asm/head.h> +#include <asm/asi.h> +#else +#define ASI_BLK_P 0xf0 +#define FRPS_FEF 0x04 +#endif + +/* Dobrou noc, SunSoft engineers. Spete sladce. + * This has a couple of tricks in and those + * tricks are UltraLinux trade secrets :)) + */ + +#define START_THE_TRICK(fz,f0,f2,f4,f6,f8,f10) \ + fcmpgt32 %fz, %f0, %g1 /* FPM Group */; \ + fcmpgt32 %fz, %f2, %g2 /* FPM Group */; \ + fcmpgt32 %fz, %f4, %g3 /* FPM Group */; \ + fcmpgt32 %fz, %f6, %g5 /* FPM Group */; \ + inc %g1 /* IEU0 */; \ + fcmpgt32 %fz, %f8, %g7 /* FPM Group */; \ + srl %g1, 1, %g1 /* IEU0 */; \ + inc %g2 /* IEU1 */; \ + fcmpgt32 %fz, %f10, %o3 /* FPM Group */; \ + srl %g2, 1, %g2 /* IEU0 */; \ + add %o2, %g1, %o2 /* IEU1 */; \ + add %g3, 1, %g3 /* IEU0 Group */; \ + srl %g3, 1, %g3 /* IEU0 Group */; \ + add %o2, %g2, %o2 /* IEU1 */; \ + inc %g5 /* IEU0 Group */; \ + add %o2, %g3, %o2 /* IEU1 */; + +#define DO_THE_TRICK(O12,O14,f0,f2,f4,f6,f8,f10,f12,f14,F0,F2,F4,F6,F8,F10,F12,F14) \ + fcmpgt32 %O12, %f12, %o4 /* FPM Group */; \ + srl %g5, 1, %g5 /* IEU0 */; \ + inc %g7 /* IEU1 */; \ + fpadd32 %F0, %f0, %F0 /* FPA */; \ + fcmpgt32 %O14, %f14, %o5 /* FPM Group */; \ + srl %g7, 1, %g7 /* IEU0 */; \ + add %o2, %g5, %o2 /* IEU1 */; \ + fpadd32 %F2, %f2, %F2 /* FPA */; \ + inc %o3 /* IEU0 Group */; \ + add %o2, %g7, %o2 /* IEU1 */; \ + fcmpgt32 %f0, %F0, %g1 /* FPM Group */; \ + srl %o3, 1, %o3 /* IEU0 */; \ + inc %o4 /* IEU1 */; \ + fpadd32 %F4, %f4, %F4 /* FPA */; \ + fcmpgt32 %f2, %F2, %g2 /* FPM Group */; \ + srl %o4, 1, %o4 /* IEU0 */; \ + add %o2, %o3, %o2 /* IEU1 */; \ + fpadd32 %F6, %f6, %F6 /* FPA */; \ + inc %o5 /* IEU0 Group */; \ + add %o2, %o4, %o2 /* IEU1 */; \ + fcmpgt32 %f4, %F4, %g3 /* FPM Group */; \ + srl %o5, 1, %o5 /* IEU0 */; \ + inc %g1 /* IEU1 */; \ + fpadd32 %F8, %f8, %F8 /* FPA */; \ + fcmpgt32 %f6, %F6, %g5 /* FPM Group */; \ + srl %g1, 1, %g1 /* IEU0 */; \ + add %o2, %o5, %o2 /* IEU1 */; \ + fpadd32 %F10, %f10, %F10 /* FPA */; \ + inc %g2 /* IEU0 Group */; \ + add %o2, %g1, %o2 /* IEU1 */; \ + fcmpgt32 %f8, %F8, %g7 /* FPM Group */; \ + srl %g2, 1, %g2 /* IEU0 */; \ + inc %g3 /* IEU1 */; \ + fpadd32 %F12, %f12, %F12 /* FPA */; \ + fcmpgt32 %f10, %F10, %o3 /* FPM Group */; \ + srl %g3, 1, %g3 /* IEU0 */; \ + add %o2, %g2, %o2 /* IEU1 */; \ + fpadd32 %F14, %f14, %F14 /* FPA */; \ + inc %g5 /* IEU0 Group */; \ + add %o2, %g3, %o2 /* IEU1 */; + +#define END_THE_TRICK(O12,O14,f0,f2,f4,f6,f8,f10,f12,f14,S0,S1,S2,S3,T0,T1,U0,fz) \ + fcmpgt32 %O12, %f12, %o4 /* FPM Group */; \ + srl %g5, 1, %g5 /* IEU0 */; \ + inc %g7 /* IEU1 */; \ + fpadd32 %f2, %f0, %S0 /* FPA */; \ + fcmpgt32 %O14, %f14, %o5 /* FPM Group */; \ + srl %g7, 1, %g7 /* IEU0 */; \ + add %o2, %g5, %o2 /* IEU1 */; \ + fpadd32 %f6, %f4, %S1 /* FPA */; \ + inc %o3 /* IEU0 Group */; \ + add %o2, %g7, %o2 /* IEU1 */; \ + fcmpgt32 %f0, %S0, %g1 /* FPM Group */; \ + srl %o3, 1, %o3 /* IEU0 */; \ + inc %o4 /* IEU1 */; \ + fpadd32 %f10, %f8, %S2 /* FPA */; \ + fcmpgt32 %f4, %S1, %g2 /* FPM Group */; \ + srl %o4, 1, %o4 /* IEU0 */; \ + add %o2, %o3, %o2 /* IEU1 */; \ + fpadd32 %f14, %f12, %S3 /* FPA */; \ + inc %o5 /* IEU0 Group */; \ + add %o2, %o4, %o2 /* IEU1 */; \ + fzero %fz /* FPA */; \ + fcmpgt32 %f8, %S2, %g3 /* FPM Group */; \ + srl %o5, 1, %o5 /* IEU0 */; \ + inc %g1 /* IEU1 */; \ + fpadd32 %S0, %S1, %T0 /* FPA */; \ + fcmpgt32 %f12, %S3, %g5 /* FPM Group */; \ + srl %g1, 1, %g1 /* IEU0 */; \ + add %o2, %o5, %o2 /* IEU1 */; \ + fpadd32 %S2, %S3, %T1 /* FPA */; \ + inc %g2 /* IEU0 Group */; \ + add %o2, %g1, %o2 /* IEU1 */; \ + fcmpgt32 %S0, %T0, %g7 /* FPM Group */; \ + srl %g2, 1, %g2 /* IEU0 */; \ + inc %g3 /* IEU1 */; \ + fcmpgt32 %S2, %T1, %o3 /* FPM Group */; \ + srl %g3, 1, %g3 /* IEU0 */; \ + add %o2, %g2, %o2 /* IEU1 */; \ + inc %g5 /* IEU0 Group */; \ + add %o2, %g3, %o2 /* IEU1 */; \ + fcmpgt32 %fz, %f2, %o4 /* FPM Group */; \ + srl %g5, 1, %g5 /* IEU0 */; \ + inc %g7 /* IEU1 */; \ + fpadd32 %T0, %T1, %U0 /* FPA */; \ + fcmpgt32 %fz, %f6, %o5 /* FPM Group */; \ + srl %g7, 1, %g7 /* IEU0 */; \ + add %o2, %g5, %o2 /* IEU1 */; \ + inc %o3 /* IEU0 Group */; \ + add %o2, %g7, %o2 /* IEU1 */; \ + fcmpgt32 %fz, %f10, %g1 /* FPM Group */; \ + srl %o3, 1, %o3 /* IEU0 */; \ + inc %o4 /* IEU1 */; \ + fcmpgt32 %fz, %f14, %g2 /* FPM Group */; \ + srl %o4, 1, %o4 /* IEU0 */; \ + add %o2, %o3, %o2 /* IEU1 */; \ + std %U0, [%sp + STACKOFF] /* Store Group */; \ + inc %o5 /* IEU0 */; \ + sub %o2, %o4, %o2 /* IEU1 */; \ + fcmpgt32 %fz, %S1, %g3 /* FPM Group */; \ + srl %o5, 1, %o5 /* IEU0 */; \ + inc %g1 /* IEU1 */; \ + fcmpgt32 %fz, %S3, %g5 /* FPM Group */; \ + srl %g1, 1, %g1 /* IEU0 */; \ + sub %o2, %o5, %o2 /* IEU1 */; \ + ldx [%sp + STACKOFF], %o5 /* Load Group */; \ + inc %g2 /* IEU0 */; \ + sub %o2, %g1, %o2 /* IEU1 */; \ + fcmpgt32 %fz, %T1, %g7 /* FPM Group */; \ + srl %g2, 1, %g2 /* IEU0 */; \ + inc %g3 /* IEU1 */; \ + fcmpgt32 %T0, %U0, %o3 /* FPM Group */; \ + srl %g3, 1, %g3 /* IEU0 */; \ + sub %o2, %g2, %o2 /* IEU1 */; \ + inc %g5 /* IEU0 Group */; \ + sub %o2, %g3, %o2 /* IEU1 */; \ + fcmpgt32 %fz, %U0, %o4 /* FPM Group */; \ + srl %g5, 1, %g5 /* IEU0 */; \ + inc %g7 /* IEU1 */; \ + srl %g7, 1, %g7 /* IEU0 Group */; \ + sub %o2, %g5, %o2 /* IEU1 */; \ + inc %o3 /* IEU0 Group */; \ + sub %o2, %g7, %o2 /* IEU1 */; \ + srl %o3, 1, %o3 /* IEU0 Group */; \ + inc %o4 /* IEU1 */; \ + srl %o4, 1, %o4 /* IEU0 Group */; \ + add %o2, %o3, %o2 /* IEU1 */; \ + sub %o2, %o4, %o2 /* IEU0 Group */; \ + addcc %o2, %o5, %o2 /* IEU1 Group */; \ + bcs,a,pn %xcc, 33f /* CTI */; \ + add %o2, 1, %o2 /* IEU0 */; \ +33: /* That's it */; + +#define CSUM_LASTCHUNK(offset) \ + ldx [%o0 - offset - 0x10], %g2; \ + ldx [%o0 - offset - 0x08], %g3; \ + addcc %g2, %o2, %o2; \ + bcs,a,pn %xcc, 31f; \ + add %o2, 1, %o2; \ +31: addcc %g3, %o2, %o2; \ + bcs,a,pn %xcc, 32f; \ + add %o2, 1, %o2; \ +32: + + .text + .globl csum_partial + .align 32 +csum_partial: + andcc %o0, 7, %g0 /* IEU1 Group */ + be,pt %icc, 4f /* CTI */ + andcc %o0, 0x38, %g3 /* IEU1 */ + mov 1, %g5 /* IEU0 Group */ + cmp %o1, 6 /* IEU1 */ + bl,pn %icc, 21f /* CTI */ + andcc %o0, 2, %g0 /* IEU1 Group */ + be,pt %icc, 1f /* CTI */ + and %o0, 4, %g7 /* IEU0 */ + lduh [%o0], %g2 /* Load */ + sub %o1, 2, %o1 /* IEU0 Group */ + add %o0, 2, %o0 /* IEU1 */ + andcc %o0, 4, %g7 /* IEU1 Group */ + sll %g5, 16, %g5 /* IEU0 */ + sll %g2, 16, %g2 /* IEU0 Group */ + addcc %g2, %o2, %o2 /* IEU1 Group (regdep) */ + bcs,a,pn %icc, 1f /* CTI */ + add %o2, %g5, %o2 /* IEU0 */ +1: ld [%o0], %g2 /* Load */ + brz,a,pn %g7, 4f /* CTI+IEU1 Group */ + and %o0, 0x38, %g3 /* IEU0 */ + add %o0, 4, %o0 /* IEU0 Group */ + sub %o1, 4, %o1 /* IEU1 */ + addcc %g2, %o2, %o2 /* IEU1 Group */ + bcs,a,pn %icc, 1f /* CTI */ + add %o2, 1, %o2 /* IEU0 */ +1: and %o0, 0x38, %g3 /* IEU1 Group */ +4: srl %o2, 0, %o2 /* IEU0 Group */ + mov 0x40, %g1 /* IEU1 */ + brz,pn %g3, 3f /* CTI+IEU1 Group */ + sub %g1, %g3, %g1 /* IEU0 */ + cmp %o1, 56 /* IEU1 Group */ + blu,pn %icc, 20f /* CTI */ + andcc %o0, 8, %g0 /* IEU1 Group */ + be,pn %icc, 1f /* CTI */ + ldx [%o0], %g2 /* Load */ + add %o0, 8, %o0 /* IEU0 Group */ + sub %o1, 8, %o1 /* IEU1 */ + addcc %g2, %o2, %o2 /* IEU1 Group */ + bcs,a,pn %xcc, 1f /* CTI */ + add %o2, 1, %o2 /* IEU0 */ +1: andcc %g1, 0x10, %g0 /* IEU1 Group */ + be,pn %icc, 2f /* CTI */ + and %g1, 0x20, %g1 /* IEU0 */ + ldx [%o0], %g2 /* Load */ + ldx [%o0+8], %g3 /* Load Group */ + add %o0, 16, %o0 /* IEU0 */ + sub %o1, 16, %o1 /* IEU1 */ + addcc %g2, %o2, %o2 /* IEU1 Group */ + bcs,a,pn %xcc, 1f /* CTI */ + add %o2, 1, %o2 /* IEU0 */ +1: addcc %g3, %o2, %o2 /* IEU1 Group */ + bcs,a,pn %xcc, 2f /* CTI */ + add %o2, 1, %o2 /* IEU0 */ +2: brz,pn %g1, 3f /* CTI+IEU1 Group */ + ldx [%o0], %g2 /* Load */ + ldx [%o0+8], %g3 /* Load Group */ + ldx [%o0+16], %g5 /* Load Group */ + ldx [%o0+24], %g7 /* Load Group */ + add %o0, 32, %o0 /* IEU0 */ + sub %o1, 32, %o1 /* IEU1 */ + addcc %g2, %o2, %o2 /* IEU1 Group */ + bcs,a,pn %xcc, 1f /* CTI */ + add %o2, 1, %o2 /* IEU0 */ +1: addcc %g3, %o2, %o2 /* IEU1 Group */ + bcs,a,pn %xcc, 1f /* CTI */ + add %o2, 1, %o2 /* IEU0 */ +1: addcc %g5, %o2, %o2 /* IEU1 Group */ + bcs,a,pn %xcc, 1f /* CTI */ + add %o2, 1, %o2 /* IEU0 */ +1: addcc %g7, %o2, %o2 /* IEU1 Group */ + bcs,a,pn %xcc, 3f /* CTI */ + add %o2, 1, %o2 /* IEU0 */ +3: cmp %o1, 0xc0 /* IEU1 Group */ + blu,pn %icc, 20f /* CTI */ + sllx %o2, 32, %g1 /* IEU0 */ + addcc %o2, %g1, %o2 /* IEU1 Group */ + sub %o1, 0xc0, %o1 /* IEU0 */ + wr %g0, ASI_BLK_P, %asi /* LSU Group */ +#ifdef __KERNEL__ + wr %g0, FPRS_FEF, %fprs /* LSU Group */ +#endif + membar #StoreLoad /* LSU Group */ + srlx %o2, 32, %o2 /* IEU0 Group */ + bcs,a,pn %xcc, 1f /* CTI */ + add %o2, 1, %o2 /* IEU1 */ +1: andcc %o1, 0x80, %g0 /* IEU1 Group */ + bne,pn %icc, 7f /* CTI */ + andcc %o1, 0x40, %g0 /* IEU1 Group */ + be,pn %icc, 6f /* CTI */ + fzero %f12 /* FPA */ + fzero %f14 /* FPA Group */ + ldda [%o0 + 0x000] %asi, %f16 + ldda [%o0 + 0x040] %asi, %f32 + ldda [%o0 + 0x080] %asi, %f48 + START_THE_TRICK(f12,f16,f18,f20,f22,f24,f26) + ba,a,pt %xcc, 3f +6: sub %o0, 0x40, %o0 /* IEU0 Group */ + fzero %f28 /* FPA */ + fzero %f30 /* FPA Group */ + ldda [%o0 + 0x040] %asi, %f32 + ldda [%o0 + 0x080] %asi, %f48 + ldda [%o0 + 0x0c0] %asi, %f0 + START_THE_TRICK(f28,f32,f34,f36,f38,f40,f42) + ba,a,pt %xcc, 4f +7: bne,pt %icc, 8f /* CTI */ + fzero %f44 /* FPA */ + add %o0, 0x40, %o0 /* IEU0 Group */ + fzero %f60 /* FPA */ + fzero %f62 /* FPA Group */ + ldda [%o0 - 0x040] %asi, %f0 + ldda [%o0 + 0x000] %asi, %f16 + ldda [%o0 + 0x040] %asi, %f32 + START_THE_TRICK(f60,f0,f2,f4,f6,f8,f10) + ba,a,pt %xcc, 2f +8: add %o0, 0x80, %o0 /* IEU0 Group */ + fzero %f46 /* FPA */ + ldda [%o0 - 0x080] %asi, %f48 + ldda [%o0 - 0x040] %asi, %f0 + ldda [%o0 + 0x000] %asi, %f16 + START_THE_TRICK(f44,f48,f50,f52,f54,f56,f58) +1: DO_THE_TRICK(f44,f46,f48,f50,f52,f54,f56,f58,f60,f62,f0,f2,f4,f6,f8,f10,f12,f14) + ldda [%o0 + 0x040] %asi, %f32 +2: DO_THE_TRICK(f60,f62,f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30) + ldda [%o0 + 0x080] %asi, %f48 +3: DO_THE_TRICK(f12,f14,f16,f18,f20,f22,f24,f26,f28,f30,f32,f34,f36,f38,f40,f42,f44,f46) + ldda [%o0 + 0x0c0] %asi, %f0 +4: DO_THE_TRICK(f28,f30,f32,f34,f36,f38,f40,f42,f44,f46,f48,f50,f52,f54,f56,f58,f60,f62) + add %o0, 0x100, %o0 /* IEU0 Group */ + subcc %o1, 0x100, %o1 /* IEU1 */ + bgeu,a,pt %icc, 1b /* CTI */ + ldda [%o0 + 0x000] %asi, %f16 + membar #Sync /* LSU Group */ + DO_THE_TRICK(f44,f46,f48,f50,f52,f54,f56,f58,f60,f62,f0,f2,f4,f6,f8,f10,f12,f14) + END_THE_TRICK(f60,f62,f0,f2,f4,f6,f8,f10,f12,f14,f16,f18,f20,f22,f24,f26,f28,f30) + and %o1, 0x3f, %o1 /* IEU0 Group */ +#ifdef __KERNEL__ + wr %g0, 0, %fprs /* LSU Group */ +#endif +20: andcc %o1, 0xf0, %g1 /* IEU1 Group */ + be,pn %icc, 23f /* CTI */ + and %o1, 0xf, %o3 /* IEU0 */ +22: rd %pc, %g7 /* LSU Group */ + sll %g1, 1, %o4 /* IEU0 Group */ + sub %g7, %o4, %g7 /* IEU0 Group (regdep) */ + jmpl %g7 + (23f - 22b), %g0 /* CTI Group brk forced */ + add %o0, %g1, %o0 /* IEU0 */ + CSUM_LASTCHUNK(0xe0) + CSUM_LASTCHUNK(0xd0) + CSUM_LASTCHUNK(0xc0) + CSUM_LASTCHUNK(0xb0) + CSUM_LASTCHUNK(0xa0) + CSUM_LASTCHUNK(0x90) + CSUM_LASTCHUNK(0x80) + CSUM_LASTCHUNK(0x70) + CSUM_LASTCHUNK(0x60) + CSUM_LASTCHUNK(0x50) + CSUM_LASTCHUNK(0x40) + CSUM_LASTCHUNK(0x30) + CSUM_LASTCHUNK(0x20) + CSUM_LASTCHUNK(0x10) + CSUM_LASTCHUNK(0x00) +23: brnz,pn %o3, 26f /* CTI+IEU1 Group */ +24: sllx %o2, 32, %g1 /* IEU0 */ +25: addcc %o2, %g1, %o0 /* IEU1 Group */ + srlx %o0, 32, %o0 /* IEU0 Group (regdep) */ + bcs,a,pn %xcc, 1f /* CTI */ + add %o0, 1, %o0 /* IEU1 */ +1: retl /* CTI Group brk forced */ + srl %o0, 0, %o0 /* IEU0 */ +26: andcc %o1, 8, %g0 /* IEU1 Group */ + be,pn %icc, 1f /* CTI */ + ldx [%o0], %g3 /* Load */ + add %o0, 8, %o0 /* IEU0 Group */ + addcc %g3, %o2, %o2 /* IEU1 Group */ + bcs,a,pn %xcc, 1f /* CTI */ + add %o2, 1, %o2 /* IEU0 */ +1: andcc %o1, 4, %g0 /* IEU1 Group */ + be,a,pn %icc, 1f /* CTI */ + clr %g2 /* IEU0 */ + ld [%o0], %g2 /* Load */ + add %o0, 4, %o0 /* IEU0 Group */ + sllx %g2, 32, %g2 /* IEU0 Group */ +1: andcc %o1, 2, %g0 /* IEU1 */ + be,a,pn %icc, 1f /* CTI */ + clr %o4 /* IEU0 Group */ + lduh [%o0], %o4 /* Load */ + add %o0, 2, %o0 /* IEU1 */ + sll %o4, 16, %o4 /* IEU0 Group */ +1: andcc %o1, 1, %g0 /* IEU1 */ + be,a,pn %icc, 1f /* CTI */ + clr %o5 /* IEU0 Group */ + ldub [%o0], %o5 /* Load */ + sll %o5, 8, %o5 /* IEU0 Group */ +1: or %g2, %o4, %o4 /* IEU1 */ + or %o5, %o4, %o4 /* IEU0 Group (regdep) */ + addcc %o4, %o2, %o2 /* IEU1 Group (regdep) */ + bcs,a,pn %xcc, 1f /* CTI */ + add %o2, 1, %o2 /* IEU0 */ +1: ba,pt %xcc, 25b /* CTI Group */ + sllx %o2, 32, %g1 /* IEU0 */ +21: srl %o2, 0, %o2 /* IEU0 Group */ + cmp %o1, 0 /* IEU1 */ + be,pn %icc, 24b /* CTI */ + andcc %o1, 4, %g0 /* IEU1 Group */ + be,a,pn %icc, 1f /* CTI */ + clr %g2 /* IEU0 */ + lduh [%o0], %g3 /* Load */ + lduh [%o0+2], %g2 /* Load Group */ + add %o0, 4, %o0 /* IEU0 Group */ + sllx %g3, 48, %g3 /* IEU0 Group */ + sllx %g2, 32, %g2 /* IEU0 Group */ + or %g3, %g2, %g2 /* IEU0 Group */ +1: andcc %o1, 2, %g0 /* IEU1 */ + be,a,pn %icc, 1f /* CTI */ + clr %o4 /* IEU0 Group */ + lduh [%o0], %o4 /* Load */ + add %o0, 2, %o0 /* IEU1 */ + sll %o4, 16, %o4 /* IEU0 Group */ +1: andcc %o1, 1, %g0 /* IEU1 */ + be,a,pn %icc, 1f /* CTI */ + clr %o5 /* IEU0 Group */ + ldub [%o0], %o5 /* Load */ + sll %o5, 8, %o5 /* IEU0 Group */ +1: or %g2, %o4, %o4 /* IEU1 */ + or %o5, %o4, %o4 /* IEU0 Group (regdep) */ + addcc %o4, %o2, %o2 /* IEU1 Group (regdep) */ + bcs,a,pn %xcc, 1f /* CTI */ + add %o2, 1, %o2 /* IEU0 */ +1: ba,pt %xcc, 25b /* CTI Group */ + sllx %o2, 32, %g1 /* IEU0 */ diff --git a/arch/sparc64/lib/VISmemset.S b/arch/sparc64/lib/VISmemset.S new file mode 100644 index 000000000..d674f2a6e --- /dev/null +++ b/arch/sparc64/lib/VISmemset.S @@ -0,0 +1,228 @@ +/* $Id: VISmemset.S,v 1.1 1997/07/18 06:26:49 ralf Exp $ + * VISmemset.S: High speed memset operations utilizing the UltraSparc + * Visual Instruction Set. + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1996, 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#include "VIS.h" + +#ifdef REGS_64BIT +#define SET_BLOCKS(base, offset, source) \ + stx source, [base - offset - 0x18]; \ + stx source, [base - offset - 0x10]; \ + stx source, [base - offset - 0x08]; \ + stx source, [base - offset - 0x00]; +#else +#define SET_BLOCKS(base, offset, source) \ + stw source, [base - offset - 0x18]; \ + stw source, [base - offset - 0x14]; \ + stw source, [base - offset - 0x10]; \ + stw source, [base - offset - 0x0c]; \ + stw source, [base - offset - 0x08]; \ + stw source, [base - offset - 0x04]; \ + stw source, [base - offset - 0x00]; \ + stw source, [base - offset + 0x04]; +#endif + +#ifndef __KERNEL__ +/* So that the brz,a,pt in memset doesn't have to get through PLT, here we go... */ +#include "VISbzero.S" +#endif + +#ifdef __KERNEL__ +#define RETL clr %o0 +#else +#define RETL mov %g3, %o0 +#endif + + /* Well, memset is a lot easier to get right than bcopy... */ + .text + .align 32 +#ifdef __KERNEL__ + .globl __memset +__memset: +#endif + .globl memset +memset: +#ifndef __KERNEL__ + brz,a,pt %o1, bzero_private + mov %o2, %o1 +#ifndef REGS_64BIT + srl %o2, 0, %o2 +#endif + mov %o0, %g3 +#endif + cmp %o2, 7 + bleu,pn %xcc, 17f + andcc %o0, 3, %g5 + be,pt %xcc, 4f + and %o1, 0xff, %o1 + cmp %g5, 3 + be,pn %xcc, 2f + stb %o1, [%o0 + 0x00] + cmp %g5, 2 + be,pt %xcc, 2f + stb %o1, [%o0 + 0x01] + stb %o1, [%o0 + 0x02] +2: sub %g5, 4, %g5 + sub %o0, %g5, %o0 + add %o2, %g5, %o2 +4: sllx %o1, 8, %g1 + andcc %o0, 4, %g0 + or %o1, %g1, %o1 + sllx %o1, 16, %g1 + or %o1, %g1, %o1 + be,pt %xcc, 2f +#ifdef REGS_64BIT + sllx %o1, 32, %g1 +#else + cmp %o2, 128 +#endif + stw %o1, [%o0] + sub %o2, 4, %o2 + add %o0, 4, %o0 +2: +#ifdef REGS_64BIT + cmp %o2, 128 + or %o1, %g1, %o1 +#endif + blu,pn %xcc, 9f + andcc %o0, 0x38, %g5 + be,pn %icc, 6f + mov 64, %o5 + andcc %o0, 8, %g0 + be,pn %icc, 1f + sub %o5, %g5, %o5 +#ifdef REGS_64BIT + stx %o1, [%o0] +#else + stw %o1, [%o0] + stw %o1, [%o0 + 4] +#endif + add %o0, 8, %o0 +1: andcc %o5, 16, %g0 + be,pn %icc, 1f + sub %o2, %o5, %o2 +#ifdef REGS_64BIT + stx %o1, [%o0] + stx %o1, [%o0 + 8] +#else + stw %o1, [%o0] + stw %o1, [%o0 + 4] + stw %o1, [%o0 + 8] + stw %o1, [%o0 + 12] +#endif + add %o0, 16, %o0 +1: andcc %o5, 32, %g0 + be,pn %icc, 7f + andncc %o2, 0x3f, %o3 +#ifdef REGS_64BIT + stx %o1, [%o0] + stx %o1, [%o0 + 8] + stx %o1, [%o0 + 16] + stx %o1, [%o0 + 24] +#else + stw %o1, [%o0] + stw %o1, [%o0 + 4] + stw %o1, [%o0 + 8] + stw %o1, [%o0 + 12] + stw %o1, [%o0 + 16] + stw %o1, [%o0 + 20] + stw %o1, [%o0 + 24] + stw %o1, [%o0 + 28] +#endif + add %o0, 32, %o0 +7: be,pn %xcc, 9f +#ifdef __KERNEL__ + wr %g0, FPRS_FEF, %fprs +#endif + ldd [%o0 - 8], %f0 +18: wr %g0, ASI_BLK_P, %asi + membar #StoreStore | #LoadStore + andcc %o3, 0xc0, %g5 + and %o2, 0x3f, %o2 + fmovd %f0, %f2 + fmovd %f0, %f4 + andn %o3, 0xff, %o3 + fmovd %f0, %f6 + cmp %g5, 64 + fmovd %f0, %f8 + fmovd %f0, %f10 + fmovd %f0, %f12 + brz,pn %g5, 10f + fmovd %f0, %f14 + be,pn %icc, 2f + stda %f0, [%o0 + 0x00] %asi + cmp %g5, 128 + be,pn %icc, 2f + stda %f0, [%o0 + 0x40] %asi + stda %f0, [%o0 + 0x80] %asi +2: brz,pn %o3, 12f + add %o0, %g5, %o0 +10: stda %f0, [%o0 + 0x00] %asi + stda %f0, [%o0 + 0x40] %asi + stda %f0, [%o0 + 0x80] %asi + stda %f0, [%o0 + 0xc0] %asi +11: subcc %o3, 256, %o3 + bne,pt %xcc, 10b + add %o0, 256, %o0 +12: +#ifdef __KERNEL__ + wr %g0, 0, %fprs +#endif + membar #Sync +9: andcc %o2, 0x78, %g5 + be,pn %xcc, 13f + andcc %o2, 7, %o2 +14: rd %pc, %o4 +#ifdef REGS_64BIT + srl %g5, 1, %o3 + sub %o4, %o3, %o4 +#else + sub %o4, %g5, %o4 +#endif + jmpl %o4 + (13f - 14b), %g0 + add %o0, %g5, %o0 +12: SET_BLOCKS(%o0, 0x68, %o1) + SET_BLOCKS(%o0, 0x48, %o1) + SET_BLOCKS(%o0, 0x28, %o1) + SET_BLOCKS(%o0, 0x08, %o1) +13: be,pn %xcc, 8f + andcc %o2, 4, %g0 + be,pn %xcc, 1f + andcc %o2, 2, %g0 + stw %o1, [%o0] + add %o0, 4, %o0 +1: be,pn %xcc, 1f + andcc %o2, 1, %g0 + sth %o1, [%o0] + add %o0, 2, %o0 +1: bne,a,pn %xcc, 8f + stb %o1, [%o0] +8: retl + RETL +17: brz,pn %o2, 0f +8: add %o0, 1, %o0 + subcc %o2, 1, %o2 + bne,pt %xcc, 8b + stb %o1, [%o0 - 1] +0: retl + RETL +6: +#ifdef REGS_64BIT + stx %o1, [%o0] +#else + stw %o1, [%o0] + stw %o1, [%o0 + 4] +#endif + andncc %o2, 0x3f, %o3 + be,pn %xcc, 9b +#ifdef __KERNEL__ + wr %g0, FPRS_FEF, %fprs +#else + nop +#endif + ba,pt %xcc, 18b + ldd [%o0], %f0 diff --git a/arch/sparc64/lib/blockops.S b/arch/sparc64/lib/blockops.S index d0f023d1b..59083aa02 100644 --- a/arch/sparc64/lib/blockops.S +++ b/arch/sparc64/lib/blockops.S @@ -1,138 +1,70 @@ -/* $Id: blockops.S,v 1.6 1997/05/18 04:16:49 davem Exp $ +/* $Id: blockops.S,v 1.10 1997/06/24 17:29:10 jj Exp $ * arch/sparc64/lib/blockops.S: UltraSparc block zero optimized routines. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ -#include <asm/asi.h> - - /* Zero out 256 bytes of memory at (buf + offset). */ -#define BLAST_BLOCK(buf, offset) \ - stda %f48, [buf + offset + 0x00] %asi; \ - stda %f48, [buf + offset + 0x40] %asi; \ - stda %f48, [buf + offset + 0x80] %asi; \ - stda %f48, [buf + offset + 0xc0] %asi; - - /* Copy 256 bytes of memory at (src + offset) to - * (dst + offset). - */ -#define MIRROR_BLOCK(dst, src, offset, sync) \ - ldda [src + offset + 0x000] %asi, %f0; \ - ldda [src + offset + 0x040] %asi, %f16; \ - ldda [src + offset + 0x080] %asi, %f32; \ - ldda [src + offset + 0x0c0] %asi, %f48; \ - membar sync; \ - stda %f0, [dst + offset + 0x000] %asi; \ - stda %f16, [dst + offset + 0x040] %asi; \ - stda %f32, [dst + offset + 0x080] %asi; \ - stda %f48, [dst + offset + 0x0c0] %asi; +#include "VIS.h" .text - .align 4 - -#if 0 - .globl bzero_1page -bzero_1page: - /* %o0 = buf */ - mov %o0, %o1 - wr %g0, ASI_BLK_P, %asi - mov 0x08, %g2 - membar #Sync|#StoreLoad - fzero %f48 - fzero %f50 - fzero %f52 - fzero %f54 - fzero %f56 - fzero %f58 - fzero %f60 - fzero %f62 -1: - BLAST_BLOCK(%o0, 0x000) - BLAST_BLOCK(%o0, 0x100) - BLAST_BLOCK(%o0, 0x200) - BLAST_BLOCK(%o0, 0x300) - subcc %g2, 1, %g2 - bne,pt %icc, 1b - add %o0, 0x400, %o0 - - membar #Sync|#LoadStore|#StoreStore - - retl - mov %o1, %o0 -#endif + .align 32 .globl __bfill64 -__bfill64: -#if 1 - /* %o0 = buf, %o1 = 64-bit pattern */ -#define FILL_BLOCK(buf, offset) \ - stx %o1, [buf + offset + 0x38]; \ - stx %o1, [buf + offset + 0x30]; \ - stx %o1, [buf + offset + 0x28]; \ - stx %o1, [buf + offset + 0x20]; \ - stx %o1, [buf + offset + 0x18]; \ - stx %o1, [buf + offset + 0x10]; \ - stx %o1, [buf + offset + 0x08]; \ - stx %o1, [buf + offset + 0x00]; +__bfill64: /* %o0 = buf, %o1= ptr to pattern */ + wr %g0, FPRS_FEF, %fprs ! FPU Group + ldd [%o1], %f48 ! Load Group + wr %g0, ASI_BLK_P, %asi ! LSU Group + membar #StoreStore | #LoadStore ! LSU Group + mov 32, %g2 ! IEU0 Group + + /* Cannot perform real arithmatic on the pattern, that can + * lead to fp_exception_other ;-) + */ + fmovd %f48, %f50 ! FPA Group + fmovd %f48, %f52 ! FPA Group + fmovd %f48, %f54 ! FPA Group + fmovd %f48, %f56 ! FPA Group + fmovd %f48, %f58 ! FPA Group + fmovd %f48, %f60 ! FPA Group + fmovd %f48, %f62 ! FPA Group - mov 0x20, %g2 -1: - FILL_BLOCK(%o0, 0x00) - FILL_BLOCK(%o0, 0x40) - FILL_BLOCK(%o0, 0x80) - FILL_BLOCK(%o0, 0xc0) - subcc %g2, 1, %g2 - bne,pt %icc, 1b - add %o0, 0x100, %o0 - retl - nop -#undef FILL_BLOCK +1: stda %f48, [%o0 + 0x00] %asi ! Store Group + stda %f48, [%o0 + 0x40] %asi ! Store Group + stda %f48, [%o0 + 0x80] %asi ! Store Group + stda %f48, [%o0 + 0xc0] %asi ! Store Group + subcc %g2, 1, %g2 ! IEU1 Group + bne,pt %icc, 1b ! CTI + add %o0, 0x100, %o0 ! IEU0 + membar #Sync ! LSU Group -#else - /* %o0 = buf */ - stx %o1, [%sp + 0x7ff + 128] - wr %g0, ASI_BLK_P, %asi - mov 0x08, %g2 - ldd [%sp + 0x7ff + 128], %f48 - membar #Sync|#StoreLoad - fmovd %f48, %f50 - fmovd %f48, %f52 - fmovd %f48, %f54 - fmovd %f48, %f56 - fmovd %f48, %f58 - fmovd %f48, %f60 - fmovd %f48, %f62 -1: - BLAST_BLOCK(%o0, 0x000) - BLAST_BLOCK(%o0, 0x100) - BLAST_BLOCK(%o0, 0x200) - BLAST_BLOCK(%o0, 0x300) - subcc %g2, 1, %g2 - bne,pt %icc, 1b - add %o0, 0x400, %o0 + jmpl %o7 + 0x8, %g0 ! CTI Group brk forced + wr %g0, 0, %fprs ! FPU Group - retl - membar #Sync|#LoadStore|#StoreStore -#endif + .align 32 + .globl __bzero_1page +__bzero_1page: + wr %g0, FPRS_FEF, %fprs ! FPU Group + fzero %f0 ! FPA Group + mov 32, %g1 ! IEU0 + fzero %f2 ! FPA Group + faddd %f0, %f2, %f4 ! FPA Group + fmuld %f0, %f2, %f6 ! FPM + faddd %f0, %f2, %f8 ! FPA Group + fmuld %f0, %f2, %f10 ! FPM -#if 0 - .globl __copy_1page -__copy_1page: - /* %o0 = dst, %o1 = src */ - or %g0, 0x08, %g1 - wr %g0, ASI_BLK_P, %asi - membar #Sync|#StoreLoad -1: - MIRROR_BLOCK(%o0, %o1, 0x000, #Sync) - MIRROR_BLOCK(%o0, %o1, 0x100, #Sync) - MIRROR_BLOCK(%o0, %o1, 0x200, #Sync) - MIRROR_BLOCK(%o0, %o1, 0x300, #Sync) - subcc %g1, 1, %g1 - add %o0, 0x400, %o0 - bne,pt %icc, 1b - add %o1, 0x400, %o1 + faddd %f0, %f2, %f12 ! FPA Group + fmuld %f0, %f2, %f14 ! FPM + wr %g0, ASI_BLK_P, %asi ! LSU Group + membar #StoreStore | #LoadStore ! LSU Group +1: stda %f0, [%o0 + 0x00] %asi ! Store Group + stda %f0, [%o0 + 0x40] %asi ! Store Group + stda %f0, [%o0 + 0x80] %asi ! Store Group + stda %f0, [%o0 + 0xc0] %asi ! Store Group - retl - membar #Sync|#LoadStore|#StoreStore -#endif + subcc %g1, 1, %g1 ! IEU1 + bne,pt %icc, 1b ! CTI + add %o0, 0x100, %o0 ! IEU0 Group + membar #Sync ! LSU Group + jmpl %o7 + 0x8, %g0 ! CTI Group brk forced + wr %g0, 0, %fprs ! FPU Group diff --git a/arch/sparc64/lib/checksum.S b/arch/sparc64/lib/checksum.S index 10eebb8df..703370fc6 100644 --- a/arch/sparc64/lib/checksum.S +++ b/arch/sparc64/lib/checksum.S @@ -17,383 +17,398 @@ #include <asm/head.h> #include <asm/ptrace.h> #include <asm/asi.h> +#include <asm/page.h> -#define CSUM_BIGCHUNK(buf, offset, sum, t0, t1, t2, t3, t4, t5) \ - ldd [buf + offset + 0x00], t0; \ - ldd [buf + offset + 0x08], t2; \ - addccc t0, sum, sum; \ - addccc t1, sum, sum; \ - ldd [buf + offset + 0x10], t4; \ - addccc t2, sum, sum; \ - addccc t3, sum, sum; \ - ldd [buf + offset + 0x18], t0; \ - addccc t4, sum, sum; \ - addccc t5, sum, sum; \ - addccc t0, sum, sum; \ - addccc t1, sum, sum; - -#define CSUM_LASTCHUNK(buf, offset, sum, t0, t1, t2, t3) \ - ldd [buf - offset - 0x08], t0; \ - ldd [buf - offset - 0x00], t2; \ - addccc t0, sum, sum; \ - addccc t1, sum, sum; \ - addccc t2, sum, sum; \ - addccc t3, sum, sum; - - /* Do end cruft out of band to get better cache patterns. */ -csum_partial_end_cruft: - andcc %o1, 8, %g0 ! check how much - be,pn %icc, 1f ! caller asks %o1 & 0x8 - and %o1, 4, %g5 ! nope, check for word remaining - ldd [%o0], %g2 ! load two - addcc %g2, %o2, %o2 ! add first word to sum - addccc %g3, %o2, %o2 ! add second word as well - add %o0, 8, %o0 ! advance buf ptr - addc %g0, %o2, %o2 ! add in final carry -1: brz,pn %g5, 1f ! nope, skip this code - andcc %o1, 3, %o1 ! check for trailing bytes - ld [%o0], %g2 ! load it - addcc %g2, %o2, %o2 ! add to sum - add %o0, 4, %o0 ! advance buf ptr - addc %g0, %o2, %o2 ! add in final carry -1: brz,pn %o1, 1f ! no trailing bytes, return - addcc %o1, -1, %g0 ! only one byte remains? - bne,pn %icc, 2f ! at least two bytes more - subcc %o1, 2, %o1 ! only two bytes more? - ba,pt %xcc, 4f ! only one byte remains - clr %o4 ! clear fake hword value -2: lduh [%o0], %o4 ! get hword - be,pn %icc, 6f ! jmp if only hword remains - add %o0, 2, %o0 ! advance buf ptr either way - sll %o4, 16, %o4 ! create upper hword -4: ldub [%o0], %o5 ! get final byte - sll %o5, 8, %o5 ! put into place - or %o5, %o4, %o4 ! coalese with hword (if any) -6: addcc %o4, %o2, %o2 ! add to sum -1: sllx %g4, 32, %g4 ! give gfp back - addc %g0, %o2, %o0 ! add final carry into retval - retl ! get outta here - srl %o0, 0, %o0 - - /* Also do alignment out of band to get better cache patterns. */ -csum_partial_fix_alignment: - - /* The common case is to get called with a nicely aligned - * buffer of size 0x20. Follow the code path for that case. + /* The problem with the "add with carry" instructions on Ultra + * are two fold. Firstly, they cannot pair with jack shit, + * and also they only add in the 32-bit carry condition bit + * into the accumulated sum. The following is much better. + * + * This should run at max bandwidth for ecache hits, a better + * technique is to use VIS and fpu operations. This is already + * done for csum_partial, needs to be written for the copy stuff + * still. */ - .globl csum_partial -csum_partial: /* %o0=buf, %o1=len, %o2=sum */ - srl %o1, 0, %o1 ! doof scheiss - andcc %o0, 0x7, %g0 ! alignment problems? - srl %o2, 0, %o2 - be,pt %icc, csum_partial_fix_aligned ! yep, handle it - andn %o1, 0x7f, %o3 ! num loop iterations - cmp %o1, 6 - bl,pn %icc, cpte - 0x4 - andcc %o0, 0x2, %g0 - be,pn %icc, 1f - and %o0, 0x4, %g7 - lduh [%o0 + 0x00], %g2 - sub %o1, 2, %o1 - add %o0, 2, %o0 - sll %g2, 16, %g2 - addcc %g2, %o2, %o2 - srl %o2, 16, %g3 - addc %g0, %g3, %g2 - sll %o2, 16, %o2 - and %o0, 0x4, %g7 - sll %g2, 16, %g3 - srl %o2, 16, %o2 - or %g3, %o2, %o2 -1: brz,pn %g7, csum_partial_fix_aligned - andn %o1, 0x7f, %o3 - ld [%o0 + 0x00], %g2 - sub %o1, 4, %o1 - addcc %g2, %o2, %o2 - add %o0, 4, %o0 - andn %o1, 0x7f, %o3 - addc %g0, %o2, %o2 -csum_partial_fix_aligned: - brz,pt %o3, 3f ! none to do - andcc %o1, 0x70, %g1 ! clears carry flag too -5: CSUM_BIGCHUNK(%o0, 0x00, %o2, %o4, %o5, %g2, %g3, %g4, %g5) - CSUM_BIGCHUNK(%o0, 0x20, %o2, %o4, %o5, %g2, %g3, %g4, %g5) - CSUM_BIGCHUNK(%o0, 0x40, %o2, %o4, %o5, %g2, %g3, %g4, %g5) - CSUM_BIGCHUNK(%o0, 0x60, %o2, %o4, %o5, %g2, %g3, %g4, %g5) - addc %g0, %o2, %o2 ! sink in final carry - subcc %o3, 128, %o3 ! detract from loop iters - bne,pt %icc, 5b ! more to do - add %o0, 128, %o0 ! advance buf ptr -3: brz,pn %g1, cpte ! nope - andcc %o1, 0xf, %o3 ! anything left at all? -10: rd %pc, %g7 ! get pc - srl %g1, 1, %o4 ! compute offset - sub %g7, %g1, %g7 ! adjust jmp ptr - sub %g7, %o4, %g7 ! final jmp ptr adjust - jmp %g7 + (11f-10b) ! enter the table - add %o0, %g1, %o0 ! advance buf ptr -cptbl: CSUM_LASTCHUNK(%o0, 0x68, %o2, %g2, %g3, %g4, %g5) - CSUM_LASTCHUNK(%o0, 0x58, %o2, %g2, %g3, %g4, %g5) - CSUM_LASTCHUNK(%o0, 0x48, %o2, %g2, %g3, %g4, %g5) - CSUM_LASTCHUNK(%o0, 0x38, %o2, %g2, %g3, %g4, %g5) - CSUM_LASTCHUNK(%o0, 0x28, %o2, %g2, %g3, %g4, %g5) - CSUM_LASTCHUNK(%o0, 0x18, %o2, %g2, %g3, %g4, %g5) - CSUM_LASTCHUNK(%o0, 0x08, %o2, %g2, %g3, %g4, %g5) -11: addc %g0, %o2, %o2 ! fetch final carry - andcc %o1, 0xf, %o3 ! anything left at all? -cpte: brnz,pn %o3, csum_partial_end_cruft ! yep, handle it - sethi %uhi(KERNBASE), %g4 - mov %o2, %o0 ! return computed csum - retl ! get outta here - sllx %g4, 32, %g4 ! give gfp back + .text .globl __csum_partial_copy_start, __csum_partial_copy_end __csum_partial_copy_start: -#define EX(x,y,a,b,z) \ -98: x,y; \ - .section .fixup,z##alloc,z##execinstr; \ - .align 4; \ -99: ba,pt %xcc, 30f; \ - a, b, %o3; \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword 98b, 99b; \ - .text; \ - .align 4 + /* I think I have an erection... Once _AGAIN_ the SunSoft + * engineers are caught asleep at the keyboard, tsk tsk... + */ +#define CSUMCOPY_ECACHE_LOAD(src, off, t0, t1, t2, t3, t4, t5, t6, t7) \ + ldxa [src + off + 0x00] %asi, t0; \ + ldxa [src + off + 0x08] %asi, t1; \ + ldxa [src + off + 0x10] %asi, t2; \ + ldxa [src + off + 0x18] %asi, t3; \ + ldxa [src + off + 0x20] %asi, t4; \ + ldxa [src + off + 0x28] %asi, t5; \ + ldxa [src + off + 0x30] %asi, t6; \ + ldxa [src + off + 0x38] %asi, t7; \ + nop; nop; /* DO NOT TOUCH THIS!!!!! */ -#define EX2(x,y,z) \ -98: x,y; \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword 98b, 30f; \ - .text; \ - .align 4 +#define CSUMCOPY_EC_STALIGNED_LDNXT(src, dest, off, sum, t0, t1, t2, t3, t4, t5, t6, t7)\ + stx t0, [dest + off - 0x40]; \ + addcc sum, t0, sum; \ + bcc,pt %xcc, 11f; \ + ldxa [src + off + 0x00] %asi, t0; \ + add sum, 1, sum; \ +11: stx t1, [dest + off - 0x38]; \ + addcc sum, t1, sum; \ + bcc,pt %xcc, 12f; \ + ldxa [src + off + 0x08] %asi, t1; \ + add sum, 1, sum; \ +12: stx t2, [dest + off - 0x30]; \ + addcc sum, t2, sum; \ + bcc,pt %xcc, 13f; \ + ldxa [src + off + 0x10] %asi, t2; \ + add sum, 1, sum; \ +13: stx t3, [dest + off - 0x28]; \ + addcc sum, t3, sum; \ + bcc,pt %xcc, 14f; \ + ldxa [src + off + 0x18] %asi, t3; \ + add sum, 1, sum; \ +14: stx t4, [dest + off - 0x20]; \ + addcc sum, t4, sum; \ + bcc,pt %xcc, 15f; \ + ldxa [src + off + 0x20] %asi, t4; \ + add sum, 1, sum; \ +15: stx t5, [dest + off - 0x18]; \ + addcc sum, t5, sum; \ + bcc,pt %xcc, 16f; \ + ldxa [src + off + 0x28] %asi, t5; \ + add sum, 1, sum; \ +16: stx t6, [dest + off - 0x10]; \ + addcc sum, t6, sum; \ + bcc,pt %xcc, 17f; \ + ldxa [src + off + 0x30] %asi, t6; \ + add sum, 1, sum; \ +17: stx t7, [dest + off - 0x08]; \ + addcc sum, t7, sum; \ + bcc,pt %xcc, 18f; \ + ldxa [src + off + 0x38] %asi, t7; \ + add sum, 1, sum; \ +18: -#define EX3(x,y,z) \ -98: x,y; \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword 98b, 96f; \ - .text; \ - .align 4 +#define CSUMCOPY_EC_STUNALIGN_LDNXT(src, dest, off, sum, t0, t1, t2, t3, t4, t5, t6, t7)\ + stw t0, [dest + off - 0x3c]; \ + addcc sum, t0, sum; \ + srlx t0, 32, t0; \ + stw t0, [dest + off - 0x40]; \ + bcc,pt %xcc, 21f; \ + ldxa [src + off + 0x00] %asi, t0; \ + add sum, 1, sum; \ +21: stw t1, [dest + off - 0x34]; \ + addcc sum, t1, sum; \ + srlx t1, 32, t1; \ + stw t1, [dest + off - 0x38]; \ + bcc,pt %xcc, 22f; \ + ldxa [src + off + 0x08] %asi, t1; \ + add sum, 1, sum; \ +22: stw t2, [dest + off - 0x2c]; \ + addcc sum, t2, sum; \ + srlx t2, 32, t2; \ + stw t2, [dest + off - 0x30]; \ + bcc,pt %xcc, 23f; \ + ldxa [src + off + 0x10] %asi, t2; \ + add sum, 1, sum; \ +23: stw t3, [dest + off - 0x24]; \ + addcc sum, t3, sum; \ + srlx t3, 32, t3; \ + stw t3, [dest + off - 0x28]; \ + bcc,pt %xcc, 24f; \ + ldxa [src + off + 0x18] %asi, t3; \ + add sum, 1, sum; \ +24: stw t4, [dest + off - 0x1c]; \ + addcc sum, t4, sum; \ + srlx t4, 32, t4; \ + stw t4, [dest + off - 0x20]; \ + bcc,pt %xcc, 25f; \ + ldxa [src + off + 0x20] %asi, t4; \ + add sum, 1, sum; \ +25: stw t5, [dest + off - 0x14]; \ + addcc sum, t5, sum; \ + srlx t5, 32, t5; \ + stw t5, [dest + off - 0x18]; \ + bcc,pt %xcc, 26f; \ + ldxa [src + off + 0x28] %asi, t5; \ + add sum, 1, sum; \ +26: stw t6, [dest + off - 0x0c]; \ + addcc sum, t6, sum; \ + srlx t6, 32, t6; \ + stw t6, [dest + off - 0x10]; \ + bcc,pt %xcc, 27f; \ + ldxa [src + off + 0x30] %asi, t6; \ + add sum, 1, sum; \ +27: stw t7, [dest + off - 0x04]; \ + addcc sum, t7, sum; \ + srlx t7, 32, t7; \ + stw t7, [dest + off - 0x08]; \ + bcc,pt %xcc, 28f; \ + ldxa [src + off + 0x38] %asi, t7; \ + add sum, 1, sum; \ +28: -#define EXT(start,end,handler,z) \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword start, 0, end, handler; \ - .text; \ - .align 4 +#define CSUMCOPY_EC_STALIGNED(dest, off, sum, t0, t1, t2, t3, t4, t5, t6, t7) \ + addcc sum, t0, sum; \ + bcc,pt %xcc, 31f; \ + stx t0, [dest + off + 0x00]; \ + add sum, 1, sum; \ +31: addcc sum, t1, sum; \ + bcc,pt %xcc, 32f; \ + stx t1, [dest + off + 0x08]; \ + add sum, 1, sum; \ +32: addcc sum, t2, sum; \ + bcc,pt %xcc, 33f; \ + stx t2, [dest + off + 0x10]; \ + add sum, 1, sum; \ +33: addcc sum, t3, sum; \ + bcc,pt %xcc, 34f; \ + stx t3, [dest + off + 0x18]; \ + add sum, 1, sum; \ +34: addcc sum, t4, sum; \ + bcc,pt %xcc, 35f; \ + stx t4, [dest + off + 0x20]; \ + add sum, 1, sum; \ +35: addcc sum, t5, sum; \ + bcc,pt %xcc, 36f; \ + stx t5, [dest + off + 0x28]; \ + add sum, 1, sum; \ +36: addcc sum, t6, sum; \ + bcc,pt %xcc, 37f; \ + stx t6, [dest + off + 0x30]; \ + add sum, 1, sum; \ +37: addcc sum, t7, sum; \ + bcc,pt %xcc, 38f; \ + stx t7, [dest + off + 0x38]; \ + add sum, 1, sum; \ +38: - /* This aligned version executes typically in 8.5 superscalar cycles, this - * is the best I can do. I say 8.5 because the final add will pair with - * the next ldd in the main unrolled loop. Thus the pipe is always full. - * If you change these macros (including order of instructions), - * please check the fixup code below as well. - */ -#define CSUMCOPY_BIGCHUNK_ALIGNED(src, dst, sum, off, t0, t1, t2, t3, t4, t5, t6, t7) \ - ldda [src + off + 0x00] %asi, t0; \ - ldda [src + off + 0x08] %asi, t2; \ - addccc t0, sum, sum; \ - ldda [src + off + 0x10] %asi, t4; \ - addccc t1, sum, sum; \ - ldda [src + off + 0x18] %asi, t6; \ - addccc t2, sum, sum; \ - std t0, [dst + off + 0x00]; \ - addccc t3, sum, sum; \ - std t2, [dst + off + 0x08]; \ - addccc t4, sum, sum; \ - std t4, [dst + off + 0x10]; \ - addccc t5, sum, sum; \ - std t6, [dst + off + 0x18]; \ - addccc t6, sum, sum; \ - addccc t7, sum, sum; +#define CSUMCOPY_EC_STUNALIGN(dest, off, sum, t0, t1, t2, t3, t4, t5, t6, t7) \ + stw t0, [dest + off + 0x04]; \ + addcc sum, t0, sum; \ + srlx t0, 32, t0; \ + bcc,pt %xcc, 41f; \ + stw t0, [dest + off + 0x00]; \ + add sum, 1, sum; \ +41: stw t1, [dest + off + 0x0c]; \ + addcc sum, t1, sum; \ + srlx t1, 32, t1; \ + bcc,pt %xcc, 42f; \ + stw t1, [dest + off + 0x08]; \ + add sum, 1, sum; \ +42: stw t2, [dest + off + 0x14]; \ + addcc sum, t2, sum; \ + srlx t2, 32, t2; \ + bcc,pt %xcc, 43f; \ + stw t2, [dest + off + 0x10]; \ + add sum, 1, sum; \ +43: stw t3, [dest + off + 0x1c]; \ + addcc sum, t3, sum; \ + srlx t3, 32, t3; \ + bcc,pt %xcc, 44f; \ + stw t3, [dest + off + 0x18]; \ + add sum, 1, sum; \ +44: stw t4, [dest + off + 0x24]; \ + addcc sum, t4, sum; \ + srlx t4, 32, t4; \ + bcc,pt %xcc, 45f; \ + stw t4, [dest + off + 0x20]; \ + add sum, 1, sum; \ +45: stw t5, [dest + off + 0x2c]; \ + addcc sum, t5, sum; \ + srlx t5, 32, t5; \ + bcc,pt %xcc, 46f; \ + stw t5, [dest + off + 0x28]; \ + add sum, 1, sum; \ +46: stw t6, [dest + off + 0x34]; \ + addcc sum, t6, sum; \ + srlx t6, 32, t6; \ + bcc,pt %xcc, 47f; \ + stw t6, [dest + off + 0x30]; \ + add sum, 1, sum; \ +47: stw t7, [dest + off + 0x3c]; \ + addcc sum, t7, sum; \ + srlx t7, 32, t7; \ + bcc,pt %xcc, 48f; \ + stw t7, [dest + off + 0x38]; \ + add sum, 1, sum; \ +48: - /* 12 superscalar cycles seems to be the limit for this case, - * because of this we thus do all the ldd's together to get - * Viking MXCC into streaming mode. Ho hum... - */ -#define CSUMCOPY_BIGCHUNK(src, dst, sum, off, t0, t1, t2, t3, t4, t5, t6, t7) \ - ldda [src + off + 0x00] %asi, t0; \ - ldda [src + off + 0x08] %asi, t2; \ - ldda [src + off + 0x10] %asi, t4; \ - ldda [src + off + 0x18] %asi, t6; \ - st t0, [dst + off + 0x00]; \ - addccc t0, sum, sum; \ - st t1, [dst + off + 0x04]; \ - addccc t1, sum, sum; \ - st t2, [dst + off + 0x08]; \ - addccc t2, sum, sum; \ - st t3, [dst + off + 0x0c]; \ - addccc t3, sum, sum; \ - st t4, [dst + off + 0x10]; \ - addccc t4, sum, sum; \ - st t5, [dst + off + 0x14]; \ - addccc t5, sum, sum; \ - st t6, [dst + off + 0x18]; \ - addccc t6, sum, sum; \ - st t7, [dst + off + 0x1c]; \ - addccc t7, sum, sum; +#define CSUMCOPY_LASTCHUNK(src, dst, sum, off, t0, t1) \ + ldxa [src - off - 0x08] %asi, t0; \ + ldxa [src - off - 0x00] %asi, t1; \ + nop; nop; \ + addcc t0, sum, sum; \ + stw t0, [dst - off - 0x04]; \ + srlx t0, 32, t0; \ + bcc,pt %xcc, 51f; \ + stw t0, [dst - off - 0x08]; \ + add sum, 1, sum; \ +51: addcc t1, sum, sum; \ + stw t1, [dst - off + 0x04]; \ + srlx t1, 32, t1; \ + bcc,pt %xcc, 52f; \ + stw t1, [dst - off - 0x00]; \ + add sum, 1, sum; \ +52: - /* Yuck, 6 superscalar cycles... */ -#define CSUMCOPY_LASTCHUNK(src, dst, sum, off, t0, t1, t2, t3) \ - ldda [src - off - 0x08] %asi, t0; \ - ldda [src - off - 0x00] %asi, t2; \ - addccc t0, sum, sum; \ - st t0, [dst - off - 0x08]; \ - addccc t1, sum, sum; \ - st t1, [dst - off - 0x04]; \ - addccc t2, sum, sum; \ - st t2, [dst - off - 0x00]; \ - addccc t3, sum, sum; \ - st t3, [dst - off + 0x04]; - - /* Handle the end cruft code out of band for better cache patterns. */ cc_end_cruft: - andcc %o3, 8, %g0 ! begin checks for that code - be,pn %icc, 1f - and %o3, 4, %g5 - EX(ldda [%o0 + 0x00] %asi, %g2, and %o3, 0xf,#) - add %o1, 8, %o1 - addcc %g2, %g7, %g7 - add %o0, 8, %o0 - addccc %g3, %g7, %g7 - EX2(st %g2, [%o1 - 0x08],#) - addc %g0, %g7, %g7 - EX2(st %g3, [%o1 - 0x04],#) -1: brz,pt %g5, 1f - andcc %o3, 3, %o3 - EX(lda [%o0 + 0x00] %asi, %g2, add %o3, 4,#) - add %o1, 4, %o1 - addcc %g2, %g7, %g7 - EX2(st %g2, [%o1 - 0x04],#) - addc %g0, %g7, %g7 - add %o0, 4, %o0 -1: brz,pn %o3, 1f - addcc %o3, -1, %g0 - bne,pn %icc, 2f - subcc %o3, 2, %o3 - ba,pt %xcc, 4f - clr %o4 -2: EX(lduha [%o0 + 0x00] %asi, %o4, add %o3, 2,#) - add %o0, 2, %o0 - EX2(sth %o4, [%o1 + 0x00],#) - be,pn %icc, 6f - add %o1, 2, %o1 - sll %o4, 16, %o4 -4: EX(lduba [%o0 + 0x00] %asi, %o5, add %g0, 1,#) - EX2(stb %o5, [%o1 + 0x00],#) - sll %o5, 8, %o5 - or %o5, %o4, %o4 -6: addcc %o4, %g7, %g7 -1: sllx %g4, 32, %g4 - addc %g0, %g7, %o0 - retl - srl %o0, 0, %o0 + andcc %o3, 8, %g0 ! IEU1 Group + be,pn %icc, 1f ! CTI + and %o3, 4, %g5 ! IEU0 + ldxa [%o0 + 0x00] %asi, %g2 ! Load Group + add %o1, 8, %o1 ! IEU0 + add %o0, 8, %o0 ! IEU1 + addcc %g2, %g7, %g7 ! IEU1 Group + 2 bubbles + stw %g2, [%o1 - 0x04] ! Store + srlx %g2, 32, %g2 ! IEU0 + bcc,pt %xcc, 1f ! CTI Group + stw %g2, [%o1 - 0x08] ! Store + add %g7, 1, %g7 ! IEU0 +1: brz,pt %g5, 1f ! CTI Group + clr %g2 ! IEU0 + lduwa [%o0 + 0x00] %asi, %g2 ! Load + add %o1, 4, %o1 ! IEU0 Group + add %o0, 4, %o0 ! IEU1 + stw %g2, [%o1 - 0x04] ! Store Group + 2 bubbles + sllx %g2, 32, %g2 ! IEU0 +1: andcc %o3, 2, %g0 ! IEU1 + be,pn %icc, 1f ! CTI Group + clr %o4 ! IEU1 + lduha [%o0 + 0x00] %asi, %o4 ! Load + add %o0, 2, %o0 ! IEU0 Group + add %o1, 2, %o1 ! IEU1 + sth %o4, [%o1 - 0x2] ! Store Group + 2 bubbles + sll %o4, 16, %o4 ! IEU0 +1: andcc %o3, 1, %g0 ! IEU1 + be,pn %icc, 1f ! CTI Group + clr %o5 ! IEU0 + lduba [%o0 + 0x00] %asi, %o5 ! Load + stb %o5, [%o1 + 0x00] ! Store Group + 2 bubbles + sll %o5, 8, %o5 ! IEU0 +1: or %g2, %o4, %o4 ! IEU1 + or %o5, %o4, %o4 ! IEU0 Group + addcc %o4, %g7, %g7 ! IEU1 + bcc,pt %xcc, ccfold ! CTI + sethi %uhi(PAGE_OFFSET), %g4 ! IEU0 Group + b,pt %xcc, ccfold ! CTI + add %g7, 1, %g7 ! IEU1 - /* Sun, you just can't beat me, you just can't. Stop trying, - * give up. I'm serious, I am going to kick the living shit - * out of you, game over, lights out. - */ - .align 8 - .globl __csum_partial_copy_sparc_generic -__csum_partial_copy_sparc_generic: - /* %o0=src, %o1=dest, %g1=len, %g7=sum */ - srl %g7, 0, %g7 ! you neve know... - xor %o0, %o1, %o4 ! get changing bits - srl %g1, 0, %g1 ! doof scheiss - andcc %o4, 3, %g0 ! check for mismatched alignment - bne,pn %icc, ccslow ! better this than unaligned/fixups - andcc %o0, 7, %g0 ! need to align things? - be,pt %icc, cc_dword_aligned ! yes, we check for short lengths there - andn %g1, 0x7f, %g2 ! can we use unrolled loop? - cmp %g1, 6 - bl,a,pn %icc, ccte - andcc %g1, 0xf, %o3 - andcc %o0, 0x1, %g0 - bne,pn %icc, ccslow - andcc %o0, 0x2, %g0 - be,pn %icc, 1f - andcc %o0, 0x4, %g0 - EX(lduha [%o0 + 0x00] %asi, %g4, add %g1, 0,#) - sub %g1, 2, %g1 - EX2(sth %g4, [%o1 + 0x00],#) - add %o0, 2, %o0 - sll %g4, 16, %g4 - addcc %g4, %g7, %g7 - add %o1, 2, %o1 - srl %g7, 16, %g3 - addc %g0, %g3, %g4 - sll %g7, 16, %g7 - sll %g4, 16, %g3 - srl %g7, 16, %g7 - andcc %o0, 0x4, %g0 - or %g3, %g7, %g7 -1: be,pt %icc, 3f - andn %g1, 0x7f, %g2 - EX(lda [%o0 + 0x00] %asi, %g4, add %g1, 0,#) - sub %g1, 4, %g1 - EX2(st %g4, [%o1 + 0x00],#) - add %o0, 4, %o0 - addcc %g4, %g7, %g7 - add %o1, 4, %o1 - andn %g1, 0x7f, %g2 - addc %g0, %g7, %g7 +cc_fixit: + bl,a,pn %icc, ccte ! CTI + andcc %g1, 0xf, %o3 ! IEU1 Group + andcc %o0, 1, %g0 ! IEU1 Group + bne,pn %icc, ccslow ! CTI + andcc %o0, 2, %g0 ! IEU1 Group + be,pn %icc, 1f ! CTI + andcc %o0, 0x4, %g0 ! IEU1 Group + lduha [%o0 + 0x00] %asi, %g4 ! Load + sub %g1, 2, %g1 ! IEU0 + add %o0, 2, %o0 ! IEU0 Group + add %o1, 2, %o1 ! IEU1 + sll %g4, 16, %g3 ! IEU0 Group + 1 bubble + addcc %g3, %g7, %g7 ! IEU1 + bcc,pt %xcc, 0f ! CTI + srl %g7, 16, %g3 ! IEU0 Group + add %g3, 1, %g3 ! IEU0 4 clocks (mispredict) +0: andcc %o0, 0x4, %g0 ! IEU1 Group + sth %g4, [%o1 - 0x2] ! Store + sll %g7, 16, %g7 ! IEU0 + sll %g3, 16, %g3 ! IEU0 Group + srl %g7, 16, %g7 ! IEU0 Group + or %g3, %g7, %g7 ! IEU0 Group (regdep) +1: be,pt %icc, cc_dword_aligned ! CTI + andn %g1, 0xff, %g2 ! IEU1 + lduwa [%o0 + 0x00] %asi, %g4 ! Load Group + sub %g1, 4, %g1 ! IEU0 + add %o0, 4, %o0 ! IEU1 + add %o1, 4, %o1 ! IEU0 Group + addcc %g4, %g7, %g7 ! IEU1 Group + 1 bubble + stw %g4, [%o1 - 0x4] ! Store + bcc,pt %xcc, cc_dword_aligned ! CTI + andn %g1, 0xff, %g2 ! IEU0 Group + b,pt %xcc, cc_dword_aligned ! CTI 4 clocks (mispredict) + add %g7, 1, %g7 ! IEU0 + + .align 32 + .globl __csum_partial_copy_sparc_generic, csum_partial_copy +csum_partial_copy: +__csum_partial_copy_sparc_generic: /* %o0=src, %o1=dest, %g1=len, %g7=sum */ + xorcc %o0, %o1, %o4 ! IEU1 Group + srl %g7, 0, %g7 ! IEU0 + andcc %o4, 3, %g0 ! IEU1 Group + srl %g1, 0, %g1 ! IEU0 + bne,pn %icc, ccslow ! CTI + andcc %o0, 7, %g0 ! IEU1 Group + be,pt %icc, cc_dword_aligned ! CTI + andn %g1, 0xff, %g2 ! IEU0 + b,pt %xcc, cc_fixit ! CTI Group + cmp %g1, 6 ! IEU1 cc_dword_aligned: -3: brz,pn %g2, 3f ! nope, less than one loop remains - andcc %o1, 4, %g0 ! dest aligned on 4 or 8 byte boundry? - be,pn %icc, ccdbl + 4 ! 8 byte aligned, kick ass -5: CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x00,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) - CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x20,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) - CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x40,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) - CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x60,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) -10: EXT(5b, 10b, 20f,#) ! note for exception handling - sub %g1, 128, %g1 ! detract from length - addc %g0, %g7, %g7 ! add in last carry bit - andncc %g1, 0x7f, %g0 ! more to csum? - add %o0, 128, %o0 ! advance src ptr - bne,pt %icc, 5b ! we did not go negative, continue looping - add %o1, 128, %o1 ! advance dest ptr -3: andcc %g1, 0x70, %o2 ! can use table? -ccmerge:be,pn %icc, ccte ! nope, go and check for end cruft - andcc %g1, 0xf, %o3 ! get low bits of length (clears carry btw) - srl %o2, 1, %o4 ! begin negative offset computation -13: rd %pc, %o5 ! set up table ptr end - add %o0, %o2, %o0 ! advance src ptr - sub %o5, %o4, %o5 ! continue table calculation - sll %o2, 1, %g2 ! constant multiplies are fun... - sub %o5, %g2, %o5 ! some more adjustments - jmpl %o5 + (12f-13b), %g0 ! jump into it, duff style, wheee... - add %o1, %o2, %o1 ! advance dest ptr (carry is clear btw) -cctbl: CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x68,%g2,%g3,%g4,%g5) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x58,%g2,%g3,%g4,%g5) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x48,%g2,%g3,%g4,%g5) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x38,%g2,%g3,%g4,%g5) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x28,%g2,%g3,%g4,%g5) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x18,%g2,%g3,%g4,%g5) - CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x08,%g2,%g3,%g4,%g5) -12: EXT(cctbl, 12b, 22f,#) ! note for exception table handling - addc %g0, %g7, %g7 - andcc %g1, 0xf, %o3 ! check for low bits set -ccte: bne,pn %icc, cc_end_cruft ! something left, handle it out of band - sethi %uhi(KERNBASE), %g4 ! restore gfp - mov %g7, %o0 ! give em the computed checksum - sllx %g4, 32, %g4 ! finish gfp restoration - retl ! return - srl %o0, 0, %o0 -ccdbl: CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x00,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) - CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x20,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) - CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x40,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) - CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x60,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) -11: EXT(ccdbl, 11b, 21f,#) ! note for exception table handling - sub %g1, 128, %g1 ! detract from length - addc %g0, %g7, %g7 ! add in last carry bit - andncc %g1, 0x7f, %g0 ! more to csum? - add %o0, 128, %o0 ! advance src ptr - bne,pt %icc, ccdbl ! we did not go negative, continue looping - add %o1, 128, %o1 ! advance dest ptr - ba,pt %xcc, ccmerge ! finish it off, above - andcc %g1, 0x70, %o2 ! can use table? (clears carry btw) + brz,pn %g2, 3f ! CTI Group + andcc %o1, 4, %g0 ! IEU1 Group (brz uses IEU1) + be,pn %icc, ccdbl + 4 ! CTI +5: CSUMCOPY_ECACHE_LOAD( %o0, 0x00, %o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) + CSUMCOPY_EC_STUNALIGN_LDNXT(%o0,%o1,0x40,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) + CSUMCOPY_EC_STUNALIGN_LDNXT(%o0,%o1,0x80,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) + CSUMCOPY_EC_STUNALIGN_LDNXT(%o0,%o1,0xc0,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) + CSUMCOPY_EC_STUNALIGN( %o1,0xc0,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) +10: + sub %g1, 256, %g1 ! IEU0 Group + add %o0, 256, %o0 ! IEU1 + andncc %g1, 0xff, %g0 ! IEU1 Group + bne,pt %icc, 5b ! CTI + add %o1, 256, %o1 ! IEU0 +3: andcc %g1, 0xf0, %o2 ! IEU1 Group +ccmerge:be,pn %icc, ccte ! CTI + andcc %g1, 0xf, %o3 ! IEU1 Group + sll %o2, 2, %o4 ! IEU0 +13: rd %pc, %o5 ! LSU Group + 4 clocks + add %o0, %o2, %o0 ! IEU0 Group + sub %o5, %o4, %o5 ! IEU1 Group + jmpl %o5 + (12f - 13b), %g0 ! CTI Group brk forced + add %o1, %o2, %o1 ! IEU0 Group +cctbl: CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0xe8,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0xd8,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0xc8,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0xb8,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0xa8,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x98,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x88,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x78,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x68,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x58,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x48,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x38,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x28,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x18,%g2,%g3) + CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x08,%g2,%g3) +12: + andcc %g1, 0xf, %o3 ! IEU1 Group +ccte: bne,pn %icc, cc_end_cruft ! CTI + sethi %uhi(PAGE_OFFSET), %g4 ! IEU0 +ccfold: sllx %g7, 32, %o0 ! IEU0 Group + addcc %g7, %o0, %o0 ! IEU1 Group (regdep) + srlx %o0, 32, %o0 ! IEU0 Group (regdep) + bcs,a,pn %xcc, 1f ! CTI + add %o0, 1, %o0 ! IEU1 4 clocks (mispredict) +1: retl ! CTI Group brk forced + sllx %g4, 32,%g4 ! IEU0 Group +ccdbl: CSUMCOPY_ECACHE_LOAD( %o0, 0x00, %o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) + CSUMCOPY_EC_STALIGNED_LDNXT(%o0,%o1,0x40,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) + CSUMCOPY_EC_STALIGNED_LDNXT(%o0,%o1,0x80,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) + CSUMCOPY_EC_STALIGNED_LDNXT(%o0,%o1,0xc0,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) + CSUMCOPY_EC_STALIGNED( %o1,0xc0,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3) +11: + sub %g1, 256, %g1 ! IEU0 Group + add %o0, 256, %o0 ! IEU1 + andncc %g1, 0xff, %g0 ! IEU1 Group + bne,pt %icc, ccdbl ! CTI + add %o1, 256, %o1 ! IEU0 + b,pt %xcc, ccmerge ! CTI Group + andcc %g1, 0xf0, %o2 ! IEU1 ccslow: mov 0, %g5 brlez,pn %g1, 4f @@ -401,9 +416,9 @@ ccslow: mov 0, %g5 be,a,pt %icc, 1f srl %g1, 1, %o3 sub %g1, 1, %g1 - EX(lduba [%o0] %asi, %g5, add %g1, 1,#) + lduba [%o0] %asi, %g5 add %o0, 1, %o0 - EX2(stb %g5, [%o1],#) + stb %g5, [%o1] srl %g1, 1, %o3 add %o1, 1, %o1 1: brz,a,pn %o3, 3f @@ -411,33 +426,33 @@ ccslow: mov 0, %g5 andcc %o0, 2, %g0 be,a,pt %icc, 1f srl %o3, 1, %o3 - EX(lduha [%o0] %asi, %o4, add %g1, 0,#) + lduha [%o0] %asi, %o4 sub %g1, 2, %g1 srl %o4, 8, %g2 sub %o3, 1, %o3 - EX2(stb %g2, [%o1],#) + stb %g2, [%o1] add %o4, %g5, %g5 - EX2(stb %o4, [%o1 + 1],#) + stb %o4, [%o1 + 1] add %o0, 2, %o0 srl %o3, 1, %o3 add %o1, 2, %o1 1: brz,a,pn %o3, 2f andcc %g1, 2, %g0 - EX3(lda [%o0] %asi, %o4,#) + lda [%o0] %asi, %o4 5: srl %o4, 24, %g2 srl %o4, 16, %g3 - EX2(stb %g2, [%o1],#) + stb %g2, [%o1] srl %o4, 8, %g2 - EX2(stb %g3, [%o1 + 1],#) + stb %g3, [%o1 + 1] add %o0, 4, %o0 - EX2(stb %g2, [%o1 + 2],#) + stb %g2, [%o1 + 2] addcc %o4, %g5, %g5 - EX2(stb %o4, [%o1 + 3],#) + stb %o4, [%o1 + 3] addc %g5, %g0, %g5 ! I am now to lazy to optimize this (question is if it add %o1, 4, %o1 ! is worthy). Maybe some day - with the sll/srl subcc %o3, 1, %o3 ! tricks bne,a,pt %icc, 5b - EX3(lda [%o0] %asi, %o4,#) + lda [%o0] %asi, %o4 sll %g5, 16, %g2 srl %g5, 16, %g5 srl %g2, 16, %g2 @@ -445,19 +460,19 @@ ccslow: mov 0, %g5 add %g2, %g5, %g5 2: be,a,pt %icc, 3f andcc %g1, 1, %g0 - EX(lduha [%o0] %asi, %o4, and %g1, 3,#) + lduha [%o0] %asi, %o4 andcc %g1, 1, %g0 srl %o4, 8, %g2 add %o0, 2, %o0 - EX2(stb %g2, [%o1],#) + stb %g2, [%o1] add %g5, %o4, %g5 - EX2(stb %o4, [%o1 + 1],#) + stb %o4, [%o1 + 1] add %o1, 2, %o1 3: be,a,pt %icc, 1f sll %g5, 16, %o4 - EX(lduba [%o0] %asi, %g2, add %g0, 1,#) + lduba [%o0] %asi, %g2 sll %g2, 8, %o4 - EX2(stb %g2, [%o1],#) + stb %g2, [%o1] add %g5, %o4, %g5 sll %g5, 16, %o4 1: addcc %o4, %g5, %g5 @@ -474,103 +489,3 @@ ccslow: mov 0, %g5 retl srl %o0, 0, %o0 __csum_partial_copy_end: - - .section .fixup,#alloc,#execinstr - .align 4 -/* We do these strange calculations for the csum_*_from_user case only, ie. - * we only bother with faults on loads... */ - -/* o2 = ((g2%20)&3)*8 - * o3 = g1 - (g2/20)*32 - o2 */ -20: - cmp %g2, 20 - blu,a,pn %icc, 1f - and %g2, 3, %o2 - sub %g1, 32, %g1 - ba,pt %xcc, 20b - sub %g2, 20, %g2 -1: - sll %o2, 3, %o2 - ba,pt %xcc, 31f - sub %g1, %o2, %o3 - -/* o2 = (!(g2 & 15) ? 0 : (((g2 & 15) + 1) & ~1)*8) - * o3 = g1 - (g2/16)*32 - o2 */ -21: - andcc %g2, 15, %o3 - srl %g2, 4, %g2 - be,a,pn %icc, 1f - clr %o2 - add %o3, 1, %o3 - and %o3, 14, %o3 - sll %o3, 3, %o2 -1: - sll %g2, 5, %g2 - sub %g1, %g2, %o3 - ba,pt %xcc, 31f - sub %o3, %o2, %o3 - -/* o0 += (g2/10)*16 - 0x70 - * 01 += (g2/10)*16 - 0x70 - * o2 = (g2 % 10) ? 8 : 0 - * o3 += 0x70 - (g2/10)*16 - o2 */ -22: - cmp %g2, 10 - blu,a,pt %xcc, 1f - sub %o0, 0x70, %o0 - add %o0, 16, %o0 - add %o1, 16, %o1 - sub %o3, 16, %o3 - ba,pt %xcc, 22b - sub %g2, 10, %g2 -1: - sub %o1, 0x70, %o1 - add %o3, 0x70, %o3 - clr %o2 - movrnz %g2, 8, %o2 - ba,pt %xcc, 31f - sub %o3, %o2, %o3 -96: - and %g1, 3, %g1 - sll %o3, 2, %o3 - add %g1, %o3, %o3 -30: -/* %o1 is dst - * %o3 is # bytes to zero out - * %o4 is faulting address - * %o5 is %pc where fault occured */ - clr %o2 -31: -/* %o0 is src - * %o1 is dst - * %o2 is # of bytes to copy from src to dst - * %o3 is # bytes to zero out - * %o4 is faulting address - * %o5 is %pc where fault occured */ - save %sp, -136, %sp - mov %i5, %o0 - mov %i7, %o1 - mov %i4, %o2 - call lookup_fault - mov %g7, %i4 - cmp %o0, 2 - bne,pn %icc, 1f - add %g0, -EFAULT, %i5 - brz,pn %i2, 2f - mov %i0, %o1 - mov %i1, %o0 - call __copy_from_user - mov %i2, %o2 - brnz,a,pn %o0, 2f - add %i3, %i2, %i3 - add %i1, %i2, %i1 -2: - mov %i1, %o0 - wr %g0, ASI_S, %asi - call __bzero_noasi - mov %i3, %o1 -1: - ldx [%sp + STACK_BIAS + 264], %o2 ! struct_ptr of parent - st %i5, [%o2] - ret - restore diff --git a/arch/sparc64/lib/copy_from_user.S b/arch/sparc64/lib/copy_from_user.S deleted file mode 100644 index 196435aed..000000000 --- a/arch/sparc64/lib/copy_from_user.S +++ /dev/null @@ -1,469 +0,0 @@ -/* copy_user.S: Sparc optimized copy_from_user code. - * - * Copyright(C) 1995 Linus Torvalds - * Copyright(C) 1996 David S. Miller - * Copyright(C) 1996 Eddie C. Dost - * Copyright(C) 1996,1997 Jakub Jelinek - * - * derived from: - * e-mail between David and Eddie. - * - * Returns 0 if successful, otherwise count of bytes not copied yet - * - * FIXME: This code should be optimized for sparc64... -jj - */ - -#include <asm/ptrace.h> -#include <asm/asi.h> -#include <asm/head.h> - -#define PRE_RETL sethi %uhi(KERNBASE), %g4; sllx %g4, 32, %g4; - -#define EX(x,y,a,b,z) \ -98: x,y; \ - .section .fixup,z##alloc,z##execinstr; \ - .align 4; \ -99: PRE_RETL \ - retl; \ - a, b, %o0; \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword 98b, 99b; \ - .text; \ - .align 4 - -#define EX2(x,y,c,d,e,a,b,z) \ -98: x,y; \ - .section .fixup,z##alloc,z##execinstr; \ - .align 4; \ -99: c, d, e; \ - PRE_RETL \ - retl; \ - a, b, %o0; \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword 98b, 99b; \ - .text; \ - .align 4 - -#define EXO2(x,y,z) \ -98: x,##y; \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword 98b, 97f; \ - .text; \ - .align 4 - -#define EXT(start,end,handler,z) \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword start, 0, end, handler; \ - .text; \ - .align 4 - -/* Please do not change following macros unless you change logic used - * in .fixup at the end of this file as well - */ - -/* Both these macros have to start with exactly the same insn */ -#define MOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \ - ldda [%src + offset + 0x00] %asi, %t0; \ - ldda [%src + offset + 0x08] %asi, %t2; \ - ldda [%src + offset + 0x10] %asi, %t4; \ - ldda [%src + offset + 0x18] %asi, %t6; \ - st %t0, [%dst + offset + 0x00]; \ - st %t1, [%dst + offset + 0x04]; \ - st %t2, [%dst + offset + 0x08]; \ - st %t3, [%dst + offset + 0x0c]; \ - st %t4, [%dst + offset + 0x10]; \ - st %t5, [%dst + offset + 0x14]; \ - st %t6, [%dst + offset + 0x18]; \ - st %t7, [%dst + offset + 0x1c]; - -#define MOVE_BIGALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \ - ldda [%src + offset + 0x00] %asi, %t0; \ - ldda [%src + offset + 0x08] %asi, %t2; \ - ldda [%src + offset + 0x10] %asi, %t4; \ - ldda [%src + offset + 0x18] %asi, %t6; \ - std %t0, [%dst + offset + 0x00]; \ - std %t2, [%dst + offset + 0x08]; \ - std %t4, [%dst + offset + 0x10]; \ - std %t6, [%dst + offset + 0x18]; - -#define MOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \ - ldda [%src - offset - 0x10] %asi, %t0; \ - ldda [%src - offset - 0x08] %asi, %t2; \ - st %t0, [%dst - offset - 0x10]; \ - st %t1, [%dst - offset - 0x0c]; \ - st %t2, [%dst - offset - 0x08]; \ - st %t3, [%dst - offset - 0x04]; - -#define MOVE_HALFCHUNK(src, dst, offset, t0, t1, t2, t3) \ - lduha [%src + offset + 0x00] %asi, %t0; \ - lduha [%src + offset + 0x02] %asi, %t1; \ - lduha [%src + offset + 0x04] %asi, %t2; \ - lduha [%src + offset + 0x06] %asi, %t3; \ - sth %t0, [%dst + offset + 0x00]; \ - sth %t1, [%dst + offset + 0x02]; \ - sth %t2, [%dst + offset + 0x04]; \ - sth %t3, [%dst + offset + 0x06]; - -#define MOVE_SHORTCHUNK(src, dst, offset, t0, t1) \ - lduba [%src - offset - 0x02] %asi, %t0; \ - lduba [%src - offset - 0x01] %asi, %t1; \ - stb %t0, [%dst - offset - 0x02]; \ - stb %t1, [%dst - offset - 0x01]; - - .text - .align 4 - - .globl __copy_from_user -dword_align: - andcc %o1, 1, %g0 - be 4f - andcc %o1, 2, %g0 - - EXO2(lduba [%o1] %asi, %g2,#) - add %o1, 1, %o1 - stb %g2, [%o0] - sub %o2, 1, %o2 - bne 3f - add %o0, 1, %o0 - - EXO2(lduha [%o1] %asi, %g2,#) - add %o1, 2, %o1 - sth %g2, [%o0] - sub %o2, 2, %o2 - ba,pt %xcc, 3f - add %o0, 2, %o0 -4: - EXO2(lduha [%o1] %asi, %g2,#) - add %o1, 2, %o1 - sth %g2, [%o0] - sub %o2, 2, %o2 - ba,pt %xcc, 3f - add %o0, 2, %o0 - -__copy_from_user: /* %o0=dst %o1=src %o2=len */ - wr %g0, ASI_S, %asi - xor %o0, %o1, %o4 -1: - andcc %o4, 3, %o5 -2: - bne,pn %icc, cannot_optimize - cmp %o2, 15 - - bleu,pn %xcc, short_aligned_end - andcc %o1, 3, %g0 - - bne,pn %icc, dword_align -3: - andcc %o1, 4, %g0 - - be,pt %icc, 2f - mov %o2, %g1 - - EXO2(lda [%o1] %asi, %o4,#) - sub %g1, 4, %g1 - st %o4, [%o0] - add %o1, 4, %o1 - add %o0, 4, %o0 -2: - andcc %g1, 0xffffffffffffff80, %g7 - be,pn %xcc, 3f - andcc %o0, 4, %g0 - - be,pn %icc, ldd_std + 4 -5: - MOVE_BIGCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGCHUNK(o1, o0, 0x20, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGCHUNK(o1, o0, 0x60, o2, o3, o4, o5, g2, g3, g4, g5) -80: - EXT(5b, 80b, 50f,#) - subcc %g7, 128, %g7 - add %o1, 128, %o1 - bne,pt %xcc, 5b - add %o0, 128, %o0 -3: - andcc %g1, 0x70, %g7 - be,pn %icc, copy_user_table_end - andcc %g1, 8, %g0 -100: - rd %pc, %o5 - srl %g7, 1, %o4 - add %g7, %o4, %o4 - add %o1, %g7, %o1 - sub %o5, %o4, %o5 - jmpl %o5 + (copy_user_table_end - 100b), %g0 - add %o0, %g7, %o0 - -copy_user_table: - MOVE_LASTCHUNK(o1, o0, 0x60, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x50, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x40, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x30, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x20, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x10, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x00, g2, g3, g4, g5) -copy_user_table_end: - EXT(copy_user_table, copy_user_table_end, 51f,#) - be,pt %icc, copy_user_last7 - andcc %g1, 4, %g0 - - EX(ldda [%o1] %asi, %g2, and %g1, 0xf,#) - add %o0, 8, %o0 - add %o1, 8, %o1 - st %g2, [%o0 - 0x08] - st %g3, [%o0 - 0x04] -copy_user_last7: - be,pn %icc, 1f - andcc %g1, 2, %g0 - - EX(lda [%o1] %asi, %g2, and %g1, 7,#) - add %o1, 4, %o1 - st %g2, [%o0] - add %o0, 4, %o0 -1: - be,pn %icc, 1f - andcc %g1, 1, %g0 - - EX(lduha [%o1] %asi, %g2, and %g1, 3,#) - add %o1, 2, %o1 - sth %g2, [%o0] - add %o0, 2, %o0 -1: - be,pn %icc, 1f - nop - - EX(lduba [%o1] %asi, %g2, add %g0, 1,#) - stb %g2, [%o0] -1: - PRE_RETL - retl - clr %o0 - -ldd_std: - MOVE_BIGALIGNCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGALIGNCHUNK(o1, o0, 0x20, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGALIGNCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGALIGNCHUNK(o1, o0, 0x60, o2, o3, o4, o5, g2, g3, g4, g5) -81: - EXT(ldd_std, 81b, 52f,#) - subcc %g7, 128, %g7 - add %o1, 128, %o1 - bne,pt %xcc, ldd_std - add %o0, 128, %o0 - - andcc %g1, 0x70, %g7 - be,pn %icc, copy_user_table_end - andcc %g1, 8, %g0 -101: - rd %pc, %o5 - srl %g7, 1, %o4 - add %g7, %o4, %o4 - add %o1, %g7, %o1 - sub %o5, %o4, %o5 - jmpl %o5 + (copy_user_table_end - 101b), %g0 - add %o0, %g7, %o0 - -cannot_optimize: - bleu short_end - cmp %o5, 2 - - bne byte_chunk - and %o2, 0xfffffffffffffff0, %o3 - - andcc %o1, 1, %g0 - be 10f - nop - - EXO2(lduba [%o1] %asi, %g2,#) - add %o1, 1, %o1 - stb %g2, [%o0] - sub %o2, 1, %o2 - andcc %o2, 0xfffffffffffffff0, %o3 - be short_end - add %o0, 1, %o0 -10: - MOVE_HALFCHUNK(o1, o0, 0x00, g2, g3, g4, g5) - MOVE_HALFCHUNK(o1, o0, 0x08, g2, g3, g4, g5) -82: - EXT(10b, 82b, 53f,#) - subcc %o3, 0x10, %o3 - add %o1, 0x10, %o1 - bne 10b - add %o0, 0x10, %o0 - ba,pt %xcc, 2f - and %o2, 0xe, %o3 - -byte_chunk: - MOVE_SHORTCHUNK(o1, o0, -0x02, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x04, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x06, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x08, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x0a, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x0c, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x0e, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x10, g2, g3) -83: - EXT(byte_chunk, 83b, 54f,#) - subcc %o3, 0x10, %o3 - add %o1, 0x10, %o1 - bne,pt %xcc, byte_chunk - add %o0, 0x10, %o0 - -short_end: - and %o2, 0xe, %o3 -2: - rd %pc, %o5 - sll %o3, 3, %o4 - add %o0, %o3, %o0 - sub %o5, %o4, %o5 - add %o1, %o3, %o1 - jmpl %o5 + (short_table_end - 2b), %g0 - andcc %o2, 1, %g0 -84: - MOVE_SHORTCHUNK(o1, o0, 0x0c, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x0a, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x08, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x06, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x04, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x02, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x00, g2, g3) -short_table_end: - EXT(84b, short_table_end, 55f,#) - be 1f - nop - EX(lduba [%o1] %asi, %g2, add %g0, 1,#) - stb %g2, [%o0] -1: - PRE_RETL - retl - clr %o0 - -short_aligned_end: - bne short_end - andcc %o2, 8, %g0 - - be 1f - andcc %o2, 4, %g0 - - EXO2(lda [%o1 + 0x00] %asi, %g2,#) - EX(lda [%o1 + 0x04] %asi, %g3, sub %o2, 4,#) - add %o1, 8, %o1 - st %g2, [%o0 + 0x00] - st %g3, [%o0 + 0x04] - add %o0, 8, %o0 -1: - ba,pt %xcc, copy_user_last7 - mov %o2, %g1 - - .section .fixup,#alloc,#execinstr - .align 4 -97: - PRE_RETL - retl - mov %o2, %o0 -/* exception routine sets %g2 to (broken_insn - first_insn)>>2 */ -50: -/* This magic counts how many bytes are left when crash in MOVE_BIGCHUNK - * happens. This is derived from the amount ldd reads, st stores, etc. - * x = g2 % 12; - * o0 = g1 + g7 - ((g2 / 12) * 32 + (x < 4) ? x * 8 : (x - 4) * 4) - */ - cmp %g2, 12 - bcs 1f - cmp %g2, 24 - bcs 2f - cmp %g2, 36 - bcs 3f - nop - sub %g2, 12, %g2 - sub %g7, 32, %g7 -3: - sub %g2, 12, %g2 - sub %g7, 32, %g7 -2: - sub %g2, 12, %g2 - sub %g7, 32, %g7 -1: - cmp %g2, 4 - bcs,a 1f - sll %g2, 3, %g2 - sub %g2, 4, %g2 - sll %g2, 2, %g2 -1: - and %g1, 0x7f, %o0 - add %o0, %g7, %o0 - PRE_RETL - retl - sub %o0, %g2, %o0 -51: -/* i = 41 - g2; j = i % 6; - * o0 = (g1 & 15) + (i / 6) * 16 + (j < 4) ? (j + 1) * 4 : (j - 3) * 8; - */ - neg %g2 - and %g1, 0xf, %g1 - add %g2, 41, %g2 -1: - cmp %g2, 6 - bcs,a 2f - cmp %g2, 4 - add %g1, 16, %g1 - b 1b - sub %g2, 6, %g2 -2: - bcs,a 3f - inc %g2 - sub %g2, 3, %g2 - b 2f - sll %g2, 3, %g2 -3: - sll %g2, 2, %g2 -2: - PRE_RETL - retl - add %g1, %g2, %o0 -52: -/* o0 = g1 + g7 - (g2 / 8) * 32 + (x & 3) * 8 */ - and %g2, 0xfffffffffffffff8, %g4 - and %g2, 3, %g2 - sll %g4, 2, %g4 - sll %g2, 3, %g2 - add %g2, %g4, %g2 - b,a 1b -53: -/* o0 = o3 + (o2 & 15) - (g2 & 8) - (g2 & 3) * 2 */ - and %g2, 3, %g4 - and %g2, 0xfffffffffffffff8, %g2 - sll %g4, 1, %g4 - add %g2, %g4, %g2 - and %o2, 0xf, %o0 - add %o0, %o3, %o0 - PRE_RETL - retl - sub %o0, %g2, %o0 -54: -/* o0 = o3 + (o2 & 15) - (g2 / 4) * 2 - (g2 & 1) */ - srl %g2, 2, %o4 - and %g2, 1, %o1 - sll %o4, 1, %o4 - and %o2, 0xf, %o2 - sub %o3, %o1, %o3 - sub %o2, %o4, %o2 - PRE_RETL - retl - add %o2, %o3, %o0 -55: -/* o0 = (o2 & 1) + (27 - g2)/4 * 2 + ((27 - g2) & 1) */ - neg %g2 - and %o2, 1, %o2 - add %g2, 27, %g2 - srl %g2, 2, %o1 - and %g2, 1, %g2 - sll %o1, 1, %o1 - add %o2, %g2, %o0 - PRE_RETL - retl - add %o0, %o1, %o0 diff --git a/arch/sparc64/lib/copy_to_user.S b/arch/sparc64/lib/copy_to_user.S deleted file mode 100644 index cc6db141f..000000000 --- a/arch/sparc64/lib/copy_to_user.S +++ /dev/null @@ -1,469 +0,0 @@ -/* copy_user.S: Sparc optimized copy_to_user code. - * - * Copyright(C) 1995 Linus Torvalds - * Copyright(C) 1996 David S. Miller - * Copyright(C) 1996 Eddie C. Dost - * Copyright(C) 1996,1997 Jakub Jelinek - * - * derived from: - * e-mail between David and Eddie. - * - * Returns 0 if successful, otherwise count of bytes not copied yet - * - * FIXME: This code should be optimized for sparc64... -jj - */ - -#include <asm/ptrace.h> -#include <asm/head.h> -#include <asm/asi.h> - -#define PRE_RETL sethi %uhi(KERNBASE), %g4; sllx %g4, 32, %g4; - -#define EX(x,y,a,b,z) \ -98: x,y; \ - .section .fixup,z##alloc,z##execinstr; \ - .align 4; \ -99: PRE_RETL \ - retl; \ - a, b, %o0; \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword 98b, 99b; \ - .text; \ - .align 4 - -#define EX2(x,y,c,d,e,a,b,z) \ -98: x,y; \ - .section .fixup,z##alloc,z##execinstr; \ - .align 4; \ -99: c, d, e; \ - PRE_RETL \ - retl; \ - a, b, %o0; \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword 98b, 99b; \ - .text; \ - .align 4 - -#define EXO2(x,y,z) \ -98: x,##y; \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword 98b, 97f; \ - .text; \ - .align 4 - -#define EXT(start,end,handler,z) \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword start, 0, end, handler; \ - .text; \ - .align 4 - -/* Please do not change following macros unless you change logic used - * in .fixup at the end of this file as well - */ - -/* Both these macros have to start with exactly the same insn */ -#define MOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \ - ldd [%src + offset + 0x00], %t0; \ - ldd [%src + offset + 0x08], %t2; \ - ldd [%src + offset + 0x10], %t4; \ - ldd [%src + offset + 0x18], %t6; \ - sta %t0, [%dst + offset + 0x00] %asi; \ - sta %t1, [%dst + offset + 0x04] %asi; \ - sta %t2, [%dst + offset + 0x08] %asi; \ - sta %t3, [%dst + offset + 0x0c] %asi; \ - sta %t4, [%dst + offset + 0x10] %asi; \ - sta %t5, [%dst + offset + 0x14] %asi; \ - sta %t6, [%dst + offset + 0x18] %asi; \ - sta %t7, [%dst + offset + 0x1c] %asi; - -#define MOVE_BIGALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \ - ldd [%src + offset + 0x00], %t0; \ - ldd [%src + offset + 0x08], %t2; \ - ldd [%src + offset + 0x10], %t4; \ - ldd [%src + offset + 0x18], %t6; \ - stda %t0, [%dst + offset + 0x00] %asi; \ - stda %t2, [%dst + offset + 0x08] %asi; \ - stda %t4, [%dst + offset + 0x10] %asi; \ - stda %t6, [%dst + offset + 0x18] %asi; - -#define MOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \ - ldd [%src - offset - 0x10], %t0; \ - ldd [%src - offset - 0x08], %t2; \ - sta %t0, [%dst - offset - 0x10] %asi; \ - sta %t1, [%dst - offset - 0x0c] %asi; \ - sta %t2, [%dst - offset - 0x08] %asi; \ - sta %t3, [%dst - offset - 0x04] %asi; - -#define MOVE_HALFCHUNK(src, dst, offset, t0, t1, t2, t3) \ - lduh [%src + offset + 0x00], %t0; \ - lduh [%src + offset + 0x02], %t1; \ - lduh [%src + offset + 0x04], %t2; \ - lduh [%src + offset + 0x06], %t3; \ - stha %t0, [%dst + offset + 0x00] %asi; \ - stha %t1, [%dst + offset + 0x02] %asi; \ - stha %t2, [%dst + offset + 0x04] %asi; \ - stha %t3, [%dst + offset + 0x06] %asi; - -#define MOVE_SHORTCHUNK(src, dst, offset, t0, t1) \ - ldub [%src - offset - 0x02], %t0; \ - ldub [%src - offset - 0x01], %t1; \ - stba %t0, [%dst - offset - 0x02] %asi; \ - stba %t1, [%dst - offset - 0x01] %asi; - - .text - .align 4 - - .globl __copy_to_user -dword_align: - andcc %o1, 1, %g0 - be 4f - andcc %o1, 2, %g0 - - ldub [%o1], %g2 - add %o1, 1, %o1 - EXO2(stba %g2, [%o0] %asi,#) - sub %o2, 1, %o2 - bne 3f - add %o0, 1, %o0 - - lduh [%o1], %g2 - add %o1, 2, %o1 - EXO2(stha %g2, [%o0] %asi,#) - sub %o2, 2, %o2 - ba,pt %xcc, 3f - add %o0, 2, %o0 -4: - lduh [%o1], %g2 - add %o1, 2, %o1 - EXO2(stha %g2, [%o0] %asi,#) - sub %o2, 2, %o2 - ba,pt %xcc, 3f - add %o0, 2, %o0 - -__copy_to_user: /* %o0=dst %o1=src %o2=len */ - wr %g0, ASI_S, %asi - xor %o0, %o1, %o4 -1: - andcc %o4, 3, %o5 -2: - bne,pn %icc, cannot_optimize - cmp %o2, 15 - - bleu,pn %xcc, short_aligned_end - andcc %o1, 3, %g0 - - bne,pn %icc, dword_align -3: - andcc %o1, 4, %g0 - - be,pt %icc, 2f - mov %o2, %g1 - - ld [%o1], %o4 - sub %g1, 4, %g1 - EXO2(sta %o4, [%o0] %asi,#) - add %o1, 4, %o1 - add %o0, 4, %o0 -2: - andcc %g1, 0xffffffffffffff80, %g7 - be,pn %xcc, 3f - andcc %o0, 4, %g0 - - be,pn %icc, ldd_std + 4 -5: - MOVE_BIGCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGCHUNK(o1, o0, 0x20, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGCHUNK(o1, o0, 0x60, o2, o3, o4, o5, g2, g3, g4, g5) -80: - EXT(5b, 80b, 50f,#) - subcc %g7, 128, %g7 - add %o1, 128, %o1 - bne,pt %xcc, 5b - add %o0, 128, %o0 -3: - andcc %g1, 0x70, %g7 - be,pn %icc, copy_user_table_end - andcc %g1, 8, %g0 -100: - rd %pc, %o5 - srl %g7, 1, %o4 - add %g7, %o4, %o4 - add %o1, %g7, %o1 - sub %o5, %o4, %o5 - jmpl %o5 + (copy_user_table_end - 100b), %g0 - add %o0, %g7, %o0 - -copy_user_table: - MOVE_LASTCHUNK(o1, o0, 0x60, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x50, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x40, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x30, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x20, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x10, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x00, g2, g3, g4, g5) -copy_user_table_end: - EXT(copy_user_table, copy_user_table_end, 51f,#) - be,pt %icc, copy_user_last7 - andcc %g1, 4, %g0 - - ldd [%o1], %g2 - add %o0, 8, %o0 - add %o1, 8, %o1 - EX(sta %g2, [%o0 - 0x08] %asi, and %g1, 0xf,#) - EX2(sta %g3, [%o0 - 0x04] %asi, and %g1, 0xf, %g1, sub %g1, 4,#) -copy_user_last7: - be,pn %icc, 1f - andcc %g1, 2, %g0 - - ld [%o1], %g2 - add %o1, 4, %o1 - EX(sta %g2, [%o0] %asi, and %g1, 7,#) - add %o0, 4, %o0 -1: - be,pn %icc, 1f - andcc %g1, 1, %g0 - - lduh [%o1], %g2 - add %o1, 2, %o1 - EX(stha %g2, [%o0] %asi, and %g1, 3,#) - add %o0, 2, %o0 -1: - be,pn %icc, 1f - nop - - ldub [%o1], %g2 - EX(stba %g2, [%o0] %asi, add %g0, 1,#) -1: - PRE_RETL - retl - clr %o0 - -ldd_std: - MOVE_BIGALIGNCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGALIGNCHUNK(o1, o0, 0x20, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGALIGNCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGALIGNCHUNK(o1, o0, 0x60, o2, o3, o4, o5, g2, g3, g4, g5) -81: - EXT(ldd_std, 81b, 52f,#) - subcc %g7, 128, %g7 - add %o1, 128, %o1 - bne,pt %xcc, ldd_std - add %o0, 128, %o0 - - andcc %g1, 0x70, %g7 - be,pn %icc, copy_user_table_end - andcc %g1, 8, %g0 -101: - rd %pc, %o5 - srl %g7, 1, %o4 - add %g7, %o4, %o4 - add %o1, %g7, %o1 - sub %o5, %o4, %o5 - jmpl %o5 + (copy_user_table_end - 101b), %g0 - add %o0, %g7, %o0 - -cannot_optimize: - bleu short_end - cmp %o5, 2 - - bne byte_chunk - and %o2, 0xfffffffffffffff0, %o3 - - andcc %o1, 1, %g0 - be 10f - nop - - ldub [%o1], %g2 - add %o1, 1, %o1 - EXO2(stba %g2, [%o0] %asi,#) - sub %o2, 1, %o2 - andcc %o2, 0xfffffffffffffff0, %o3 - be short_end - add %o0, 1, %o0 -10: - MOVE_HALFCHUNK(o1, o0, 0x00, g2, g3, g4, g5) - MOVE_HALFCHUNK(o1, o0, 0x08, g2, g3, g4, g5) -82: - EXT(10b, 82b, 53f,#) - subcc %o3, 0x10, %o3 - add %o1, 0x10, %o1 - bne 10b - add %o0, 0x10, %o0 - ba,pt %xcc, 2f - and %o2, 0xe, %o3 - -byte_chunk: - MOVE_SHORTCHUNK(o1, o0, -0x02, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x04, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x06, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x08, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x0a, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x0c, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x0e, g2, g3) - MOVE_SHORTCHUNK(o1, o0, -0x10, g2, g3) -83: - EXT(byte_chunk, 83b, 54f,#) - subcc %o3, 0x10, %o3 - add %o1, 0x10, %o1 - bne,pt %xcc, byte_chunk - add %o0, 0x10, %o0 - -short_end: - and %o2, 0xe, %o3 -2: - rd %pc, %o5 - sll %o3, 3, %o4 - add %o0, %o3, %o0 - sub %o5, %o4, %o5 - add %o1, %o3, %o1 - jmpl %o5 + (short_table_end - 2b), %g0 - andcc %o2, 1, %g0 -84: - MOVE_SHORTCHUNK(o1, o0, 0x0c, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x0a, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x08, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x06, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x04, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x02, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x00, g2, g3) -short_table_end: - EXT(84b, short_table_end, 55f,#) - be 1f - nop - ldub [%o1], %g2 - EX(stba %g2, [%o0] %asi, add %g0, 1,#) -1: - PRE_RETL - retl - clr %o0 - -short_aligned_end: - bne short_end - andcc %o2, 8, %g0 - - be 1f - andcc %o2, 4, %g0 - - ld [%o1 + 0x00], %g2 - ld [%o1 + 0x04], %g3 - add %o1, 8, %o1 - EXO2(sta %g2, [%o0 + 0x00] %asi,#) - EX(sta %g3, [%o0 + 0x04] %asi, sub %o2, 4,#) - add %o0, 8, %o0 -1: - ba,pt %xcc, copy_user_last7 - mov %o2, %g1 - - .section .fixup,#alloc,#execinstr - .align 4 -97: - PRE_RETL - retl - mov %o2, %o0 -/* exception routine sets %g2 to (broken_insn - first_insn)>>2 */ -50: -/* This magic counts how many bytes are left when crash in MOVE_BIGCHUNK - * happens. This is derived from the amount ldd reads, st stores, etc. - * x = g2 % 12; - * o0 = g1 + g7 - ((g2 / 12) * 32 + (x < 4) ? x * 8 : (x - 4) * 4) - */ - cmp %g2, 12 - bcs 1f - cmp %g2, 24 - bcs 2f - cmp %g2, 36 - bcs 3f - nop - sub %g2, 12, %g2 - sub %g7, 32, %g7 -3: - sub %g2, 12, %g2 - sub %g7, 32, %g7 -2: - sub %g2, 12, %g2 - sub %g7, 32, %g7 -1: - cmp %g2, 4 - bcs,a 1f - sll %g2, 3, %g2 - sub %g2, 4, %g2 - sll %g2, 2, %g2 -1: - and %g1, 0x7f, %o0 - add %o0, %g7, %o0 - PRE_RETL - retl - sub %o0, %g2, %o0 -51: -/* i = 41 - g2; j = i % 6; - * o0 = (g1 & 15) + (i / 6) * 16 + (j < 4) ? (j + 1) * 4 : (j - 3) * 8; - */ - neg %g2 - and %g1, 0xf, %g1 - add %g2, 41, %g2 -1: - cmp %g2, 6 - bcs,a 2f - cmp %g2, 4 - add %g1, 16, %g1 - b 1b - sub %g2, 6, %g2 -2: - bcs,a 3f - inc %g2 - sub %g2, 3, %g2 - b 2f - sll %g2, 3, %g2 -3: - sll %g2, 2, %g2 -2: - PRE_RETL - retl - add %g1, %g2, %o0 -52: -/* o0 = g1 + g7 - (g2 / 8) * 32 + (x & 3) * 8 */ - and %g2, 0xfffffffffffffff8, %g4 - and %g2, 3, %g2 - sll %g4, 2, %g4 - sll %g2, 3, %g2 - add %g2, %g4, %g2 - b,a 1b -53: -/* o0 = o3 + (o2 & 15) - (g2 & 8) - (g2 & 3) * 2 */ - and %g2, 3, %g4 - and %g2, 0xfffffffffffffff8, %g2 - sll %g4, 1, %g4 - add %g2, %g4, %g2 - and %o2, 0xf, %o0 - add %o0, %o3, %o0 - PRE_RETL - retl - sub %o0, %g2, %o0 -54: -/* o0 = o3 + (o2 & 15) - (g2 / 4) * 2 - (g2 & 1) */ - srl %g2, 2, %o4 - and %g2, 1, %o1 - sll %o4, 1, %o4 - and %o2, 0xf, %o2 - sub %o3, %o1, %o3 - sub %o2, %o4, %o2 - PRE_RETL - retl - add %o2, %o3, %o0 -55: -/* o0 = (o2 & 1) + (27 - g2)/4 * 2 + ((27 - g2) & 1) */ - neg %g2 - and %o2, 1, %o2 - add %g2, 27, %g2 - srl %g2, 2, %o1 - and %g2, 1, %g2 - sll %o1, 1, %o1 - add %o2, %g2, %o0 - PRE_RETL - retl - add %o0, %o1, %o0 diff --git a/arch/sparc64/lib/memcpy.S b/arch/sparc64/lib/memcpy.S deleted file mode 100644 index e9462345a..000000000 --- a/arch/sparc64/lib/memcpy.S +++ /dev/null @@ -1,526 +0,0 @@ -/* memcpy.S: Sparc optimized memcpy, bcopy and memmove code - * Hand optimized from GNU libc's memcpy, bcopy and memmove - * for UltraSparc - * Copyright (C) 1991,1996 Free Software Foundation - * Copyright (C) 1995 Linus Torvalds (Linus.Torvalds@helsinki.fi) - * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) - * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) - */ - -#include <asm/asi.h> -#include <asm/head.h> - -#ifdef __KERNEL__ - -#define FUNC(x) \ - .globl x; \ - .type x,@function; \ - .align 4; \ -x: - -#define FASTER_ALIGNED - -/* In kernel these functions don't return a value. - * One should use macros in asm/string.h for that purpose. - * We return 0, so that bugs are more apparent. - */ -#define SETUP_RETL -#define PRE_RETL sethi %uhi(KERNBASE), %g4; clr %o0 -#define RETL_INSN sllx %g4, 32, %g4 - -#else - -/* libc */ - -#define FASTER_ALIGNED - -#ifdef DEBUG -#define FUNC(x) \ - .globl jj##x##1; \ - .type jj##x##1,@function; \ - .align 4; \ -jj##x##1: -#else -#include "DEFS.h" -#endif - -#define SETUP_RETL mov %o0, %g6 -#define PRE_RETL -#define RETL_INSN mov %g6, %o0 - -#endif - -#define MOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \ - ldd [%src + offset + 0x00], %t0; \ - ldd [%src + offset + 0x08], %t2; \ - ldd [%src + offset + 0x10], %t4; \ - ldd [%src + offset + 0x18], %t6; \ - stw %t0, [%dst + offset + 0x00]; \ - stw %t1, [%dst + offset + 0x04]; \ - stw %t2, [%dst + offset + 0x08]; \ - stw %t3, [%dst + offset + 0x0c]; \ - stw %t4, [%dst + offset + 0x10]; \ - stw %t5, [%dst + offset + 0x14]; \ - stw %t6, [%dst + offset + 0x18]; \ - stw %t7, [%dst + offset + 0x1c]; - -#define MOVE_BIGALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \ - ldx [%src + offset + 0x00], %t0; \ - ldx [%src + offset + 0x08], %t1; \ - ldx [%src + offset + 0x10], %t2; \ - ldx [%src + offset + 0x18], %t3; \ - ldx [%src + offset + 0x20], %t4; \ - ldx [%src + offset + 0x28], %t5; \ - ldx [%src + offset + 0x30], %t6; \ - ldx [%src + offset + 0x38], %t7; \ - stx %t0, [%dst + offset + 0x00]; \ - stx %t1, [%dst + offset + 0x08]; \ - stx %t2, [%dst + offset + 0x10]; \ - stx %t3, [%dst + offset + 0x18]; \ - stx %t4, [%dst + offset + 0x20]; \ - stx %t5, [%dst + offset + 0x28]; \ - stx %t6, [%dst + offset + 0x30]; \ - stx %t7, [%dst + offset + 0x38]; - -#define MOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \ - ldd [%src - offset - 0x10], %t0; \ - ldd [%src - offset - 0x08], %t2; \ - stw %t0, [%dst - offset - 0x10]; \ - stw %t1, [%dst - offset - 0x0c]; \ - stw %t2, [%dst - offset - 0x08]; \ - stw %t3, [%dst - offset - 0x04]; - -#define MOVE_LASTALIGNCHUNK(src, dst, offset, t0, t1) \ - ldx [%src - offset - 0x10], %t0; \ - ldx [%src - offset - 0x08], %t1; \ - stx %t0, [%dst - offset - 0x10]; \ - stx %t1, [%dst - offset - 0x08]; - -#define MOVE_SHORTCHUNK(src, dst, offset, t0, t1) \ - ldub [%src - offset - 0x02], %t0; \ - ldub [%src - offset - 0x01], %t1; \ - stb %t0, [%dst - offset - 0x02]; \ - stb %t1, [%dst - offset - 0x01]; - - .text - .align 4 - -FUNC(bcopy) - - mov %o0, %o3 - mov %o1, %o0 - mov %o3, %o1 - brgez,a,pt %o2, 1f - cmp %o0, %o1 - - retl - nop ! Only bcopy returns here and it retuns void... - -#ifdef __KERNEL__ -FUNC(amemmove) -FUNC(__memmove) -#endif -FUNC(memmove) - - cmp %o0, %o1 -1: - SETUP_RETL - bleu,pt %xcc, 9f - sub %o0, %o1, %o4 - - add %o1, %o2, %o3 - cmp %o3, %o0 - bleu,pt %xcc, 0f - andcc %o4, 3, %o5 - - add %o1, %o2, %o1 - add %o0, %o2, %o0 - sub %o1, 1, %o1 - sub %o0, 1, %o0 - -1: - ldub [%o1], %o4 - subcc %o2, 1, %o2 - sub %o1, 1, %o1 - stb %o4, [%o0] - bne,pt %icc, 1b - sub %o0, 1, %o0 - - PRE_RETL - retl - RETL_INSN - -#ifdef __KERNEL__ -FUNC(__memcpy) -#endif -FUNC(memcpy) /* %o0=dst %o1=src %o2=len */ - - sub %o0, %o1, %o4 - SETUP_RETL -9: - andcc %o4, 3, %o5 -0: - bne,pn %icc, 86f - cmp %o2, 15 - - bleu,pn %xcc, 90f - andcc %o1, 3, %g0 - - be,a,pt %icc, 3f ! check if we need to align - andcc %o1, 4, %g0 - - andcc %o1, 1, %g0 - be,pn %icc, 4f - andcc %o1, 2, %g0 - - ldub [%o1], %g2 - add %o1, 1, %o1 - sub %o2, 1, %o2 - stb %g2, [%o0] - bne,pn %icc, 5f - add %o0, 1, %o0 -4: - lduh [%o1], %g2 - add %o1, 2, %o1 - sub %o2, 2, %o2 - sth %g2, [%o0] - add %o0, 2, %o0 -5: - andcc %o1, 4, %g0 -3: - be,pn %icc, 2f - mov %o2, %g1 - - lduw [%o1], %o4 - sub %g1, 4, %g1 - stw %o4, [%o0] - add %o1, 4, %o1 - add %o0, 4, %o0 -2: - andcc %g1, -128, %g7 - be,pn %xcc, 3f - andcc %o0, 4, %g0 - - be,a,pn %icc, 82f + 4 - ldx [%o1], %o2 -5: - MOVE_BIGCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGCHUNK(o1, o0, 0x20, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGCHUNK(o1, o0, 0x60, o2, o3, o4, o5, g2, g3, g4, g5) - subcc %g7, 128, %g7 - add %o1, 128, %o1 - bne,pt %xcc, 5b - add %o0, 128, %o0 -3: - andcc %g1, 0x70, %g7 - be,pn %icc, 80f - andcc %g1, 8, %g0 -79: - rd %pc, %o5 - srl %g7, 1, %o4 - add %g7, %o4, %o4 - add %o1, %g7, %o1 - sub %o5, %o4, %o5 - jmpl %o5 + %lo(80f-79b), %g0 - add %o0, %g7, %o0 - - MOVE_LASTCHUNK(o1, o0, 0x60, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x50, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x40, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x30, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x20, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x10, g2, g3, g4, g5) - MOVE_LASTCHUNK(o1, o0, 0x00, g2, g3, g4, g5) - -80: /* memcpy_table_end */ - be,pt %icc, 81f - andcc %g1, 4, %g0 - - ldd [%o1], %g2 - add %o0, 8, %o0 - stw %g2, [%o0 - 0x08] - add %o1, 8, %o1 - stw %g3, [%o0 - 0x04] - -81: /* memcpy_last7 */ - - be,pt %icc, 1f - andcc %g1, 2, %g0 - - lduw [%o1], %g2 - add %o1, 4, %o1 - stw %g2, [%o0] - add %o0, 4, %o0 -1: - be,pt %icc, 1f - andcc %g1, 1, %g0 - - lduh [%o1], %g2 - add %o1, 2, %o1 - sth %g2, [%o0] - add %o0, 2, %o0 -1: - be,pt %icc, 1f - nop - - ldub [%o1], %g2 - stb %g2, [%o0] -1: - PRE_RETL - retl - RETL_INSN - -82: /* ldx_stx */ - MOVE_BIGALIGNCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5) - MOVE_BIGALIGNCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5) - subcc %g7, 128, %g7 - add %o1, 128, %o1 - bne,pt %xcc, 82b - add %o0, 128, %o0 - -#ifndef FASTER_ALIGNED - - andcc %g1, 0x70, %g7 - be,pn %icc, 80b - andcc %g1, 8, %g0 -83: - rd %pc, %o5 - srl %g7, 1, %o4 - add %g7, %o4, %o4 - add %o1, %g7, %o1 - sub %o5, %o4, %o5 - jmpl %o5 + %lo(80b - 83b), %g0 - add %o0, %g7, %o0 - -#else /* FASTER_ALIGNED */ - - andcc %g1, 0x70, %g7 - be,pn %icc, 84f - andcc %g1, 8, %g0 -83: - rd %pc, %o5 - add %o1, %g7, %o1 - sub %o5, %g7, %o5 - jmpl %o5 + %lo(84f - 83b), %g0 - add %o0, %g7, %o0 - - MOVE_LASTALIGNCHUNK(o1, o0, 0x60, g2, g3) - MOVE_LASTALIGNCHUNK(o1, o0, 0x50, g2, g3) - MOVE_LASTALIGNCHUNK(o1, o0, 0x40, g2, g3) - MOVE_LASTALIGNCHUNK(o1, o0, 0x30, g2, g3) - MOVE_LASTALIGNCHUNK(o1, o0, 0x20, g2, g3) - MOVE_LASTALIGNCHUNK(o1, o0, 0x10, g2, g3) - MOVE_LASTALIGNCHUNK(o1, o0, 0x00, g2, g3) - -84: /* amemcpy_table_end */ - be,pt %icc, 85f - andcc %g1, 4, %g0 - - ldx [%o1], %g2 - add %o1, 8, %o1 - stx %g2, [%o0] - add %o0, 8, %o0 -85: /* amemcpy_last7 */ - be,pt %icc, 1f - andcc %g1, 2, %g0 - - lduw [%o1], %g2 - add %o1, 4, %o1 - stw %g2, [%o0] - add %o0, 4, %o0 -1: - be,pt %icc, 1f - andcc %g1, 1, %g0 - - lduh [%o1], %g2 - add %o1, 2, %o1 - sth %g2, [%o0] - add %o0, 2, %o0 -1: - be,pt %icc, 1f - nop - - ldub [%o1], %g2 - stb %g2, [%o0] -1: - PRE_RETL - retl - RETL_INSN - -#endif /* FASTER_ALIGNED */ - -86: /* non_aligned */ - cmp %o2, 15 - bleu,pn %xcc, 88f - - andcc %o0, 3, %g0 - be,pn %icc, 61f - andcc %o0, 1, %g0 - be,pn %icc, 60f - andcc %o0, 2, %g0 - - ldub [%o1], %g5 - add %o1, 1, %o1 - stb %g5, [%o0] - sub %o2, 1, %o2 - bne,pn %icc, 61f - add %o0, 1, %o0 -60: - ldub [%o1], %g3 - add %o1, 2, %o1 - stb %g3, [%o0] - sub %o2, 2, %o2 - ldub [%o1 - 1], %g3 - add %o0, 2, %o0 - stb %g3, [%o0 - 1] -61: - and %o1, 3, %g2 - and %o2, 0xc, %g3 - and %o1, -4, %o1 - cmp %g3, 4 - sll %g2, 3, %g4 - mov 32, %g2 - be,pn %icc, 4f - sub %g2, %g4, %g7 - - blu,pn %icc, 3f - cmp %g3, 0x8 - - be,pn %icc, 2f - srl %o2, 2, %g3 - - lduw [%o1], %o3 - add %o0, -8, %o0 - lduw [%o1 + 4], %o4 - ba,pt %xcc, 8f - add %g3, 1, %g3 -2: - lduw [%o1], %o4 - add %o0, -12, %o0 - lduw [%o1 + 4], %o5 - add %g3, 2, %g3 - ba,pt %xcc, 9f - add %o1, -4, %o1 -3: - lduw [%o1], %g1 - add %o0, -4, %o0 - lduw [%o1 + 4], %o3 - srl %o2, 2, %g3 - ba,pt %xcc, 7f - add %o1, 4, %o1 -4: - lduw [%o1], %o5 - cmp %o2, 7 - lduw [%o1 + 4], %g1 - srl %o2, 2, %g3 - bleu,pn %xcc, 10f - add %o1, 8, %o1 - - lduw [%o1], %o3 - add %g3, -1, %g3 -5: - sll %o5, %g4, %g2 - srl %g1, %g7, %g5 - or %g2, %g5, %g2 - stw %g2, [%o0] -7: - lduw [%o1 + 4], %o4 - sll %g1, %g4, %g2 - srl %o3, %g7, %g5 - or %g2, %g5, %g2 - stw %g2, [%o0 + 4] -8: - lduw [%o1 + 8], %o5 - sll %o3, %g4, %g2 - srl %o4, %g7, %g5 - or %g2, %g5, %g2 - stw %g2, [%o0 + 8] -9: - lduw [%o1 + 12], %g1 - sll %o4, %g4, %g2 - srl %o5, %g7, %g5 - addcc %g3, -4, %g3 - or %g2, %g5, %g2 - add %o1, 16, %o1 - stw %g2, [%o0 + 12] - add %o0, 16, %o0 - bne,a,pt %xcc, 5b - lduw [%o1], %o3 -10: - sll %o5, %g4, %g2 - srl %g1, %g7, %g5 - srl %g7, 3, %g3 - or %g2, %g5, %g2 - sub %o1, %g3, %o1 - andcc %o2, 2, %g0 - stw %g2, [%o0] - be,pt %icc, 1f - andcc %o2, 1, %g0 - - ldub [%o1], %g2 - add %o1, 2, %o1 - stb %g2, [%o0 + 4] - add %o0, 2, %o0 - ldub [%o1 - 1], %g2 - stb %g2, [%o0 + 3] -1: - be,pt %icc, 1f - nop - - ldub [%o1], %g2 - stb %g2, [%o0 + 4] -1: - PRE_RETL - retl - RETL_INSN - -88: /* short_end */ - - and %o2, 0xe, %o3 -20: - rd %pc, %o5 - sll %o3, 3, %o4 - add %o0, %o3, %o0 - sub %o5, %o4, %o5 - add %o1, %o3, %o1 - jmpl %o5 + %lo(89f - 20b), %g0 - andcc %o2, 1, %g0 - - MOVE_SHORTCHUNK(o1, o0, 0x0c, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x0a, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x08, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x06, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x04, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x02, g2, g3) - MOVE_SHORTCHUNK(o1, o0, 0x00, g2, g3) - -89: /* short_table_end */ - - be,pt %icc, 1f - nop - - ldub [%o1], %g2 - stb %g2, [%o0] -1: - PRE_RETL - retl - RETL_INSN - -90: /* short_aligned_end */ - bne,pn %xcc, 88b - andcc %o2, 8, %g0 - - be,pt %icc, 1f - andcc %o2, 4, %g0 - - lduw [%o1 + 0x00], %g2 - lduw [%o1 + 0x04], %g3 - add %o1, 8, %o1 - stw %g2, [%o0 + 0x00] - stw %g3, [%o0 + 0x04] - add %o0, 8, %o0 -1: - ba,pt %xcc, 81b - mov %o2, %g1 diff --git a/arch/sparc64/lib/memset.S b/arch/sparc64/lib/memset.S deleted file mode 100644 index 713c78ca8..000000000 --- a/arch/sparc64/lib/memset.S +++ /dev/null @@ -1,196 +0,0 @@ -/* linux/arch/sparc64/lib/memset.S: Sparc optimized memset, bzero and clear_user code - * Copyright (C) 1991,1996 Free Software Foundation - * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) - * - * Returns 0, if ok, and number of bytes not yet set if exception - * occurs and we were called as clear_user. - */ - -#include <asm/asi.h> -#include <asm/ptrace.h> - -#define EX(x,y,a,b,z) \ -98: x,y; \ - .section .fixup,z##alloc,z##execinstr; \ - .align 4; \ -99: ba,pt %xcc, 30f; \ - a, b, %o0; \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword 98b, 99b; \ - .text; \ - .align 4 - -#define EXT(start,end,handler,z) \ - .section __ex_table,z##alloc; \ - .align 8; \ - .xword start, 0, end, handler; \ - .text; \ - .align 4 - -/* Please don't change these macros, unless you change the logic - * in the .fixup section below as well. - * Store 64 bytes at (BASE + OFFSET) using value SOURCE. */ -#define ZERO_BIG_BLOCK(base, offset, source) \ - stxa source, [base + offset + 0x00] %asi; \ - stxa source, [base + offset + 0x08] %asi; \ - stxa source, [base + offset + 0x10] %asi; \ - stxa source, [base + offset + 0x18] %asi; \ - stxa source, [base + offset + 0x20] %asi; \ - stxa source, [base + offset + 0x28] %asi; \ - stxa source, [base + offset + 0x30] %asi; \ - stxa source, [base + offset + 0x38] %asi; - -#define ZERO_LAST_BLOCKS(base, offset, source) \ - stxa source, [base - offset - 0x38] %asi; \ - stxa source, [base - offset - 0x30] %asi; \ - stxa source, [base - offset - 0x28] %asi; \ - stxa source, [base - offset - 0x20] %asi; \ - stxa source, [base - offset - 0x18] %asi; \ - stxa source, [base - offset - 0x10] %asi; \ - stxa source, [base - offset - 0x08] %asi; \ - stxa source, [base - offset - 0x00] %asi; - - .text - .align 4 - - .globl __bzero, __memset, __bzero_noasi - .globl memset, __memset_start, __memset_end -__memset_start: -__memset: -memset: - and %o1, 0xff, %g3 - sll %g3, 8, %g2 - or %g3, %g2, %g3 - sll %g3, 16, %g2 - or %g3, %g2, %g3 - mov %o2, %o1 - wr %g0, ASI_P, %asi - sllx %g3, 32, %g2 - ba,pt %xcc, 1f - or %g3, %g2, %g3 -__bzero: - wr %g0, ASI_P, %asi -__bzero_noasi: - mov %g0, %g3 -1: - cmp %o1, 7 - bleu,pn %xcc, 7f - andcc %o0, 3, %o2 - - be,a,pt %icc, 4f - andcc %o0, 4, %g0 - - cmp %o2, 3 - be,pn %icc, 2f - EX(stba %g3, [%o0] %asi, sub %o1, 0,#) - - cmp %o2, 2 - be,pt %icc, 2f - EX(stba %g3, [%o0 + 0x01] %asi, sub %o1, 1,#) - - EX(stba %g3, [%o0 + 0x02] %asi, sub %o1, 2,#) -2: - sub %o2, 4, %o2 - sub %o0, %o2, %o0 - add %o1, %o2, %o1 - andcc %o0, 4, %g0 -4: - be,a,pt %icc, 2f - andncc %o1, 0x7f, %o3 - - EX(sta %g3, [%o0] %asi, sub %o1, 0,#) - sub %o1, 4, %o1 - add %o0, 4, %o0 - andncc %o1, 0x7f, %o3 ! Now everything is 8 aligned and o1 is len to run -2: - be,pn %xcc, 9f - andcc %o1, 0x78, %o2 -10: - ZERO_BIG_BLOCK(%o0, 0x00, %g3) - subcc %o3, 128, %o3 - ZERO_BIG_BLOCK(%o0, 0x40, %g3) -11: - EXT(10b, 11b, 20f,#) - bne,pt %xcc, 10b - add %o0, 128, %o0 - - tst %o2 -9: - be,pn %xcc, 13f - andcc %o1, 7, %o1 -14: - rd %pc, %o4 - srl %o2, 1, %o3 - sub %o4, %o3, %o4 - jmpl %o4 + (13f - 14b), %g0 - add %o0, %o2, %o0 -12: - ZERO_LAST_BLOCKS(%o0, 0x48, %g3) - ZERO_LAST_BLOCKS(%o0, 0x08, %g3) -13: - be,pn %icc, 8f - andcc %o1, 4, %g0 - - be,pn %icc, 1f - andcc %o1, 2, %g0 - - EX(sta %g3, [%o0] %asi, and %o1, 7,#) - add %o0, 4, %o0 -1: - be,pn %icc, 1f - andcc %o1, 1, %g0 - - EX(stha %g3, [%o0] %asi, and %o1, 3,#) - add %o0, 2, %o0 -1: - bne,a,pn %icc, 8f - EX(stba %g3, [%o0] %asi, and %o1, 1,#) -8: - retl - clr %o0 -7: - be,pn %icc, 13b - orcc %o1, 0, %g0 - - be,pn %icc, 0f -8: - add %o0, 1, %o0 - subcc %o1, 1, %o1 - bne,a,pt %icc, 8b - EX(stba %g3, [%o0 - 1] %asi, add %o1, 1,#) -0: - retl - clr %o0 -__memset_end: - - .section .fixup,#alloc,#execinstr - .align 4 -20: - cmp %g2, 8 - bleu,pn %xcc, 1f - and %o1, 0x7f, %o1 - sub %g2, 9, %g2 - add %o3, 64, %o3 -1: - sll %g2, 3, %g2 - add %o3, %o1, %o0 - ba,pt %xcc, 30f - sub %o0, %g2, %o0 -21: - mov 8, %o0 - and %o1, 7, %o1 - sub %o0, %g2, %o0 - sll %o0, 3, %o0 - ba,pt %xcc, 30f - add %o0, %o1, %o0 -30: -/* %o4 is faulting address, %o5 is %pc where fault occured */ - save %sp, -160, %sp - mov %i5, %o0 - mov %i7, %o1 - call lookup_fault - mov %i4, %o2 - ret - restore diff --git a/arch/sparc64/lib/strlen_user.S b/arch/sparc64/lib/strlen_user.S index 4d57aed64..ef6cee5a6 100644 --- a/arch/sparc64/lib/strlen_user.S +++ b/arch/sparc64/lib/strlen_user.S @@ -20,36 +20,27 @@ __strlen_user: andcc %o0, 3, %g0 be,pt %icc, 9f sethi %hi(HI_MAGIC), %o4 -10: - lduba [%o0] ASI_S, %o5 +10: lduba [%o0] ASI_S, %o5 brz,pn %o5, 21f add %o0, 1, %o0 andcc %o0, 3, %g0 be,pn %icc, 4f or %o4, %lo(HI_MAGIC), %o3 -11: - lduba [%o0] ASI_S, %o5 +11: lduba [%o0] ASI_S, %o5 brz,pn %o5, 22f add %o0, 1, %o0 andcc %o0, 3, %g0 - be,pt %icc, 5f - sethi %hi(LO_MAGIC), %o4 -12: - lduba [%o0] ASI_S, %o5 + be,pt %icc, 13f + srl %o3, 7, %o2 +12: lduba [%o0] ASI_S, %o5 brz,pn %o5, 23f add %o0, 1, %o0 - ba,pt %icc, 13f - or %o4, %lo(LO_MAGIC), %o2 -9: - or %o4, %lo(HI_MAGIC), %o3 -4: - sethi %hi(LO_MAGIC), %o4 -5: - or %o4, %lo(LO_MAGIC), %o2 -13: - lda [%o0] ASI_S, %o5 -2: - sub %o5, %o2, %o4 + ba,pt %icc, 2f +15: lda [%o0] ASI_S, %o5 +9: or %o4, %lo(HI_MAGIC), %o3 +4: srl %o3, 7, %o2 +13: lda [%o0] ASI_S, %o5 +2: sub %o5, %o2, %o4 andcc %o4, %o3, %g0 be,pt %icc, 13b add %o0, 4, %o0 @@ -69,20 +60,15 @@ __strlen_user: add %o4, 1, %o4 andcc %o5, 0xff, %g0 bne,a,pt %icc, 2b -14: - lda [%o0] ASI_S, %o5 +14: lda [%o0] ASI_S, %o5 add %o4, 1, %o4 -1: - retl +1: retl sub %o4, %o1, %o0 -21: - retl +21: retl mov 1, %o0 -22: - retl +22: retl mov 2, %o0 -23: - retl +23: retl mov 3, %o0 .section .fixup,#alloc,#execinstr @@ -97,5 +83,6 @@ __strlen_user: .xword 10b, 30b .xword 11b, 30b .xword 12b, 30b + .xword 15b, 30b .xword 13b, 30b .xword 14b, 30b diff --git a/arch/sparc64/mm/Makefile b/arch/sparc64/mm/Makefile index c41c7a938..dddf3153b 100644 --- a/arch/sparc64/mm/Makefile +++ b/arch/sparc64/mm/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.1 1996/12/26 10:24:22 davem Exp $ +# $Id: Makefile,v 1.3 1997/06/27 14:53:38 jj Exp $ # Makefile for the linux Sparc64-specific parts of the memory manager. # # Note! Dependencies are done automagically by 'make dep', which also @@ -7,7 +7,13 @@ # # Note 2! The CFLAGS definition is now in the main makefile... +.S.s: + $(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s + +.S.o: + $(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o + O_TARGET := mm.o -O_OBJS := fault.o init.o generic.o asyncd.o extable.o +O_OBJS := ultra.o fault.o init.o generic.o asyncd.o extable.o modutil.o include $(TOPDIR)/Rules.make diff --git a/arch/sparc64/mm/fault.c b/arch/sparc64/mm/fault.c index 6df923a4b..5e16e1218 100644 --- a/arch/sparc64/mm/fault.c +++ b/arch/sparc64/mm/fault.c @@ -1,4 +1,4 @@ -/* $Id: fault.c,v 1.11 1997/06/01 05:46:15 davem Exp $ +/* $Id: fault.c,v 1.18 1997/07/17 02:20:56 davem Exp $ * arch/sparc64/mm/fault.c: Page fault handlers for the 64-bit Sparc. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -134,44 +134,14 @@ asmlinkage int lookup_fault(unsigned long pc, unsigned long ret_pc, return 0; } -/* #define FAULT_TRACER */ -/* #define FAULT_TRACER_VERBOSE */ +/* #define DEBUG_EXCEPTIONS */ -asmlinkage void do_sparc64_fault(struct pt_regs *regs, int text_fault, int write, - unsigned long address, unsigned long tag, - unsigned long sfsr) +asmlinkage void do_sparc64_fault(struct pt_regs *regs, unsigned long address, int write) { + struct mm_struct *mm = current->mm; struct vm_area_struct *vma; - struct task_struct *tsk = current; - struct mm_struct *mm = tsk->mm; - unsigned long fixup; - unsigned long g2; - int from_user = !(regs->tstate & TSTATE_PRIV); -#ifdef FAULT_TRACER - static unsigned long last_addr = 0; - static int rcnt = 0; - -#ifdef FAULT_TRACER_VERBOSE - printk("FAULT(PC[%016lx],t[%d],w[%d],addr[%016lx])...", - regs->tpc, text_fault, write, address); -#else - printk("F[%016lx:%016lx:w(%d)", regs->tpc, address, write); -#endif - if(address == last_addr) { - if(rcnt++ > 15) { - printk("Wheee lotsa bogus faults, something wrong, spinning\n"); - __asm__ __volatile__("flushw"); - printk("o7[%016lx] i7[%016lx]\n", - regs->u_regs[UREG_I7], - ((struct reg_window *)(regs->u_regs[UREG_FP]+STACK_BIAS))->ins[7]); - sti(); - while(1) - barrier(); - } - } else rcnt = 0; - last_addr = address; -#endif - lock_kernel (); + + lock_kernel(); down(&mm->mmap_sem); vma = find_vma(mm, address); if(!vma) @@ -204,40 +174,99 @@ good_area: */ bad_area: up(&mm->mmap_sem); - /* Is this in ex_table? */ + + { + unsigned long g2 = regs->u_regs[UREG_G2]; + + /* Is this in ex_table? */ + if (regs->tstate & TSTATE_PRIV) { + unsigned char asi = ASI_P; + unsigned int insn; + unsigned long fixup; + + insn = *(unsigned int *)regs->tpc; + if ((insn & 0xc0800000) == 0xc0800000) { + if (insn & 0x2000) + asi = (regs->tstate >> 24); + else + asi = (insn >> 5); + } - g2 = regs->u_regs[UREG_G2]; - if (!from_user && (fixup = search_exception_table (regs->tpc, &g2))) { - printk("Exception: PC<%016lx> faddr<%016lx>\n", regs->tpc, address); - printk("EX_TABLE: insn<%016lx> fixup<%016lx> g2<%016lx>\n", - regs->tpc, fixup, g2); - regs->tpc = fixup; - regs->tnpc = regs->tpc + 4; - regs->u_regs[UREG_G2] = g2; - goto out; - } - if(from_user) { -#if 1 - unsigned long cpc; - __asm__ __volatile__("mov %%i7, %0" : "=r" (cpc)); - printk("[%s:%d] SIGSEGV pc[%016lx] addr[%016lx] w[%d] sfsr[%016lx] " - "caller[%016lx]\n", current->comm, current->pid, regs->tpc, - address, write, sfsr, cpc); + /* Look in asi.h: All _S asis have LS bit set */ + if ((asi & 0x1) && + (fixup = search_exception_table (regs->tpc, &g2))) { +#ifdef DEBUG_EXCEPTIONS + printk("Exception: PC<%016lx> faddr<%016lx>\n", + regs->tpc, address); + printk("EX_TABLE: insn<%016lx> fixup<%016lx> " + "g2<%016lx>\n", regs->tpc, fixup, g2); #endif - tsk->tss.sig_address = address; - tsk->tss.sig_desc = SUBSIG_NOMAPPING; - send_sig(SIGSEGV, tsk, 1); - goto out; + regs->tpc = fixup; + regs->tnpc = regs->tpc + 4; + regs->u_regs[UREG_G2] = g2; + goto out; + } + } else { + current->tss.sig_address = address; + current->tss.sig_desc = SUBSIG_NOMAPPING; + send_sig(SIGSEGV, current, 1); + goto out; + } + unhandled_fault (address, current, regs); } - unhandled_fault (address, tsk, regs); out: unlock_kernel(); -#ifdef FAULT_TRACER -#ifdef FAULT_TRACER_VERBOSE - printk(" done\n"); -#else - printk("]"); -#endif -#endif } +void fixup_dcache_alias(struct vm_area_struct *vma, unsigned long address, pte_t pte) +{ + struct vm_area_struct *vmaring; + struct inode *inode; + unsigned long vaddr, offset, start; + pgd_t *pgdp; + pmd_t *pmdp; + pte_t *ptep; + int alias_found = 0; + + inode = vma->vm_dentry->d_inode; + if(!inode) + return; + + offset = (address & PAGE_MASK) - vma->vm_start; + vmaring = inode->i_mmap; + do { + vaddr = vmaring->vm_start + offset; + + /* This conditional is misleading... */ + if((vaddr ^ address) & PAGE_SIZE) { + alias_found++; + start = vmaring->vm_start; + while(start < vmaring->vm_end) { + pgdp = pgd_offset(vmaring->vm_mm, start); + if(!pgdp) goto next; + pmdp = pmd_offset(pgdp, start); + if(!pmdp) goto next; + ptep = pte_offset(pmdp, start); + if(!ptep) goto next; + + if(pte_val(*ptep) & _PAGE_PRESENT) { + flush_cache_page(vmaring, start); + *ptep = __pte(pte_val(*ptep) & + ~(_PAGE_CV)); + flush_tlb_page(vmaring, start); + } + next: + start += PAGE_SIZE; + } + } + } while((vmaring = vmaring->vm_next_share) != NULL); + + if(alias_found && (pte_val(pte) & _PAGE_CV)) { + pgdp = pgd_offset(vma->vm_mm, address); + pmdp = pmd_offset(pgdp, address); + ptep = pte_offset(pmdp, address); + flush_cache_page(vma, address); + *ptep = __pte(pte_val(*ptep) & ~(_PAGE_CV)); + flush_tlb_page(vma, address); + } +} diff --git a/arch/sparc64/mm/generic.c b/arch/sparc64/mm/generic.c index 289ddd411..730e8cb32 100644 --- a/arch/sparc64/mm/generic.c +++ b/arch/sparc64/mm/generic.c @@ -1,4 +1,4 @@ -/* $Id: generic.c,v 1.1 1996/12/26 10:24:23 davem Exp $ +/* $Id: generic.c,v 1.2 1997/07/01 09:11:42 jj Exp $ * generic.c: Generic Sparc mm routines that are not dependent upon * MMU type but are Sparc specific. * @@ -66,13 +66,35 @@ static inline void io_remap_pte_range(pte_t * pte, unsigned long address, unsign if (end > PMD_SIZE) end = PMD_SIZE; do { - pte_t oldpage = *pte; - pte_clear(pte); - set_pte(pte, mk_pte_io(offset, prot, space)); - forget_pte(oldpage); - address += PAGE_SIZE; + pte_t oldpage; + pte_t entry; + unsigned long curend = address + PAGE_SIZE; + + entry = mk_pte_io(offset, prot, space); offset += PAGE_SIZE; - pte++; + if (!(address & 0xffff)) { + if (!(address & 0x3fffff) && !(offset & 0x3fffff) && end >= address + 0x400000) { + entry = mk_pte_io(offset, __pgprot(pgprot_val (prot) | _PAGE_SZ4MB), space); + curend = address + 0x400000; + offset += 0x400000 - PAGE_SIZE; + } else if (!(address & 0x7ffff) && !(offset & 0x7ffff) && end >= address + 0x80000) { + entry = mk_pte_io(offset, __pgprot(pgprot_val (prot) | _PAGE_SZ512K), space); + curend = address + 0x80000; + offset += 0x80000 - PAGE_SIZE; + } else if (!(offset & 0xffff) && end >= address + 0x10000) { + entry = mk_pte_io(offset, __pgprot(pgprot_val (prot) | _PAGE_SZ64K), space); + curend = address + 0x10000; + offset += 0x10000 - PAGE_SIZE; + } + } + do { + oldpage = *pte; + pte_clear(pte); + set_pte(pte, entry); + forget_pte(oldpage); + address += PAGE_SIZE; + pte++; + } while (address < curend); } while (address < end); } diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index 960b3cbbd..a8d903b25 100644 --- a/arch/sparc64/mm/init.c +++ b/arch/sparc64/mm/init.c @@ -1,15 +1,17 @@ -/* $Id: init.c,v 1.29 1997/05/27 06:28:13 davem Exp $ +/* $Id: init.c,v 1.39 1997/07/07 02:50:57 davem Exp $ * arch/sparc64/mm/init.c * * Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ +#include <linux/config.h> #include <linux/string.h> #include <linux/init.h> #include <linux/blk.h> #include <linux/swap.h> +#include <asm/head.h> #include <asm/system.h> #include <asm/page.h> #include <asm/pgtable.h> @@ -33,7 +35,7 @@ extern unsigned long empty_null_pte_table; unsigned long tlb_context_cache = CTX_FIRST_VERSION; /* References to section boundaries */ -extern char __init_begin, __init_end, etext, __p1275_loc, __bss_start; +extern char __init_begin, __init_end, etext, __bss_start; /* * BAD_PAGE is the page that is used for page faults when linux @@ -59,13 +61,15 @@ pmd_t *__bad_pmd(void) pte_t *__bad_pte(void) { memset((void *) &empty_bad_pte_table, 0, PAGE_SIZE); - return (pte_t *) (((unsigned long)&empty_bad_pte_table) + phys_base); + return (pte_t *) (((unsigned long)&empty_bad_pte_table) + - ((unsigned long)&empty_zero_page) + phys_base + PAGE_OFFSET); } pte_t __bad_page(void) { memset((void *) &empty_bad_page, 0, PAGE_SIZE); - return pte_mkdirty(mk_pte((((unsigned long) &empty_bad_page)+phys_base), + return pte_mkdirty(mk_pte((((unsigned long) &empty_bad_page) + - ((unsigned long)&empty_zero_page) + phys_base + PAGE_OFFSET), PAGE_SHARED)); } @@ -288,7 +292,7 @@ struct linux_prom_translation { }; #define MAX_TRANSLATIONS 64 -static void inherit_prom_mappings(void) +static inline void inherit_prom_mappings(void) { struct linux_prom_translation transl[MAX_TRANSLATIONS]; pgd_t *pgdp; @@ -332,7 +336,11 @@ static void inherit_prom_mappings(void) } } -static void inherit_locked_prom_mappings(void) +int prom_itlb_ent, prom_dtlb_ent; +unsigned long prom_itlb_tag, prom_itlb_data; +unsigned long prom_dtlb_tag, prom_dtlb_data; + +static inline void inherit_locked_prom_mappings(void) { int i; int dtlb_seen = 0; @@ -359,6 +367,9 @@ static void inherit_locked_prom_mappings(void) data = spitfire_get_dtlb_data(i); if(!dtlb_seen && (data & _PAGE_L)) { unsigned long tag = spitfire_get_dtlb_tag(i); + prom_dtlb_ent = i; + prom_dtlb_tag = tag; + prom_dtlb_data = data; __asm__ __volatile__("stxa %%g0, [%0] %1" : : "r" (TLB_TAG_ACCESS), "i" (ASI_DMMU)); membar("#Sync"); @@ -379,6 +390,9 @@ static void inherit_locked_prom_mappings(void) data = spitfire_get_itlb_data(i); if(!itlb_seen && (data & _PAGE_L)) { unsigned long tag = spitfire_get_itlb_tag(i); + prom_itlb_ent = i; + prom_itlb_tag = tag; + prom_itlb_data = data; __asm__ __volatile__("stxa %%g0, [%0] %1" : : "r" (TLB_TAG_ACCESS), "i" (ASI_IMMU)); membar("#Sync"); @@ -399,6 +413,64 @@ static void inherit_locked_prom_mappings(void) } } +/* Give PROM back his world, done during reboots... */ +void prom_reload_locked(void) +{ + __asm__ __volatile__("stxa %0, [%1] %2" + : : "r" (prom_dtlb_tag), "r" (TLB_TAG_ACCESS), + "i" (ASI_DMMU)); + membar("#Sync"); + spitfire_put_dtlb_data(prom_dtlb_ent, prom_dtlb_data); + membar("#Sync"); + + __asm__ __volatile__("stxa %0, [%1] %2" + : : "r" (prom_itlb_tag), "r" (TLB_TAG_ACCESS), + "i" (ASI_IMMU)); + membar("#Sync"); + spitfire_put_itlb_data(prom_itlb_ent, prom_itlb_data); + membar("#Sync"); +} + +/* If not locked, zap it. */ +void flush_tlb_all(void) +{ + unsigned long flags; + int i; + + save_flags(flags); cli(); + for(i = 0; i < 64; i++) { + if(!(spitfire_get_dtlb_data(i) & _PAGE_L)) { + __asm__ __volatile__("stxa %%g0, [%0] %1" + : /* no outputs */ + : "r" (TLB_TAG_ACCESS), "i" (ASI_DMMU)); + membar("#Sync"); + spitfire_put_dtlb_data(i, 0x0UL); + membar("#Sync"); + } + if(!(spitfire_get_itlb_data(i) & _PAGE_L)) { + __asm__ __volatile__("stxa %%g0, [%0] %1" + : /* no outputs */ + : "r" (TLB_TAG_ACCESS), "i" (ASI_IMMU)); + membar("#Sync"); + spitfire_put_itlb_data(i, 0x0UL); + membar("#Sync"); + } + } + restore_flags(flags); +} + +void get_new_mmu_context(struct mm_struct *mm, unsigned long ctx) +{ + if((ctx & ~(CTX_VERSION_MASK)) == 0) { + flush_tlb_all(); + ctx = (ctx & CTX_VERSION_MASK) + CTX_FIRST_VERSION; + if(ctx == 1) + ctx = CTX_FIRST_VERSION; + } + tlb_context_cache = ctx + 1; + mm->context = ctx; +} + __initfunc(static void allocate_ptable_skeleton(unsigned long start, unsigned long end)) { @@ -440,9 +512,9 @@ void sparc_ultra_mapioaddr(unsigned long physaddr, unsigned long virt_addr, physaddr &= PAGE_MASK; if(rdonly) - pte = mk_pte_phys(physaddr, __pgprot(pg_iobits)); + pte = mk_pte_phys(physaddr, __pgprot(pg_iobits | __PRIV_BITS)); else - pte = mk_pte_phys(physaddr, __pgprot(pg_iobits | __DIRTY_BITS)); + pte = mk_pte_phys(physaddr, __pgprot(pg_iobits | __DIRTY_BITS | __PRIV_BITS)); set_pte(ptep, pte); } @@ -500,51 +572,42 @@ paging_init(unsigned long start_mem, unsigned long end_mem)) { extern unsigned long phys_base; extern void setup_tba(unsigned long kpgdir); - extern void __bfill64(void *, unsigned long); - pgd_t *pgdp; + extern void __bfill64(void *, unsigned long *); pmd_t *pmdp; - pte_t *ptep, pte; int i; - - /* Must create 2nd locked DTLB entry if physical ram starts at - * 4MB absolute or higher, kernel image has been placed in the - * right place at PAGE_OFFSET but references to start_mem and pages - * will be to the perfect alias mapping, so set it up now. + unsigned long alias_base = phys_base + PAGE_OFFSET; + unsigned long pt; + unsigned long flags; + unsigned long shift = alias_base - ((unsigned long)&empty_zero_page); + + /* We assume physical memory starts at some 4mb multiple, + * if this were not true we wouldn't boot up to this point + * anyways. */ - if(phys_base >= (4 * 1024 * 1024)) { - unsigned long alias_base = phys_base + PAGE_OFFSET; - unsigned long pte; - unsigned long flags; - - /* We assume physical memory starts at some 4mb multiple, - * if this were not true we wouldn't boot up to this point - * anyways. - */ - pte = phys_base | _PAGE_VALID | _PAGE_SZ4MB; - pte |= _PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_L | _PAGE_W; - save_flags(flags); cli(); - __asm__ __volatile__(" - stxa %1, [%0] %3 - stxa %2, [%5] %4 - membar #Sync - flush %%g4 - nop - nop - nop" - : /* No outputs */ - : "r" (TLB_TAG_ACCESS), "r" (alias_base), "r" (pte), - "i" (ASI_DMMU), "i" (ASI_DTLB_DATA_ACCESS), "r" (61 << 3) - : "memory"); - restore_flags(flags); - - /* Now set kernel pgd to upper alias so physical page computations - * work. - */ - init_mm.pgd += (phys_base / (sizeof(pgd_t *))); - } + pt = phys_base | _PAGE_VALID | _PAGE_SZ4MB; + pt |= _PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_L | _PAGE_W; + save_flags(flags); cli(); + __asm__ __volatile__(" + stxa %1, [%0] %3 + stxa %2, [%5] %4 + membar #Sync + flush %%g6 + nop + nop + nop" + : /* No outputs */ + : "r" (TLB_TAG_ACCESS), "r" (alias_base), "r" (pt), + "i" (ASI_DMMU), "i" (ASI_DTLB_DATA_ACCESS), "r" (61 << 3) + : "memory"); + restore_flags(flags); + + /* Now set kernel pgd to upper alias so physical page computations + * work. + */ + init_mm.pgd += ((shift) / (sizeof(pgd_t *))); - null_pmd_table = __pa(((unsigned long)&empty_null_pmd_table) + phys_base); - null_pte_table = __pa(((unsigned long)&empty_null_pte_table) + phys_base); + null_pmd_table = __pa(((unsigned long)&empty_null_pmd_table) + shift); + null_pte_table = __pa(((unsigned long)&empty_null_pte_table) + shift); pmdp = (pmd_t *) &empty_null_pmd_table; for(i = 0; i < 1024; i++) @@ -553,13 +616,13 @@ paging_init(unsigned long start_mem, unsigned long end_mem)) memset((void *) &empty_null_pte_table, 0, PAGE_SIZE); /* Now can init the kernel/bad page tables. */ - __bfill64((void *)swapper_pg_dir, null_pmd_table); - __bfill64((void *)&empty_bad_pmd_table, null_pte_table); + __bfill64((void *)swapper_pg_dir, &null_pmd_table); + __bfill64((void *)&empty_bad_pmd_table, &null_pte_table); /* We use mempool to create page tables, therefore adjust it up * such that __pa() macros etc. work. */ - mempool = PAGE_ALIGN(start_mem) + phys_base; + mempool = PAGE_ALIGN(start_mem) + shift; /* FIXME: This should be done much nicer. * Just now we allocate 64M for each. @@ -567,48 +630,29 @@ paging_init(unsigned long start_mem, unsigned long end_mem)) allocate_ptable_skeleton(IOBASE_VADDR, IOBASE_VADDR + 0x4000000); allocate_ptable_skeleton(DVMA_VADDR, DVMA_VADDR + 0x4000000); inherit_prom_mappings(); - allocate_ptable_skeleton(0, 0x8000 + PAGE_SIZE); - - /* Map prom interface page. */ - pgdp = pgd_offset(init_task.mm, 0x8000); - pmdp = pmd_offset(pgdp, 0x8000); - ptep = pte_offset(pmdp, 0x8000); - pte = mk_pte(((unsigned long)&__p1275_loc)+phys_base, PAGE_KERNEL); - set_pte(ptep, pte); - + /* Ok, we can use our TLB miss and window trap handlers safely. */ setup_tba((unsigned long)init_mm.pgd); - /* Kill locked PROM interface page mapping, the mapping will - * re-enter on the next PROM interface call via our TLB miss - * handlers. - */ - spitfire_flush_dtlb_primary_page(0x8000); - membar("#Sync"); - spitfire_flush_itlb_primary_page(0x8000); - membar("#Sync"); - /* Really paranoid. */ - flushi(PAGE_OFFSET); + flushi((long)&empty_zero_page); membar("#Sync"); /* Cleanup the extra locked TLB entry we created since we have the * nice TLB miss handlers of ours installed now. */ - if(phys_base >= (4 * 1024 * 1024)) { - /* We only created DTLB mapping of this stuff. */ - spitfire_flush_dtlb_nucleus_page(phys_base + PAGE_OFFSET); - membar("#Sync"); - - /* Paranoid */ - flushi(PAGE_OFFSET); - membar("#Sync"); - } + /* We only created DTLB mapping of this stuff. */ + spitfire_flush_dtlb_nucleus_page(alias_base); + membar("#Sync"); - inherit_locked_prom_mappings(); + /* Paranoid */ + flushi((long)&empty_zero_page); + membar("#Sync"); + inherit_locked_prom_mappings(); + flush_tlb_all(); - + start_mem = free_area_init(PAGE_ALIGN(mempool), end_mem); return device_scan (PAGE_ALIGN (start_mem)); @@ -642,9 +686,8 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) int codepages = 0; int datapages = 0; int initpages = 0; - int prompages = 0; unsigned long tmp2, addr; - unsigned long data_end; + unsigned long alias_base = phys_base + PAGE_OFFSET - (long)(&empty_zero_page); end_mem &= PAGE_MASK; max_mapnr = MAP_NR(end_mem); @@ -665,16 +708,14 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) } taint_real_pages(start_mem, end_mem); - data_end = start_mem - phys_base; for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) { if(PageReserved(mem_map + MAP_NR(addr))) { - if ((addr < (unsigned long) &etext) && (addr >= PAGE_OFFSET)) + if ((addr < ((unsigned long) &etext) + alias_base) && (addr >= alias_base)) codepages++; - else if((addr >= (unsigned long)&__init_begin && addr < (unsigned long)&__init_end)) + else if((addr >= ((unsigned long)&__init_begin) + alias_base) + && (addr < ((unsigned long)&__init_end) + alias_base)) initpages++; - else if((addr >= (unsigned long)&__p1275_loc && addr < (unsigned long)&__bss_start)) - prompages++; - else if((addr < data_end) && (addr >= PAGE_OFFSET)) + else if((addr < start_mem) && (addr >= alias_base)) datapages++; continue; } @@ -689,12 +730,11 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) tmp2 = nr_free_pages << PAGE_SHIFT; - printk("Memory: %luk available (%dk kernel code, %dk data, %dk init, %dk prom) [%016lx,%016lx]\n", + printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%016lx,%016lx]\n", tmp2 >> 10, codepages << (PAGE_SHIFT-10), datapages << (PAGE_SHIFT-10), initpages << (PAGE_SHIFT-10), - prompages << (PAGE_SHIFT-10), PAGE_OFFSET, end_mem); min_free_pages = nr_free_pages >> 7; @@ -702,11 +742,6 @@ __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) min_free_pages = 16; free_pages_low = min_free_pages + (min_free_pages >> 1); free_pages_high = min_free_pages + min_free_pages; - -#if 0 - printk("Testing fault handling...\n"); - *(char *)0x00000deadbef0000UL = 0; -#endif } void free_initmem (void) @@ -715,10 +750,8 @@ void free_initmem (void) addr = (unsigned long)(&__init_begin); for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { - unsigned long page = addr; - - if(page < ((unsigned long)__va(phys_base))) - page += phys_base; + unsigned long page = addr + (long)__va(phys_base) + - (long)(&empty_zero_page); mem_map[MAP_NR(page)].flags &= ~(1 << PG_reserved); atomic_set(&mem_map[MAP_NR(page)].count, 1); diff --git a/arch/sparc64/mm/modutil.c b/arch/sparc64/mm/modutil.c new file mode 100644 index 000000000..a0eba8019 --- /dev/null +++ b/arch/sparc64/mm/modutil.c @@ -0,0 +1,66 @@ +/* $Id: modutil.c,v 1.1 1997/07/18 06:26:54 ralf Exp $ + * arch/sparc64/mm/modutil.c + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Based upon code written by Linus Torvalds and others. + */ + +#include <linux/malloc.h> +#include <linux/vmalloc.h> + +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/vaddrs.h> + +static struct vm_struct * modvmlist = NULL; + +void module_unmap (void * addr) +{ + struct vm_struct **p, *tmp; + + if (!addr) + return; + if ((PAGE_SIZE-1) & (unsigned long) addr) { + printk("Trying to vfree() bad address (%p)\n", addr); + return; + } + for (p = &modvmlist ; (tmp = *p) ; p = &tmp->next) { + if (tmp->addr == addr) { + *p = tmp->next; + vmfree_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size); + kfree(tmp); + return; + } + } + printk("Trying to unmap nonexistent module vm area (%p)\n", addr); +} + +void * module_map (unsigned long size) +{ + void * addr; + struct vm_struct **p, *tmp, *area; + + size = PAGE_ALIGN(size); + if (!size || size > MODULES_LEN) return NULL; + + addr = (void *) MODULES_VADDR; + for (p = &modvmlist; (tmp = *p) ; p = &tmp->next) { + if (size + (unsigned long) addr < (unsigned long) tmp->addr) + break; + addr = (void *) (tmp->size + (unsigned long) tmp->addr); + } + if ((unsigned long) addr + size >= MODULES_END) return NULL; + + area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL); + if (!area) return NULL; + area->size = size + PAGE_SIZE; + area->addr = addr; + area->next = *p; + *p = area; + + if (vmalloc_area_pages(VMALLOC_VMADDR(addr), size)) { + vfree(addr); + return NULL; + } + return addr; +} diff --git a/arch/sparc64/mm/ultra.S b/arch/sparc64/mm/ultra.S new file mode 100644 index 000000000..b11903a25 --- /dev/null +++ b/arch/sparc64/mm/ultra.S @@ -0,0 +1,226 @@ +/* $Id: ultra.S,v 1.1 1997/07/18 06:26:55 ralf Exp $ + * ultra.S: Don't expand these all over the place... + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + */ + +#include <asm/asi.h> +#include <asm/spitfire.h> + + /* All callers check mm->context != NO_CONTEXT for us. */ + .text + .align 32 + .globl __flush_tlb_mm, __flush_tlb_range, __flush_tlb_page +__flush_tlb_mm: /* %o0 == (mm->context & 0x1fff) */ + rdpr %otherwin, %g1 + brz,pt %g1, 1f + mov %o7, %g3 + call __flushw_user + clr %g2 +1: rdpr %pil, %g1 +9: mov SECONDARY_CONTEXT, %g7 + wrpr %g0, 15, %pil + + ldxa [%g7] ASI_DMMU, %g2 + cmp %g2, %o0 + be,pt %icc, 1f + mov 0x50, %g3 + stxa %o0, [%g7] ASI_DMMU +1: stxa %g0, [%g3] ASI_DMMU_DEMAP + be,pt %icc, 1f + stxa %g0, [%g3] ASI_IMMU_DEMAP + + stxa %g2, [%g7] ASI_DMMU +1: wrpr %g1, 0x0, %pil + retl + flush %g6 +__flush_tlb_range: /* %o0 == (mm->context & 0x1fff), %o1 == start, %o2 == end */ + sethi %hi(8192 - 1), %g5 + or %g5, %lo(8192 - 1), %g5 + andn %o1, %g5, %o1 + andn %o2, %g5, %o2 + + sub %o2, %o1, %o3 + add %g5, 1, %g5 + orcc %o1, 0x50, %o1 + srlx %o3, 13, %o4 + rdpr %otherwin, %g1 + brz,pt %g1, 1f + mov %o7, %g3 + call __flushw_user + + clr %g2 +1: cmp %o4, 96 + bgu,pn %icc, 9b + rdpr %pil, %g1 + mov SECONDARY_CONTEXT, %g7 + wrpr %g0, 15, %pil + ldxa [%g7] ASI_DMMU, %g2 + cmp %g2, %o0 + + be,pt %icc, 1f + sub %o3, %g5, %o3 + stxa %o0, [%g7] ASI_DMMU +1: stxa %g0, [%o1 + %o3] ASI_DMMU_DEMAP + stxa %g0, [%o1 + %o3] ASI_IMMU_DEMAP + brnz,pt %o3, 1b + sub %o3, %g5, %o3 + nop + + be,pt %icc, 1f + wrpr %g1, 0x0, %pil + stxa %g2, [%g7] ASI_DMMU +1: retl + flush %g6 + + .align 32 +__flush_tlb_page: /* %o0 == (mm->context & 0x1fff), %o1 == page & PAGE_MASK */ + rdpr %otherwin, %g1 + brz,pt %g1, 1f + mov %o7, %g3 + call __flushw_user + clr %g2 +1: rdpr %pil, %g1 + mov SECONDARY_CONTEXT, %g7 + wrpr %g0, 15, %pil + + ldxa [%g7] ASI_DMMU, %g2 + cmp %g2, %o0 + be,pt %icc, 1f + or %o1, 0x10, %g3 + stxa %o0, [%g7] ASI_DMMU +1: stxa %g0, [%g3] ASI_DMMU_DEMAP + be,pt %icc, 1f + stxa %g0, [%g3] ASI_IMMU_DEMAP + stxa %g2, [%g7] ASI_DMMU +1: wrpr %g1, 0x0, %pil + retl + flush %g6 + +#ifdef __SMP__ + /* These are all called by the slaves of a cross call, at + * trap level 1, with interrupts fully disabled. + * + * Register usage: + * %g5 mm->context (all tlb flushes) + * %g6 address arg 1 (tlb page and range flushes) + * %g7 address arg 2 (tlb range flush only) + * + * %g1 ivector table, don't touch + * %g2 scratch 1 + * %g3 scratch 2 + * %g4 scratch 3 + * + * NOTE: We do not acknowledge the UPA until we are done + * with the service. This is what tells the master + * that he can consider the effects of the flush + * "complete" on this cpu. + */ + .align 32 + .globl xcall_flush_tlb_page +xcall_flush_tlb_page: + mov SECONDARY_CONTEXT, %g2 + nop + ldxa [%g2] ASI_DMMU, %g3 + cmp %g3, %g5 + be,pt %icc, 1f + or %g6, 0x10, %g4 + stxa %g5, [%g2] ASI_DMMU +1: stxa %g0, [%g4] ASI_DMMU_DEMAP + + be,pt %icc, 1f + stxa %g0, [%g4] ASI_IMMU_DEMAP + stxa %g3, [%g2] ASI_DMMU +1: b,pt %xcc, do_ivec_return + flush %g1 + + .align 32 + .globl xcall_flush_tlb_mm +xcall_flush_tlb_mm: + mov SECONDARY_CONTEXT, %g2 + nop + ldxa [%g2] ASI_DMMU, %g3 + cmp %g3, %g5 + be,pt %icc, 1f + mov 0x50, %g4 + stxa %g5, [%g2] ASI_DMMU +1: stxa %g0, [%g4] ASI_DMMU_DEMAP + + be,pt %icc, 1f + stxa %g0, [%g4] ASI_IMMU_DEMAP + stxa %g3, [%g2] ASI_DMMU +1: b,pt %xcc, do_ivec_return + flush %g1 + + .align 32 + .globl xcall_flush_tlb_range +xcall_flush_tlb_range: + sethi %hi(8192 - 1), %g2 + or %g2, %lo(8192 - 1), %g2 + andn %g6, %g2, %g6 + andn %g7, %g2, %g7 + sub %g7, %g6, %g3 + add %g2, 1, %g2 + orcc %g6, 0x50, %g6 + srlx %g3, 13, %g4 + + cmp %g4, 96 + bgu,pn %icc, xcall_flush_tlb_mm + mov SECONDARY_CONTEXT, %g4 + ldxa [%g4] ASI_DMMU, %g7 + cmp %g7, %g5 + be,pt %icc, 1f + sub %g3, %g2, %g3 + stxa %g5, [%g4] ASI_DMMU + +1: stxa %g0, [%g6 + %g3] ASI_DMMU_DEMAP + stxa %g0, [%g6 + %g3] ASI_IMMU_DEMAP + brnz,pt %g3, 1b + sub %g3, %g2, %g3 + bne,a,pn %icc, 1f + stxa %g7, [%g4] ASI_DMMU +1: b,pt %xcc, do_ivec_return + flush %g1 + + /* These two are not performance critical... */ + .globl xcall_flush_tlb_all +xcall_flush_tlb_all: + clr %g2 + clr %g3 +1: ldxa [%g3] ASI_DTLB_DATA_ACCESS, %g4 + and %g4, _PAGE_L, %g5 + brnz,pn %g5, 2f + mov TLB_TAG_ACCESS, %g7 + stxa %g0, [%g7] ASI_DMMU + membar #Sync + + stxa %g0, [%g3] ASI_DTLB_DATA_ACCESS + membar #Sync +2: ldxa [%g3] ASI_ITLB_DATA_ACCESS, %g4 + and %g4, _PAGE_L, %g5 + brnz,pn %g5, 2f + mov TLB_TAG_ACCESS, %g7 + stxa %g0, [%g7] ASI_IMMU + membar #Sync + + stxa %g0, [%g3] ASI_ITLB_DATA_ACCESS +2: add %g2, 1, %g2 + cmp %g2, 63 + ble,pt %icc, 1b + sll %g2, 3, %g3 + b,pt %xcc, do_ivec_return + flush %g1 + + .globl xcall_flush_cache_all +xcall_flush_cache_all: + sethi %hi(16383), %g2 + or %g2, %lo(16383), %g2 + clr %g3 +1: stxa %g0, [%g3] ASI_IC_TAG + add %g3, 32, %g3 + cmp %g3, %g2 + bleu,pt %xcc, 1b + nop + b,pt %xcc, do_ivec_return + flush %g1 +#endif /* __SMP__ */ diff --git a/arch/sparc64/prom/bootstr.c b/arch/sparc64/prom/bootstr.c index e226c6e95..7ef17159d 100644 --- a/arch/sparc64/prom/bootstr.c +++ b/arch/sparc64/prom/bootstr.c @@ -1,4 +1,4 @@ -/* $Id: bootstr.c,v 1.3 1997/03/04 16:27:06 jj Exp $ +/* $Id: bootstr.c,v 1.4 1997/06/17 13:25:35 jj Exp $ * bootstr.c: Boot string/argument acquisition from the PROM. * * Copyright(C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -6,18 +6,20 @@ */ #include <linux/string.h> +#include <linux/init.h> #include <asm/oplib.h> #define BARG_LEN 256 -static char barg_buf[BARG_LEN]; -static char fetched = 0; +int bootstr_len __initdata = BARG_LEN; +static int bootstr_valid __initdata = 0; +static char bootstr_buf[BARG_LEN] __initdata = { 0 }; -char * -prom_getbootargs(void) +__initfunc(char * +prom_getbootargs(void)) { /* This check saves us from a panic when bootfd patches args. */ - if (fetched) return barg_buf; - prom_getstring(prom_chosen_node, "bootargs", barg_buf, BARG_LEN); - fetched = 1; - return barg_buf; + if (bootstr_valid) return bootstr_buf; + prom_getstring(prom_chosen_node, "bootargs", bootstr_buf, BARG_LEN); + bootstr_valid = 1; + return bootstr_buf; } diff --git a/arch/sparc64/prom/misc.c b/arch/sparc64/prom/misc.c index fe9bf9c6b..8b738fd41 100644 --- a/arch/sparc64/prom/misc.c +++ b/arch/sparc64/prom/misc.c @@ -1,4 +1,4 @@ -/* $Id: misc.c,v 1.6 1997/04/10 05:13:05 davem Exp $ +/* $Id: misc.c,v 1.8 1997/07/14 23:45:28 davem Exp $ * misc.c: Miscellaneous prom functions that don't belong * anywhere else. * @@ -6,6 +6,7 @@ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ +#include <linux/config.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/sched.h> @@ -45,8 +46,8 @@ void prom_cmdline(void) { extern void kernel_enter_debugger(void); - extern void install_obp_ticker(void); - extern void install_linux_ticker(void); + /* extern void install_obp_ticker(void); */ + /* extern void install_linux_ticker(void); */ unsigned long flags; /* kernel_enter_debugger(); */ @@ -132,3 +133,10 @@ void prom_set_trap_table(unsigned long tba) { p1275_cmd("SUNW,set-trap-table", P1275_INOUT(1, 0), tba); } + +#ifdef __SMP__ +void prom_start_cpu(int cpunode, unsigned long pc, unsigned long o0) +{ + p1275_cmd("SUNW,start-cpu", P1275_INOUT(3, 0), cpunode, pc, o0); +} +#endif diff --git a/arch/sparc64/prom/p1275.c b/arch/sparc64/prom/p1275.c index 3eb0311df..18de40deb 100644 --- a/arch/sparc64/prom/p1275.c +++ b/arch/sparc64/prom/p1275.c @@ -1,4 +1,4 @@ -/* $Id: p1275.c,v 1.8 1997/04/03 09:29:21 davem Exp $ +/* $Id: p1275.c,v 1.10 1997/06/27 04:18:30 davem Exp $ * p1275.c: Sun IEEE 1275 PROM low level interface routines * * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -14,50 +14,42 @@ #include <asm/spitfire.h> #include <asm/pstate.h> -/* If you change layout of this structure, please change the prom_doit - function below as well. */ -typedef struct { - unsigned prom_doit_code [24]; /* 0x8000 */ - long prom_sync_routine; /* 0x8060 */ - void (*prom_cif_handler)(long *); /* 0x8068 */ - unsigned long prom_cif_stack; /* 0x8070 */ - unsigned long prom_args [23]; /* 0x8078 */ - char prom_buffer [7888]; -} at0x8000; +struct { + long prom_sync_routine; /* 0x00 */ + void (*prom_cif_handler)(long *); /* 0x08 */ + unsigned long prom_cif_stack; /* 0x10 */ + unsigned long prom_args [23]; /* 0x18 */ + char prom_buffer [3000]; +} p1275buf; -static void (*prom_do_it)(void); - -void prom_cif_interface (void) __attribute__ ((__section__ (".p1275"))); - -/* At most 14 insns */ void prom_cif_interface (void) { __asm__ __volatile__ (" - sethi %%hi(0x8000), %%o0 - ldx [%%o0 + 0x070], %%o1 ! prom_cif_stack + mov %0, %%o0 + ldx [%%o0 + 0x010], %%o1 ! prom_cif_stack save %%o1, -0xc0, %%sp - ldx [%%i0 + 0x068], %%l2 ! prom_cif_handler + ldx [%%i0 + 0x008], %%l2 ! prom_cif_handler rdpr %%pstate, %%l4 mov %%g4, %%l0 mov %%g6, %%l1 - wrpr %%l4, %0, %%pstate ! turn on address masking + wrpr %%l4, %1, %%pstate ! turn on address masking call %%l2 - or %%i0, 0x078, %%o0 ! prom_args + add %%i0, 0x018, %%o0 ! prom_args wrpr %%l4, 0, %%pstate ! put pstate back mov %%l0, %%g4 ret restore %%l1, 0, %%g6 save %%sp, -0xc0, %%sp ! If you change the offset of the save rdpr %%pstate, %%l4 ! here, please change the 0x8038 - andn %%l4, %0, %%l3 ! constant below as well + andn %%l4, %1, %%l3 ! constant below as well wrpr %%l3, %%pstate - ldx [%%o0 + 0x060], %%l2 + ldx [%%o0 + 0x000], %%l2 call %%l2 nop wrpr %%l4, 0, %%pstate ret restore - " : : "i" (PSTATE_AM)); + " : : "r" (&p1275buf), "i" (0 /* PSTATE_AM */)); } long p1275_cmd (char *service, long fmt, ...) @@ -68,61 +60,60 @@ long p1275_cmd (char *service, long fmt, ...) va_list list; long attrs, x; long ctx = 0; - at0x8000 *low = (at0x8000 *)(0x8000); - p = low->prom_buffer; + p = p1275buf.prom_buffer; save_and_cli(flags); ctx = spitfire_get_primary_context (); if (ctx) { flushw_user (); spitfire_set_primary_context (0); } - low->prom_args[0] = (unsigned long)p; /* service */ + p1275buf.prom_args[0] = (unsigned long)p; /* service */ strcpy (p, service); p = (char *)(((long)(strchr (p, 0) + 8)) & ~7); - low->prom_args[1] = nargs = (fmt & 0x0f); /* nargs */ - low->prom_args[2] = nrets = ((fmt & 0xf0) >> 4); /* nrets */ + p1275buf.prom_args[1] = nargs = (fmt & 0x0f); /* nargs */ + p1275buf.prom_args[2] = nrets = ((fmt & 0xf0) >> 4); /* nrets */ attrs = fmt >> 8; va_start(list, fmt); for (i = 0; i < nargs; i++, attrs >>= 3) { switch (attrs & 0x7) { case P1275_ARG_NUMBER: - low->prom_args[i + 3] = (unsigned)va_arg(list, long); break; + p1275buf.prom_args[i + 3] = (unsigned)va_arg(list, long); break; case P1275_ARG_IN_STRING: strcpy (p, va_arg(list, char *)); - low->prom_args[i + 3] = (unsigned long)p; + p1275buf.prom_args[i + 3] = (unsigned long)p; p = (char *)(((long)(strchr (p, 0) + 8)) & ~7); break; case P1275_ARG_OUT_BUF: (void) va_arg(list, char *); - low->prom_args[i + 3] = (unsigned long)p; + p1275buf.prom_args[i + 3] = (unsigned long)p; x = va_arg(list, long); i++; attrs >>= 3; p = (char *)(((long)(p + (int)x + 7)) & ~7); - low->prom_args[i + 3] = x; + p1275buf.prom_args[i + 3] = x; break; case P1275_ARG_IN_BUF: q = va_arg(list, char *); - low->prom_args[i + 3] = (unsigned long)p; + p1275buf.prom_args[i + 3] = (unsigned long)p; x = va_arg(list, long); i++; attrs >>= 3; memcpy (p, q, (int)x); p = (char *)(((long)(p + (int)x + 7)) & ~7); - low->prom_args[i + 3] = x; + p1275buf.prom_args[i + 3] = x; break; case P1275_ARG_OUT_32B: (void) va_arg(list, char *); - low->prom_args[i + 3] = (unsigned long)p; + p1275buf.prom_args[i + 3] = (unsigned long)p; p += 32; break; case P1275_ARG_IN_FUNCTION: - low->prom_args[i + 3] = 0x8038; - low->prom_sync_routine = va_arg(list, long); break; + p1275buf.prom_args[i + 3] = (unsigned long)prom_cif_interface + 0x38; + p1275buf.prom_sync_routine = va_arg(list, long); break; } } va_end(list); - - (*prom_do_it)(); + + prom_cif_interface(); attrs = fmt >> 8; va_start(list, fmt); @@ -142,17 +133,17 @@ long p1275_cmd (char *service, long fmt, ...) case P1275_ARG_OUT_BUF: p = va_arg(list, char *); x = va_arg(list, long); - memcpy (p, (char *)(low->prom_args[i + 3]), (int)x); + memcpy (p, (char *)(p1275buf.prom_args[i + 3]), (int)x); i++; attrs >>= 3; break; case P1275_ARG_OUT_32B: p = va_arg(list, char *); - memcpy (p, (char *)(low->prom_args[i + 3]), 32); + memcpy (p, (char *)(p1275buf.prom_args[i + 3]), 32); break; } } va_end(list); - x = low->prom_args [nargs + 3]; + x = p1275buf.prom_args [nargs + 3]; if (ctx) spitfire_set_primary_context (ctx); @@ -162,9 +153,6 @@ long p1275_cmd (char *service, long fmt, ...) void prom_cif_init(void *cif_handler, void *cif_stack) { - at0x8000 *low = (at0x8000 *)(0x8000); - - low->prom_cif_handler = (void (*)(long *))cif_handler; - low->prom_cif_stack = (unsigned long)cif_stack; - prom_do_it = (void (*)(void))(0x8000); + p1275buf.prom_cif_handler = (void (*)(long *))cif_handler; + p1275buf.prom_cif_stack = (unsigned long)cif_stack; } diff --git a/arch/sparc64/vmlinux.lds b/arch/sparc64/vmlinux.lds index d2d0cac34..eac8314ca 100644 --- a/arch/sparc64/vmlinux.lds +++ b/arch/sparc64/vmlinux.lds @@ -5,10 +5,10 @@ ENTRY(_start) SECTIONS { - empty_zero_page = 0xfffff80000000000; - swapper_pg_dir = 0xfffff80000002000; + empty_zero_page = 0x0000000000400000; + swapper_pg_dir = 0x0000000000402000; . = 0x4000; - .text 0xfffff80000004000 : + .text 0x0000000000404000 : { *(.text) *(.gnu.warning) @@ -40,12 +40,6 @@ SECTIONS .data.init : { *(.data.init) } . = ALIGN(8192); __init_end = .; - __p1275_loc = .; - .p1275 : - { - *(.p1275) - . = ALIGN(8192); - } __bss_start = .; .sbss : { *(.sbss) *(.scommon) } .bss : diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 3f90981fd..d46336e34 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -2897,6 +2897,8 @@ static void raw_cmd_done(int flag) raw_cmd->flags |= FD_RAW_HARDFAILURE; } else { raw_cmd->reply_count = inr; + if(raw_cmd->reply_count > MAX_REPLIES) + raw_cmd->reply_count=0; for (i=0; i< raw_cmd->reply_count; i++) raw_cmd->reply[i] = reply_buffer[i]; diff --git a/drivers/block/ide.c b/drivers/block/ide.c index 1b293c539..61b792f74 100644 --- a/drivers/block/ide.c +++ b/drivers/block/ide.c @@ -2602,6 +2602,7 @@ __initfunc(static void probe_for_hwifs (void)) ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371_0, &ide_init_triton, 1); ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, &ide_init_triton, 0); ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, &ide_init_triton, 0); + ide_probe_pci (PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1, &ide_init_triton, 0); #endif /* CONFIG_BLK_DEV_TRITON */ #ifdef CONFIG_BLK_DEV_OPTI621 ide_probe_pci (PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C621, &ide_init_opti621, 0); diff --git a/drivers/block/ide.h b/drivers/block/ide.h index 95724e6e9..ecfdc12ea 100644 --- a/drivers/block/ide.h +++ b/drivers/block/ide.h @@ -302,7 +302,7 @@ typedef void (ide_selectproc_t) (ide_drive_t *); typedef enum { ide_unknown, ide_generic, ide_triton, ide_cmd640, ide_dtc2278, ide_ali14xx, ide_qd6580, ide_umc8672, ide_ht6560b, - ide_promise } + ide_promise, ide_via } hwif_chipset_t; typedef struct hwif_s { diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 53fef9b74..9bb78d63e 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -292,7 +292,7 @@ static int loop_set_fd(struct loop_device *lo, kdev_t dev, unsigned int arg) return -EBADF; if (lo->lo_inode) return -EBUSY; - inode = file->f_inode; + inode = file->f_dentry->d_inode; if (!inode) { printk("loop_set_fd: NULL inode?!?\n"); return -EINVAL; @@ -318,7 +318,7 @@ static int loop_set_fd(struct loop_device *lo, kdev_t dev, unsigned int arg) } lo->lo_inode = inode; - atomic_inc(&lo->lo_inode->i_count); + lo->lo_inode->i_count++; lo->transfer = NULL; figure_loop_size(lo); MOD_INC_USE_COUNT; diff --git a/drivers/block/md.c b/drivers/block/md.c index 12cb6dcf0..8f8ba19d5 100644 --- a/drivers/block/md.c +++ b/drivers/block/md.c @@ -202,10 +202,10 @@ static int do_md_stop (int minor, struct inode *inode) { int i; - if (atomic_read(&inode->i_count)>1 || md_dev[minor].busy>1) /* ioctl : one open channel */ + if (inode->i_count > 1 || md_dev[minor].busy>1) /* ioctl : one open channel */ { printk ("STOP_MD md%x failed : i_count=%d, busy=%d\n", minor, - atomic_read(&inode->i_count), md_dev[minor].busy); + inode->i_count, md_dev[minor].busy); return -EBUSY; } diff --git a/drivers/block/rd.c b/drivers/block/rd.c index 4ac6c0c41..7d7a3931c 100644 --- a/drivers/block/rd.c +++ b/drivers/block/rd.c @@ -360,10 +360,10 @@ identify_ramdisk_image(kdev_t device, struct file *fp, int start_block)) * Read block 0 to test for gzipped kernel */ if (fp->f_op->llseek) - fp->f_op->llseek(fp->f_inode, fp, start_block * BLOCK_SIZE, 0); + fp->f_op->llseek(fp->f_dentry->d_inode, fp, start_block * BLOCK_SIZE, 0); fp->f_pos = start_block * BLOCK_SIZE; - fp->f_op->read(fp->f_inode, fp, buf, size); + fp->f_op->read(fp->f_dentry->d_inode, fp, buf, size); /* * If it matches the gzip magic numbers, return -1 @@ -390,11 +390,11 @@ identify_ramdisk_image(kdev_t device, struct file *fp, int start_block)) * Read block 1 to test for minix and ext2 superblock */ if (fp->f_op->llseek) - fp->f_op->llseek(fp->f_inode, fp, + fp->f_op->llseek(fp->f_dentry->d_inode, fp, (start_block+1) * BLOCK_SIZE, 0); fp->f_pos = (start_block+1) * BLOCK_SIZE; - fp->f_op->read(fp->f_inode, fp, buf, size); + fp->f_op->read(fp->f_dentry->d_inode, fp, buf, size); /* Try minix */ if (minixsb->s_magic == MINIX_SUPER_MAGIC || @@ -421,7 +421,7 @@ identify_ramdisk_image(kdev_t device, struct file *fp, int start_block)) done: if (fp->f_op->llseek) - fp->f_op->llseek(fp->f_inode, fp, start_block * BLOCK_SIZE, 0); + fp->f_op->llseek(fp->f_dentry->d_inode, fp, start_block * BLOCK_SIZE, 0); fp->f_pos = start_block * BLOCK_SIZE; if ((nblocks > 0) && blk_size[MAJOR(device)]) { @@ -444,8 +444,9 @@ done: */ __initfunc(static void rd_load_image(kdev_t device,int offset)) { - struct inode inode, out_inode; + struct inode inode, out_inode; struct file infile, outfile; + struct dentry in_dentry, out_dentry; unsigned short fs; kdev_t ram_device; int nblocks, i; @@ -457,15 +458,19 @@ __initfunc(static void rd_load_image(kdev_t device,int offset)) memset(&infile, 0, sizeof(infile)); memset(&inode, 0, sizeof(inode)); + memset(&in_dentry, 0, sizeof(in_dentry)); inode.i_rdev = device; infile.f_mode = 1; /* read only */ - infile.f_inode = &inode; + infile.f_dentry = &in_dentry; + in_dentry.d_inode = &inode; memset(&outfile, 0, sizeof(outfile)); memset(&out_inode, 0, sizeof(out_inode)); + memset(&out_dentry, 0, sizeof(out_dentry)); out_inode.i_rdev = ram_device; outfile.f_mode = 3; /* read/write */ - outfile.f_inode = &out_inode; + outfile.f_dentry = &out_dentry; + out_dentry.d_inode = &out_inode; if (blkdev_open(&inode, &infile) != 0) return; if (blkdev_open(&out_inode, &outfile) != 0) return; @@ -506,9 +511,9 @@ __initfunc(static void rd_load_image(kdev_t device,int offset)) printk(KERN_NOTICE "RAMDISK: Loading %d blocks into ram disk... ", nblocks); for (i=0; i < nblocks; i++) { - infile.f_op->read(infile.f_inode, &infile, buf, + infile.f_op->read(infile.f_dentry->d_inode, &infile, buf, BLOCK_SIZE); - outfile.f_op->write(outfile.f_inode, &outfile, buf, + outfile.f_op->write(outfile.f_dentry->d_inode, &outfile, buf, BLOCK_SIZE); if (!(i % 16)) { printk("%c\b", rotator[rotate & 0x3]); @@ -637,7 +642,7 @@ __initfunc(static int fill_inbuf(void)) { if (exit_code) return -1; - insize = crd_infp->f_op->read(crd_infp->f_inode, crd_infp, + insize = crd_infp->f_op->read(crd_infp->f_dentry->d_inode, crd_infp, inbuf, INBUFSIZ); if (insize == 0) return -1; @@ -656,7 +661,7 @@ __initfunc(static void flush_window(void)) unsigned n; uch *in, ch; - crd_outfp->f_op->write(crd_outfp->f_inode, crd_outfp, window, + crd_outfp->f_op->write(crd_outfp->f_dentry->d_inode, crd_outfp, window, outcnt); in = window; for (n = 0; n < outcnt; n++) { diff --git a/drivers/block/triton.c b/drivers/block/triton.c index 7956307c8..006051af6 100644 --- a/drivers/block/triton.c +++ b/drivers/block/triton.c @@ -122,15 +122,25 @@ static unsigned int piix_key; #define PIIX_FLAGS_PREFETCH 4 #define PIIX_FLAGS_FAST_DMA 8 -typedef struct { - unsigned d0_flags :4; - unsigned d1_flags :4; - unsigned recovery :2; - unsigned reserved :2; - unsigned sample :2; - unsigned sidetim_enabled:1; - unsigned ports_enabled :1; -} piix_timing_t; + +union chip_en_reg_u { + struct { + unsigned d0_flags :4; + unsigned d1_flags :4; + unsigned recovery :2; + unsigned reserved :2; + unsigned sample :2; + unsigned sidetim_enabled:1; + unsigned ports_enabled :1; + } piix_s; + struct { + unsigned sec_en :1; + unsigned pri_en :1; + unsigned reserved :14; + } via_s; +}; + +typedef union chip_en_reg_u piix_timing_t; typedef struct { unsigned pri_recovery :2; @@ -269,16 +279,16 @@ static int piix_dmaproc (ide_dma_action_t func, ide_drive_t *drive) printk("%s: pcibios read failed\n", HWIF(drive)->name); return 1; } - dflags = drive->select.b.unit ? timing.d1_flags : timing.d0_flags; + dflags = drive->select.b.unit ? timing.piix_s.d1_flags : timing.piix_s.d0_flags; if (dflags & PIIX_FLAGS_FAST_PIO) { if (func == ide_dma_on && drive->media == ide_disk) dflags |= PIIX_FLAGS_FAST_DMA; else dflags &= ~PIIX_FLAGS_FAST_DMA; if (drive->select.b.unit == 0) - timing.d0_flags = dflags; + timing.piix_s.d0_flags = dflags; else - timing.d1_flags = dflags; + timing.piix_s.d1_flags = dflags; if (pcibios_write_config_word(piix_pci_bus, piix_pci_fn, reg, *(short *)&timing)) { printk("%s: pcibios write failed\n", HWIF(drive)->name); return 1; @@ -456,8 +466,14 @@ void ide_init_triton (byte bus, byte fn) chipset = "PIIX4"; else if (devid == PCI_DEVICE_ID_INTEL_82371SB_1) chipset = "PIIX3"; - else + else if (devid == PCI_DEVICE_ID_INTEL_82371_1) chipset = "PIIX"; + else if (devid == PCI_DEVICE_ID_VIA_82C586_1) + chipset = "VP1"; + else { + printk("Unknown PCI IDE interface 0x%x\n", devid); + goto quit; + } printk("%s: bus-master IDE device on PCI bus %d function %d\n", chipset, bus, fn); @@ -470,13 +486,24 @@ void ide_init_triton (byte bus, byte fn) printk("%s: IDE ports are not enabled (BIOS)\n", chipset); goto quit; } - if ((rc = pcibios_read_config_word(bus, fn, 0x40, (short *)&timings[0]))) - goto quit; - if ((rc = pcibios_read_config_word(bus, fn, 0x42, (short *)&timings[1]))) - goto quit; - if ((!timings[0].ports_enabled) && (!timings[1].ports_enabled)) { - printk("%s: neither IDE port is enabled\n", chipset); - goto quit; + if (devid == PCI_DEVICE_ID_VIA_82C586_1) { + /* pri and sec channel enables are in port 0x40 */ + if ((rc = pcibios_read_config_word(bus, fn, 0x40, (short *)&timings[0]))) + goto quit; + if ((!timings[0].via_s.pri_en && (!timings[0].via_s.sec_en))) { + printk("%s: neither IDE port is enabled\n", chipset); + goto quit; + } + } + else { /* INTEL piix */ + if ((rc = pcibios_read_config_word(bus, fn, 0x40, (short *)&timings[0]))) + goto quit; + if ((rc = pcibios_read_config_word(bus, fn, 0x42, (short *)&timings[1]))) + goto quit; + if ((!timings[0].piix_s.ports_enabled) && (!timings[1].piix_s.ports_enabled)) { + printk("%s: neither IDE port is enabled\n", chipset); + goto quit; + } } /* @@ -526,10 +553,30 @@ void ide_init_triton (byte bus, byte fn) case 0x170: pri_sec = 1; break; default: continue; } + + if (devid == PCI_DEVICE_ID_VIA_82C586_1) { + timing = timings[0]; + switch (h) { + case 0: + if (!timing.piix_s.ports_enabled) { + printk("port 0 DMA not enabled\n"); + continue; + } + case 1: + if (!timing.piix_s.sidetim_enabled) { + printk("port 1 DMA not enabled\n"); + continue; + } + } + hwif->chipset = ide_via; + } + else { /* PIIX */ + timing = timings[pri_sec]; - if (!timing.ports_enabled) /* interface disabled? */ + if (!timing.piix_s.ports_enabled) /* interface disabled? */ continue; hwif->chipset = ide_triton; + } if (dma_enabled) init_piix_dma(hwif, bmiba + (pri_sec ? 8 : 0)); #ifdef DISPLAY_PIIX_TIMINGS @@ -539,17 +586,32 @@ void ide_init_triton (byte bus, byte fn) { const char *slave; piix_sidetim_t sidetim; - byte sample = 5 - timing.sample; - byte recovery = 4 - timing.recovery; + byte sample = 5 - timing.piix_s.sample; + byte recovery = 4 - timing.piix_s.recovery; + unsigned int drvtim; + + if (devid == PCI_DEVICE_ID_VIA_82C586_1) { + pcibios_read_config_dword(bus, fn, 0x48, &drvtim); + if (pri_sec == 0) { + printk(" %s master: active_pulse_CLKs=%d, recovery_CLKs=%d\n", hwif->name, 1+(drvtim>>28), 1+((drvtim & 0x0f000000)>>24)); + printk(" %s slave: active_pulse_CLKs=%d, recovery_CLKs=%d\n", hwif->name, 1+((drvtim & 0xf00000)>>20), 1+((drvtim & 0x0f0000)>>16)); + continue; + } else { + printk(" %s master: active_pulse_CLKs=%d, recovery_CLKs=%d\n", hwif->name, 1+((drvtim & 0xf000)>>12), 1+((drvtim & 0x0f00)>>8)); + printk(" %s slave: active_pulse_CLKs=%d, recovery_CLKs=%d\n", hwif->name, 1+((drvtim & 0xf0)>>4), 1+(drvtim & 0x0f)); + continue; + } + } + if ((devid == PCI_DEVICE_ID_INTEL_82371SB_1 || devid == PCI_DEVICE_ID_INTEL_82371AB) - && timing.sidetim_enabled + && timing.piix_s.sidetim_enabled && !pcibios_read_config_byte(bus, fn, 0x44, (byte *) &sidetim)) slave = ""; /* PIIX3 and later */ else slave = "/slave"; /* PIIX, or PIIX3 in compatibility mode */ printk(" %s master%s: sample_CLKs=%d, recovery_CLKs=%d\n", hwif->name, slave, sample, recovery); - print_piix_drive_flags ("master:", timing.d0_flags); + print_piix_drive_flags ("master:", timing.piix_s.d0_flags); if (!*slave) { if (pri_sec == 0) { sample = 5 - sidetim.pri_sample; @@ -560,7 +622,7 @@ void ide_init_triton (byte bus, byte fn) } printk(" slave : sample_CLKs=%d, recovery_CLKs=%d\n", sample, recovery); } - print_piix_drive_flags ("slave :", timing.d1_flags); + print_piix_drive_flags ("slave :", timing.piix_s.d1_flags); } #endif /* DISPLAY_PIIX_TIMINGS */ } diff --git a/drivers/char/ChangeLog b/drivers/char/ChangeLog index 6a0009511..3083c0c54 100644 --- a/drivers/char/ChangeLog +++ b/drivers/char/ChangeLog @@ -1,3 +1,27 @@ +Thu Jun 19 20:05:58 1997 Theodore Ts'o <tytso@rsts-11.mit.edu> + + * serial.c (begin_break, end_break, rs_ioctl): Applied patch + to support BSD ioctls to set and clear the break + condition explicitly. + + * console.c (scrup, scrdown, insert_line, delete_line): Applied + fix suggested by Aaron Tiensivu to speed up block scrolls + up and down. + + * n_tty.c (opost_block, write_chan): Added a modified "fast + console" patch which processes a block of text via + "cooking" efficiently. + +Wed Jun 18 15:25:50 1997 Theodore Ts'o <tytso@rsts-11.mit.edu> + + * tty_io.c (init_dev, release_dev): Applied fix suggested by Bill + Hawes to prevent race conditions in the tty code. + + * n_tty.c (n_tty_chars_in_buffer): Applied fix suggested by Bill + Hawes so that n_tty_chars_in_buffer returns the correct + value in the case when the tty is in cannonical mode. (To + avoid a pty deadlock with telnetd.) + Thu Feb 27 01:53:08 1997 Theodore Ts'o <tytso@rsts-11.mit.edu> * serial.c (change_speed): Add support for the termios flag diff --git a/drivers/char/Config.in b/drivers/char/Config.in index 4216b3154..c02b376e8 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -52,6 +52,7 @@ if [ "$CONFIG_MOUSE" = "y" ]; then if [ "$CONFIG_PSMOUSE" != "n" ]; then bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE fi + tristate 'PC110 digitizer pad support' CONFIG_PC110_PAD fi if [ "$CONFIG_MODULES" = "y" ]; then @@ -97,4 +98,5 @@ if [ "$CONFIG_WATCHDOG" != "n" ]; then tristate ' Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG fi bool 'Enhanced Real Time Clock Support' CONFIG_RTC +tristate 'PC joystick support' CONFIG_JOYSTICK endmenu diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 14efcf554..73c1ac599 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -145,6 +145,14 @@ else endif endif +ifeq ($(CONFIG_JOYSTICK),y) +L_OBJS += joystick.o +else + ifeq ($(CONFIG_JOYSTICK),m) + M_OBJS += joystick.o + endif +endif + ifeq ($(CONFIG_MS_BUSMOUSE),y) M = y L_OBJS += msbusmouse.o @@ -218,6 +226,16 @@ ifdef CONFIG_SUN_MOUSE M = y endif +ifeq ($(CONFIG_PC110_PAD),y) +M = y +L_OBJS += pc110pad.o +else + ifeq ($(CONFIG_PC110_PAD),m) + M_OBJS += pc110pad.o + MM = m + endif +endif + ifeq ($(CONFIG_SUN_OPENPROMIO),y) M = y else diff --git a/drivers/char/console.c b/drivers/char/console.c index 61ab64aac..b24def4a2 100644 --- a/drivers/char/console.c +++ b/drivers/char/console.c @@ -514,13 +514,15 @@ static void set_origin(int currcons) __set_origin(__real_origin); } -static void scrup(int currcons, unsigned int t, unsigned int b) +static void scrup(int currcons, unsigned int t, unsigned int b, unsigned int nr) { int hardscroll = hardscroll_enabled; - if (b > video_num_lines || t >= b) + if (t+nr >= b) + nr = b - t - 1; + if (b > video_num_lines || t >= b || nr < 1) return; - if (t || b != video_num_lines) + if (t || b != video_num_lines || nr > 1) hardscroll = 0; if (hardscroll) { origin += video_size_row; @@ -575,31 +577,35 @@ static void scrup(int currcons, unsigned int t, unsigned int b) set_origin(currcons); } else { unsigned short * d = (unsigned short *) (origin+video_size_row*t); - unsigned short * s = (unsigned short *) (origin+video_size_row*(t+1)); + unsigned short * s = (unsigned short *) (origin+video_size_row*(t+nr)); - memcpyw(d, s, (b-t-1) * video_size_row); - memsetw(d + (b-t-1) * video_num_columns, video_erase_char, video_size_row); + memcpyw(d, s, (b-t-nr) * video_size_row); + memsetw(d + (b-t-nr) * video_num_columns, video_erase_char, video_size_row*nr); } } static void -scrdown(int currcons, unsigned int t, unsigned int b) +scrdown(int currcons, unsigned int t, unsigned int b, unsigned int nr) { unsigned short *s; unsigned int count; + unsigned int step; - if (b > video_num_lines || t >= b) + if (t+nr >= b) + nr = b - t - 1; + if (b > video_num_lines || t >= b || nr < 1) return; - s = (unsigned short *) (origin+video_size_row*(b-2)); - if (b >= t + 1) { - count = b - t - 1; - while (count) { - count--; - memcpyw(s + video_num_columns, s, video_size_row); - s -= video_num_columns; - } + s = (unsigned short *) (origin+video_size_row*(b-nr-1)); + step = video_num_columns * nr; + count = b - t - nr; + while (count--) { + memcpyw(s + step, s, video_size_row); + s -= video_num_columns; + } + while (nr--) { + s += video_num_columns; + memsetw(s, video_erase_char, video_size_row); } - memsetw(s + video_num_columns, video_erase_char, video_size_row); has_scrolled = 1; } @@ -609,7 +615,7 @@ static void lf(int currcons) * if below scrolling region */ if (y+1 == bottom) - scrup(currcons,top,bottom); + scrup(currcons,top,bottom, 1); else if (y < video_num_lines-1) { y++; pos += video_size_row; @@ -623,7 +629,7 @@ static void ri(int currcons) * if above scrolling region */ if (y == top) - scrdown(currcons,top,bottom); + scrdown(currcons,top,bottom,1); else if (y > 0) { y--; pos -= video_size_row; @@ -1148,9 +1154,9 @@ static void insert_char(int currcons) need_wrap = 0; } -static void insert_line(int currcons) +static void insert_line(int currcons, unsigned int nr) { - scrdown(currcons,y,bottom); + scrdown(currcons,y,bottom,nr); need_wrap = 0; } @@ -1167,9 +1173,9 @@ static void delete_char(int currcons) need_wrap = 0; } -static void delete_line(int currcons) +static void delete_line(int currcons, unsigned int nr) { - scrup(currcons,y,bottom); + scrup(currcons,y,bottom,nr); need_wrap = 0; } @@ -1189,8 +1195,7 @@ static void csi_L(int currcons, unsigned int nr) nr = video_num_lines; else if (!nr) nr = 1; - while (nr--) - insert_line(currcons); + insert_line(currcons, nr); } static void csi_P(int currcons, unsigned int nr) @@ -1209,8 +1214,7 @@ static void csi_M(int currcons, unsigned int nr) nr = video_num_lines; else if (!nr) nr=1; - while (nr--) - delete_line(currcons); + delete_line(currcons, nr); } static void save_cur(int currcons) diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index efa895a69..94969bf28 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -1,6 +1,6 @@ #define BLOCKMOVE static char rcsid[] = -"$Revision: 1.36.4.27 $$Date: 1997/03/26 10:30:00 $"; +"$Revision: 1.1.1.1 $$Date: 1997/06/01 03:17:29 $"; /* * linux/drivers/char/cyclades.c @@ -29,6 +29,9 @@ static char rcsid[] = * void cleanup_module(void); * * $Log: cyclades.c,v $ + * Revision 1.1.1.1 1997/06/01 03:17:29 ralf + * Initial import of Linux/MIPS pre-2.1.40. + * * Revision 1.36.4.27 1997/03/26 10:30:00 daniel * Changed for suport linux versions 2.1.X. * Backward compatible with linux versions 2.0.X. @@ -98,7 +101,7 @@ static char rcsid[] = * after -Y stuff (to make changes clearer) * * Revision 1.36.4.12 1996/07/11 15:40:55 bentson - * Add code to poll Cyclom-Z. Add code to get & set RS-232 control. + * Add code to poll Cyclades-Z. Add code to get & set RS-232 control. * Add code to send break. Clear firmware ID word at startup (so * that other code won't talk to inactive board). * @@ -391,7 +394,13 @@ static char rcsid[] = constant in the definition below. No other change is necessary to support more boards/ports. */ -#define NR_PORTS 64 +//#define NR_PORTS 64 +#define NR_PORTS 128 + +#define ZE_V1_NPORTS 64 +#define ZO_V1 0 +#define ZO_V2 1 +#define ZE_V1 2 #define SERIAL_PARANOIA_CHECK #undef SERIAL_DEBUG_OPEN @@ -453,35 +462,51 @@ static char rcsid[] = #include <linux/kernel.h> #include <linux/bios32.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/version.h> -#if LINUX_VERSION_CODE >= 131328 +#if (LINUX_VERSION_CODE >= 0x020100) #include <asm/uaccess.h> +#include <linux/init.h> -#define memcpy_fromfs copy_from_user -#define memcpy_tofs copy_to_user -#define put_fs_long put_user -#define vremap ioremap +#define cy_put_user put_user -static unsigned long get_fs_long(unsigned long *addr) +static unsigned long cy_get_user(unsigned long *addr) { unsigned long result = 0; int error = get_user (result, addr); if (error) - printk ("cyclades: get_fs_long: error == %d\n", error); + printk ("cyclades: cy_get_user: error == %d\n", error); return result; } +#else + +#define __initfunc(__arginit) __arginit +#define copy_from_user memcpy_fromfs +#define copy_to_user memcpy_tofs +#define cy_get_user get_fs_long +#define cy_put_user put_fs_long +#define ioremap vremap + #endif #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif + #define IS_CYC_Z(card) ((card).num_chips == 1) +#define Z_FPGA_CHECK(card) \ + ((((struct RUNTIME_9060 *)((card).ctl_addr))->init_ctrl&(1<<17))!=0) + +#define ISZLOADED(card) (((ZO_V1==((struct RUNTIME_9060 *) \ + ((card).ctl_addr))->mail_box_0) || \ + Z_FPGA_CHECK(card)) && \ + (ZFIRM_ID==((struct FIRM_ID *) \ + ((card).base_addr+ID_ADDRESS))->signature)) + #define WAKEUP_CHARS (SERIAL_XMIT_SIZE-256) #define STD_COM_FLAGS (0) @@ -548,7 +573,7 @@ static struct cyclades_card *IRQ_cards[16]; /* * tmp_buf is used as a temporary buffer by serial_write. We need to - * lock it in case the memcpy_fromfs blocks while swapping in a page, + * lock it in case the copy_from_user blocks while swapping in a page, * and some other program tries to do a serial write at the same time. * Since the lock will only come under contention when the system is * swapping and available memory is low, it makes sense to share one @@ -698,10 +723,12 @@ static void CP4(int data) { (data<10)? CP(data+'0'): CP(data+'A'-10); }/* CP4 */ static void CP8(int data) { CP4((data>>4) & 0x0f); CP4( data & 0x0f); }/* CP8 */ +#if 0 static void CP16(int data) { CP8((data>>8) & 0xff); CP8(data & 0xff); }/* CP16 */ static void CP32(long data) { CP16((data>>16) & 0xffff); CP16(data & 0xffff); }/* CP32 */ +#endif /* @@ -758,6 +785,7 @@ do_softint(void *private_) if (!tty) return; +#if (LINUX_VERSION_CODE >= 0x020125) if (test_and_clear_bit(Cy_EVENT_HANGUP, &info->event)) { tty_hangup(info->tty); wake_up_interruptible(&info->open_wait); @@ -774,6 +802,24 @@ do_softint(void *private_) } wake_up_interruptible(&tty->write_wait); } +#else + if (clear_bit(Cy_EVENT_HANGUP, &info->event)) { + tty_hangup(info->tty); + wake_up_interruptible(&info->open_wait); + info->flags &= ~(ASYNC_NORMAL_ACTIVE| + ASYNC_CALLOUT_ACTIVE); + } + if (clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) { + wake_up_interruptible(&info->open_wait); + } + if (clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) { + if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP)) + && tty->ldisc.write_wakeup){ + (tty->ldisc.write_wakeup)(tty); + } + wake_up_interruptible(&tty->write_wait); + } +#endif } /* do_softint */ @@ -1351,7 +1397,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) /***********************************************************/ /********* End of block of Cyclom-Y specific code **********/ -/******** Start of block of Cyclom-Z specific code *********/ +/******** Start of block of Cyclades-Z specific code *********/ /***********************************************************/ @@ -1365,7 +1411,7 @@ cyz_fetch_msg( struct cyclades_card *cinfo, unsigned long loc_doorbell; firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); - if (firm_id->signature != ZFIRM_ID){ + if (!ISZLOADED(*cinfo)){ return (-1); } zfw_ctrl = (struct ZFW_CTRL *) @@ -1397,7 +1443,7 @@ cyz_issue_cmd( struct cyclades_card *cinfo, int index; firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); - if (firm_id->signature != ZFIRM_ID){ + if (!ISZLOADED(*cinfo)){ return (-1); } zfw_ctrl = (struct ZFW_CTRL *) @@ -1408,7 +1454,7 @@ cyz_issue_cmd( struct cyclades_card *cinfo, pci_doorbell = &((struct RUNTIME_9060 *) (cinfo->ctl_addr))->pci_doorbell; while( (*pci_doorbell & 0xff) != 0){ - if (index++ == 100){ + if (index++ == 1000){ return(-1); } udelay(50L); @@ -1421,6 +1467,7 @@ cyz_issue_cmd( struct cyclades_card *cinfo, } /* cyz_issue_cmd */ +#if 0 static int cyz_update_channel( struct cyclades_card *cinfo, u_long channel, u_char mode, u_char cmd) @@ -1430,7 +1477,7 @@ cyz_update_channel( struct cyclades_card *cinfo, struct ZFW_CTRL *zfw_ctrl; struct CH_CTRL *ch_ctrl; - if (firm_id->signature != ZFIRM_ID){ + if (!ISZLOADED(*cinfo)){ return (-1); } zfw_ctrl = @@ -1442,6 +1489,7 @@ cyz_update_channel( struct cyclades_card *cinfo, return cyz_issue_cmd(cinfo, channel, cmd, 0L); } /* cyz_update_channel */ +#endif static void @@ -1467,6 +1515,7 @@ cyz_poll(unsigned long arg) u_long channel; u_char cmd; u_long *param; + u_long hw_ver, fw_ver; cyz_timerlist.expires = jiffies + 100; @@ -1476,7 +1525,7 @@ cyz_poll(unsigned long arg) firm_id = (struct FIRM_ID *) (cinfo->base_addr + ID_ADDRESS); - if (firm_id->signature != ZFIRM_ID){ + if (!ISZLOADED(*cinfo)){ continue; } @@ -1484,6 +1533,8 @@ cyz_poll(unsigned long arg) (struct ZFW_CTRL *) (cinfo->base_addr + firm_id->zfwctrl_addr); board_ctrl = &zfw_ctrl->board_ctrl; + fw_ver = board_ctrl->fw_version; + hw_ver = ((struct RUNTIME_9060 *)(cinfo->ctl_addr))->mail_box_0; while( cyz_fetch_msg( cinfo, &channel, &cmd, ¶m) == 1){ char_count = 0; @@ -1514,7 +1565,9 @@ cyz_poll(unsigned long arg) break; case C_CM_MDCD: if (info->flags & ASYNC_CHECK_CD){ - if( ch_ctrl[channel].rs_status & C_RS_DCD){ + if (((hw_ver != 0 || fw_ver > 241) + ? ((u_long)param) + : ch_ctrl[channel].rs_status) & C_RS_DCD) { /* SP("Open Wakeup\n"); */ cy_sched_event(info, Cy_EVENT_OPEN_WAKEUP); @@ -1709,7 +1762,7 @@ cyz_poll(unsigned long arg) } /* cyz_poll */ -/********** End of block of Cyclom-Z specific code *********/ +/********** End of block of Cyclades-Z specific code *********/ /***********************************************************/ @@ -1794,7 +1847,7 @@ startup(struct cyclades_port * info) base_addr = (unsigned char*) (cy_card[card].base_addr); firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS); - if (firm_id->signature != ZFIRM_ID){ + if (!ISZLOADED(cy_card[card])){ return -ENODEV; } @@ -1951,7 +2004,7 @@ shutdown(struct cyclades_port * info) #endif firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS); - if (firm_id->signature != ZFIRM_ID){ + if (!ISZLOADED(cy_card[card])) { return; } @@ -2143,7 +2196,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, base_addr = (char *)(cinfo->base_addr); firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS); - if (firm_id->signature != ZFIRM_ID){ + if (!ISZLOADED(*cinfo)){ return -EINVAL; } @@ -2237,11 +2290,17 @@ cy_open(struct tty_struct *tty, struct file * filp) will make the user pay attention. */ if (IS_CYC_Z(cy_card[info->card])) { - struct FIRM_ID *firm_id = - (struct FIRM_ID *) - (cy_card[info->card].base_addr + ID_ADDRESS); - if (firm_id->signature != ZFIRM_ID){ - printk("Cyclom-Z firmware not yet loaded\n"); + if (!ISZLOADED(cy_card[info->card])) { + if (((ZE_V1 ==((struct RUNTIME_9060 *) + ((cy_card[info->card]).ctl_addr))->mail_box_0) && + Z_FPGA_CHECK(cy_card[info->card])) && + (ZFIRM_HLT==((struct FIRM_ID *) + ((cy_card[info->card]).base_addr+ID_ADDRESS))->signature)) + { + printk ("Cyclades-Z Error: you need an external power supply for this number of ports.\n\rFirmware halted.\r\n"); + } else { + printk("Cyclades-Z firmware not yet loaded\n"); + } return -ENODEV; } } @@ -2448,7 +2507,7 @@ cy_write(struct tty_struct * tty, int from_user, } if (from_user) { - memcpy_fromfs(tmp_buf, buf, c); + copy_from_user(tmp_buf, buf, c); c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); @@ -2816,7 +2875,7 @@ set_line_char(struct cyclades_port * info) firm_id = (struct FIRM_ID *) (cy_card[card].base_addr + ID_ADDRESS); - if (firm_id->signature != ZFIRM_ID){ + if (!ISZLOADED(cy_card[card])) { return; } @@ -2956,7 +3015,7 @@ get_serial_info(struct cyclades_port * info, tmp.baud_base = info->baud; tmp.custom_divisor = 0; /*!!!*/ tmp.hub6 = 0; /*!!!*/ - memcpy_tofs(retinfo,&tmp,sizeof(*retinfo)); + copy_to_user(retinfo,&tmp,sizeof(*retinfo)); return 0; } /* get_serial_info */ @@ -2970,7 +3029,7 @@ set_serial_info(struct cyclades_port * info, if (!new_info) return -EFAULT; - memcpy_fromfs(&new_serial,new_info,sizeof(new_serial)); + copy_from_user(&new_serial,new_info,sizeof(new_serial)); old_info = *info; if (!suser()) { @@ -3051,7 +3110,7 @@ get_modem_info(struct cyclades_port * info, unsigned int *value) firm_id = (struct FIRM_ID *) (cy_card[card].base_addr + ID_ADDRESS); - if (firm_id->signature == ZFIRM_ID){ + if (ISZLOADED(cy_card[card])) { zfw_ctrl = (struct ZFW_CTRL *) (cy_card[card].base_addr + firm_id->zfwctrl_addr); @@ -3070,7 +3129,7 @@ get_modem_info(struct cyclades_port * info, unsigned int *value) } } - put_fs_long(result,(unsigned long *) value); + cy_put_user(result,(unsigned long *) value); return 0; } /* get_modem_info */ @@ -3082,7 +3141,7 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, int card,chip,channel,index; unsigned char *base_addr; unsigned long flags; - unsigned int arg = get_fs_long((unsigned long *) value); + unsigned int arg = cy_get_user((unsigned long *) value); struct FIRM_ID *firm_id; struct ZFW_CTRL *zfw_ctrl; struct BOARD_CTRL *board_ctrl; @@ -3180,7 +3239,7 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, firm_id = (struct FIRM_ID *) (cy_card[card].base_addr + ID_ADDRESS); - if (firm_id->signature == ZFIRM_ID){ + if (ISZLOADED(cy_card[card])) { zfw_ctrl = (struct ZFW_CTRL *) (cy_card[card].base_addr + firm_id->zfwctrl_addr); @@ -3281,7 +3340,7 @@ static int get_mon_info(struct cyclades_port * info, struct cyclades_monitor * mon) { - memcpy_tofs(mon, &info->mon, sizeof(struct cyclades_monitor)); + copy_to_user(mon, &info->mon, sizeof(struct cyclades_monitor)); info->mon.int_count = 0; info->mon.char_count = 0; info->mon.char_max = 0; @@ -3335,7 +3394,7 @@ get_threshold(struct cyclades_port * info, unsigned long *value) + (cy_chip_offset[chip]<<index)); tmp = base_addr[CyCOR3<<index] & CyREC_FIFO; - put_fs_long(tmp,value); + cy_put_user(tmp,value); } else { // Nothing to do! } @@ -3354,7 +3413,7 @@ set_default_threshold(struct cyclades_port * info, unsigned long value) static int get_default_threshold(struct cyclades_port * info, unsigned long *value) { - put_fs_long(info->default_threshold,value); + cy_put_user(info->default_threshold,value); return 0; }/* get_default_threshold */ @@ -3401,7 +3460,7 @@ get_timeout(struct cyclades_port * info, unsigned long *value) + (cy_chip_offset[chip]<<index)); tmp = base_addr[CyRTPR<<index]; - put_fs_long(tmp,value); + cy_put_user(tmp,value); } else { // Nothing to do! } @@ -3420,7 +3479,7 @@ set_default_timeout(struct cyclades_port * info, unsigned long value) static int get_default_timeout(struct cyclades_port * info, unsigned long *value) { - put_fs_long(info->default_timeout,value); + cy_put_user(info->default_timeout,value); return 0; }/* get_default_timeout */ @@ -3530,7 +3589,7 @@ cy_ioctl(struct tty_struct *tty, struct file * file, ret_val = error; break; } - put_fs_long(C_CLOCAL(tty) ? 1 : 0, + cy_put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg); break; case TIOCSSOFTCAR: @@ -3541,7 +3600,7 @@ cy_ioctl(struct tty_struct *tty, struct file * file, break; } - arg = get_fs_long((unsigned long *) arg); + arg = cy_get_user((unsigned long *) arg); tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); @@ -3967,8 +4026,8 @@ cy_detect_isa(void)) } /* probe for CD1400... */ -#if LINUX_VERSION_CODE >= 131328 - cy_isa_address = vremap((unsigned int)cy_isa_address,0x2000); +#if (LINUX_VERSION_CODE >= 0x020100) + cy_isa_address = ioremap((unsigned int)cy_isa_address,0x2000); #endif cy_isa_nchan = 4 * cyy_init_card(cy_isa_address,0); if (cy_isa_nchan == 0) { @@ -3988,6 +4047,7 @@ cy_detect_isa(void)) printk("Cyclom-Y/ISA found at 0x%x ", (unsigned int) cy_isa_address); printk("but no more channels are available.\n"); + printk("Change NR_PORTS in cyclades.c and recompile kernel.\n"); return(nboard); } /* fill the next cy_card structure available */ @@ -3998,6 +4058,7 @@ cy_detect_isa(void)) printk("Cyclom-Y/ISA found at 0x%x ", (unsigned int) cy_isa_address); printk("but no more cards can be used .\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); return(nboard); } @@ -4051,6 +4112,8 @@ cy_detect_pci(void)) unsigned int cy_pci_addr0, cy_pci_addr1, cy_pci_addr2; unsigned short i,j,cy_pci_nchan; unsigned short device_id,dev_index = 0,board_index = 0; + unsigned long mailbox; + unsigned int Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0; if(pcibios_present() == 0) { /* PCI bus not present */ return(0); @@ -4070,6 +4133,9 @@ cy_detect_pci(void)) } } + if (device_id == 0) + break; + /* read PCI configuration area */ pcibios_read_config_byte(cyy_bus, cyy_dev_fn, PCI_INTERRUPT_LINE, &cy_pci_irq); @@ -4081,9 +4147,7 @@ cy_detect_pci(void)) PCI_BASE_ADDRESS_2, &cy_pci_addr2); pcibios_read_config_byte(cyy_bus, cyy_dev_fn, PCI_REVISION_ID, &cyy_rev_id); - if (device_id == 0){ - break; - }else if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) + if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) || (device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi)){ #ifdef CY_PCI_DEBUG printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ", @@ -4096,11 +4160,11 @@ cy_detect_pci(void)) cy_pci_addr1 &= 0xfffffffc; cy_pci_addr2 &= 0xfffffff0; -#if LINUX_VERSION_CODE < 131328 +#if (LINUX_VERSION_CODE < 0x020100) if ((ulong)cy_pci_addr2 >= 0x100000) /* above 1M? */ #endif cy_pci_addr2 = - (unsigned int) vremap(cy_pci_addr2,CyPCI_Ywin); + (unsigned int) ioremap(cy_pci_addr2,CyPCI_Ywin); #ifdef CY_PCI_DEBUG printk("Cyclom-Y/PCI: relocate winaddr=0x%x ioaddr=0x%x\n", @@ -4112,12 +4176,14 @@ cy_detect_pci(void)) printk("Cyclom-Y PCI host card with "); printk("no Serial-Modules at 0x%x.\n", (unsigned int) cy_pci_addr2); + i--; continue; } if((cy_next_channel+cy_pci_nchan) > NR_PORTS) { printk("Cyclom-Y/PCI found at 0x%x ", (unsigned int) cy_pci_addr2); printk("but no channels are available.\n"); + printk("Change NR_PORTS in cyclades.c and recompile kernel.\n"); return(i); } /* fill the next cy_card structure available */ @@ -4128,6 +4194,7 @@ cy_detect_pci(void)) printk("Cyclom-Y/PCI found at 0x%x ", (unsigned int) cy_pci_addr2); printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); return(i); } @@ -4167,65 +4234,101 @@ cy_detect_pci(void)) cy_next_channel += cy_pci_nchan; }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo){ /* print message */ - printk("Cyclom-Z/PCI (bus=0x0%x, pci_id=0x%x, ", + printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ", cyy_bus, cyy_dev_fn); printk("rev_id=%d) IRQ%d\n", cyy_rev_id, (int)cy_pci_irq); - printk("Cyclom-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n", + printk("Cyclades-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n", cy_pci_addr2, cy_pci_addr0); - printk("Cyclom-Z/PCI not supported for low addresses\n"); + printk("Cyclades-Z/PCI not supported for low addresses\n"); break; }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi){ #ifdef CY_PCI_DEBUG - printk("Cyclom-Z/PCI (bus=0x0%x, pci_id=0x%x, ", + printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ", cyy_bus, cyy_dev_fn); printk("rev_id=%d) IRQ%d\n", cyy_rev_id, (int)cy_pci_irq); - printk("Cyclom-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n", + printk("Cyclades-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n", cy_pci_addr2, cy_pci_addr0); #endif - cy_pci_addr2 &= 0xfffffff0; - cy_pci_addr2 = (unsigned int) vremap( - cy_pci_addr2 & PAGE_MASK, - PAGE_ALIGN(CyPCI_Zwin)) - + (cy_pci_addr2 & (PAGE_SIZE-1)); cy_pci_addr0 &= 0xfffffff0; - cy_pci_addr0 = (unsigned int) vremap( + cy_pci_addr0 = (unsigned int) ioremap( cy_pci_addr0 & PAGE_MASK, PAGE_ALIGN(CyPCI_Zctl)) + (cy_pci_addr0 & (PAGE_SIZE-1)); + + mailbox = ((struct RUNTIME_9060 *) cy_pci_addr0)->mail_box_0; + cy_pci_addr2 &= 0xfffffff0; + if (mailbox == ZE_V1) { + cy_pci_addr2 = (unsigned int) ioremap( + cy_pci_addr2 & PAGE_MASK, + PAGE_ALIGN(CyPCI_Ze_win)) + + (cy_pci_addr2 & (PAGE_SIZE-1)); + if (ZeIndex == NR_CARDS) { + printk("Cyclades-Z/PCI found at 0x%x ", + (unsigned int) cy_pci_addr2); + printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); + } else { + Ze_addr0[ZeIndex] = cy_pci_addr0; + Ze_addr2[ZeIndex] = cy_pci_addr2; + ZeIndex++; + } + i--; + continue; + } else { + cy_pci_addr2 = (unsigned int) ioremap( + cy_pci_addr2 & PAGE_MASK, + PAGE_ALIGN(CyPCI_Zwin)) + + (cy_pci_addr2 & (PAGE_SIZE-1)); + } + #ifdef CY_PCI_DEBUG - printk("Cyclom-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n", + printk("Cyclades-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n", cy_pci_addr2, cy_pci_addr0); - ((struct RUNTIME_9060 *)(cy_pci_addr0)) + if (mailbox == ZO_V1) { + ((struct RUNTIME_9060 *)(cy_pci_addr0)) ->loc_addr_base = WIN_CREG; - PAUSE - printk("Cyclom-Z/PCI: FPGA id %lx, ver %lx\n", - 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_id, - 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_version); - ((struct RUNTIME_9060 *)(cy_pci_addr0)) - ->loc_addr_base = WIN_RAM; + PAUSE + printk("Cyclades-Z/PCI: FPGA id %lx, ver %lx\n", + 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_id, + 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_version); + ((struct RUNTIME_9060 *)(cy_pci_addr0)) + ->loc_addr_base = WIN_RAM; + } else { + printk("Cyclades-Z/PCI: New Cyclades-Z board. FPGA not loaded\n"); + } #endif /* The following clears the firmware id word. This ensures that the driver will not attempt to talk to the board until it has been properly initialized. */ PAUSE - *(unsigned long *)(cy_pci_addr2+ID_ADDRESS) = 0L; + if (mailbox == ZO_V1) + *(unsigned long *)(cy_pci_addr2+ID_ADDRESS) = 0L; - /* This must be a Cyclom-8Zo/PCI. The extendable + /* This must be a Cyclades-8Zo/PCI. The extendable version will have a different device_id and will be allocated its maximum number of ports. */ cy_pci_nchan = 8; + if((cy_next_channel+cy_pci_nchan) > NR_PORTS) { + printk("Cyclades-Z/PCI found at 0x%x ", + (unsigned int) cy_pci_addr2); + printk("but no channels are available.\n"); + printk("Change NR_PORTS in cyclades.c and recompile kernel.\n"); + return(i); + } + /* fill the next cy_card structure available */ for (j = 0 ; j < NR_CARDS ; j++) { if (cy_card[j].base_addr == 0) break; } if (j == NR_CARDS) { /* no more cy_cards available */ - printk("Cyclom-Z/PCI found at 0x%x ", + printk("Cyclades-Z/PCI found at 0x%x ", (unsigned int) cy_pci_addr2); printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); return(i); } @@ -4235,9 +4338,9 @@ cy_detect_pci(void)) SA_INTERRUPT,"cyclomZ",NULL)) { printk("Could not allocate IRQ%d ", - (unsigned int) cy_pci_addr2); - printk("for Cyclom-Z/PCI at 0x%x.\n", cy_pci_irq); + printk("for Cyclades-Z/PCI at 0x%x.\n", + (unsigned int) cy_pci_addr2); return(i); } } @@ -4254,12 +4357,12 @@ cy_detect_pci(void)) /* print message */ /* don't report IRQ if board is no IRQ */ if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) { - printk("Cyclom-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ", + printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ", j+1,cy_pci_addr2, (cy_pci_addr2 + CyPCI_Zwin - 1), (int)cy_pci_irq); }else{ - printk("Cyclom-Z/PCI #%d: 0x%x-0x%x, ", + printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, ", j+1,cy_pci_addr2, (cy_pci_addr2 + CyPCI_Zwin - 1)); } @@ -4268,6 +4371,93 @@ cy_detect_pci(void)) cy_next_channel += cy_pci_nchan; } } + + for (; ZeIndex != 0 && i < NR_CARDS; i++) { + cy_pci_addr0 = Ze_addr0[0]; + cy_pci_addr2 = Ze_addr2[0]; + for (j = 0 ; j < ZeIndex-1 ; j++) { + Ze_addr0[j] = Ze_addr0[j+1]; + Ze_addr2[j] = Ze_addr2[j+1]; + } + ZeIndex--; + mailbox = ((struct RUNTIME_9060 *) cy_pci_addr0)->mail_box_0; +#ifdef CY_PCI_DEBUG + printk("Cyclades-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n", + cy_pci_addr2, cy_pci_addr0); + printk("Cyclades-Z/PCI: New Cyclades-Z board. FPGA not loaded\n"); +#endif + /* The following clears the firmware id word. This ensures + that the driver will not attempt to talk to the board + until it has been properly initialized. + */ + PAUSE + /* This must be the new Cyclades-Ze/PCI. */ + cy_pci_nchan = ZE_V1_NPORTS; + + if((cy_next_channel+cy_pci_nchan) > NR_PORTS) { + printk("Cyclades-Z/PCI found at 0x%x ", + (unsigned int) cy_pci_addr2); + printk("but no channels are available.\n"); + printk("Change NR_PORTS in cyclades.c and recompile kernel.\n"); + return(i); + } + + /* fill the next cy_card structure available */ + for (j = 0 ; j < NR_CARDS ; j++) { + if (cy_card[j].base_addr == 0) break; + } + if (j == NR_CARDS) { /* no more cy_cards available */ + printk("Cyclades-Z/PCI found at 0x%x ", + (unsigned int) cy_pci_addr2); + printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); + return(i); + } + + /* allocate IRQ only if board has an IRQ */ + if( (1 < cy_pci_irq) && (cy_pci_irq < 15) ) { + if(request_irq(cy_pci_irq,cyz_interrupt, + SA_INTERRUPT,"cyclomZ",NULL)) + { + printk("Could not allocate IRQ%d ", + cy_pci_irq); + printk("for Cyclades-Z/PCI at 0x%x.\n", + (unsigned int) cy_pci_addr2); + return(i); + } + } + + /* set cy_card */ + cy_card[j].base_addr = cy_pci_addr2; + cy_card[j].ctl_addr = cy_pci_addr0; + cy_card[j].irq = (int) cy_pci_irq; + cy_card[j].bus_index = 1; + cy_card[j].first_line = cy_next_channel; + cy_card[j].num_chips = 1; + IRQ_cards[cy_pci_irq] = &cy_card[j]; + + /* print message */ + /* don't report IRQ if board is no IRQ */ + if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) { + printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ", + j+1,cy_pci_addr2, + (cy_pci_addr2 + CyPCI_Ze_win - 1), + (int)cy_pci_irq); + }else{ + printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, ", + j+1,cy_pci_addr2, + (cy_pci_addr2 + CyPCI_Ze_win - 1)); + } + printk("%d channels starting from port %d.\n", + cy_pci_nchan,cy_next_channel); + cy_next_channel += cy_pci_nchan; + } + if (ZeIndex != 0) { + printk("Cyclades-Z/PCI found at 0x%x ", + (unsigned int) Ze_addr2[0]); + printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); + } return(i); #else return(0); @@ -4319,6 +4509,8 @@ cy_init(void)) struct cyclades_card *cinfo; int number_z_boards = 0; int board,port,i; + unsigned long mailbox; + int nports; show_version(); @@ -4416,10 +4608,13 @@ cy_init(void)) /* initialize per-port data structures for each valid board found */ for (board = 0 ; board < cy_nboard ; board++) { cinfo = &cy_card[board]; - if (cinfo->num_chips == 1){ /* Cyclom-8Zo/PCI */ + if (cinfo->num_chips == 1){ /* Cyclades-8Zo/PCI */ number_z_boards++; + mailbox = ((struct RUNTIME_9060 *) + cy_card[board].ctl_addr)->mail_box_0; + nports = (mailbox == ZE_V1) ? ZE_V1_NPORTS : 8; for (port = cinfo->first_line ; - port < cinfo->first_line + 8; + port < cinfo->first_line + nports; port++) { info = &cy_port[port]; @@ -4523,7 +4718,7 @@ cy_init(void)) cyz_timerlist.expires = jiffies + 1; add_timer(&cyz_timerlist); #ifdef CY_PCI_DEBUG - printk("Cyclom-Z polling initialized\n"); + printk("Cyclades-Z polling initialized\n"); #endif } diff --git a/drivers/char/joystick.c b/drivers/char/joystick.c new file mode 100644 index 000000000..e85d36a98 --- /dev/null +++ b/drivers/char/joystick.c @@ -0,0 +1,376 @@ +/* + + linux/drivers/char/joystick.c + Copyright (C) 1992, 1993 Arthur C. Smith + Joystick driver for Linux running on an IBM compatible computer. + +VERSION INFO: +01/08/93 ACS 0.1: Works but needs multi-joystick support +01/13/93 ACS 0.2: Added multi-joystick support (minor 0 and 1) + Added delay between measuring joystick axis + Added scaling ioctl +02/16/93 ACS 0.3: Modified scaling to use ints to prevent kernel + panics 8-) +02/28/93 ACS 0.4: Linux99.6 and fixed race condition in js_read. + After looking at a schematic of a joystick card + it became apparent that any write to the joystick + port started ALL the joystick one shots. If the + one that we are reading is short enough and the + first one to be read, the second one will return + bad data if it's one shot has not expired when + the joystick port is written for the second time. + Thus solves the mystery delay problem in 0.2! +05/05/93 ACS/Eyal 0.5: Upgraded the driver to the 99.9 kernel, added + joystick support to the make config options, + updated the driver to return the buttons as + positive logic, and read both axis at once + (thanks Eyal!), and added some new ioctls. +02/12/94 Jeff Tranter 0.6: Made necessary changes to work with 0.99pl15 + kernel (and hopefully 1.0). Also did some + cleanup: indented code, fixed some typos, wrote + man page, etc... +05/17/95 Dan Fandrich 0.7.3: Added I/O port registration, cleaned up code +04/03/96 Matt Rhoten 0.8: many minor changes: + new read loop from Hal Maney <maney@norden.com> + cleaned up #includes to allow #include of + joystick.h with gcc -Wall and from g++ + made js_init fail if it finds zero joysticks + general source/comment cleanup + use of MOD_(INC|DEC)_USE_COUNT + changes from Bernd Schmidt <crux@Pool.Informatik.RWTH-Aachen.DE> + to compile correctly under 1.3 in kernel or as module +06/30/97 Alan Cox 0.9: Ported to 2.1.x + Reformatted to resemble Linux coding standard + Removed semaphore bug (we can dump the lot I think) + Fixed xntp timer adjust during joystick timer0 bug + Changed variable names to lower case. Kept binary + compatibility. + Better ioctl names. Kept binary compatibility. + Removed 'save_busy'. Just set busy to 1. +*/ + +#include <linux/module.h> +#include <linux/joystick.h> +#include <linux/mm.h> +#include <linux/major.h> +#include <linux/ioport.h> +#include <asm/io.h> +#include <asm/uaccess.h> + +static struct js_config js_data[JS_MAX]; /* misc data */ +static int js_exist; /* which joysticks' axis exist? */ +static int js_read_semaphore; /* to prevent two processes from trying + to read different joysticks at the + same time */ + +/* + * get_timer0(): + * returns the current value of timer 0. This is a 16 bit counter that starts + * at LATCH and counts down to 0 + */ + +extern inline int get_timer0(void) +{ + unsigned long flags; + int t0, t1; + save_flags(flags); + cli(); + outb (0, PIT_MODE); + t0 = (int) inb (PIT_COUNTER_0); + t1 = ((int) inb (PIT_COUNTER_0) << 8) + t0; + restore_flags(flags); + return (t1); +} + +/* + * find_axes(): + * + * returns which axes are hooked up, in a bitfield. 2^n is set if + * axis n is hooked up, for 0 <= n < 4. + * + * REVIEW: should update this to handle eight-axis (four-stick) game port + * cards. anyone have one of these to test on? mattrh 3/23/96 + */ + +extern inline int find_axes(void) +{ + int j; + outb (0xff, JS_PORT); /* trigger oneshots */ + /* and see what happens */ + for (j = JS_DEF_TIMEOUT; (0x0f & inb (JS_PORT)) && j; j--); + /* do nothing; wait for the timeout */ + js_exist = inb (JS_PORT) & 0x0f; /* get joystick status byte */ + js_exist = (~js_exist) & 0x0f; +/* printk("find_axes: js_exist is %d (0x%04X)\n", js_exist, js_exist);*/ + return js_exist; +} + +static int js_ioctl (struct inode *inode, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + unsigned int minor = MINOR (inode->i_rdev); + if (minor >= JS_MAX) + return -ENODEV; + + if ((((inb (JS_PORT) & 0x0f) >> (minor << 1)) & 0x03) == 0x03) /*js minor exists?*/ + return -ENODEV; + switch (cmd) + { + + case JSIOCSCAL: /*from struct *arg to js_data[minor]*/ + if(copy_from_user(&js_data[minor].js_corr, + (void *)arg, sizeof(struct js_status))) + return -EFAULT; + break; + case JSIOCGCAL: /*to struct *arg from js_data[minor]*/ + if(copy_to_user((void *) arg, &js_data[minor].js_corr, + sizeof(struct js_status))) + return -EFAULT; + break; + case JSIOCSTIMEOUT: + if(copy_from_user(&js_data[minor].js_timeout, + (void *)arg, sizeof(js_data[0].js_timeout))) + return -EFAULT; + break; + case JSIOCGTIMEOUT: + if(copy_to_user((void *)arg, &js_data[minor].js_timeout, + sizeof(js_data[0].js_timeout))) + return -EFAULT; + break; + case JSIOCSTIMELIMIT: + if(copy_from_user(&js_data[minor].js_timelimit, + (void *)arg, sizeof(js_data[0].js_timelimit))) + return -EFAULT; + break; + case JSIOCGTIMELIMIT: + if(copy_to_user((void *)arg, &js_data[minor].js_timelimit, + sizeof(js_data[0].js_timelimit))) + return -EFAULT; + break; + case JSIOCGCONFIG: + if(copy_to_user((void *)arg, &js_data[minor], + sizeof(struct js_config))) + return -EFAULT; + break; + case JSIOCSCONFIG: + if(copy_from_user(&js_data[minor], (void *)arg, + sizeof(struct js_config))) + return -EFAULT; + /* Must be busy to do this ioctl! */ + js_data[minor].busy = 1; + break; + default: + return -EINVAL; + } + return 0; +} + +/* + * js_open(): + * device open routine. increments module usage count, initializes + * data for that joystick. + * + * returns: 0 or + * -ENODEV: asked for joystick other than #0 or #1 + * -ENODEV: asked for joystick on axis where there is none + * -EBUSY: attempt to open joystick already open + */ + +static int js_open (struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR (inode->i_rdev); + int j; + + if (minor >= JS_MAX) + return -ENODEV; /*check for joysticks*/ + + for (j = JS_DEF_TIMEOUT; (js_exist & inb (JS_PORT)) && j; j--); + cli(); /*block js_read while js_exist is being modified*/ + /*js minor exists?*/ + if ((((js_exist = inb (JS_PORT)) >> (minor << 1)) & 0x03) == 0x03) { + js_exist = (~js_exist) & 0x0f; + sti(); + return -ENODEV; + } + js_exist = (~js_exist) & 0x0f; + sti(); + + if (js_data[minor].busy) + return -EBUSY; + js_data[minor].busy = JS_TRUE; + js_data[minor].js_corr.x = JS_DEF_CORR; /*default scale*/ + js_data[minor].js_corr.y = JS_DEF_CORR; + js_data[minor].js_timeout = JS_DEF_TIMEOUT; + js_data[minor].js_timelimit = JS_DEF_TIMELIMIT; + js_data[minor].js_expiretime = jiffies; + + MOD_INC_USE_COUNT; + return 0; +} + +static int js_release (struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR (inode->i_rdev); + inode->i_atime = CURRENT_TIME; + js_data[minor].busy = JS_FALSE; + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_read() reads the buttons x, and y axis from both joysticks if a + * given interval has expired since the last read or is equal to + * -1l. The buttons are in port 0x201 in the high nibble. The axis are + * read by writing to 0x201 and then measuring the time it takes the + * one shots to clear. + */ + +static long js_read (struct inode *inode, struct file *file, char *buf, unsigned long count) +{ + int j, chk, jsmask; + int t0, t_x0, t_y0, t_x1, t_y1; + unsigned int minor, minor2; + int buttons; + + if (count != JS_RETURN) + return -EINVAL; + minor = MINOR (inode->i_rdev); + inode->i_atime = CURRENT_TIME; + if (jiffies >= js_data[minor].js_expiretime) + { + minor2 = minor << 1; + j = js_data[minor].js_timeout; + for (; (js_exist & inb (JS_PORT)) && j; j--); + if (j == 0) + return -ENODEV; /*no joystick here*/ + /*Make sure no other proc is using port*/ + + cli(); + js_read_semaphore++; + sti(); + + buttons = ~(inb (JS_PORT) >> 4); + js_data[0].js_save.buttons = buttons & 0x03; + js_data[1].js_save.buttons = (buttons >> 2) & 0x03; + j = js_data[minor].js_timeout; + jsmask = 0; + + cli(); /*no interrupts!*/ + outb (0xff, JS_PORT); /*trigger one-shots*/ + /*get init timestamp*/ + t_x0 = t_y0 = t_x1 = t_y1 = t0 = get_timer0 (); + /*wait for an axis' bit to clear or timeout*/ + while (j-- && (chk = (inb (JS_PORT) & js_exist ) | jsmask)) + { + if (!(chk & JS_X_0)) { + t_x0 = get_timer0(); + jsmask |= JS_X_0; + } + if (!(chk & JS_Y_0)) { + t_y0 = get_timer0(); + jsmask |= JS_Y_0; + } + if (!(chk & JS_X_1)) { + t_x1 = get_timer0(); + jsmask |= JS_X_1; + } + if (!(chk & JS_Y_1)) { + t_y1 = get_timer0(); + jsmask |= JS_Y_1; + } + } + sti(); /* allow interrupts */ + + js_read_semaphore = 0; /* allow other reads to progress */ + if (j == 0) + return -ENODEV; /*read timed out*/ + js_data[0].js_expiretime = jiffies + + js_data[0].js_timelimit; /*update data*/ + js_data[1].js_expiretime = jiffies + + js_data[1].js_timelimit; + js_data[0].js_save.x = DELTA_TIME (t0, t_x0) >> + js_data[0].js_corr.x; + js_data[0].js_save.y = DELTA_TIME (t0, t_y0) >> + js_data[0].js_corr.y; + js_data[1].js_save.x = DELTA_TIME (t0, t_x1) >> + js_data[1].js_corr.x; + js_data[1].js_save.y = DELTA_TIME (t0, t_y1) >> + js_data[1].js_corr.y; + } + + if(copy_to_user(buf, &js_data[minor].js_save, JS_RETURN)) + return -EFAULT; + return JS_RETURN; +} + + +static struct file_operations js_fops = +{ + NULL, /* js_lseek*/ + js_read, /* js_read */ + NULL, /* js_write*/ + NULL, /* js_readaddr*/ + NULL, /* js_select */ + js_ioctl, /* js_ioctl*/ + NULL, /* js_mmap */ + js_open, /* js_open*/ + js_release, /* js_release*/ + NULL /* js_fsync */ +}; + +#ifdef MODULE + +#define joystick_init init_module + +void cleanup_module (void) +{ + if (unregister_chrdev (JOYSTICK_MAJOR, "joystick")) + printk ("joystick: cleanup_module failed\n"); + release_region(JS_PORT, 1); +} + +#endif /* MODULE */ + +int joystick_init(void) +{ + int js_num; + int js_count; + + if (check_region(JS_PORT, 1)) { + printk("js_init: port already in use\n"); + return -EBUSY; + } + + js_num = find_axes(); + js_count = !!(js_num & 0x3) + !!(js_num & 0xC); + + + if (js_count == 0) + { + printk("No joysticks found.\n"); + return -ENODEV; + /* if the user boots the machine, which runs insmod, and THEN + decides to hook up the joystick, well, then we do the wrong + thing. But it's a good idea to avoid giving out a false sense + of security by letting the module load otherwise. */ + } + + if (register_chrdev (JOYSTICK_MAJOR, "joystick", &js_fops)) { + printk ("Unable to get major=%d for joystick\n", + JOYSTICK_MAJOR); + return -EBUSY; + } + request_region(JS_PORT, 1, "joystick"); + + for (js_num = 0; js_num < JS_MAX; js_num++) + js_data[js_num].busy = JS_FALSE; + js_read_semaphore = 0; + + printk (KERN_INFO "Found %d joystick%c.\n", + js_count, + (js_num == 1) ? ' ' : 's'); + return 0; +} + diff --git a/drivers/char/lp.c b/drivers/char/lp.c index 5eb52752d..9f9e5a992 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -724,7 +724,11 @@ __initfunc(int lp_init(void)) return 0; printk(KERN_INFO "lp: driver loaded but no devices found\n"); +#ifdef MODULE + return 0; +#else return 1; +#endif } #ifdef MODULE diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 499132bf8..063503595 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -20,6 +20,7 @@ #include <linux/mm.h> #include <linux/random.h> #include <linux/init.h> +#include <linux/joystick.h> #include <asm/uaccess.h> #include <asm/io.h> @@ -134,8 +135,7 @@ static int mmap_mem(struct inode * inode, struct file * file, struct vm_area_str #endif if (remap_page_range(vma->vm_start, offset, vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + vma->vm_dentry = dget(file->f_dentry); return 0; } @@ -532,6 +532,13 @@ __initfunc(int chr_dev_init(void)) #ifdef CONFIG_SOUND soundcard_init(); #endif +#ifdef CONFIG_JOYSTICK + /* + * Some joysticks only appear when the soundcard they are + * connected too is confgured. Keep the sound/joystick ordering. + */ + joystick_init(); +#endif #if CONFIG_QIC02_TAPE qic02_tape_init(); #endif diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 90ff2026f..6262792b6 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -217,6 +217,9 @@ __initfunc(int misc_init(void)) #ifdef CONFIG_SUN_MOUSE sun_mouse_init(); #endif +#ifdef CONFIG_PC110_PAD + pc110pad_init(); +#endif /* * Only one watchdog can succeed. We probe the pcwatchdog first, * then the wdt cards and finally the software watchdog which always diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 8db4e1443..32dc3a51d 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -88,9 +88,17 @@ void n_tty_flush_buffer(struct tty_struct * tty) /* * Return number of characters buffered to be delivered to user + * */ int n_tty_chars_in_buffer(struct tty_struct *tty) { + if (tty->icanon) { + if (!tty->canon_data) return 0; + + return (tty->canon_head > tty->read_tail) ? + tty->canon_head - tty->read_tail : + tty->canon_head + (N_TTY_BUF_SIZE - tty->read_tail); + } return tty->read_cnt; } @@ -157,6 +165,72 @@ static int opost(unsigned char c, struct tty_struct *tty) return 0; } +/* + * opost_block --- to speed up block console writes, among other + * things. + */ +static int opost_block(struct tty_struct * tty, + const unsigned char * inbuf, unsigned int nr) +{ + char buf[80]; + int space; + int i; + char *cp; + + space = tty->driver.write_room(tty); + if (!space) + return 0; + if (nr > space) + nr = space; + if (nr > sizeof(buf)) + nr = sizeof(buf); + nr -= copy_from_user(buf, inbuf, nr); + if (!nr) + return 0; + + for (i = 0, cp = buf; i < nr; i++, cp++) { + switch (*cp) { + case '\n': + if (O_ONLRET(tty)) + tty->column = 0; + if (O_ONLCR(tty)) + goto break_out; + tty->canon_column = tty->column; + break; + case '\r': + if (O_ONOCR(tty) && tty->column == 0) + goto break_out; + if (O_OCRNL(tty)) { + *cp = '\n'; + if (O_ONLRET(tty)) + tty->canon_column = tty->column = 0; + break; + } + tty->canon_column = tty->column = 0; + break; + case '\t': + goto break_out; + case '\b': + if (tty->column > 0) + tty->column--; + break; + default: + if (O_OLCUC(tty)) + *cp = toupper(*cp); + if (!iscntrl(*cp)) + tty->column++; + break; + } + } +break_out: + if (tty->driver.flush_chars) + tty->driver.flush_chars(tty); + i = tty->driver.write(tty, 0, buf, i); + return i; +} + + + static inline void put_char(unsigned char c, struct tty_struct *tty) { tty->driver.put_char(tty, c); @@ -632,7 +706,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct termios * old) return; tty->icanon = (L_ICANON(tty) != 0); - if (tty->flags & (1<<TTY_HW_COOK_IN)) { + if (test_bit(TTY_HW_COOK_IN, &tty->flags)) { tty->raw = 1; tty->real_raw = 1; return; @@ -780,7 +854,7 @@ do_it_again: /* NOTE: not yet done after every sleep pending a thorough check of the logic of this change. -- jlc */ /* don't stop on /dev/console */ - if (file->f_inode->i_rdev != CONSOLE_DEV && + if (file->f_dentry->d_inode->i_rdev != CONSOLE_DEV && current->tty == tty) { if (tty->pgrp <= 0) printk("read_chan: tty->pgrp <= 0!\n"); @@ -838,7 +912,7 @@ do_it_again: tty->minimum_to_wake = (minimum - (b - buf)); if (!input_available_p(tty, 0)) { - if (tty->flags & (1 << TTY_OTHER_CLOSED)) { + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { retval = -EIO; break; } @@ -934,12 +1008,12 @@ static int write_chan(struct tty_struct * tty, struct file * file, const unsigned char * buf, unsigned int nr) { struct wait_queue wait = { current, NULL }; - int c; + int c, num; const unsigned char *b = buf; int retval = 0; /* Job control check -- must be done at start (POSIX.1 7.1.1.4). */ - if (L_TOSTOP(tty) && file->f_inode->i_rdev != CONSOLE_DEV) { + if (L_TOSTOP(tty) && file->f_dentry->d_inode->i_rdev != CONSOLE_DEV) { retval = tty_check_change(tty); if (retval) return retval; @@ -956,8 +1030,13 @@ static int write_chan(struct tty_struct * tty, struct file * file, retval = -EIO; break; } - if (O_OPOST(tty) && !(tty->flags & (1<<TTY_HW_COOK_OUT))) { + if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) { while (nr > 0) { + num = opost_block(tty, b, nr); + b += num; + nr -= num; + if (nr == 0) + break; get_user(c, b); if (opost(c, tty) < 0) break; @@ -993,7 +1072,7 @@ static unsigned int normal_poll(struct tty_struct * tty, struct file * file, pol mask |= POLLIN | POLLRDNORM; if (tty->packet && tty->link->ctrl_status) mask |= POLLPRI | POLLIN | POLLRDNORM; - if (tty->flags & (1 << TTY_OTHER_CLOSED)) + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) mask |= POLLHUP; if (tty_hung_up_p(file)) mask |= POLLHUP; diff --git a/drivers/char/pc110pad.c b/drivers/char/pc110pad.c new file mode 100644 index 000000000..7a6b8cde0 --- /dev/null +++ b/drivers/char/pc110pad.c @@ -0,0 +1,690 @@ +/* + * Linux driver for the PC110 pad + * + * The pad provides triples of data. The first byte has + * 0x80=bit 8 X, 0x01=bit 7 X, 0x08=bit 8 Y, 0x01=still down + * The second byte is bits 0-6 X + * The third is bits 0-6 Y + * + * This is read internally and used to synthesize a stream of + * triples in the form expected from a PS/2 device. + * + * 0.0 1997-05-16 Alan Cox <alan@cymru.net> - Pad reader + * 0.1 1997-05-19 Robin O'Leary <robin@acm.org> - PS/2 emulation + * 0.2 1997-06-03 Robin O'Leary <robin@acm.org> - tap gesture + * 0.3 1997-06-27 Alan Cox <alan@cymru.net> - 2.1 commit + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/busmouse.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/mm.h> +#include <linux/miscdevice.h> +#include <linux/ptrace.h> +#include <linux/poll.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> + +#include <asm/signal.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/uaccess.h> + +#include "pc110pad.h" + + +static struct pc110pad_params default_params = { + PC110PAD_PS2, /* read mode */ + 50 MS, /* bounce interval */ + 200 MS, /* tap interval */ + 10, /* IRQ */ + 0x15E0, /* I/O port */ +}; + + +static struct pc110pad_params current_params; + + +/* driver/filesystem interface management */ +static struct wait_queue *queue; +static struct fasync_struct *asyncptr; +static int active=0; /* number of concurrent open()s */ + + +/* + * Utility to reset a timer to go off some time in the future. + */ + +static void set_timer_callback(struct timer_list *timer, int ticks) +{ + del_timer(timer); + timer->expires = jiffies+ticks; + add_timer(timer); +} + + +/* + * Take care of letting any waiting processes know that + * now would be a good time to do a read(). Called + * whenever a state transition occurs, real or synthetic. + */ + +static void wake_readers(void) +{ + wake_up_interruptible(&queue); + if(asyncptr) + kill_fasync(asyncptr, SIGIO); +} + + +/*****************************************************************************/ +/* + * Deal with the messy business of synthesizing button tap and drag + * events. + * + * Exports: + * notify_pad_up_down() + * Must be called whenever debounced pad up/down state changes. + * button_pending + * Flag is set whenever read_button() has new values + * to return. + * read_button() + * Obtains the current synthetic mouse button state. + */ + +/* + * These keep track of up/down transitions needed to generate the + * synthetic mouse button events. While recent_transition is set, + * up/down events cause transition_count to increment. tap_timer + * turns off the recent_transition flag and may cause some synthetic + * up/down mouse events to be created by incrementing synthesize_tap. + */ + +static int button_pending=0; +static int recent_transition=0; +static int transition_count=0; +static int synthesize_tap=0; +static void tap_timeout(unsigned long data); +static struct timer_list tap_timer = { NULL, NULL, 0, 0, tap_timeout }; + + +/* + * This callback goes off a short time after an up/down transition; + * before it goes off, transitions will be considered part of a + * single PS/2 event and counted in transition_count. Once the + * timeout occurs the recent_transition flag is cleared and + * any synthetic mouse up/down events are generated. + */ + +static void tap_timeout(unsigned long data) +{ + if(!recent_transition) + { + printk("pc110pad: tap_timeout but no recent transition!\n"); + } + if( transition_count==2 || transition_count==4 || transition_count==6 ) + { + synthesize_tap+=transition_count; + button_pending = 1; + wake_readers(); + } + recent_transition=0; +} + + +/* + * Called by the raw pad read routines when a (debounced) up/down + * transition is detected. + */ + +void notify_pad_up_down(void) +{ + if(recent_transition) + { + transition_count++; + } + else + { + transition_count=1; + recent_transition=1; + } + set_timer_callback(&tap_timer, current_params.tap_interval); + + /* changes to transition_count can cause reported button to change */ + button_pending = 1; + wake_readers(); +} + + +static void read_button(int *b) +{ + if(synthesize_tap) + { + *b=--synthesize_tap & 1; + } + else + { + *b=(!recent_transition && transition_count==3); /* drag */ + } + button_pending=(synthesize_tap>0); +} + + +/*****************************************************************************/ +/* + * Read pad absolute co-ordinates and debounced up/down state. + * + * Exports: + * pad_irq() + * Function to be called whenever the pad signals + * that it has new data available. + * read_raw_pad() + * Returns the most current pad state. + * xy_pending + * Flag is set whenever read_raw_pad() has new values + * to return. + * Imports: + * wake_readers() + * Called when movement occurs. + * notify_pad_up_down() + * Called when debounced up/down status changes. + */ + +/* + * These are up/down state and absolute co-ords read directly from pad + */ + +static int raw_data[3]; +static int raw_data_count=0; +static int raw_x=0, raw_y=0; /* most recent absolute co-ords read */ +static int raw_down=0; /* raw up/down state */ +static int debounced_down=0; /* up/down state after debounce processing */ +static enum { NO_BOUNCE, JUST_GONE_UP, JUST_GONE_DOWN } bounce=NO_BOUNCE; + /* set just after an up/down transition */ +static int xy_pending=0; /* set if new data have not yet been read */ + +/* + * Timer goes off a short while after an up/down transition and copies + * the value of raw_down to debounced_down. + */ + +static void bounce_timeout(unsigned long data); +static struct timer_list bounce_timer = { NULL, NULL, 0, 0, bounce_timeout }; + + +static void bounce_timeout(unsigned long data) +{ + /* + * No further up/down transitions happened within the + * bounce period, so treat this as a genuine transition. + */ + switch(bounce) + { + case NO_BOUNCE: + { + /* + * Strange; the timer callback should only go off if + * we were expecting to do bounce processing! + */ + printk("pc110pad, bounce_timeout: bounce flag not set!\n"); + break; + } + case JUST_GONE_UP: + { + /* + * The last up we spotted really was an up, so set + * debounced state the same as raw state. + */ + bounce=NO_BOUNCE; + if(debounced_down==raw_down) + { + printk("pc110pad, bounce_timeout: raw already debounced!\n"); + } + debounced_down=raw_down; + + notify_pad_up_down(); + break; + } + case JUST_GONE_DOWN: + { + /* + * We don't debounce down events, but we still time + * out soon after one occurs so we can avoid the (x,y) + * skittering that sometimes happens. + */ + bounce=NO_BOUNCE; + break; + } + } +} + + +/* + * Callback when pad's irq goes off; copies values in to raw_* globals; + * initiates debounce processing. + */ +static void pad_irq(int irq, void *ptr, struct pt_regs *regs) +{ + + /* Obtain byte from pad and prime for next byte */ + { + int value=inb_p(current_params.io); + int handshake=inb_p(current_params.io+2); + outb_p(handshake | 1, current_params.io+2); + outb_p(handshake &~1, current_params.io+2); + inb_p(0x64); + + raw_data[raw_data_count++]=value; + } + + if(raw_data_count==3) + { + int new_down=raw_data[0]&0x01; + int new_x=raw_data[1]; + int new_y=raw_data[2]; + if(raw_data[0]&0x10) new_x+=128; + if(raw_data[0]&0x80) new_x+=256; + if(raw_data[0]&0x08) new_y+=128; + + if( (raw_x!=new_x) || (raw_y!=new_y) ) + { + raw_x=new_x; + raw_y=new_y; + xy_pending=1; + } + + if(new_down != raw_down) + { + /* Down state has changed. raw_down always holds + * the most recently observed state. + */ + raw_down=new_down; + + /* Forget any earlier bounce processing */ + if(bounce) + { + del_timer(&bounce_timer); + bounce=NO_BOUNCE; + } + + if(new_down) + { + if(debounced_down) + { + /* pad gone down, but we were reporting + * it down anyway because we suspected + * (correctly) that the last up was just + * a bounce + */ + } + else + { + bounce=JUST_GONE_DOWN; + set_timer_callback(&bounce_timer, + current_params.bounce_interval); + /* start new stroke/tap */ + debounced_down=new_down; + notify_pad_up_down(); + } + } + else /* just gone up */ + { + if(recent_transition) + { + /* early bounces are probably part of + * a multi-tap gesture, so process + * immediately + */ + debounced_down=new_down; + notify_pad_up_down(); + } + else + { + /* don't trust it yet */ + bounce=JUST_GONE_UP; + set_timer_callback(&bounce_timer, + current_params.bounce_interval); + } + } + } + wake_readers(); + raw_data_count=0; + } +} + + +static void read_raw_pad(int *down, int *debounced, int *x, int *y) +{ + disable_irq(current_params.irq); + { + *down=raw_down; + *debounced=debounced_down; + *x=raw_x; + *y=raw_y; + xy_pending = 0; + } + enable_irq(current_params.irq); +} + +/*****************************************************************************/ +/* + * Filesystem interface + */ + +/* + * Read returns byte triples, so we need to keep track of + * how much of a triple has been read. This is shared across + * all processes which have this device open---not that anything + * will make much sense in that case. + */ +static int read_bytes[3]; +static int read_byte_count=0; + + + +static void sample_raw(int d[3]) +{ + d[0]=raw_data[0]; + d[1]=raw_data[1]; + d[2]=raw_data[2]; +} + + +static void sample_rare(int d[3]) +{ + int thisd, thisdd, thisx, thisy; + + read_raw_pad(&thisd, &thisdd, &thisx, &thisy); + + d[0]=(thisd?0x80:0) + | (thisx/256)<<4 + | (thisdd?0x08:0) + | (thisy/256) + ; + d[1]=thisx%256; + d[2]=thisy%256; +} + + +static void sample_debug(int d[3]) +{ + int thisd, thisdd, thisx, thisy; + int b; + cli(); + read_raw_pad(&thisd, &thisdd, &thisx, &thisy); + d[0]=(thisd?0x80:0) | (thisdd?0x40:0) | bounce; + d[1]=(recent_transition?0x80:0)+transition_count; + read_button(&b); + d[2]=(synthesize_tap<<4) | (b?0x01:0); + sti(); +} + + +static void sample_ps2(int d[3]) +{ + static int lastx, lasty, lastd; + + int thisd, thisdd, thisx, thisy; + int dx, dy, b; + + /* + * Obtain the current mouse parameters and limit as appropriate for + * the return data format. Interrupts are only disabled while + * obtaining the parameters, NOT during the puts_fs_byte() calls, + * so paging in put_user() does not affect mouse tracking. + */ + read_raw_pad(&thisd, &thisdd, &thisx, &thisy); + read_button(&b); + + /* Now compare with previous readings. Note that we use the + * raw down flag rather than the debounced one. + */ + if( (thisd && !lastd) /* new stroke */ + || (bounce!=NO_BOUNCE) ) + { + dx=0; + dy=0; + } + else + { + dx = (thisx-lastx); + dy = -(thisy-lasty); + } + lastx=thisx; + lasty=thisy; + lastd=thisd; + +/* + d[0]= ((dy<0)?0x20:0) + | ((dx<0)?0x10:0) + | 0x08 + | (b? 0x01:0x00) + ; +*/ + d[0]= ((dy<0)?0x20:0) + | ((dx<0)?0x10:0) + | (b? 0x00:0x08) + ; + d[1]=dx; + d[2]=dy; +} + + + +static int fasync_pad(struct inode *inode, struct file *filp, int on) +{ + int retval; + + retval = fasync_helper(inode, filp, on, &asyncptr); + if (retval < 0) + return retval; + return 0; +} + + +/* + * close access to the pad + */ +static int close_pad(struct inode * inode, struct file * file) +{ + fasync_pad(inode, file, 0); + if (--active) + return; + outb(0x30, current_params.io+2); /* switch off digitiser */ + MOD_DEC_USE_COUNT; + return 0; +} + + +/* + * open access to the pad + */ +static int open_pad(struct inode * inode, struct file * file) +{ + if (active++) + return 0; + MOD_INC_USE_COUNT; + + cli(); + outb(0x30, current_params.io+2); /* switch off digitiser */ + pad_irq(0,0,0); /* read to flush any pending bytes */ + pad_irq(0,0,0); /* read to flush any pending bytes */ + pad_irq(0,0,0); /* read to flush any pending bytes */ + outb(0x38, current_params.io+2); /* switch on digitiser */ + current_params = default_params; + raw_data_count=0; /* re-sync input byte counter */ + read_byte_count=0; /* re-sync output byte counter */ + button_pending=0; + recent_transition=0; + transition_count=0; + synthesize_tap=0; + del_timer(&bounce_timer); + del_timer(&tap_timer); + sti(); + + return 0; +} + + +/* + * writes are disallowed + */ +static long write_pad(struct inode * inode, struct file * file, const char * buffer, unsigned long count) +{ + return -EINVAL; +} + + +void new_sample(int d[3]) +{ + switch(current_params.mode) + { + case PC110PAD_RAW: sample_raw(d); break; + case PC110PAD_RARE: sample_rare(d); break; + case PC110PAD_DEBUG: sample_debug(d); break; + case PC110PAD_PS2: sample_ps2(d); break; + } +} + + +/* + * Read pad data. Currently never blocks. + */ +static long read_pad(struct inode * inode, struct file * file, char * buffer, unsigned long count) +{ + int r; + + for(r=0; r<count; r++) + { + if(!read_byte_count) + new_sample(read_bytes); + if(put_user(read_bytes[read_byte_count], buffer+r)) + return -EFAULT; + read_byte_count = (read_byte_count+1)%3; + } + return r; +} + + +/* + * select for pad input + */ + +static unsigned int pad_poll(struct file *file, poll_table * wait) +{ + poll_wait(&queue, wait); + if(button_pending || xy_pending) + return POLLIN | POLLRDNORM; + return 0; +} + + +static int pad_ioctl(struct inode *inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct pc110pad_params new; + + if (!inode) + return -EINVAL; + + switch (cmd) { + case PC110PADIOCGETP: + new = current_params; + if(copy_to_user((void *)arg, &new, sizeof(new))) + return -EFAULT; + return 0; + + case PC110PADIOCSETP: + if(copy_from_user(&new, (void *)arg, sizeof(new))) + return -EFAULT; + + if( (new.mode<PC110PAD_RAW) + || (new.mode>PC110PAD_PS2) + || (new.bounce_interval<0) + || (new.tap_interval<0) + ) + return -EINVAL; + + current_params.mode = new.mode; + current_params.bounce_interval = new.bounce_interval; + current_params.tap_interval = new.tap_interval; + return 0; + } + return -ENOIOCTLCMD; +} + + +static struct file_operations pad_fops = { + NULL, /* pad_seek */ + read_pad, + write_pad, + NULL, /* pad_readdir */ + pad_poll, + pad_ioctl, + NULL, /* pad_mmap */ + open_pad, + close_pad, + NULL, /* fsync */ + fasync_pad, + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL /* lock */ +}; + + +static struct miscdevice pc110_pad = { + PC110PAD_MINOR, "pc110 pad", &pad_fops +}; + + +static int pc110pad_init(void) +{ + current_params = default_params; + + if(request_irq(current_params.irq, pad_irq, 0, "pc110pad", 0)) + { + printk("pc110pad: Unable to get IRQ.\n"); + return -EBUSY; + } + if(check_region(current_params.io, 4)) + { + printk("pc110pad: I/O area in use.\n"); + free_irq(current_params.irq,0); + return -EBUSY; + } + request_region(current_params.io, 4, "pc110pad"); + printk("PC110 digitizer pad at 0x%X, irq %d.\n", + current_params.io,current_params.irq); + misc_register(&pc110_pad); + outb(0x30, current_params.io+2); /* switch off digitiser */ + + return 0; +} + +#ifdef MODULE + +static void pc110pad_unload(void) +{ + outb(0x30, current_params.io+2); /* switch off digitiser */ + if(current_params.irq) + free_irq(current_params.irq, 0); + current_params.irq=0; + release_region(current_params.io, 4); + misc_deregister(&pc110_pad); +} + + + +int init_module(void) +{ + return pc110pad_init(); +} + +void cleanup_module(void) +{ + pc110pad_unload(); +} +#endif diff --git a/drivers/char/pc110pad.h b/drivers/char/pc110pad.h new file mode 100644 index 000000000..56d8d82e0 --- /dev/null +++ b/drivers/char/pc110pad.h @@ -0,0 +1,31 @@ +#ifndef _PC110PAD_H +#define _PC110PAD_H + +#include <linux/ioctl.h> + +enum pc110pad_mode { + PC110PAD_RAW, /* bytes as they come out of the hardware */ + PC110PAD_RARE, /* debounced up/down and absolute x,y */ + PC110PAD_DEBUG, /* up/down, debounced, transitions, button */ + PC110PAD_PS2, /* ps2 relative (default) */ +}; + + +struct pc110pad_params { + enum pc110pad_mode mode; + int bounce_interval; + int tap_interval; + int irq; + int io; +}; + +#define MS *HZ/1000 + +/* Appears as device major=10 (MISC), minor=PC110_PAD */ + +#define PC110PAD_IOCTL_TYPE 0x9a + +#define PC110PADIOCGETP _IOR(PC110PAD_IOCTL_TYPE, 0, struct pc110pad_params) +#define PC110PADIOCSETP _IOR(PC110PAD_IOCTL_TYPE, 1, struct pc110pad_params) + +#endif /* _PC110PAD_H */ diff --git a/drivers/char/psaux.c b/drivers/char/psaux.c index 006c60ddf..eea857b8a 100644 --- a/drivers/char/psaux.c +++ b/drivers/char/psaux.c @@ -53,6 +53,8 @@ #include <asm/uaccess.h> #include <asm/system.h> +#include "pc_keyb.h" + #ifdef CONFIG_SGI #include <asm/segment.h> #include <asm/sgihpc.h> @@ -80,17 +82,6 @@ #define AUX_DISABLE 0xa7 /* disable aux */ #define AUX_ENABLE 0xa8 /* enable aux */ -/* aux device commands */ -#define AUX_SET_RES 0xe8 /* set resolution */ -#define AUX_SET_SCALE11 0xe6 /* set 1:1 scaling */ -#define AUX_SET_SCALE21 0xe7 /* set 2:1 scaling */ -#define AUX_GET_SCALE 0xe9 /* get scaling factor */ -#define AUX_SET_STREAM 0xea /* set stream mode */ -#define AUX_SET_SAMPLE 0xf3 /* set sample rate */ -#define AUX_ENABLE_DEV 0xf4 /* enable aux device */ -#define AUX_DISABLE_DEV 0xf5 /* disable aux device */ -#define AUX_RESET 0xff /* reset aux device */ - #define MAX_RETRIES 60 /* some aux operations take long time*/ #if defined(__alpha__) && !defined(CONFIG_PCI) # define AUX_IRQ 9 /* Jensen is odd indeed */ @@ -212,7 +203,16 @@ static void aux_write_dev(int val) /* * Write to device & handle returned ack */ -#if defined INITIALIZE_DEVICE + +#ifdef INITIALIZE_DEVICE +__initfunc(static void aux_write_dev_nosleep(int val)) +{ + poll_aux_status_nosleep(); + ps2_outb_p(KBD_CCMD_WRITE_MOUSE, KBD_CNTL_REG); + poll_aux_status_nosleep(); + ps2_outb_p(val, KBD_DATA_REG); +} + static int aux_write_ack(int val) { int retries = 0; @@ -663,11 +663,11 @@ __initfunc(int psaux_init(void)) aux_write_ack(AUX_SET_SCALE21); /* 2:1 scaling */ poll_aux_status_nosleep(); #endif /* INITIALIZE_DEVICE */ - ps2_outb_p(AUX_DISABLE,AUX_COMMAND); /* Disable Aux device */ + ps2_outb_p(KBD_CCMD_MOUSE_DISABLE, AUX_COMMAND); /* Disable Aux device */ + poll_aux_status_nosleep(); + ps2_outb_p(KBD_CCMD_WRITE_MODE, AUX_COMMAND); poll_aux_status_nosleep(); - ps2_outb_p(AUX_CMD_WRITE,AUX_COMMAND); - poll_aux_status_nosleep(); /* Disable interrupts */ - ps2_outb_p(AUX_INTS_OFF, AUX_OUTPUT_PORT); /* on the controller */ + ps2_outb_p(AUX_INTS_OFF, AUX_OUTPUT_PORT); } return 0; } diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 8f1015397..929bb2f85 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -26,20 +26,6 @@ struct pty_struct { #define PTY_MAGIC 0x5001 -#define PTY_BUF_SIZE PAGE_SIZE/2 - -/* - * tmp_buf is used as a temporary buffer by pty_write. We need to - * lock it in case the copy_from_user blocks while swapping in a page, - * and some other program tries to do a pty write at the same time. - * Since the lock will only come under contention when the system is - * swapping and available memory is low, it makes sense to share one - * buffer across all the PTY's, since it significantly saves memory if - * large numbers of PTY's are open. - */ -static unsigned char *tmp_buf; -static struct semaphore tmp_buf_sem = MUTEX; - static struct tty_driver pty_driver, pty_slave_driver; static struct tty_driver old_pty_driver, old_pty_slave_driver; static int pty_refcount; @@ -104,37 +90,51 @@ static void pty_unthrottle(struct tty_struct * tty) set_bit(TTY_THROTTLED, &tty->flags); } +/* + * WSH 05/24/97: modified to + * (1) use space in tty->flip instead of a shared temp buffer + * The flip buffers aren't being used for a pty, so there's lots + * of space available. The buffer is protected by a per-pty + * semaphore that should almost never come under contention. + * (2) avoid redundant copying for cases where count >> receive_room + * N.B. Calls from user space may now return an error code instead of + * a count. + */ static int pty_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count) { struct tty_struct *to = tty->link; - int c=0, n, r; + int c=0, n; char *temp_buffer; if (!to || tty->stopped) return 0; - + if (from_user) { - down(&tmp_buf_sem); - temp_buffer = tmp_buf + - ((tty->driver.subtype-1) * PTY_BUF_SIZE); + down(&tty->flip.pty_sem); + temp_buffer = &tty->flip.char_buf[0]; while (count > 0) { - n = MIN(count, PTY_BUF_SIZE); + /* check space so we don't copy needlessly */ + n = MIN(count, to->ldisc.receive_room(to)); + if (!n) break; + + n = MIN(n, PTY_BUF_SIZE); n -= copy_from_user(temp_buffer, buf, n); if (!n) { if (!c) c = -EFAULT; break; } - r = to->ldisc.receive_room(to); - if (r <= 0) - break; - n = MIN(n, r); - to->ldisc.receive_buf(to, temp_buffer, 0, n); - buf += n; c+= n; + + /* check again in case the buffer filled up */ + n = MIN(n, to->ldisc.receive_room(to)); + if (!n) break; + buf += n; + c += n; count -= n; + to->ldisc.receive_buf(to, temp_buffer, 0, n); } - up(&tmp_buf_sem); + up(&tty->flip.pty_sem); } else { c = MIN(count, to->ldisc.receive_room(to)); to->ldisc.receive_buf(to, buf, 0, c); @@ -153,14 +153,42 @@ static int pty_write_room(struct tty_struct *tty) return to->ldisc.receive_room(to); } +/* + * WSH 05/24/97: Modified for asymmetric MASTER/SLAVE behavior + * The chars_in_buffer() value is used by the ldisc select() function + * to hold off writing when chars_in_buffer > WAKEUP_CHARS (== 256). + * The pty driver chars_in_buffer() Master/Slave must behave differently: + * + * The Master side needs to allow typed-ahead commands to accumulate + * while being canonicalized, so we report "our buffer" as empty until + * some threshold is reached, and then report the count. (Any count > + * WAKEUP_CHARS is regarded by select() as "full".) To avoid deadlock + * the count returned must be 0 if no canonical data is available to be + * read. (The N_TTY ldisc.chars_in_buffer now knows this.) + * + * The Slave side passes all characters in raw mode to the Master side's + * buffer where they can be read immediately, so in this case we can + * return the true count in the buffer. + */ static int pty_chars_in_buffer(struct tty_struct *tty) { struct tty_struct *to = tty->link; + int count; if (!to || !to->ldisc.chars_in_buffer) return 0; - return to->ldisc.chars_in_buffer(to); + /* The ldisc must report 0 if no characters available to be read */ + count = to->ldisc.chars_in_buffer(to); + + if (tty->driver.subtype == PTY_TYPE_SLAVE) return count; + + /* Master side driver ... if the other side's read buffer is less than + * half full, return 0 to allow writers to proceed; otherwise return + * the count. This leaves a comfortable margin to avoid overflow, + * and still allows half a buffer's worth of typed-ahead commands. + */ + return ((count < N_TTY_BUF_SIZE/2) ? 0 : count); } static void pty_flush_buffer(struct tty_struct *tty) @@ -194,17 +222,6 @@ static int pty_open(struct tty_struct *tty, struct file * filp) pty = pty_state + line; tty->driver_data = pty; - if (!tmp_buf) { - unsigned long page = __get_free_page(GFP_KERNEL); - if (!tmp_buf) { - retval = -ENOMEM; - if (!page) - goto out; - tmp_buf = (unsigned char *) page; - memset((void *) page, 0, PAGE_SIZE); - } else - free_page(page); - } retval = -EIO; if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) goto out; @@ -288,8 +305,6 @@ __initfunc(int pty_init(void)) old_pty_slave_driver.num = (NR_PTYS > 64) ? 64 : NR_PTYS; old_pty_slave_driver.other = &old_pty_driver; - tmp_buf = 0; - if (tty_register_driver(&pty_driver)) panic("Couldn't register pty driver"); if (tty_register_driver(&pty_slave_driver)) diff --git a/drivers/char/random.c b/drivers/char/random.c index 5f7619391..527ac8609 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1,7 +1,7 @@ /* * random.c -- A strong random number generator * - * Version 1.02, last modified 15-Apr-97 + * Version 1.03, last modified 26-Apr-97 * * Copyright Theodore Ts'o, 1994, 1995, 1996, 1997. All rights reserved. * @@ -227,6 +227,7 @@ */ #include <linux/utsname.h> +#include <linux/config.h> #include <linux/kernel.h> #include <linux/major.h> #include <linux/string.h> @@ -1124,8 +1125,7 @@ random_read(struct inode * inode, struct file * file, char * buf, unsigned long * update the access time. */ if (inode && count != 0) { - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; + UPDATE_ATIME(inode); } return (count ? count : retval); @@ -1181,7 +1181,7 @@ random_write(struct inode * inode, struct file * file, } if ((ret > 0) && inode) { inode->i_mtime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); } return ret; } @@ -1335,11 +1335,15 @@ struct file_operations urandom_fops = { * starting point for each pair of TCP endpoints. This defeats * attacks which rely on guessing the initial TCP sequence number. * This algorithm was suggested by Steve Bellovin. + * + * Using a very strong hash was taking an appreciable amount of the total + * TCP connection establishment time, so this is a weaker hash, + * compensated for by changing the secret periodically. */ /* F, G and H are basic MD4 functions: selection, majority, parity */ -#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) -#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) (((x) & (y)) + (((x) ^ (y)) & (z))) #define H(x, y, z) ((x) ^ (y) ^ (z)) #define ROTL(n,X) ( ( ( X ) << n ) | ( ( X ) >> ( 32 - n ) ) ) @@ -1357,9 +1361,9 @@ struct file_operations urandom_fops = { (a) = ROTL ((s), (a));} /* - * Basic cut-down MD4 transform + * Basic cut-down MD4 transform. Returns only 32 bits of result. */ -static void halfMD4Transform (__u32 buf[4], __u32 in[8]) +static __u32 halfMD4Transform (__u32 const buf[4], __u32 const in[8]) { __u32 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; @@ -1376,77 +1380,141 @@ static void halfMD4Transform (__u32 buf[4], __u32 in[8]) /* Round 2 */ GG (a, b, c, d, in[ 0], 3); GG (d, a, b, c, in[ 4], 5); - GG (a, b, c, d, in[ 1], 9); - GG (d, a, b, c, in[ 5], 13); + GG (c, d, a, b, in[ 1], 9); + GG (b, c, d, a, in[ 5], 13); GG (a, b, c, d, in[ 2], 3); GG (d, a, b, c, in[ 6], 5); - GG (a, b, c, d, in[ 3], 9); - GG (d, a, b, c, in[ 7], 13); + GG (c, d, a, b, in[ 3], 9); + GG (b, c, d, a, in[ 7], 13); /* Round 3 */ HH (a, b, c, d, in[ 0], 3); - HH (c, d, a, b, in[ 4], 9); - HH (a, b, c, d, in[ 2], 11); - HH (c, d, a, b, in[ 6], 15); + HH (d, a, b, c, in[ 4], 9); + HH (c, d, a, b, in[ 2], 11); + HH (b, c, d, a, in[ 6], 15); HH (a, b, c, d, in[ 1], 3); - HH (c, d, a, b, in[ 5], 9); - HH (a, b, c, d, in[ 3], 11); - HH (c, d, a, b, in[ 7], 15); + HH (d, a, b, c, in[ 5], 9); + HH (c, d, a, b, in[ 3], 11); + HH (b, c, d, a, in[ 7], 15); - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; + return buf[1] + b; /* "most hashed" word */ + /* Alternative: return sum of all words? */ } +/* This should not be decreased so low that ISNs wrap too fast. */ #define REKEY_INTERVAL 300 +#define HASH_BITS 24 __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr, __u16 sport, __u16 dport) { static __u32 rekey_time = 0; + static __u32 count = 0; static __u32 secret[12]; - static char count = 0; struct timeval tv; - __u32 tmp[12]; __u32 seq; /* - * Pick a random secret every REKEY_INTERVAL seconds + * Pick a random secret every REKEY_INTERVAL seconds. */ - do_gettimeofday(&tv); + do_gettimeofday(&tv); /* We need the usecs below... */ + if (!rekey_time || (tv.tv_sec - rekey_time) > REKEY_INTERVAL) { - get_random_bytes(&secret, sizeof(secret)); rekey_time = tv.tv_sec; - count++; + /* First three words are overwritten below. */ + get_random_bytes(&secret+3, sizeof(secret)-12); + count = (tv.tv_sec/REKEY_INTERVAL) << HASH_BITS; } - memcpy(tmp, secret, sizeof(tmp)); /* - * Pick a unique starting offset for each - * TCP connection endpoints (saddr, daddr, sport, dport) + * Pick a unique starting offset for each TCP connection endpoints + * (saddr, daddr, sport, dport). + * Note that the words are placed into the first words to be + * mixed in with the halfMD4. This is because the starting + * vector is also a random secret (at secret+8), and further + * hashing fixed data into it isn't going to improve anything, + * so we should get started with the variable data. */ - tmp[8]=saddr; - tmp[9]=daddr; - tmp[10]=(sport << 16) + dport; - halfMD4Transform(tmp, tmp+4); - + secret[0]=saddr; + secret[1]=daddr; + secret[2]=(sport << 16) + dport; + + seq = (halfMD4Transform(secret+8, secret) & + ((1<<HASH_BITS)-1)) + (count << HASH_BITS); + /* * As close as possible to RFC 793, which * suggests using a 250kHz clock. - * Further reading shows this assumes 2MB/s networks. - * For 10MB/s ethernet, a 1MHz clock is appropriate. + * Further reading shows this assumes 2Mb/s networks. + * For 10Mb/s ethernet, a 1MHz clock is appropriate. * That's funny, Linux has one built in! Use it! + * (Networks are faster now - should this be increased?) */ - seq = (tmp[1]&0xFFFFFF) + (tv.tv_usec+tv.tv_sec*1000000) + - (count << 24); + seq += tv.tv_usec + tv.tv_sec*1000000; #if 0 printk("init_seq(%lx, %lx, %d, %d) = %d\n", saddr, daddr, sport, dport, seq); #endif - return (seq); + return seq; +} + +#ifdef CONFIG_SYN_COOKIES +/* + * Secure SYN cookie computation. This is the algorithm worked out by + * Dan Bernstein and Eric Schenk. + * + * For linux I implement the 1 minute counter by looking at the jiffies clock. + * The count is passed in as a parameter; + * + */ +__u32 secure_tcp_syn_cookie(__u32 saddr, __u32 daddr, + __u16 sport, __u16 dport, __u32 sseq, __u32 count) +{ + static int is_init = 0; + static __u32 secret[2][16]; + __u32 tmp[16]; + __u32 seq; + + /* + * Pick two random secret the first time we open a TCP connection. + */ + if (is_init == 0) { + get_random_bytes(&secret[0], sizeof(secret[0])); + get_random_bytes(&secret[1], sizeof(secret[1])); + is_init = 1; + } + + /* + * Compute the secure sequence number. + * The output should be: + * MD5(sec1,saddr,sport,daddr,dport,sec1) + their sequence number + * + (count * 2^24) + * + (MD5(sec2,saddr,sport,daddr,dport,count,sec2) % 2^24). + * Where count increases every minute by 1. + */ + + memcpy(tmp, secret[0], sizeof(tmp)); + tmp[8]=saddr; + tmp[9]=daddr; + tmp[10]=(sport << 16) + dport; + HASH_TRANSFORM(tmp, tmp); + seq = tmp[1]; + + memcpy(tmp, secret[1], sizeof(tmp)); + tmp[8]=saddr; + tmp[9]=daddr; + tmp[10]=(sport << 16) + dport; + tmp[11]=count; /* minute counter */ + HASH_TRANSFORM(tmp, tmp); + + seq += sseq + (count << 24) + (tmp[1] & 0x00ffffff); + + /* Zap lower 3 bits to leave room for the MSS representation */ + return (seq & 0xfffff8); } +#endif + #ifdef RANDOM_BENCHMARK /* diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index a8614999d..5f03f8887 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -152,7 +152,7 @@ static long rtc_read(struct inode *inode, struct file *file, char *buf, unsigned long count) { struct wait_queue wait = { current, NULL }; - int retval; + int retval = 0; if (count < sizeof(unsigned long)) return -EINVAL; @@ -180,7 +180,9 @@ static long rtc_read(struct inode *inode, struct file *file, char *buf, data = rtc_irq_data; rtc_irq_data = 0; restore_flags(flags); - retval = put_user(data, (unsigned long *)buf)) ?: sizeof(unsigned long); + retval = put_user(data, (unsigned long *)buf); + if (!retval) + retval = sizeof(unsigned long); } current->state = TASK_RUNNING; @@ -262,7 +264,6 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, * "don't care" or "match all". Only the tm_hour, * tm_min and tm_sec are used. */ - int retval; unsigned char hrs, min, sec; struct rtc_time alm_tm; @@ -305,7 +306,6 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, } case RTC_SET_TIME: /* Set the RTC */ { - int retval; struct rtc_time rtc_tm; unsigned char mon, day, hrs, min, sec, leap_yr; unsigned char save_control, save_freq_select; @@ -418,7 +418,7 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, default: return -EINVAL; } - return copy_to_user(arg, &wtime, sizeof wtime) ? -EFAULT : 0; + return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0; } /* diff --git a/drivers/char/serial.c b/drivers/char/serial.c index e2a93a0c3..ca561c4e9 100644 --- a/drivers/char/serial.c +++ b/drivers/char/serial.c @@ -1421,6 +1421,9 @@ static void shutdown(struct async_struct * info) info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2; #endif + /* disable break condition */ + serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); serial_outp(info, UART_MCR, info->MCR); @@ -2089,6 +2092,30 @@ static void send_break( struct async_struct * info, int duration) } /* + * This routine sets the break condition on the serial port. + */ +static void begin_break(struct async_struct * info) +{ + if (!info->port) + return; + cli(); + serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC); + sti(); +} + +/* + * This routine clears the break condition on the serial port. + */ +static void end_break(struct async_struct * info) +{ + if (!info->port) + return; + cli(); + serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); + sti(); +} + +/* * This routine returns a bitfield of "wild interrupts". Basically, * any unclaimed interrupts which is flapping around. */ @@ -2292,6 +2319,19 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, if (current->signal & ~current->blocked) return -EINTR; return 0; + case TIOCSBRK: + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + begin_break(info); + return 0; + case TIOCCBRK: + retval = tty_check_change(tty); + if (retval) + return retval; + end_break(info); + return 0; case TIOCGSOFTCAR: return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg); case TIOCSSOFTCAR: diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index e2044c086..1e0e1a2a0 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -1,6 +1,6 @@ /* -*- linux-c -*- * - * $Id: sysrq.c,v 1.2 1997/05/31 18:33:11 mj Exp $ + * $Id: sysrq.c,v 1.1 1997/06/17 13:24:07 ralf Exp $ * * Linux Magic System Request Key Hacks * @@ -112,7 +112,7 @@ void handle_sysrq(int key, struct pt_regs *pt_regs, show_mem(); break; case 2 ... 11: /* 0-9 -- set console logging level */ - key -= 2; + key--; if (key == 10) key = 0; orig_log_level = key; diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index c08e44a27..7a31e162d 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -45,9 +45,12 @@ * Restrict vt switching via ioctl() * -- grif@cs.ucr.edu, 5-Dec-95 * - * Move console and virtual terminal code to more apropriate files, + * Move console and virtual terminal code to more appropriate files, * implement CONFIG_VT and generalize console device interface. * -- Marko Kohtala <Marko.Kohtala@hut.fi>, March 97 + * + * Rewrote init_dev and release_dev to eliminate races. + * -- Bill Hawes <whawes@star.net>, June 97 */ #include <linux/config.h> @@ -90,8 +93,8 @@ #undef TTY_DEBUG_HANGUP -#define TTY_PARANOIA_CHECK -#define CHECK_TTY_COUNT +#define TTY_PARANOIA_CHECK 1 +#define CHECK_TTY_COUNT 1 struct termios tty_std_termios; /* for the benefit of tty drivers */ struct tty_driver *tty_drivers = NULL; /* linked list of tty drivers */ @@ -370,13 +373,15 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops) for (filp = inuse_filps; filp; filp = filp->f_next) { if (filp->private_data != tty) continue; - if (!filp->f_inode) + if (!filp->f_dentry) + continue; + if (!filp->f_dentry->d_inode) continue; - if (filp->f_inode->i_rdev == CONSOLE_DEV) + if (filp->f_dentry->d_inode->i_rdev == CONSOLE_DEV) continue; if (filp->f_op != &tty_fops) continue; - tty_fasync(filp->f_inode, filp, 0); + tty_fasync(filp->f_dentry->d_inode, filp, 0); filp->f_op = fops; } @@ -384,7 +389,7 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops) tty->ldisc.flush_buffer(tty); if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait); @@ -533,7 +538,7 @@ void start_tty(struct tty_struct *tty) } if (tty->driver.start) (tty->driver.start)(tty); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait); @@ -548,7 +553,7 @@ static long tty_read(struct inode * inode, struct file * file, tty = (struct tty_struct *)file->private_data; if (tty_paranoia_check(tty, inode->i_rdev, "tty_read")) return -EIO; - if (!tty || (tty->flags & (1 << TTY_IO_ERROR))) + if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags))) return -EIO; /* This check not only needs to be done before reading, but also @@ -630,7 +635,7 @@ static long tty_write(struct inode * inode, struct file * file, tty = (struct tty_struct *)file->private_data; if (tty_paranoia_check(tty, inode->i_rdev, "tty_write")) return -EIO; - if (!tty || !tty->driver.write || (tty->flags & (1 << TTY_IO_ERROR))) + if (!tty || !tty->driver.write || (test_bit(TTY_IO_ERROR, &tty->flags))) return -EIO; #if 0 if (!is_console && L_TOSTOP(tty) && (tty->pgrp > 0) && @@ -651,18 +656,31 @@ static long tty_write(struct inode * inode, struct file * file, (unsigned int)count); } +/* Semaphore to protect creating and releasing a tty */ +static struct semaphore tty_sem = MUTEX; +static void down_tty_sem(int index) +{ + down(&tty_sem); +} +static void up_tty_sem(int index) +{ + up(&tty_sem); +} +static void release_mem(struct tty_struct *tty, int idx); + /* - * This is so ripe with races that you should *really* not touch this - * unless you know exactly what you are doing. All the changes have to be - * made atomically, or there may be incorrect pointers all over the place. + * WSH 06/09/97: Rewritten to remove races and properly clean up after a + * failed open. The new code protects the open with a semaphore, so it's + * really quite straightforward. The semaphore locking can probably be + * relaxed for the (most common) case of reopening a tty. */ static int init_dev(kdev_t device, struct tty_struct **ret_tty) { - struct tty_struct *tty, **tty_loc, *o_tty, **o_tty_loc; + struct tty_struct *tty, *o_tty; struct termios *tp, **tp_loc, *o_tp, **o_tp_loc; struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc; struct tty_driver *driver; - int retval; + int retval=0; int idx; driver = get_tty_driver(device); @@ -670,189 +688,251 @@ static int init_dev(kdev_t device, struct tty_struct **ret_tty) return -ENODEV; idx = MINOR(device) - driver->minor_start; - tty = o_tty = NULL; + tty = driver->table[idx]; + + /* + * Check whether we need to acquire the tty semaphore to avoid + * race conditions. For now, play it safe. + */ + down_tty_sem(idx); + + /* check whether we're reopening an existing tty */ + if(tty) goto fast_track; + + /* + * First time open is complex, especially for PTY devices. + * This code guarantees that either everything succeeds and the + * TTY is ready for operation, or else the table slots are vacated + * and the allocated memory released. (Except that the termios + * and locked termios may be retained.) + */ + + o_tty = NULL; tp = o_tp = NULL; ltp = o_ltp = NULL; - o_tty_loc = NULL; - o_tp_loc = o_ltp_loc = NULL; - tty_loc = &driver->table[idx]; - tp_loc = &driver->termios[idx]; - ltp_loc = &driver->termios_locked[idx]; + tty = (struct tty_struct*) get_free_page(GFP_KERNEL); + if(!tty) + goto fail_no_mem; + initialize_tty_struct(tty); + tty->device = device; + tty->driver = *driver; -repeat: - retval = -EIO; - if (driver->type == TTY_DRIVER_TYPE_PTY && - driver->subtype == PTY_TYPE_MASTER && - *tty_loc && (*tty_loc)->count) - goto end_init; - retval = -ENOMEM; - if (!*tty_loc && !tty) { - if (!(tty = (struct tty_struct*) get_free_page(GFP_KERNEL))) - goto end_init; - initialize_tty_struct(tty); - tty->device = device; - tty->driver = *driver; - goto repeat; - } - if (!*tp_loc && !tp) { + tp_loc = &driver->termios[idx]; + if (!*tp_loc) { tp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!tp) - goto end_init; + goto free_mem_out; *tp = driver->init_termios; - goto repeat; } - if (!*ltp_loc && !ltp) { + + ltp_loc = &driver->termios_locked[idx]; + if (!*ltp_loc) { ltp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!ltp) - goto end_init; + goto free_mem_out; memset(ltp, 0, sizeof(struct termios)); - goto repeat; } - if (driver->type == TTY_DRIVER_TYPE_PTY) { - o_tty_loc = &driver->other->table[idx]; - o_tp_loc = &driver->other->termios[idx]; - o_ltp_loc = &driver->other->termios_locked[idx]; - if (!*o_tty_loc && !o_tty) { - kdev_t o_device; - - o_tty = (struct tty_struct *) - get_free_page(GFP_KERNEL); - if (!o_tty) - goto end_init; - o_device = MKDEV(driver->other->major, - driver->other->minor_start + idx); - initialize_tty_struct(o_tty); - o_tty->device = o_device; - o_tty->driver = *driver->other; - goto repeat; - } - if (!*o_tp_loc && !o_tp) { + if (driver->type == TTY_DRIVER_TYPE_PTY) { + o_tty = (struct tty_struct *) get_free_page(GFP_KERNEL); + if (!o_tty) + goto free_mem_out; + initialize_tty_struct(o_tty); + o_tty->device = (kdev_t) MKDEV(driver->other->major, + driver->other->minor_start + idx); + o_tty->driver = *driver->other; + + o_tp_loc = &driver->other->termios[idx]; + if (!*o_tp_loc) { o_tp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!o_tp) - goto end_init; + goto free_mem_out; *o_tp = driver->other->init_termios; - goto repeat; } - if (!*o_ltp_loc && !o_ltp) { + + o_ltp_loc = &driver->other->termios_locked[idx]; + if (!*o_ltp_loc) { o_ltp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!o_ltp) - goto end_init; + goto free_mem_out; memset(o_ltp, 0, sizeof(struct termios)); - goto repeat; } - + + /* + * Everything allocated ... set up the o_tty structure. + */ + driver->other->table[idx] = o_tty; + if (!*o_tp_loc) + *o_tp_loc = o_tp; + if (!*o_ltp_loc) + *o_ltp_loc = o_ltp; + o_tty->termios = *o_tp_loc; + o_tty->termios_locked = *o_ltp_loc; + (*driver->other->refcount)++; + if (driver->subtype == PTY_TYPE_MASTER) + o_tty->count++; + + /* Establish the links in both directions */ + tty->link = o_tty; + o_tty->link = tty; } - /* Now we have allocated all the structures: update all the pointers.. */ - if (!*tp_loc) { + + /* + * All structures have been allocated, so now we install them. + * Failures after this point use release_mem to clean up, so + * there's no need to null out the local pointers. + */ + driver->table[idx] = tty; + if (!*tp_loc) *tp_loc = tp; - tp = NULL; - } - if (!*ltp_loc) { + if (!*ltp_loc) *ltp_loc = ltp; - ltp = NULL; + tty->termios = *tp_loc; + tty->termios_locked = *ltp_loc; + (*driver->refcount)++; + tty->count++; + + /* + * Structures all installed ... call the ldisc open routines. + * If we fail here just call release_mem to clean up. No need + * to decrement the use counts, as release_mem doesn't care. + */ + if (tty->ldisc.open) { + retval = (tty->ldisc.open)(tty); + if (retval) + goto release_mem_out; } - if (!*tty_loc) { - tty->termios = *tp_loc; - tty->termios_locked = *ltp_loc; - *tty_loc = tty; - (*driver->refcount)++; - (*tty_loc)->count++; - if (tty->ldisc.open) { - retval = (tty->ldisc.open)(tty); - if (retval < 0) { - (*tty_loc)->count--; - tty = NULL; - goto end_init; - } - } - tty = NULL; - } else { - if ((*tty_loc)->flags & (1 << TTY_CLOSING)) { - printk("Attempt to open closing tty %s.\n", - tty_name(*tty_loc)); - printk("Ack!!!! This should never happen!!\n"); - return -EINVAL; + if (o_tty && o_tty->ldisc.open) { + retval = (o_tty->ldisc.open)(o_tty); + if (retval) { + if (tty->ldisc.close) + (tty->ldisc.close)(tty); + goto release_mem_out; } - (*tty_loc)->count++; } - if (driver->type == TTY_DRIVER_TYPE_PTY) { - if (!*o_tp_loc) { - *o_tp_loc = o_tp; - o_tp = NULL; - } - if (!*o_ltp_loc) { - *o_ltp_loc = o_ltp; - o_ltp = NULL; - } - if (!*o_tty_loc) { - o_tty->termios = *o_tp_loc; - o_tty->termios_locked = *o_ltp_loc; - *o_tty_loc = o_tty; - (*driver->other->refcount)++; - if (o_tty->ldisc.open) { - retval = (o_tty->ldisc.open)(o_tty); - if (retval < 0) { - (*tty_loc)->count--; - o_tty = NULL; - goto end_init; - } - } - o_tty = NULL; + goto success; + + /* + * This fast open can be used if the tty is already open. + * No memory is allocated, and the only failures are from + * attempting to open a closing tty or attempting multiple + * opens on a pty master. + */ +fast_track: + if (test_bit(TTY_CLOSING, &tty->flags)) { + retval = -EIO; + goto end_init; + } + if (driver->type == TTY_DRIVER_TYPE_PTY && + driver->subtype == PTY_TYPE_MASTER) { + /* + * special case for PTY masters: only one open permitted, + * and the slave side open count is incremented as well. + */ + if (tty->count) { + retval = -EIO; + goto end_init; } - (*tty_loc)->link = *o_tty_loc; - (*o_tty_loc)->link = *tty_loc; - if (driver->subtype == PTY_TYPE_MASTER) - (*o_tty_loc)->count++; + tty->link->count++; } - (*tty_loc)->driver = *driver; - *ret_tty = *tty_loc; - retval = 0; + tty->count++; + tty->driver = *driver; /* N.B. why do this every time?? */ + +success: + *ret_tty = tty; + + /* All paths come through here to release the semaphore */ end_init: - if (tty) - free_page((unsigned long) tty); - if (o_tty) - free_page((unsigned long) o_tty); - if (tp) - kfree_s(tp, sizeof(struct termios)); + up_tty_sem(idx); + return retval; + + /* Release locally allocated memory ... nothing placed in slots */ +free_mem_out: if (o_tp) kfree_s(o_tp, sizeof(struct termios)); + if (o_tty) + free_page((unsigned long) o_tty); if (ltp) kfree_s(ltp, sizeof(struct termios)); - if (o_ltp) - kfree_s(o_ltp, sizeof(struct termios)); - return retval; + if (tp) + kfree_s(tp, sizeof(struct termios)); + free_page((unsigned long) tty); + +fail_no_mem: + retval = -ENOMEM; + goto end_init; + + /* call the tty release_mem routine to clean out this slot */ +release_mem_out: + printk("init_dev: ldisc open failed, clearing slot %d\n", idx); + release_mem(tty, idx); + goto end_init; +} + +/* + * Releases memory associated with a tty structure, and clears out the + * driver table slots. + */ +static void release_mem(struct tty_struct *tty, int idx) +{ + struct tty_struct *o_tty; + struct termios *tp; + + if ((o_tty = tty->link) != NULL) { + o_tty->driver.table[idx] = NULL; + if (o_tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) { + tp = o_tty->driver.termios[idx]; + o_tty->driver.termios[idx] = NULL; + kfree_s(tp, sizeof(struct termios)); + } + o_tty->magic = 0; + (*o_tty->driver.refcount)--; + free_page((unsigned long) o_tty); + } + + tty->driver.table[idx] = NULL; + if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) { + tp = tty->driver.termios[idx]; + tty->driver.termios[idx] = NULL; + kfree_s(tp, sizeof(struct termios)); + } + tty->magic = 0; + (*tty->driver.refcount)--; + free_page((unsigned long) tty); } /* * Even releasing the tty structures is a tricky business.. We have * to be very careful that the structures are all released at the * same time, as interrupts might otherwise get the wrong pointers. + * + * WSH 09/09/97: rewritten to avoid some nasty race conditions that could + * lead to double frees or releasing memory still in use. */ static void release_dev(struct file * filp) { struct tty_struct *tty, *o_tty; - struct termios *tp, *o_tp, *ltp, *o_ltp; - struct task_struct *p; + int pty_master, tty_closing, o_tty_closing, do_sleep; int idx; tty = (struct tty_struct *)filp->private_data; - if (tty_paranoia_check(tty, filp->f_inode->i_rdev, "release_dev")) + if (tty_paranoia_check(tty, filp->f_dentry->d_inode->i_rdev, "release_dev")) return; check_tty_count(tty, "release_dev"); - tty_fasync(filp->f_inode, filp, 0); - - tp = tty->termios; - ltp = tty->termios_locked; + tty_fasync(filp->f_dentry->d_inode, filp, 0); idx = MINOR(tty->device) - tty->driver.minor_start; + pty_master = (tty->driver.type == TTY_DRIVER_TYPE_PTY && + tty->driver.subtype == PTY_TYPE_MASTER); + o_tty = tty->link; + #ifdef TTY_PARANOIA_CHECK if (idx < 0 || idx >= tty->driver.num) { printk("release_dev: bad idx when trying to free (%s)\n", @@ -864,15 +944,15 @@ static void release_dev(struct file * filp) idx, kdevname(tty->device)); return; } - if (tp != tty->driver.termios[idx]) { - printk("release_dev: driver.termios[%d] not termios for (" - "%s)\n", + if (tty->termios != tty->driver.termios[idx]) { + printk("release_dev: driver.termios[%d] not termios " + "for (%s)\n", idx, kdevname(tty->device)); return; } - if (ltp != tty->driver.termios_locked[idx]) { - printk("release_dev: driver.termios_locked[%d] not termios_locked for (" - "%s)\n", + if (tty->termios_locked != tty->driver.termios_locked[idx]) { + printk("release_dev: driver.termios_locked[%d] not " + "termios_locked for (%s)\n", idx, kdevname(tty->device)); return; } @@ -883,10 +963,6 @@ static void release_dev(struct file * filp) tty->count); #endif - o_tty = tty->link; - o_tp = (o_tty) ? o_tty->termios : NULL; - o_ltp = (o_tty) ? o_tty->termios_locked : NULL; - #ifdef TTY_PARANOIA_CHECK if (tty->driver.other) { if (o_tty != tty->driver.other->table[idx]) { @@ -895,34 +971,90 @@ static void release_dev(struct file * filp) idx, kdevname(tty->device)); return; } - if (o_tp != tty->driver.other->termios[idx]) { - printk("release_dev: other->termios[%d] not o_termios for (" - "%s)\n", + if (o_tty->termios != tty->driver.other->termios[idx]) { + printk("release_dev: other->termios[%d] not o_termios " + "for (%s)\n", idx, kdevname(tty->device)); return; } - if (o_ltp != tty->driver.other->termios_locked[idx]) { - printk("release_dev: other->termios_locked[%d] not o_termios_locked for (" - "%s)\n", + if (o_tty->termios_locked != + tty->driver.other->termios_locked[idx]) { + printk("release_dev: other->termios_locked[%d] not " + "o_termios_locked for (%s)\n", idx, kdevname(tty->device)); return; } - if (o_tty->link != tty) { printk("release_dev: bad pty pointers\n"); return; } } #endif - + /* + * Sanity check: if tty->count is going to zero, there shouldn't be + * any waiters on tty->read_wait or tty->write_wait. We test the + * wait queues and kick everyone out _before_ actually starting to + * close. This ensures that we won't block while releasing the tty + * structure. + * + * The test for the o_tty closing is necessary, since the master and + * slave sides may close in any order. If the slave side closes out + * first, its count will be one, since the master side holds an open. + * Thus this test wouldn't be triggered at the time the slave closes, + * so we do it now. + * + * Note that it's possible for the tty to be opened again while we're + * flushing out waiters. By recalculating the closing flags before + * each iteration we avoid any problems. + */ + while (1) { + tty_closing = tty->count <= 1; + o_tty_closing = o_tty && + (o_tty->count <= (pty_master ? 1 : 0)); + do_sleep = 0; + + if (tty_closing) { + if (waitqueue_active(&tty->read_wait)) { + wake_up(&tty->read_wait); + do_sleep++; + } + if (waitqueue_active(&tty->write_wait)) { + wake_up(&tty->write_wait); + do_sleep++; + } + } + if (o_tty_closing) { + if (waitqueue_active(&o_tty->read_wait)) { + wake_up(&o_tty->read_wait); + do_sleep++; + } + if (waitqueue_active(&o_tty->write_wait)) { + wake_up(&o_tty->write_wait); + do_sleep++; + } + } + if (!do_sleep) + break; + + printk("release_dev: %s: read/write wait queue active!\n", + tty_name(tty)); + schedule(); + } + + /* + * The closing flags are now consistent with the open counts on + * both sides, and we've completed the last operation that could + * block, so it's safe to proceed with closing. + */ + if (tty->driver.close) tty->driver.close(tty, filp); - if (tty->driver.type == TTY_DRIVER_TYPE_PTY && - tty->driver.subtype == PTY_TYPE_MASTER) { - if (--tty->link->count < 0) { + + if (pty_master) { + if (--o_tty->count < 0) { printk("release_dev: bad pty slave count (%d) for %s\n", - tty->count, tty_name(tty)); - tty->link->count = 0; + o_tty->count, tty_name(o_tty)); + o_tty->count = 0; } } if (--tty->count < 0) { @@ -930,60 +1062,50 @@ static void release_dev(struct file * filp) tty->count, tty_name(tty)); tty->count = 0; } - if (tty->count) - return; /* - * Sanity check --- if tty->count is zero, there shouldn't be - * any waiters on tty->read_wait or tty->write_wait. But just - * in case.... + * Perform some housekeeping before deciding whether to return. + * + * Set the TTY_CLOSING flag if this was the last open. In the + * case of a pty we may have to wait around for the other side + * to close, and TTY_CLOSING makes sure we can't be reopened. */ - while (1) { - if (waitqueue_active(&tty->read_wait)) { - printk("release_dev: %s: read_wait active?!?\n", - tty_name(tty)); - wake_up(&tty->read_wait); - } else if (waitqueue_active(&tty->write_wait)) { - printk("release_dev: %s: write_wait active?!?\n", - tty_name(tty)); - wake_up(&tty->write_wait); - } else - break; - schedule(); - } - + if(tty_closing) + set_bit(TTY_CLOSING, &tty->flags); + if(o_tty_closing) + set_bit(TTY_CLOSING, &o_tty->flags); + /* - * We're committed; at this point, we must not block! + * If _either_ side is closing, make sure there aren't any + * processes that still think tty or o_tty is their controlling + * tty. Also, clear redirect if it points to either tty. */ - if (o_tty) { - if (o_tty->count) - return; - tty->driver.other->table[idx] = NULL; - tty->driver.other->termios[idx] = NULL; - kfree_s(o_tp, sizeof(struct termios)); + if (tty_closing || o_tty_closing) { + struct task_struct *p; + + read_lock(&tasklist_lock); + for_each_task(p) { + if (p->tty == tty || (o_tty && p->tty == o_tty)) + p->tty = NULL; + } + read_unlock(&tasklist_lock); + + if (redirect == tty || (o_tty && redirect == o_tty)) + redirect = NULL; } + + /* check whether both sides are closing ... */ + if (!tty_closing || (o_tty && !o_tty_closing)) + return; + filp->private_data = 0; #ifdef TTY_DEBUG_HANGUP printk("freeing tty structure..."); #endif - tty->flags |= (1 << TTY_CLOSING); - - /* - * Make sure there aren't any processes that still think this - * tty is their controlling tty. - */ - read_lock(&tasklist_lock); - for_each_task(p) { - if (p->tty == tty) - p->tty = NULL; - if (o_tty && p->tty == o_tty) - p->tty = NULL; - } - read_unlock(&tasklist_lock); /* - * Shutdown the current line discipline, and reset it to - * N_TTY. + * Shutdown the current line discipline, and reset it to N_TTY. + * N.B. why reset ldisc when we're releasing the memory?? */ if (tty->ldisc.close) (tty->ldisc.close)(tty); @@ -995,41 +1117,34 @@ static void release_dev(struct file * filp) o_tty->ldisc = ldiscs[N_TTY]; } - tty->driver.table[idx] = NULL; - if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) { - tty->driver.termios[idx] = NULL; - kfree_s(tp, sizeof(struct termios)); - } - if (tty == redirect || o_tty == redirect) - redirect = NULL; /* * Make sure that the tty's task queue isn't activated. If it - * is, take it out of the linked list. + * is, take it out of the linked list. The tqueue isn't used by + * pty's, so skip the test for them. */ - spin_lock_irq(&tqueue_lock); - if (tty->flip.tqueue.sync) { - struct tq_struct *tq, *prev; - - for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) { - if (tq == &tty->flip.tqueue) { - if (prev) - prev->next = tq->next; - else - tq_timer = tq->next; - break; + if (tty->driver.type != TTY_DRIVER_TYPE_PTY) { + spin_lock_irq(&tqueue_lock); + if (tty->flip.tqueue.sync) { + struct tq_struct *tq, *prev; + + for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) { + if (tq == &tty->flip.tqueue) { + if (prev) + prev->next = tq->next; + else + tq_timer = tq->next; + break; + } } } + spin_unlock_irq(&tqueue_lock); } - spin_unlock_irq(&tqueue_lock); - tty->magic = 0; - (*tty->driver.refcount)--; - free_page((unsigned long) tty); - filp->private_data = 0; - if (o_tty) { - o_tty->magic = 0; - (*o_tty->driver.refcount)--; - free_page((unsigned long) o_tty); - } + + /* + * The release_mem function takes care of the details of clearing + * the slots and preserving the termios structure. + */ + release_mem(tty, idx); } /* @@ -1077,6 +1192,7 @@ retry_open: retval = init_dev(device, &tty); if (retval) return retval; + /* N.B. this error exit may leave filp->f_flags with O_NONBLOCK set */ filp->private_data = tty; check_tty_count(tty, "tty_open"); if (tty->driver.type == TTY_DRIVER_TYPE_PTY && @@ -1123,11 +1239,6 @@ retry_open: return 0; } -/* - * Note that releasing a pty master also releases the child, so - * we have to make the redirection checks after that and on both - * sides of a pty. - */ static int tty_release(struct inode * inode, struct file * filp) { release_dev(filp); @@ -1139,7 +1250,7 @@ static unsigned int tty_poll(struct file * filp, poll_table * wait) struct tty_struct * tty; tty = (struct tty_struct *)filp->private_data; - if (tty_paranoia_check(tty, filp->f_inode->i_rdev, "tty_poll")) + if (tty_paranoia_check(tty, filp->f_dentry->d_inode->i_rdev, "tty_poll")) return 0; if (tty->ldisc.poll) @@ -1545,6 +1656,7 @@ static void initialize_tty_struct(struct tty_struct *tty) tty->flip.flag_buf_ptr = tty->flip.flag_buf; tty->flip.tqueue.routine = flush_to_ldisc; tty->flip.tqueue.data = tty; + tty->flip.pty_sem = MUTEX; } /* diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c index 603250b81..c0d7440c3 100644 --- a/drivers/char/vc_screen.c +++ b/drivers/char/vc_screen.c @@ -237,6 +237,11 @@ vcs_write(struct inode *inode, struct file *file, const char *buf, unsigned long func_scr_writew((func_scr_readw(org) & 0xff00) | c, org); } } +#ifdef CONFIG_FB_CONSOLE + if (currcons == fg_console) + /* Horribly inefficient if count < screen size. */ + update_screen(currcons); +#endif written = buf - buf0; file->f_pos += written; RETURN( written ); diff --git a/drivers/isdn/avmb1/capiutil.c b/drivers/isdn/avmb1/capiutil.c index b3c25cd2a..51d57fe9c 100644 --- a/drivers/isdn/avmb1/capiutil.c +++ b/drivers/isdn/avmb1/capiutil.c @@ -1,5 +1,5 @@ /* - * $Id: capiutil.c,v 1.3 1997/05/18 09:24:18 calle Exp $ + * $Id: capiutil.c,v 1.1 1997/06/08 14:58:41 ralf Exp $ * * CAPI 2.0 convert capi message to capi message struct * @@ -7,6 +7,9 @@ * Rewritten for Linux 1996 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capiutil.c,v $ + * Revision 1.1 1997/06/08 14:58:41 ralf + * These files were missing in the 2.1.42 merge. + * * Revision 1.3 1997/05/18 09:24:18 calle * added verbose disconnect reason reporting to avmb1. * some fixes in capi20 interface. @@ -26,6 +29,7 @@ * */ #include <linux/module.h> +#include <linux/config.h> #include <linux/string.h> #include <linux/ctype.h> #include <linux/stddef.h> diff --git a/drivers/net/8390.c b/drivers/net/8390.c index fbf67cb42..1150ddcf4 100644 --- a/drivers/net/8390.c +++ b/drivers/net/8390.c @@ -186,6 +186,7 @@ static int ei_start_xmit(struct sk_buff *skb, struct device *dev) /* Mask interrupts from the ethercard. */ outb_p(0x00, e8390_base + EN0_IMR); + synchronize_irq(); if (dev->interrupt) { printk("%s: Tx request while isr active.\n",dev->name); outb_p(ENISR_ALL, e8390_base + EN0_IMR); diff --git a/drivers/net/Config.in b/drivers/net/Config.in index 23befe03c..bba3e43b8 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -84,6 +84,7 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then tristate 'Digi Intl. RightSwitch SE-X support' CONFIG_DGRS tristate 'EtherExpressPro/100 support' CONFIG_EEXPRESS_PRO100 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'TI ThunderLAN support (EXPERIMENTAL)' CONFIG_TLAN tristate 'Racal-Interlan EISA ES3210 support (EXPERIMENTAL)' CONFIG_ES3210 bool 'Zenith Z-Note support (EXPERIMENTAL)' CONFIG_ZNET fi @@ -113,7 +114,12 @@ fi # if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then if [ "$CONFIG_ATALK" != "n" ]; then - tristate 'LocalTalk PC support' CONFIG_LTPC + tristate 'Apple/Farallon LocalTalk PC support' CONFIG_LTPC + tristate 'COPS LocalTalk PC support' CONFIG_COPS + if [ "$CONFIG_COPS" != "n" ]; then + bool 'Dayna firmware support' CONFIG_COPS_DAYNA + bool 'Tangent firmware support' CONFIG_COPS_TANGENT + fi fi fi @@ -146,12 +152,14 @@ if [ "$CONFIG_NET_RADIO" != "n" ]; then bool 'Soundmodem support for 2400 baud AFSK modulation (8MHz crystal)' CONFIG_SOUNDMODEM_AFSK2400_8 bool 'Soundmodem support for 4800 baud HAPN-1 modulation' CONFIG_SOUNDMODEM_HAPN4800 bool 'Soundmodem support for 9600 baud FSK G3RUH modulation' CONFIG_SOUNDMODEM_FSK9600 - if [ -f drivers/net/soundmodem/sm_afsk2666.c ]; then - bool 'Soundmodem support for 2666 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK2666 - fi - if [ -f drivers/net/soundmodem/sm_psk4800.c ]; then - bool 'Soundmodem support for 4800 baud PSK modulation' CONFIG_SOUNDMODEM_PSK4800 - fi +# if [ -f doesn't work with xconfig. Enable it again when the drivers are really +# included. +# if [ -f drivers/net/soundmodem/sm_afsk2666.c ]; then +# bool 'Soundmodem support for 2666 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK2666 +# fi +# if [ -f drivers/net/soundmodem/sm_psk4800.c ]; then +# bool 'Soundmodem support for 4800 baud PSK modulation' CONFIG_SOUNDMODEM_PSK4800 +# fi fi fi tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 3a8721933..b149eac58 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -391,6 +391,14 @@ else endif endif +ifeq ($(CONFIG_TLAN),y) +L_OBJS += tlan.o +else + ifeq ($(CONFIG_TLAN),m) + M_OBJS += tlan.o + endif +endif + ifeq ($(CONFIG_ZNET),y) L_OBJS += znet.o endif @@ -663,6 +671,14 @@ else endif endif +ifeq ($(CONFIG_COPS),y) +L_OBJS += cops.o +else + ifeq ($(CONFIG_COPS),m) + M_OBJS += cops.o + endif +endif + ifeq ($(CONFIG_BAYCOM),y) L_OBJS += baycom.o CONFIG_HDLCDRV_BUILTIN = y @@ -785,7 +801,7 @@ dlci.o: dlci.c CONFIG sdladrv.o: sdladrv.c CONFIG wanpipe.o: $(WANPIPE_OBJS) - ld -r -o $@ $^ + ld -r -o $@ $(WANPIPE_OBJS) sdlamain.o: sdlamain.c CONFIG diff --git a/drivers/net/README.wanpipe b/drivers/net/README.wanpipe index bcfed26fe..9650edb73 100644 --- a/drivers/net/README.wanpipe +++ b/drivers/net/README.wanpipe @@ -1,10 +1,10 @@ ------------------------------------------------------------------------------ WANPIPE(tm) Multiprotocol WAN Driver for Linux WAN Router ------------------------------------------------------------------------------ -Release 3.0.0 -December 31, 1996 +Release 3.1.0 +January 30, 1997 Author: Gene Kozin <genek@compuserve.com> -Copyright (c) 1995-1996 Sangoma Technologies Inc. +Copyright (c) 1995-1997 Sangoma Technologies Inc. ------------------------------------------------------------------------------ INTRODUCTION @@ -81,6 +81,12 @@ include/linux: REVISION HISTORY +3.1.0 January 30, 1997 + + o Implemented IOCTL for executing adapter commands. + o Fixed a bug in frame relay code causing driver configured as a FR + switch to be stuck in WAN_DISCONNECTED mode. + 3.0.0 December 31, 1996 o Uses Linux WAN Router interface diff --git a/drivers/net/Space.c b/drivers/net/Space.c index ce820df60..64bc62c41 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -89,6 +89,7 @@ extern int atarilance_probe(struct device *); extern int a2065_probe(struct device *); extern int ariadne_probe(struct device *); extern int hydra_probe(struct device *); +extern int tlan_probe(struct device *); extern int cs89x0_probe(struct device *dev); /* Detachable devices ("pocket adaptors") */ @@ -242,6 +243,9 @@ __initfunc(static int ethif_probe(struct device *dev)) #ifdef CONFIG_SUNLANCE && sparc_lance_probe(dev) #endif +#ifdef CONFIG_TLAN + && tlan_probe(dev) +#endif #ifdef CONFIG_HAPPYMEAL && happy_meal_probe(dev) #endif @@ -301,6 +305,17 @@ static struct device atp_dev = { # define NEXT_DEV (&dev_ltpc) #endif /* LTPC */ +#if defined(CONFIG_COPS) + extern int cops_probe(struct device *); + static struct device dev_cops = { + "lt0", + 0, 0, 0, 0, + 0x0, 0, + 0, 0, 0, NEXT_DEV, cops_probe }; +# undef NEXT_DEV +# define NEXT_DEV (&dev_cops) +#endif /* COPS */ + /* The first device defaults to I/O base '0', which means autoprobe. */ #ifndef ETH0_ADDR # define ETH0_ADDR 0 diff --git a/drivers/net/cops.c b/drivers/net/cops.c new file mode 100644 index 000000000..66e3c5fea --- /dev/null +++ b/drivers/net/cops.c @@ -0,0 +1,1018 @@ +/* cops.c: LocalTalk driver for Linux. + * + * Authors: + * - Jay Schulist <Jay.Schulist@spacs.k12.wi.us> + * + * With more than a little help from; + * - Alan Cox <Alan.Cox@linux.org> + * + * Derived from: + * - skeleton.c: A network driver outline for linux. + * Written 1993-94 by Donald Becker. + * - ltpc.c: A driver for the LocalTalk PC card. + * Written by Bradford W. Johnson. + * + * Copyright 1993 United States Government as represented by the + * Director, National Security Agency. + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + * Changes: + * 19970608 Alan Cox Allowed dual card type support + * Can set board type in insmod + * Hooks for cops_setup routine + * (not yet implemented). + */ + +static const char *version = + "cops.c:v0.01 3/17/97 Jay Schulist <Jay.Schulist@spacs.k12.wi.us>\n"; +/* + * Sources: + * COPS Localtalk SDK. This provides almost all of the information + * needed. + */ + +/* + * insmod/modprobe configurable stuff. + * - IO Port, choose one your card supports or 0 if you dare. + * - IRQ, also choose one your card supports or nothing and let + * the driver figure it out. + */ + +#include <linux/config.h> +#ifdef MODULE +#include <linux/module.h> +#include <linux/version.h> +#endif + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/ioport.h> +#include <linux/in.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <asm/system.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/dma.h> +#include <linux/errno.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#include <linux/if_arp.h> +#include <linux/if_ltalk.h> /* For ltalk_setup() */ +#include <linux/delay.h> /* For udelay() */ +#include <linux/atalk.h> + +#include "cops.h" /* Our Stuff */ +#include "cops_ltdrv.h" /* Firmware code for Tangent type cards. */ +#include "cops_ffdrv.h" /* Firmware code for Dayna type cards. */ + +/* + * The name of the card. Is used for messages and in the requests for + * io regions, irqs and dma channels + */ + +static const char *cardname = "cops"; + +#ifdef CONFIG_COPS_DAYNA +static int board_type = DAYNA; /* Module exported */ +#else +static int board_type = TANGENT; +#endif + +#ifdef MODULE +static int io = 0x240; /* Default IO for Dayna */ +static int irq = 5; /* Default IRQ */ +#else +static int io = 0; /* Default IO for Dayna */ +static int irq = 0; /* Default IRQ */ +#endif + +/* + * COPS Autoprobe information. + * Right now if port address is right but IRQ is not 5 this will + * return a 5 no matter what since we will still get a status response. + * Need one more additional check to narrow down after we have gotten + * the ioaddr. But since only other possible IRQs is 3 and 4 so no real + * hurry on this. I *STRONGLY* recommend using IRQ 5 for your card with + * this driver. + * + * This driver has 2 modes and they are: Dayna mode and Tangent mode. + * Each mode corresponds with the type of card. It has been found + * that there are 2 main types of cards and all other cards are + * the same and just have different names or only have minor differences + * such as more IO ports. As this driver is tested it will + * become more clear on exactly what cards are supported. The driver + * defaults to using Dayna mode. To change the drivers mode adjust + * drivers/net/CONFIG, and the line COPS_OPTS = -DDAYNA to -DTANGENT. + * + * This driver should support: + * TANGENT driver mode: + * Tangent ATB-II, Novell NL-1000, Daystar Digital LT-200 + * DAYNA driver mode: + * Dayna DL2000/DaynaTalk PC (Half Length), COPS LT-95, Farallon PhoneNET PC III + * Other cards possibly supported mode unkown though: + * Farallon PhoneNET PC II + * Dayna DL2000 (Full length) + * + * Cards NOT supported by this driver but supported by the ltpc.c + * driver written by Bradford W. Johnson <johns393@maroon.tc.umn.edu> + * Farallon PhoneNET PC + * Original Apple LocalTalk PC card + */ + +/* + * Zero terminated list of IO ports to probe. + */ + +static unsigned int cops_portlist[] = { + 0x240, 0x340, 0x200, 0x210, 0x220, 0x230, 0x260, + 0x2A0, 0x300, 0x310, 0x320, 0x330, 0x350, 0x360, + 0 +}; + +/* + * Zero terminated list of IRQ ports to probe. + */ + +static int cops_irqlist[] = { + 5, 4, 3, 0 +}; + +/* use 0 for production, 1 for verification, 2 for debug, 3 for very verbose debug */ +#ifndef COPS_DEBUG +#define COPS_DEBUG 1 +#endif +static unsigned int cops_debug = COPS_DEBUG; + +/* The number of low I/O ports used by the card. */ +#define COPS_IO_EXTENT 8 + +/* Information that needs to be kept for each board. */ + +struct cops_local +{ + struct enet_statistics stats; + int board; /* Holds what board type is. */ + int nodeid; /* Set to 1 once have nodeid. */ + unsigned char node_acquire; /* Node ID when acquired. */ +}; + +/* Allocate a new device with the form of lt0, lt1, lt2, etc. */ +struct device *cops_dev_alloc(char *name) +{ + int i=0; + struct device *d=kmalloc(sizeof(struct device)+8, GFP_KERNEL); + + memset(d,0,sizeof(*d)); /* Clear the structure */ + if(d==NULL) + return NULL; + d->name=(char *)(d+1); /* Name string space */ + + /* Get next free device name */ + for(i=0;i<100;i++) + { + sprintf(d->name,name,i); + if(dev_get(d->name)==NULL) + return d; + } + return NULL; /* Over 100 of the things .. bail out! */ +} + +/* Index to functions, as function prototypes. */ +extern int cops_probe (struct device *dev); +static int cops_probe1 (struct device *dev, int ioaddr); +static int cops_irq (int ioaddr, int board); + +static int cops_open (struct device *dev); +static int cops_jumpstart (struct device *dev); +static void cops_reset (struct device *dev, int sleep); +static void cops_load (struct device *dev); +static int cops_nodeid (struct device *dev, int nodeid); + +static void cops_interrupt (int irq, void *dev_id, struct pt_regs *regs); +static void cops_rx (struct device *dev); +static int cops_send_packet (struct sk_buff *skb, struct device *dev); +static void set_multicast_list (struct device *dev); +static int cops_hard_header (struct sk_buff *skb, struct device *dev, + unsigned short type, void *daddr, void *saddr, unsigned len); + +static int cops_ioctl (struct device *dev, struct ifreq *rq, int cmd); +static int cops_close (struct device *dev); +static struct enet_statistics *cops_get_stats (struct device *dev); + + +/* + * Check for a network adaptor of this type, and return '0' iff one exists. + * If dev->base_addr == 0, probe all likely locations. + * If dev->base_addr == 1, always return failure. + * If dev->base_addr == 2, allocate space for the device and return success + * (detachable devices only). + */ +int cops_probe(struct device *dev) +{ + int i; + int base_addr = dev ? dev->base_addr : 0; + + if (base_addr == 0 && io) + base_addr=io; + + if (base_addr > 0x1ff) /* Check a single specified location. */ + return cops_probe1(dev, base_addr); + else if (base_addr != 0) /* Don't probe at all. */ + return -ENXIO; + + for (i=0; cops_portlist[i]; i++) { + int ioaddr = cops_portlist[i]; + if (check_region(ioaddr, COPS_IO_EXTENT)) + continue; + if (cops_probe1(dev, ioaddr) == 0) + return 0; + } + + /* No "lt" devices found. */ + printk(KERN_WARNING "%s: No COPS localtalk devices found!\n", dev->name); + return -ENODEV; +} + +/* + * This is the real probe routine. Linux has a history of friendly device + * probes on the ISA bus. A good device probes avoids doing writes, and + * verifies that the correct device exists and functions. + */ +static int cops_probe1(struct device *dev, int ioaddr) +{ + struct cops_local *lp; + static unsigned version_printed = 0; + int irqaddr = 0; + int irqval; + + int board = board_type; + +/* Defined here to save some trouble */ + + /* Allocate a new 'dev' if needed. */ + if (dev == NULL) + { + dev=cops_dev_alloc(dev->name); /* New "lt" device; beyond lt0. */ + if(dev==NULL) + return -ENOMEM; + } + + if (cops_debug && version_printed++ == 0) + printk("%s", version); + + /* Fill in the 'dev' fields. */ + dev->base_addr = ioaddr; + + /* + * Since this board has jumpered interrupts, allocate the interrupt + * vector now. There is no point in waiting since no other device + * can use the interrupt, and this marks the irq as busy. Jumpered + * interrupts are typically not reported by the boards, and we must + * used AutoIRQ to find them. + * + */ + + if (dev->irq < 2 && irq) + dev->irq = irq; + + if (dev->irq < 2) + { + irqaddr = cops_irq(ioaddr, board); /* COPS AutoIRQ routine */ + if (irqaddr == 0) + return -EAGAIN; /* No IRQ found on this port */ + else + dev->irq = irqaddr; + } + else if (dev->irq == 2) + /* + * Fixup for users that don't know that IRQ 2 is really + * IRQ 9, or don't know which one to set. + */ + dev->irq = 9; + + /* Snarf the interrupt now. */ + irqval = request_irq(dev->irq, &cops_interrupt, 0, cardname, NULL); + if (irqval) + { + printk(KERN_WARNING "%s: Unable to get IRQ %d (irqval=%d).\n", dev->name, dev->irq, irqval); + return -EAGAIN; + } + + dev->hard_start_xmit = &cops_send_packet; + + /* Initialize the device structure. */ + dev->priv = kmalloc(sizeof(struct cops_local), GFP_KERNEL); + if (dev->priv == NULL) + return -ENOMEM; + + lp = (struct cops_local *)dev->priv; + memset(lp, 0, sizeof(struct cops_local)); + + /* Copy local board variable to lp struct. */ + lp->board = board; + + /* Tell the user where the card is and what mode were in. */ + if(board==DAYNA) + printk("%s: %s found at %#3x, using IRQ %d, in Dayna mode.\n", + dev->name, cardname, ioaddr, dev->irq); + if(board==TANGENT) + printk("%s: %s found at %#3x, using IRQ %d, in Tangent mode.\n", + dev->name, cardname, ioaddr, dev->irq); + + /* Grab the region so no one else tries to probe our ioports. */ + request_region(ioaddr, COPS_IO_EXTENT, cardname); + + /* Fill in the fields of the device structure with LocalTalk values. */ + ltalk_setup(dev); + + dev->hard_header = cops_hard_header; + dev->get_stats = cops_get_stats; + dev->open = cops_open; + dev->stop = cops_close; + dev->do_ioctl = &cops_ioctl; + dev->set_multicast_list = &set_multicast_list; + dev->mc_list = NULL; + + return 0; +} + +static int cops_irq (int ioaddr, int board) +{ /* + * This does not use the IRQ to determine where the IRQ is. We just + * assume that when we get a correct status response that is the IRQ then. + * This really just verifies the IO port but since we only have access + * to such a small number of IRQs (5, 4, 3) this is not bad. + * This will probably not work for more than one card. + */ + int irqaddr=0; + int i, x, status; + + if(board==DAYNA) + { + outb(0, ioaddr+DAYNA_RESET); + inb(ioaddr+DAYNA_RESET); + udelay(333333); + } + if(board==TANGENT) + { + inb(ioaddr); + outb(0, ioaddr); + outb(0, ioaddr+TANG_RESET); + } + + for(i=0; cops_irqlist[i] !=0; i++) + { + irqaddr = cops_irqlist[i]; + for(x = 0xFFFF; x>0; x --) /* wait for response */ + { + if(board==DAYNA) + { + status = (inb(ioaddr+DAYNA_CARD_STATUS)&3); + if (status == 1) + return irqaddr; + } + if(board==TANGENT) + { + if((inb(ioaddr+TANG_CARD_STATUS)& TANG_TX_READY) !=0) + return irqaddr; + } + } + } + return 0; /* no IRQ found */ +} + +/* + * Open/initialize the board. This is called (in the current kernel) + * sometime after booting when the 'ifconfig' program is run. + */ +static int cops_open(struct device *dev) +{ + irq2dev_map[dev->irq] = dev; + + cops_jumpstart(dev); /* Start the card up. */ + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + + return 0; +} + +/* + * This allows for a dynamic start/restart of the entire card. + */ +static int cops_jumpstart(struct device *dev) +{ + struct cops_local *lp = (struct cops_local *)dev->priv; + + /* + * Once the card has the firmware loaded and has acquired + * the nodeid, if it is reset it will lose it all. + */ + cops_reset(dev,1); /* Need to reset card before load firmware. */ + cops_load(dev); /* Load the firmware. */ + + /* + * If atalkd already gave us a nodeid we will use that + * one again, else we wait for atalkd to give us a nodeid + * in cops_ioctl. This may cause a problem if someone steals + * our nodeid while we are resetting. + */ + if(lp->nodeid == 1) + cops_nodeid(dev,lp->node_acquire); + + return 0; +} + +static int tangent_wait_reset(int ioaddr) +{ + int timeout=0; + + while(timeout < 5000 && (inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0) + udelay(1000); /* Wait 1000 useconds */ + + return 0; +} + +/* + * Reset the LocalTalk board. + */ +static void cops_reset(struct device *dev, int sleep) +{ + struct cops_local *lp = (struct cops_local *)dev->priv; + int ioaddr=dev->base_addr; + + if(lp->board==TANGENT) + { + inb(ioaddr); /* Clear request latch. */ + outb(0,ioaddr); /* Clear the TANG_TX_READY flop. */ + outb(0, ioaddr+TANG_RESET); /* Reset the adapter. */ + + /* Can take 5 seconds max - youch! */ + if(sleep) + { + long snapt=jiffies; + while(jiffies-snapt<5*HZ) + { + if(inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY) + break; + schedule(); + } + } + else + tangent_wait_reset(ioaddr); + outb(0, ioaddr+TANG_CLEAR_INT); + } + if(lp->board==DAYNA) + { + outb(0, ioaddr+DAYNA_RESET); /* Assert the reset port */ + inb(ioaddr+DAYNA_RESET); /* Clear the reset */ + if(sleep) + { + long snap=jiffies; + + /* Let card finish initializing, about 1/3 second */ + while(jiffies-snap<HZ/3) + schedule(); + } + else + udelay(333333); + } + dev->tbusy=0; + + return; +} + +static void cops_load (struct device *dev) +{ + struct ifreq ifr; + struct ltfirmware *ltf= (struct ltfirmware *)&ifr.ifr_data; + struct cops_local *lp=(struct cops_local *)dev->priv; + int ioaddr=dev->base_addr; + int length, i = 0; + + strcpy(ifr.ifr_name,"lt0"); + + /* Get card's firmware code and do some checks on it. */ +#ifdef CONFIG_COPS_DAYNA + if (lp->board==DAYNA) + { + ltf->length=sizeof(ffdrv_code); + ltf->data=ffdrv_code; + } + else +#endif +#ifdef CONFIG_COPS_TANGENT + if (lp->board==TANGENT) + { + ltf->length=sizeof(ltdrv_code); + ltf->data=ltdrv_code; + } + else +#endif + { + printk(KERN_INFO "%s; unsupported board type.\n", dev->name); + return; + } + + /* Check to make sure firmware is correct length. */ + if(lp->board==DAYNA && ltf->length!=5983) + { + printk(KERN_WARNING "%s: Firmware is not length of FFDRV.BIN.\n", dev->name); + return; + } + if(lp->board==TANGENT && ltf->length!=2501) + { + printk(KERN_WARNING "%s: Firmware is not length of DRVCODE.BIN.\n", dev->name); + return; + } + + if(lp->board==DAYNA) + { + /* + * We must wait for a status response + * with the DAYNA board. + */ + while(++i<65536) + { + if((inb(ioaddr+DAYNA_CARD_STATUS)&3)==1) + break; + } + + if(i==65536) + return; + } + + /* + * Upload the firmware and kick. Byte-by-byte works nicely here. + */ + i=0; + length = ltf->length; + while(length--) + { + outb(ltf->data[i], ioaddr); + i++; + } + + if(cops_debug > 1) + printk(KERN_DEBUG "%s: Uploaded firmware - %d bytes of %d bytes.\n", dev->name, i, ltf->length); + + if(lp->board==DAYNA) + outb(1, ioaddr+DAYNA_INT_CARD); /* Tell Dayna to run the firmware code. */ + else + inb(ioaddr); /* Tell Tang to run the firmware code. */ + + if(lp->board==TANGENT) + { + tangent_wait_reset(ioaddr); + inb(ioaddr); /* Clear initial ready signal. */ + } + + return; +} + +/* + * Get the LocalTalk Nodeid from the card. We can suggest + * any nodeid 1-254. The card will try and get that exact + * address else we can specify 0 as the nodeid and the card + * will autoprobe for a nodeid. + */ +static int cops_nodeid (struct device *dev, int nodeid) +{ + struct cops_local *lp = (struct cops_local *) dev->priv; + int ioaddr = dev->base_addr; + + if (lp->board == DAYNA) + { + /* Empty any pending adapter responses. */ + while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0) + { + outb(0, ioaddr+COPS_CLEAR_INT); /* Clear any interrupt. */ + if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST) + cops_rx(dev); /* Kick out any packet waiting. */ + schedule(); + } + + outb(2, ioaddr); /* Output command packet length as 2. */ + outb(0, ioaddr); + outb(LAP_INIT, ioaddr); /* Send LAP_INIT command byte. */ + outb(nodeid, ioaddr); /* Suggest node address. */ + } + + if (lp->board == TANGENT) + { + /* Empty any pending adapter responses. */ + while(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY) + { + outb(0, ioaddr+COPS_CLEAR_INT); /* Clear any interrupt. */ + cops_rx(dev); /* Kick out any packet waiting. */ + schedule(); + } + + /* Not sure what Tangent does if random nodeid we picked is already used. */ + if(nodeid == 0) /* Seed. */ + nodeid = jiffies&0xFF; /* Get a random try .*/ + outb(2, ioaddr); /* Command length LSB. */ + outb(0, ioaddr); /* Command length MSB. */ + outb(LAP_INIT, ioaddr); /* Send LAP_INIT command byte. */ + outb(nodeid, ioaddr); /* LAP address hint. */ + outb(0xFF, ioaddr); /* Interrupt level to use (NONE). */ + } + + lp->node_acquire=0; /* Set nodeid holder to 0. */ + while(lp->node_acquire==0) /* Get *True* nodeid finally. */ + { + outb(0, ioaddr+COPS_CLEAR_INT); /* Clear any interrupt. */ + + if(lp->board == DAYNA) + { + if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST) + cops_rx(dev); /* Grab the nodeid put in lp->node_acquire. */ + } + if(lp->board == TANGENT) + { + if(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY) + cops_rx(dev); /* Grab the nodeid put in lp->node_acquire. */ + } + schedule(); + } + + if(cops_debug > 1) + printk(KERN_DEBUG "%s: Node ID %d has been acquired.\n", dev->name, lp->node_acquire); + + lp->nodeid=1; /* Set got nodeid to 1. */ + + return 0; +} + +/* + * The typical workload of the driver: + * Handle the network interface interrupts. + */ +static void cops_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct device *dev = (struct device *) irq2dev_map[irq]; + struct cops_local *lp; + int ioaddr, status; + int boguscount = 0; + + if (dev == NULL) + { + printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq); + return; + } + dev->interrupt = 1; + + ioaddr = dev->base_addr; + lp = (struct cops_local *)dev->priv; + + do + { + /* Clear any interrupt. */ + outb(0, ioaddr + COPS_CLEAR_INT); + + if(lp->board==DAYNA) + { + status=inb(ioaddr+DAYNA_CARD_STATUS); + if((status&0x03)==DAYNA_RX_REQUEST) + cops_rx(dev); + } + else + { + status=inb(ioaddr+TANG_CARD_STATUS); + if (status&TANG_RX_READY) + cops_rx(dev); + } + + dev->tbusy = 0; + mark_bh(NET_BH); + } while (++boguscount < 20 ); + dev->interrupt = 0; + + return; +} + +/* + * We have a good packet(s), get it/them out of the buffers. + */ +static void cops_rx(struct device *dev) +{ + int pkt_len = 0; + int rsp_type = 0; + struct sk_buff *skb; + struct cops_local *lp = (struct cops_local *)dev->priv; + int ioaddr = dev->base_addr; + int boguscount = 0; + + cli(); /* Disable interrupts. */ + + if(lp->board==DAYNA) + { + outb(0, ioaddr); /* Send out Zero length. */ + outb(0, ioaddr); + outb(DATA_READ, ioaddr); /* Send read command out. */ + + /* Wait for DMA to turn around. */ + while(++boguscount<1000000) + { + if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_READY) + break; + } + + if(boguscount==1000000) + { + printk(KERN_WARNING "%s: DMA timed out.\n",dev->name); + return; + } + } + + /* Get response length. */ + pkt_len = inb(ioaddr) & 0xFF; + pkt_len |= (inb(ioaddr) << 8); + /* Input IO code. */ + rsp_type=inb(ioaddr); + + /* Malloc up new buffer. */ + skb = dev_alloc_skb(pkt_len); + if (skb == NULL) + { + printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name); + lp->stats.rx_dropped++; + while(pkt_len--) /* Discard packet */ + inb(ioaddr); + return; + } + skb->dev = dev; + skb_put(skb, pkt_len); + skb->protocol = htons(ETH_P_LOCALTALK); + + insb(ioaddr, skb->data, pkt_len); /* Eat the Data */ + + if(lp->board==DAYNA) + outb(1, ioaddr+DAYNA_INT_CARD); /* Interrupt the card. */ + + sti(); /* Restore interrupts. */ + + /* Check for bad response length */ + if (pkt_len < 0 || pkt_len > MAX_LLAP_SIZE) + { + printk(KERN_NOTICE "%s: Bad packet length of %d bytes.\n", dev->name, pkt_len); + lp->stats.tx_errors++; + kfree_skb(skb, FREE_READ); + return; + } + + /* Set nodeid and then get out. */ + if(rsp_type == LAP_INIT_RSP) + { + lp->node_acquire = skb->data[0]; /* Nodeid taken from received packet. */ + kfree_skb(skb, FREE_READ); + return; + } + + /* One last check to make sure we have a good packet. */ + if(rsp_type != LAP_RESPONSE) + { + printk("%s: Bad packet type %d.\n", dev->name, rsp_type); + lp->stats.tx_errors++; + kfree_skb(skb, FREE_READ); + return; + } + + skb->mac.raw = skb->data; /* Point to entire packet. */ + skb_pull(skb,3); + skb->h.raw = skb->data; /* Point to just the data (Skip header). */ + + /* Update the counters. */ + lp->stats.rx_packets++; + lp->stats.rx_bytes += skb->len; + + /* Send packet to a higher place. */ + netif_rx(skb); + + return; +} + +/* + * Make the card transmit a LocalTalk packet. + */ +static int cops_send_packet(struct sk_buff *skb, struct device *dev) +{ + struct cops_local *lp = (struct cops_local *)dev->priv; + int ioaddr = dev->base_addr; + + if (dev->tbusy) + { + /* + * If we get here, some higher level has decided we are broken. + * There should really be a "kick me" function call instead. + */ + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < 5) + return 1; + lp->stats.tx_errors++; + if(lp->board==TANGENT) + { + if((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0) + printk(KERN_WARNING "%s: No TX complete interrupt.\n", dev->name); + } + printk(KERN_WARNING "%s: Transmit timed out.\n", dev->name); + cops_jumpstart(dev); /* Restart the card. */ + dev->tbusy=0; + dev->trans_start = jiffies; + } + + /* + * Block a timer-based transmit from overlapping. This could better be + * done with atomic_swap(1, dev->tbusy), but set_bit() works as well. + */ + if (test_and_set_bit(0, (void*) &dev->tbusy) != 0) + printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name); + else + { + cli(); /* Disable interrupts. */ + if(lp->board == DAYNA) /* Wait for adapter transmit buffer. */ + while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0); + if(lp->board == TANGENT) /* Wait for adapter transmit buffer. */ + while((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0); + + /* Output IO length. */ + if(lp->board == DAYNA) + { + outb(skb->len, ioaddr); + outb(skb->len >> 8, ioaddr); + } + else + { + outb(skb->len&0x0FF, ioaddr); + outb((skb->len >> 8)&0x0FF, ioaddr); + } + + /* Output IO code. */ + outb(LAP_WRITE, ioaddr); + + if(lp->board == DAYNA) /* Check the transmit buffer again. */ + while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0); + + outsb(ioaddr, skb->data, skb->len); /* Send out the data. */ + + if(lp->board==DAYNA) /* The Dayna requires you kick the card. */ + outb(1, ioaddr+DAYNA_INT_CARD); + + sti(); /* Restore interrupts. */ + + /* Done sending packet, update counters and cleanup. */ + lp->stats.tx_packets++; + lp->stats.tx_bytes += skb->len; + dev->trans_start = jiffies; + } + + dev_kfree_skb (skb, FREE_WRITE); + dev->tbusy = 0; + + return 0; +} + +/* + * Dummy function to keep the Appletalk layer happy. + */ + +static void set_multicast_list(struct device *dev) +{ + if(cops_debug >= 3) + printk("%s: set_mulicast_list executed. NeatO.\n", dev->name); +} + +/* + * Another Dummy function to keep the Appletalk layer happy. + */ + +static int cops_hard_header(struct sk_buff *skb, struct device *dev, + unsigned short type, void *daddr, void *saddr, unsigned len) +{ + if(cops_debug >= 3) + printk("%s: cops_hard_header executed. Wow!\n", dev->name); + return 0; +} + +/* + * System ioctls for the COPS LocalTalk card. + */ + +static int cops_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +{ + struct cops_local *lp = (struct cops_local *)dev->priv; + struct sockaddr_at *sa=(struct sockaddr_at *)&ifr->ifr_addr; + struct at_addr *aa=(struct at_addr *)&dev->pa_addr; + + switch(cmd) + { + case SIOCSIFADDR: + /* Get and set the nodeid and network # atalkd wants. */ + cops_nodeid(dev, sa->sat_addr.s_node); + aa->s_net = sa->sat_addr.s_net; + aa->s_node = lp->node_acquire; + + /* Set broardcast address. */ + dev->broadcast[0] = 0xFF; + + /* Set hardware address. */ + dev->dev_addr[0] = aa->s_node; + dev->addr_len = 1; + return 0; + + case SIOCGIFADDR: + sa->sat_addr.s_net = aa->s_net; + sa->sat_addr.s_node = aa->s_node; + return 0; + + default: + return -EOPNOTSUPP; + } +} + +/* + * The inverse routine to cops_open(). + */ + +static int cops_close(struct device *dev) +{ + dev->tbusy = 1; + dev->start = 0; + irq2dev_map[dev->irq] = 0; + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + + return 0; +} + +/* + * Get the current statistics. + * This may be called with the card open or closed. + */ +static struct enet_statistics *cops_get_stats(struct device *dev) +{ + struct cops_local *lp = (struct cops_local *)dev->priv; + return &lp->stats; +} + +#ifdef MODULE +static struct device dev_cops = +{ + "lt0", /* device name */ + 0, 0, 0, 0, + 0x0, 0, /* I/O address, IRQ */ + 0, 0, 0, NULL, cops_probe +}; + + +MODULE_PARM(io, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(board_type, "i"); + +int init_module(void) +{ + int result; + + if (io == 0) + printk(KERN_WARNING "%s: You shouldn't use auto-probing with insmod!\n", cardname); + + /* Copy the parameters from insmod into the device structure. */ + dev_cops.base_addr = io; + dev_cops.irq = irq; + + if ((result = register_netdev(&dev_cops)) != 0) + return result; + + return 0; +} + +void cleanup_module(void) +{ + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + + free_irq(dev_cops.irq, NULL); + release_region(dev_cops.base_addr, COPS_IO_EXTENT); + unregister_netdev(&dev_cops); + + if (dev_cops.priv) + kfree_s(dev_cops.priv, sizeof(struct cops_local)); +} +#endif /* MODULE */ diff --git a/drivers/net/cops.h b/drivers/net/cops.h new file mode 100644 index 000000000..a064bc12b --- /dev/null +++ b/drivers/net/cops.h @@ -0,0 +1,60 @@ +/* cops.h: LocalTalk driver for Linux. + * + * Authors: + * - Jay Schulist <Jay.Schulist@spacs.k12.wi.us> + */ + +#ifndef __LINUX_COPSLTALK_H +#define __LINUX_COPSLTALK_H + +#ifdef __KERNEL__ + +/* Max LLAP size we will accept. */ +#define MAX_LLAP_SIZE 603 + +/* Tangent */ +#define TANG_CARD_STATUS 1 +#define TANG_CLEAR_INT 1 +#define TANG_RESET 3 + +#define TANG_TX_READY 1 +#define TANG_RX_READY 2 + +/* Dayna */ +#define DAYNA_CMD_DATA 0 +#define DAYNA_CLEAR_INT 1 +#define DAYNA_CARD_STATUS 2 +#define DAYNA_INT_CARD 3 +#define DAYNA_RESET 4 + +#define DAYNA_RX_READY 0 +#define DAYNA_TX_READY 1 +#define DAYNA_RX_REQUEST 3 + +/* Same on both card types */ +#define COPS_CLEAR_INT 1 + +/* LAP response codes recieved from the cards. */ +#define LAP_INIT 1 /* Init cmd */ +#define LAP_INIT_RSP 2 /* Init response */ +#define LAP_WRITE 3 /* Write cmd */ +#define DATA_READ 4 /* Data read */ +#define LAP_RESPONSE 4 /* Received ALAP frame response */ +#define LAP_GETSTAT 5 /* Get LAP and HW status */ +#define LAP_RSPSTAT 6 /* Status response */ + +#endif + +/* + * Structure to hold the firmware information. + */ +struct ltfirmware +{ + unsigned int length; + unsigned char * data; +}; + +#define DAYNA 1 +#define TANGENT 2 + +#endif diff --git a/drivers/net/cops_ffdrv.h b/drivers/net/cops_ffdrv.h new file mode 100644 index 000000000..d3e337afc --- /dev/null +++ b/drivers/net/cops_ffdrv.h @@ -0,0 +1,533 @@ + +/* + * The firmware this driver downloads into the Localtalk card is a + * seperate program and is not GPL'd source code, even though the Linux + * side driver and the routine that loads this data into the card are. + * + * It is taken from the COPS SDK and is under the following license + * + * This material is licensed to you strictly for use in conjunction with + * the use of COPS LocalTalk adapters. + * There is no charge for this SDK. And no waranty express or implied + * about its fitness for any purpose. However, we will cheerefully + * refund every penny you paid for this SDK... + * Regards, + * + * Thomas F. Divine + * Chief Scientist + */ + + +/* cops_ffdrv.h: LocalTalk driver firmware dump for Linux. + * + * Authors: + * - Jay Schulist <Jay.Schulist@spacs.k12.wi.us> + */ + +#include <linux/config.h> + +#ifdef CONFIG_COPS_DAYNA + +unsigned char ffdrv_code[] = { + 58,3,0,50,228,149,33,255,255,34,226,149, + 249,17,40,152,33,202,154,183,237,82,77,68, + 11,107,98,19,54,0,237,176,175,50,80,0, + 62,128,237,71,62,32,237,57,51,62,12,237, + 57,50,237,57,54,62,6,237,57,52,62,12, + 237,57,49,33,107,137,34,32,128,33,83,130, + 34,40,128,33,86,130,34,42,128,33,112,130, + 34,36,128,33,211,130,34,38,128,62,0,237, + 57,16,33,63,148,34,34,128,237,94,205,15, + 130,251,205,168,145,24,141,67,111,112,121,114, + 105,103,104,116,32,40,67,41,32,49,57,56, + 56,32,45,32,68,97,121,110,97,32,67,111, + 109,109,117,110,105,99,97,116,105,111,110,115, + 32,32,32,65,108,108,32,114,105,103,104,116, + 115,32,114,101,115,101,114,118,101,100,46,32, + 32,40,68,40,68,7,16,8,34,7,22,6, + 16,5,12,4,8,3,6,140,0,16,39,128, + 0,4,96,10,224,6,0,7,126,2,64,11, + 118,12,6,13,0,14,193,15,0,5,96,3, + 192,1,64,9,8,62,9,211,66,62,192,211, + 66,62,100,61,32,253,6,28,33,205,129,14, + 66,237,163,194,253,129,6,28,33,205,129,14, + 64,237,163,194,9,130,201,62,47,50,71,152, + 62,47,211,68,58,203,129,237,57,20,58,204, + 129,237,57,21,33,77,152,54,132,205,233,129, + 58,228,149,254,209,40,6,56,4,62,0,24, + 2,219,96,33,233,149,119,230,62,33,232,149, + 119,213,33,8,152,17,7,0,25,119,19,25, + 119,209,201,251,237,77,245,197,213,229,221,229, + 205,233,129,62,1,50,106,137,205,158,139,221, + 225,225,209,193,241,251,237,77,245,197,213,219, + 72,237,56,16,230,46,237,57,16,237,56,12, + 58,72,152,183,32,26,6,20,17,128,2,237, + 56,46,187,32,35,237,56,47,186,32,29,219, + 72,230,1,32,3,5,32,232,175,50,72,152, + 229,221,229,62,1,50,106,137,205,158,139,221, + 225,225,24,25,62,1,50,72,152,58,201,129, + 237,57,12,58,202,129,237,57,13,237,56,16, + 246,17,237,57,16,209,193,241,251,237,77,245, + 197,229,213,221,229,237,56,16,230,17,237,57, + 16,237,56,20,58,34,152,246,16,246,8,211, + 68,62,6,61,32,253,58,34,152,246,8,211, + 68,58,203,129,237,57,20,58,204,129,237,57, + 21,237,56,16,246,34,237,57,16,221,225,209, + 225,193,241,251,237,77,33,2,0,57,126,230, + 3,237,100,1,40,2,246,128,230,130,245,62, + 5,211,64,241,211,64,201,229,213,243,237,56, + 16,230,46,237,57,16,237,56,12,251,70,35, + 35,126,254,175,202,77,133,254,129,202,15,133, + 230,128,194,191,132,43,58,44,152,119,33,76, + 152,119,35,62,132,119,120,254,255,40,4,58, + 49,152,119,219,72,43,43,112,17,3,0,237, + 56,52,230,248,237,57,52,219,72,230,1,194, + 141,131,209,225,237,56,52,246,6,237,57,52, + 62,1,55,251,201,62,3,211,66,62,192,211, + 66,62,48,211,66,0,0,219,66,230,1,40, + 4,219,67,24,240,205,203,135,58,75,152,254, + 255,202,128,132,58,49,152,254,161,250,207,131, + 58,34,152,211,68,62,10,211,66,62,128,211, + 66,62,11,211,66,62,6,211,66,24,0,62, + 14,211,66,62,33,211,66,62,1,211,66,62, + 64,211,66,62,3,211,66,62,209,211,66,62, + 100,71,219,66,230,1,32,6,5,32,247,195, + 248,132,219,67,71,58,44,152,184,194,248,132, + 62,100,71,219,66,230,1,32,6,5,32,247, + 195,248,132,219,67,62,100,71,219,66,230,1, + 32,6,5,32,247,195,248,132,219,67,254,133, + 32,7,62,0,50,74,152,24,17,254,173,32, + 7,62,1,50,74,152,24,6,254,141,194,248, + 132,71,209,225,58,49,152,254,132,32,10,62, + 50,205,2,134,205,144,135,24,27,254,140,32, + 15,62,110,205,2,134,62,141,184,32,5,205, + 144,135,24,8,62,10,205,2,134,205,8,134, + 62,1,50,106,137,205,158,139,237,56,52,246, + 6,237,57,52,175,183,251,201,62,20,135,237, + 57,20,175,237,57,21,237,56,16,246,2,237, + 57,16,237,56,20,95,237,56,21,123,254,10, + 48,244,237,56,16,230,17,237,57,16,209,225, + 205,144,135,62,1,50,106,137,205,158,139,237, + 56,52,246,6,237,57,52,175,183,251,201,209, + 225,243,219,72,230,1,40,13,62,10,211,66, + 0,0,219,66,230,192,202,226,132,237,56,52, + 246,6,237,57,52,62,1,55,251,201,205,203, + 135,62,1,50,106,137,205,158,139,237,56,52, + 246,6,237,57,52,183,251,201,209,225,62,1, + 50,106,137,205,158,139,237,56,52,246,6,237, + 57,52,62,2,55,251,201,209,225,243,219,72, + 230,1,202,213,132,62,10,211,66,0,0,219, + 66,230,192,194,213,132,229,62,1,50,106,137, + 42,40,152,205,65,143,225,17,3,0,205,111, + 136,62,6,211,66,58,44,152,211,66,237,56, + 52,246,6,237,57,52,183,251,201,209,197,237, + 56,52,230,248,237,57,52,219,72,230,1,32, + 15,193,225,237,56,52,246,6,237,57,52,62, + 1,55,251,201,14,23,58,37,152,254,0,40, + 14,14,2,254,1,32,5,62,140,119,24,3, + 62,132,119,43,43,197,205,203,135,193,62,1, + 211,66,62,64,211,66,62,3,211,66,62,193, + 211,66,62,100,203,39,71,219,66,230,1,32, + 6,5,32,247,195,229,133,33,238,151,219,67, + 71,58,44,152,184,194,229,133,119,62,100,71, + 219,66,230,1,32,6,5,32,247,195,229,133, + 219,67,35,119,13,32,234,193,225,62,1,50, + 106,137,205,158,139,237,56,52,246,6,237,57, + 52,175,183,251,201,33,234,151,35,35,62,255, + 119,193,225,62,1,50,106,137,205,158,139,237, + 56,52,246,6,237,57,52,175,251,201,243,61, + 32,253,251,201,62,3,211,66,62,192,211,66, + 58,49,152,254,140,32,19,197,229,213,17,181, + 129,33,185,129,1,2,0,237,176,209,225,193, + 24,27,229,213,33,187,129,58,49,152,230,15, + 87,30,2,237,92,25,17,181,129,126,18,19, + 35,126,18,209,225,58,34,152,246,8,211,68, + 58,49,152,254,165,40,14,254,164,40,10,62, + 10,211,66,62,224,211,66,24,25,58,74,152, + 254,0,40,10,62,10,211,66,62,160,211,66, + 24,8,62,10,211,66,62,128,211,66,62,11, + 211,66,62,6,211,66,205,147,143,62,5,211, + 66,62,224,211,66,62,5,211,66,62,96,211, + 66,62,5,61,32,253,62,5,211,66,62,224, + 211,66,62,14,61,32,253,62,5,211,66,62, + 233,211,66,62,128,211,66,58,181,129,61,32, + 253,62,1,211,66,62,192,211,66,1,254,19, + 237,56,46,187,32,6,13,32,247,195,226,134, + 62,192,211,66,0,0,219,66,203,119,40,250, + 219,66,203,87,40,250,243,237,56,16,230,17, + 237,57,16,237,56,20,251,62,5,211,66,62, + 224,211,66,58,182,129,61,32,253,229,33,181, + 129,58,183,129,203,63,119,35,58,184,129,119, + 225,62,10,211,66,62,224,211,66,62,11,211, + 66,62,118,211,66,62,47,211,68,62,5,211, + 66,62,233,211,66,58,181,129,61,32,253,62, + 5,211,66,62,224,211,66,58,182,129,61,32, + 253,62,5,211,66,62,96,211,66,201,229,213, + 58,50,152,230,15,87,30,2,237,92,33,187, + 129,25,17,181,129,126,18,35,19,126,18,209, + 225,58,71,152,246,8,211,68,58,50,152,254, + 165,40,14,254,164,40,10,62,10,211,66,62, + 224,211,66,24,8,62,10,211,66,62,128,211, + 66,62,11,211,66,62,6,211,66,195,248,135, + 62,3,211,66,62,192,211,66,197,229,213,17, + 181,129,33,183,129,1,2,0,237,176,209,225, + 193,62,47,211,68,62,10,211,66,62,224,211, + 66,62,11,211,66,62,118,211,66,62,1,211, + 66,62,0,211,66,205,147,143,195,16,136,62, + 3,211,66,62,192,211,66,197,229,213,17,181, + 129,33,183,129,1,2,0,237,176,209,225,193, + 62,47,211,68,62,10,211,66,62,224,211,66, + 62,11,211,66,62,118,211,66,205,147,143,62, + 5,211,66,62,224,211,66,62,5,211,66,62, + 96,211,66,62,5,61,32,253,62,5,211,66, + 62,224,211,66,62,14,61,32,253,62,5,211, + 66,62,233,211,66,62,128,211,66,58,181,129, + 61,32,253,62,1,211,66,62,192,211,66,1, + 254,19,237,56,46,187,32,6,13,32,247,195, + 88,136,62,192,211,66,0,0,219,66,203,119, + 40,250,219,66,203,87,40,250,62,5,211,66, + 62,224,211,66,58,182,129,61,32,253,62,5, + 211,66,62,96,211,66,201,197,14,67,6,0, + 62,3,211,66,62,192,211,66,62,48,211,66, + 0,0,219,66,230,1,40,4,219,67,24,240, + 62,5,211,66,62,233,211,66,62,128,211,66, + 58,181,129,61,32,253,237,163,29,62,192,211, + 66,219,66,230,4,40,250,237,163,29,32,245, + 219,66,230,4,40,250,62,255,71,219,66,230, + 4,40,3,5,32,247,219,66,230,4,40,250, + 62,5,211,66,62,224,211,66,58,182,129,61, + 32,253,62,5,211,66,62,96,211,66,58,71, + 152,254,1,202,18,137,62,16,211,66,62,56, + 211,66,62,14,211,66,62,33,211,66,62,1, + 211,66,62,248,211,66,237,56,48,246,153,230, + 207,237,57,48,62,3,211,66,62,221,211,66, + 193,201,58,71,152,211,68,62,10,211,66,62, + 128,211,66,62,11,211,66,62,6,211,66,62, + 6,211,66,58,44,152,211,66,62,16,211,66, + 62,56,211,66,62,48,211,66,0,0,62,14, + 211,66,62,33,211,66,62,1,211,66,62,248, + 211,66,237,56,48,246,145,246,8,230,207,237, + 57,48,62,3,211,66,62,221,211,66,193,201, + 44,3,1,0,70,69,1,245,197,213,229,175, + 50,72,152,237,56,16,230,46,237,57,16,237, + 56,12,62,1,211,66,0,0,219,66,95,230, + 160,32,3,195,20,139,123,230,96,194,72,139, + 62,48,211,66,62,1,211,66,62,64,211,66, + 237,91,40,152,205,207,143,25,43,55,237,82, + 218,70,139,34,42,152,98,107,58,44,152,190, + 194,210,138,35,35,62,130,190,194,200,137,62, + 1,50,48,152,62,175,190,202,82,139,62,132, + 190,32,44,50,50,152,62,47,50,71,152,229, + 175,50,106,137,42,40,152,205,65,143,225,54, + 133,43,70,58,44,152,119,43,112,17,3,0, + 62,10,205,2,134,205,111,136,195,158,138,62, + 140,190,32,19,50,50,152,58,233,149,230,4, + 202,222,138,62,1,50,71,152,195,219,137,126, + 254,160,250,185,138,254,166,242,185,138,50,50, + 152,43,126,35,229,213,33,234,149,95,22,0, + 25,126,254,132,40,18,254,140,40,14,58,50, + 152,230,15,87,126,31,21,242,65,138,56,2, + 175,119,58,50,152,230,15,87,58,233,149,230, + 62,31,21,242,85,138,218,98,138,209,225,195, + 20,139,58,50,152,33,100,137,230,15,95,22, + 0,25,126,50,71,152,209,225,58,50,152,254, + 164,250,135,138,58,73,152,254,0,40,4,54, + 173,24,2,54,133,43,70,58,44,152,119,43, + 112,17,3,0,205,70,135,175,50,106,137,205, + 208,139,58,199,129,237,57,12,58,200,129,237, + 57,13,237,56,16,246,17,237,57,16,225,209, + 193,241,251,237,77,62,129,190,194,227,138,54, + 130,43,70,58,44,152,119,43,112,17,3,0, + 205,144,135,195,20,139,35,35,126,254,132,194, + 227,138,175,50,106,137,205,158,139,24,42,58, + 201,154,254,1,40,7,62,1,50,106,137,24, + 237,58,106,137,254,1,202,222,138,62,128,166, + 194,222,138,221,229,221,33,67,152,205,127,142, + 205,109,144,221,225,225,209,193,241,251,237,77, + 58,106,137,254,1,202,44,139,58,50,152,254, + 164,250,44,139,58,73,152,238,1,50,73,152, + 221,229,221,33,51,152,205,127,142,221,225,62, + 1,50,106,137,205,158,139,195,13,139,24,208, + 24,206,24,204,230,64,40,3,195,20,139,195, + 20,139,43,126,33,8,152,119,35,58,44,152, + 119,43,237,91,35,152,205,203,135,205,158,139, + 195,13,139,175,50,78,152,62,3,211,66,62, + 192,211,66,201,197,33,4,0,57,126,35,102, + 111,62,1,50,106,137,219,72,205,141,139,193, + 201,62,1,50,78,152,34,40,152,54,0,35, + 35,54,0,195,163,139,58,78,152,183,200,229, + 33,181,129,58,183,129,119,35,58,184,129,119, + 225,62,47,211,68,62,14,211,66,62,193,211, + 66,62,10,211,66,62,224,211,66,62,11,211, + 66,62,118,211,66,195,3,140,58,78,152,183, + 200,58,71,152,211,68,254,69,40,4,254,70, + 32,17,58,73,152,254,0,40,10,62,10,211, + 66,62,160,211,66,24,8,62,10,211,66,62, + 128,211,66,62,11,211,66,62,6,211,66,62, + 6,211,66,58,44,152,211,66,62,16,211,66, + 62,56,211,66,62,48,211,66,0,0,219,66, + 230,1,40,4,219,67,24,240,62,14,211,66, + 62,33,211,66,42,40,152,205,65,143,62,1, + 211,66,62,248,211,66,237,56,48,246,145,246, + 8,230,207,237,57,48,62,3,211,66,62,221, + 211,66,201,62,16,211,66,62,56,211,66,62, + 48,211,66,0,0,219,66,230,1,40,4,219, + 67,24,240,62,14,211,66,62,33,211,66,62, + 1,211,66,62,248,211,66,237,56,48,246,153, + 230,207,237,57,48,62,3,211,66,62,221,211, + 66,201,229,213,33,234,149,95,22,0,25,126, + 254,132,40,4,254,140,32,2,175,119,123,209, + 225,201,6,8,14,0,31,48,1,12,16,250, + 121,201,33,4,0,57,94,35,86,33,2,0, + 57,126,35,102,111,221,229,34,89,152,237,83, + 91,152,221,33,63,152,205,127,142,58,81,152, + 50,82,152,58,80,152,135,50,80,152,205,162, + 140,254,3,56,16,58,81,152,135,60,230,15, + 50,81,152,175,50,80,152,24,23,58,79,152, + 205,162,140,254,3,48,13,58,81,152,203,63, + 50,81,152,62,255,50,79,152,58,81,152,50, + 82,152,58,79,152,135,50,79,152,62,32,50, + 83,152,50,84,152,237,56,16,230,17,237,57, + 16,219,72,62,192,50,93,152,62,93,50,94, + 152,58,93,152,61,50,93,152,32,9,58,94, + 152,61,50,94,152,40,44,62,170,237,57,20, + 175,237,57,21,237,56,16,246,2,237,57,16, + 219,72,230,1,202,29,141,237,56,20,71,237, + 56,21,120,254,10,48,237,237,56,16,230,17, + 237,57,16,243,62,14,211,66,62,65,211,66, + 251,58,39,152,23,23,60,50,39,152,71,58, + 82,152,160,230,15,40,22,71,14,10,219,66, + 230,16,202,186,141,219,72,230,1,202,186,141, + 13,32,239,16,235,42,89,152,237,91,91,152, + 205,47,131,48,7,61,202,186,141,195,227,141, + 221,225,33,0,0,201,221,33,55,152,205,127, + 142,58,84,152,61,50,84,152,40,19,58,82, + 152,246,1,50,82,152,58,79,152,246,1,50, + 79,152,195,29,141,221,225,33,1,0,201,221, + 33,59,152,205,127,142,58,80,152,246,1,50, + 80,152,58,82,152,135,246,1,50,82,152,58, + 83,152,61,50,83,152,194,29,141,221,225,33, + 2,0,201,221,229,33,0,0,57,17,4,0, + 25,126,50,44,152,230,128,50,85,152,58,85, + 152,183,40,6,221,33,88,2,24,4,221,33, + 150,0,58,44,152,183,40,53,60,40,50,60, + 40,47,61,61,33,86,152,119,35,119,35,54, + 129,175,50,48,152,221,43,221,229,225,124,181, + 40,42,33,86,152,17,3,0,205,189,140,17, + 232,3,27,123,178,32,251,58,48,152,183,40, + 224,58,44,152,71,62,7,128,230,127,71,58, + 85,152,176,50,44,152,24,162,221,225,201,183, + 221,52,0,192,221,52,1,192,221,52,2,192, + 221,52,3,192,55,201,245,62,1,211,100,241, + 201,245,62,1,211,96,241,201,33,2,0,57, + 126,35,102,111,237,56,48,230,175,237,57,48, + 62,48,237,57,49,125,237,57,32,124,237,57, + 33,62,0,237,57,34,62,88,237,57,35,62, + 0,237,57,36,237,57,37,33,128,2,125,237, + 57,38,124,237,57,39,237,56,48,246,97,230, + 207,237,57,48,62,0,237,57,0,62,0,211, + 96,211,100,201,33,2,0,57,126,35,102,111, + 237,56,48,230,175,237,57,48,62,12,237,57, + 49,62,76,237,57,32,62,0,237,57,33,237, + 57,34,125,237,57,35,124,237,57,36,62,0, + 237,57,37,33,128,2,125,237,57,38,124,237, + 57,39,237,56,48,246,97,230,207,237,57,48, + 62,1,211,96,201,33,2,0,57,126,35,102, + 111,229,237,56,48,230,87,237,57,48,125,237, + 57,40,124,237,57,41,62,0,237,57,42,62, + 67,237,57,43,62,0,237,57,44,58,106,137, + 254,1,32,5,33,6,0,24,3,33,128,2, + 125,237,57,46,124,237,57,47,237,56,50,230, + 252,246,2,237,57,50,225,201,33,4,0,57, + 94,35,86,33,2,0,57,126,35,102,111,237, + 56,48,230,87,237,57,48,125,237,57,40,124, + 237,57,41,62,0,237,57,42,62,67,237,57, + 43,62,0,237,57,44,123,237,57,46,122,237, + 57,47,237,56,50,230,244,246,0,237,57,50, + 237,56,48,246,145,230,207,237,57,48,201,213, + 237,56,46,95,237,56,47,87,237,56,46,111, + 237,56,47,103,183,237,82,32,235,33,128,2, + 183,237,82,209,201,213,237,56,38,95,237,56, + 39,87,237,56,38,111,237,56,39,103,183,237, + 82,32,235,33,128,2,183,237,82,209,201,245, + 197,1,52,0,237,120,230,253,237,121,193,241, + 201,245,197,1,52,0,237,120,246,2,237,121, + 193,241,201,33,2,0,57,126,35,102,111,126, + 35,110,103,201,33,0,0,34,102,152,34,96, + 152,34,98,152,33,202,154,34,104,152,237,91, + 104,152,42,226,149,183,237,82,17,0,255,25, + 34,100,152,203,124,40,6,33,0,125,34,100, + 152,42,104,152,35,35,35,229,205,120,139,193, + 201,205,186,149,229,42,40,152,35,35,35,229, + 205,39,144,193,124,230,3,103,221,117,254,221, + 116,255,237,91,42,152,35,35,35,183,237,82, + 32,12,17,5,0,42,42,152,205,171,149,242, + 169,144,42,40,152,229,205,120,139,193,195,198, + 149,237,91,42,152,42,98,152,25,34,98,152, + 19,19,19,42,102,152,25,34,102,152,237,91, + 100,152,33,158,253,25,237,91,102,152,205,171, + 149,242,214,144,33,0,0,34,102,152,62,1, + 50,95,152,205,225,144,195,198,149,58,95,152, + 183,200,237,91,96,152,42,102,152,205,171,149, + 242,5,145,237,91,102,152,33,98,2,25,237, + 91,96,152,205,171,149,250,37,145,237,91,96, + 152,42,102,152,183,237,82,32,7,42,98,152, + 125,180,40,13,237,91,102,152,42,96,152,205, + 171,149,242,58,145,237,91,104,152,42,102,152, + 25,35,35,35,229,205,120,139,193,175,50,95, + 152,201,195,107,139,205,206,149,250,255,243,205, + 225,144,251,58,230,149,183,194,198,149,17,1, + 0,42,98,152,205,171,149,250,198,149,62,1, + 50,230,149,237,91,96,152,42,104,152,25,221, + 117,252,221,116,253,237,91,104,152,42,96,152, + 25,35,35,35,221,117,254,221,116,255,35,35, + 35,229,205,39,144,124,230,3,103,35,35,35, + 221,117,250,221,116,251,235,221,110,252,221,102, + 253,115,35,114,35,54,4,62,1,211,100,211, + 84,195,198,149,33,0,0,34,102,152,34,96, + 152,34,98,152,33,202,154,34,104,152,237,91, + 104,152,42,226,149,183,237,82,17,0,255,25, + 34,100,152,33,109,152,54,0,33,107,152,229, + 205,240,142,193,62,47,50,34,152,62,132,50, + 49,152,205,241,145,205,61,145,58,39,152,60, + 50,39,152,24,241,205,206,149,251,255,33,109, + 152,126,183,202,198,149,110,221,117,251,33,109, + 152,54,0,221,126,251,254,1,40,28,254,3, + 40,101,254,4,202,190,147,254,5,202,147,147, + 254,8,40,87,33,107,152,229,205,240,142,195, + 198,149,58,201,154,183,32,21,33,111,152,126, + 50,229,149,205,52,144,33,110,152,110,38,0, + 229,205,11,142,193,237,91,96,152,42,104,152, + 25,221,117,254,221,116,255,35,35,54,2,17, + 2,0,43,43,115,35,114,58,44,152,35,35, + 119,58,228,149,35,119,62,1,211,100,211,84, + 62,1,50,201,154,24,169,205,153,142,58,231, + 149,183,40,250,175,50,231,149,33,110,152,126, + 254,255,40,91,58,233,149,230,63,183,40,83, + 94,22,0,33,234,149,25,126,183,40,13,33, + 110,152,94,33,234,150,25,126,254,3,32,36, + 205,81,148,125,180,33,110,152,94,22,0,40, + 17,33,234,149,25,54,0,33,107,152,229,205, + 240,142,193,195,198,149,33,234,150,25,54,0, + 33,110,152,94,22,0,33,234,149,25,126,50, + 49,152,254,132,32,37,62,47,50,34,152,42, + 107,152,229,33,110,152,229,205,174,140,193,193, + 125,180,33,110,152,94,22,0,33,234,150,202, + 117,147,25,52,195,120,147,58,49,152,254,140, + 32,7,62,1,50,34,152,24,210,62,32,50, + 106,152,24,19,58,49,152,95,58,106,152,163, + 183,58,106,152,32,11,203,63,50,106,152,58, + 106,152,183,32,231,254,2,40,51,254,4,40, + 38,254,8,40,26,254,16,40,13,254,32,32, + 158,62,165,50,49,152,62,69,24,190,62,164, + 50,49,152,62,70,24,181,62,163,50,49,152, + 175,24,173,62,162,50,49,152,62,1,24,164, + 62,161,50,49,152,62,3,24,155,25,54,0, + 221,126,251,254,8,40,7,58,230,149,183,202, + 32,146,33,107,152,229,205,240,142,193,211,84, + 195,198,149,237,91,96,152,42,104,152,25,221, + 117,254,221,116,255,35,35,54,6,17,2,0, + 43,43,115,35,114,58,228,149,35,35,119,58, + 233,149,35,119,205,146,142,195,32,146,237,91, + 96,152,42,104,152,25,229,205,160,142,193,58, + 231,149,183,40,250,175,50,231,149,243,237,91, + 96,152,42,104,152,25,221,117,254,221,116,255, + 78,35,70,221,113,252,221,112,253,89,80,42, + 98,152,183,237,82,34,98,152,203,124,40,19, + 33,0,0,34,98,152,34,102,152,34,96,152, + 62,1,50,95,152,24,40,221,94,252,221,86, + 253,19,19,19,42,96,152,25,34,96,152,237, + 91,100,152,33,158,253,25,237,91,96,152,205, + 171,149,242,55,148,33,0,0,34,96,152,175, + 50,230,149,251,195,32,146,245,62,1,50,231, + 149,62,16,237,57,0,211,80,241,251,237,77, + 201,205,186,149,229,229,33,0,0,34,37,152, + 33,110,152,126,50,234,151,58,44,152,33,235, + 151,119,221,54,253,0,221,54,254,0,195,230, + 148,33,236,151,54,175,33,3,0,229,33,234, + 151,229,205,174,140,193,193,33,236,151,126,254, + 255,40,74,33,245,151,110,221,117,255,33,249, + 151,126,221,166,255,221,119,255,33,253,151,126, + 221,166,255,221,119,255,58,232,149,95,221,126, + 255,163,221,119,255,183,40,15,230,191,33,110, + 152,94,22,0,33,234,149,25,119,24,12,33, + 110,152,94,22,0,33,234,149,25,54,132,33, + 0,0,195,198,149,221,110,253,221,102,254,35, + 221,117,253,221,116,254,17,32,0,221,110,253, + 221,102,254,205,171,149,250,117,148,58,233,149, + 203,87,40,84,33,1,0,34,37,152,221,54, + 253,0,221,54,254,0,24,53,33,236,151,54, + 175,33,3,0,229,33,234,151,229,205,174,140, + 193,193,33,236,151,126,254,255,40,14,33,110, + 152,94,22,0,33,234,149,25,54,140,24,159, + 221,110,253,221,102,254,35,221,117,253,221,116, + 254,17,32,0,221,110,253,221,102,254,205,171, + 149,250,12,149,33,2,0,34,37,152,221,54, + 253,0,221,54,254,0,24,54,33,236,151,54, + 175,33,3,0,229,33,234,151,229,205,174,140, + 193,193,33,236,151,126,254,255,40,15,33,110, + 152,94,22,0,33,234,149,25,54,132,195,211, + 148,221,110,253,221,102,254,35,221,117,253,221, + 116,254,17,32,0,221,110,253,221,102,254,205, + 171,149,250,96,149,33,1,0,195,198,149,124, + 170,250,179,149,237,82,201,124,230,128,237,82, + 60,201,225,253,229,221,229,221,33,0,0,221, + 57,233,221,249,221,225,253,225,201,233,225,253, + 229,221,229,221,33,0,0,221,57,94,35,86, + 35,235,57,249,235,233,0,0,0,0,0,0, + 62,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 175,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,133,1,0,0,0,63, + 255,255,255,255,0,0,0,63,0,0,0,0, + 0,0,0,0,0,0,0,24,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0 + } ; + +#endif diff --git a/drivers/net/cops_ltdrv.h b/drivers/net/cops_ltdrv.h new file mode 100644 index 000000000..33f3d9a06 --- /dev/null +++ b/drivers/net/cops_ltdrv.h @@ -0,0 +1,242 @@ +/* + * The firmware this driver downloads into the Localtalk card is a + * seperate program and is not GPL'd source code, even though the Linux + * side driver and the routine that loads this data into the card are. + * + * It is taken from the COPS SDK and is under the following license + * + * This material is licensed to you strictly for use in conjunction with + * the use of COPS LocalTalk adapters. + * There is no charge for this SDK. And no waranty express or implied + * about its fitness for any purpose. However, we will cheerefully + * refund every penny you paid for this SDK... + * Regards, + * + * Thomas F. Divine + * Chief Scientist + */ + + +/* cops_ltdrv.h: LocalTalk driver firmware dump for Linux. + * + * Authors: + * - Jay Schulist <Jay.Schulist@spacs.k12.wi.us> + */ + +#include <linux/config.h> + +#ifdef CONFIG_COPS_TANGENT + +unsigned char ltdrv_code[] = { + 58,3,0,50,148,10,33,143,15,62,85,119, + 190,32,9,62,170,119,190,32,3,35,24,241, + 34,146,10,249,17,150,10,33,143,15,183,237, + 82,77,68,11,107,98,19,54,0,237,176,62, + 16,237,57,51,62,0,237,57,50,237,57,54, + 62,12,237,57,49,62,195,33,39,2,50,56, + 0,34,57,0,237,86,205,30,2,251,205,60, + 10,24,169,67,111,112,121,114,105,103,104,116, + 32,40,99,41,32,49,57,56,56,45,49,57, + 57,50,44,32,80,114,105,110,116,105,110,103, + 32,67,111,109,109,117,110,105,99,97,116,105, + 111,110,115,32,65,115,115,111,99,105,97,116, + 101,115,44,32,73,110,99,46,65,108,108,32, + 114,105,103,104,116,115,32,114,101,115,101,114, + 118,101,100,46,32,32,4,4,22,40,255,60, + 4,96,10,224,6,0,7,126,2,64,11,246, + 12,6,13,0,14,193,15,0,5,96,3,192, + 1,0,9,8,62,3,211,82,62,192,211,82, + 201,62,3,211,82,62,213,211,82,201,62,5, + 211,82,62,224,211,82,201,62,5,211,82,62, + 224,211,82,201,62,5,211,82,62,96,211,82, + 201,6,28,33,180,1,14,82,237,163,194,4, + 2,33,39,2,34,64,0,58,3,0,230,1, + 192,62,11,237,121,62,118,237,121,201,33,182, + 10,54,132,205,253,1,201,245,197,213,229,42, + 150,10,14,83,17,98,2,67,20,237,162,58, + 179,1,95,219,82,230,1,32,6,29,32,247, + 195,17,3,62,1,211,82,219,82,95,230,160, + 32,10,237,162,32,225,21,32,222,195,15,3, + 237,162,123,230,96,194,21,3,62,48,211,82, + 62,1,211,82,175,211,82,237,91,150,10,43, + 55,237,82,218,19,3,34,152,10,98,107,58, + 154,10,190,32,81,62,1,50,158,10,35,35, + 62,132,190,32,44,54,133,43,70,58,154,10, + 119,43,112,17,3,0,205,137,3,62,16,211, + 82,62,56,211,82,205,217,1,42,150,10,14, + 83,17,98,2,67,20,58,178,1,95,195,59, + 2,62,129,190,194,227,2,54,130,43,70,58, + 154,10,119,43,112,17,3,0,205,137,3,195, + 254,2,35,35,126,254,132,194,227,2,205,61, + 3,24,20,62,128,166,194,222,2,221,229,221, + 33,175,10,205,93,6,205,144,7,221,225,225, + 209,193,241,251,237,77,221,229,221,33,159,10, + 205,93,6,221,225,205,61,3,195,247,2,24, + 237,24,235,24,233,230,64,40,2,24,227,24, + 225,175,50,179,10,205,208,1,201,197,33,4, + 0,57,126,35,102,111,205,51,3,193,201,62, + 1,50,179,10,34,150,10,54,0,58,179,10, + 183,200,62,14,211,82,62,193,211,82,62,10, + 211,82,62,224,211,82,62,6,211,82,58,154, + 10,211,82,62,16,211,82,62,56,211,82,62, + 48,211,82,219,82,230,1,40,4,219,83,24, + 242,62,14,211,82,62,33,211,82,62,1,211, + 82,62,9,211,82,62,32,211,82,205,217,1, + 201,14,83,205,208,1,24,23,14,83,205,208, + 1,205,226,1,58,174,1,61,32,253,205,244, + 1,58,174,1,61,32,253,205,226,1,58,175, + 1,61,32,253,62,5,211,82,62,233,211,82, + 62,128,211,82,58,176,1,61,32,253,237,163, + 27,62,192,211,82,219,82,230,4,40,250,237, + 163,27,122,179,32,243,219,82,230,4,40,250, + 58,178,1,71,219,82,230,4,40,3,5,32, + 247,219,82,230,4,40,250,205,235,1,58,177, + 1,61,32,253,205,244,1,201,229,213,35,35, + 126,230,128,194,145,4,43,58,154,10,119,43, + 70,33,181,10,119,43,112,17,3,0,243,62, + 10,211,82,219,82,230,128,202,41,4,209,225, + 62,1,55,251,201,205,144,3,58,180,10,254, + 255,202,127,4,205,217,1,58,178,1,71,219, + 82,230,1,32,6,5,32,247,195,173,4,219, + 83,71,58,154,10,184,194,173,4,58,178,1, + 71,219,82,230,1,32,6,5,32,247,195,173, + 4,219,83,58,178,1,71,219,82,230,1,32, + 6,5,32,247,195,173,4,219,83,254,133,194, + 173,4,58,179,1,24,4,58,179,1,135,61, + 32,253,209,225,205,137,3,205,61,3,183,251, + 201,209,225,243,62,10,211,82,219,82,230,128, + 202,164,4,62,1,55,251,201,205,144,3,205, + 61,3,183,251,201,209,225,62,2,55,251,201, + 243,62,14,211,82,62,33,211,82,251,201,33, + 4,0,57,94,35,86,33,2,0,57,126,35, + 102,111,221,229,34,193,10,237,83,195,10,221, + 33,171,10,205,93,6,58,185,10,50,186,10, + 58,184,10,135,50,184,10,205,112,6,254,3, + 56,16,58,185,10,135,60,230,15,50,185,10, + 175,50,184,10,24,23,58,183,10,205,112,6, + 254,3,48,13,58,185,10,203,63,50,185,10, + 62,255,50,183,10,58,185,10,50,186,10,58, + 183,10,135,50,183,10,62,32,50,187,10,50, + 188,10,6,255,219,82,230,16,32,3,5,32, + 247,205,180,4,6,40,219,82,230,16,40,3, + 5,32,247,62,10,211,82,219,82,230,128,194, + 46,5,219,82,230,16,40,214,237,95,71,58, + 186,10,160,230,15,40,32,71,14,10,62,10, + 211,82,219,82,230,128,202,119,5,205,180,4, + 195,156,5,219,82,230,16,202,156,5,13,32, + 229,16,225,42,193,10,237,91,195,10,205,252, + 3,48,7,61,202,156,5,195,197,5,221,225, + 33,0,0,201,221,33,163,10,205,93,6,58, + 188,10,61,50,188,10,40,19,58,186,10,246, + 1,50,186,10,58,183,10,246,1,50,183,10, + 195,46,5,221,225,33,1,0,201,221,33,167, + 10,205,93,6,58,184,10,246,1,50,184,10, + 58,186,10,135,246,1,50,186,10,58,187,10, + 61,50,187,10,194,46,5,221,225,33,2,0, + 201,221,229,33,0,0,57,17,4,0,25,126, + 50,154,10,230,128,50,189,10,58,189,10,183, + 40,6,221,33,88,2,24,4,221,33,150,0, + 58,154,10,183,40,49,60,40,46,61,33,190, + 10,119,35,119,35,54,129,175,50,158,10,221, + 43,221,229,225,124,181,40,42,33,190,10,17, + 3,0,205,206,4,17,232,3,27,123,178,32, + 251,58,158,10,183,40,224,58,154,10,71,62, + 7,128,230,127,71,58,189,10,176,50,154,10, + 24,166,221,225,201,183,221,52,0,192,221,52, + 1,192,221,52,2,192,221,52,3,192,55,201, + 6,8,14,0,31,48,1,12,16,250,121,201, + 33,2,0,57,94,35,86,35,78,35,70,35, + 126,35,102,105,79,120,68,103,237,176,201,33, + 2,0,57,126,35,102,111,62,17,237,57,48, + 125,237,57,40,124,237,57,41,62,0,237,57, + 42,62,64,237,57,43,62,0,237,57,44,33, + 128,2,125,237,57,46,124,237,57,47,62,145, + 237,57,48,211,68,58,149,10,211,66,201,33, + 2,0,57,126,35,102,111,62,33,237,57,48, + 62,64,237,57,32,62,0,237,57,33,237,57, + 34,125,237,57,35,124,237,57,36,62,0,237, + 57,37,33,128,2,125,237,57,38,124,237,57, + 39,62,97,237,57,48,211,67,58,149,10,211, + 66,201,237,56,46,95,237,56,47,87,237,56, + 46,111,237,56,47,103,183,237,82,32,235,33, + 128,2,183,237,82,201,237,56,38,95,237,56, + 39,87,237,56,38,111,237,56,39,103,183,237, + 82,32,235,33,128,2,183,237,82,201,205,106, + 10,221,110,6,221,102,7,126,35,110,103,195, + 118,10,205,106,10,33,0,0,34,205,10,34, + 198,10,34,200,10,33,143,15,34,207,10,237, + 91,207,10,42,146,10,183,237,82,17,0,255, + 25,34,203,10,203,124,40,6,33,0,125,34, + 203,10,42,207,10,229,205,37,3,195,118,10, + 205,106,10,229,42,150,10,35,35,35,229,205, + 70,7,193,124,230,3,103,221,117,254,221,116, + 255,237,91,152,10,35,35,35,183,237,82,32, + 12,17,5,0,42,152,10,205,91,10,242,203, + 7,42,150,10,229,205,37,3,195,118,10,237, + 91,152,10,42,200,10,25,34,200,10,42,205, + 10,25,34,205,10,237,91,203,10,33,158,253, + 25,237,91,205,10,205,91,10,242,245,7,33, + 0,0,34,205,10,62,1,50,197,10,205,5, + 8,33,0,0,57,249,195,118,10,205,106,10, + 58,197,10,183,202,118,10,237,91,198,10,42, + 205,10,205,91,10,242,46,8,237,91,205,10, + 33,98,2,25,237,91,198,10,205,91,10,250, + 78,8,237,91,198,10,42,205,10,183,237,82, + 32,7,42,200,10,125,180,40,13,237,91,205, + 10,42,198,10,205,91,10,242,97,8,237,91, + 207,10,42,205,10,25,229,205,37,3,175,50, + 197,10,195,118,10,205,29,3,33,0,0,57, + 249,195,118,10,205,106,10,58,202,10,183,40, + 22,205,14,7,237,91,209,10,19,19,19,205, + 91,10,242,139,8,33,1,0,195,118,10,33, + 0,0,195,118,10,205,126,10,252,255,205,108, + 8,125,180,194,118,10,237,91,200,10,33,0, + 0,205,91,10,242,118,10,237,91,207,10,42, + 198,10,25,221,117,254,221,116,255,35,35,35, + 229,205,70,7,193,124,230,3,103,35,35,35, + 221,117,252,221,116,253,229,221,110,254,221,102, + 255,229,33,212,10,229,205,124,6,193,193,221, + 110,252,221,102,253,34,209,10,33,211,10,54, + 4,33,209,10,227,205,147,6,193,62,1,50, + 202,10,243,221,94,252,221,86,253,42,200,10, + 183,237,82,34,200,10,203,124,40,17,33,0, + 0,34,200,10,34,205,10,34,198,10,50,197, + 10,24,37,221,94,252,221,86,253,42,198,10, + 25,34,198,10,237,91,203,10,33,158,253,25, + 237,91,198,10,205,91,10,242,68,9,33,0, + 0,34,198,10,205,5,8,33,0,0,57,249, + 251,195,118,10,205,106,10,33,49,13,126,183, + 40,16,205,42,7,237,91,47,13,19,19,19, + 205,91,10,242,117,9,58,142,15,198,1,50, + 142,15,195,118,10,33,49,13,126,254,1,40, + 25,254,3,202,7,10,254,5,202,21,10,33, + 49,13,54,0,33,47,13,229,205,207,6,195, + 118,10,58,141,15,183,32,72,33,51,13,126, + 50,149,10,205,86,7,33,50,13,126,230,127, + 183,32,40,58,142,15,230,127,50,142,15,183, + 32,5,198,1,50,142,15,33,50,13,126,111, + 23,159,103,203,125,58,142,15,40,5,198,128, + 50,142,15,33,50,13,119,33,50,13,126,111, + 23,159,103,229,205,237,5,193,33,211,10,54, + 2,33,2,0,34,209,10,58,154,10,33,212, + 10,119,58,148,10,33,213,10,119,33,209,10, + 229,205,147,6,193,24,128,42,47,13,229,33, + 50,13,229,205,191,4,193,24,239,33,211,10, + 54,6,33,3,0,34,209,10,58,154,10,33, + 212,10,119,58,148,10,33,213,10,119,33,214, + 10,54,5,33,209,10,229,205,147,6,24,200, + 205,106,10,33,49,13,54,0,33,47,13,229, + 205,207,6,33,209,10,227,205,147,6,193,205, + 80,9,205,145,8,24,248,124,170,250,99,10, + 237,82,201,124,230,128,237,82,60,201,225,253, + 229,221,229,221,33,0,0,221,57,233,221,249, + 221,225,253,225,201,233,225,253,229,221,229,221, + 33,0,0,221,57,94,35,86,35,235,57,249, + 235,233,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0 + } ; + +#endif diff --git a/drivers/net/ibmtr.c b/drivers/net/ibmtr.c index e0d71b202..d803e953e 100644 --- a/drivers/net/ibmtr.c +++ b/drivers/net/ibmtr.c @@ -55,7 +55,10 @@ * + completed multiple adapter support. (November 20 1996) * + implemented csum_partial_copy in tr_rx and increased receive * buffer size and count. Minor fixes. (March 15, 1997) -*/ + * + * Changes by Christopher Turcksin <wabbit@rtfc.demon.co.uk> + * + Now compiles ok as a module again. + */ #ifdef PCMCIA #define MODULE @@ -163,9 +166,9 @@ unsigned char ibmtr_debug_trace=0; int ibmtr_probe(struct device *dev); static int ibmtr_probe1(struct device *dev, int ioaddr); -unsigned char get_sram_size(struct tok_info *adapt_info); +static unsigned char get_sram_size(struct tok_info *adapt_info); static int tok_init_card(struct device *dev); -int trdev_init(struct device *dev); +static int trdev_init(struct device *dev); void tok_interrupt(int irq, void *dev_id, struct pt_regs *regs); static void initial_tok_int(struct device *dev); static void open_sap(unsigned char type,struct device *dev); @@ -1583,7 +1586,7 @@ int init_module(void) dev_ibmtr[i]->init = &ibmtr_probe; if (register_trdev(dev_ibmtr[i]) != 0) { - kfree_s(dev_ibmtr[i], sizeof(struct dev)); + kfree_s(dev_ibmtr[i], sizeof(struct device)); dev_ibmtr[i] = NULL; if (i == 0) { printk("ibmtr: register_trdev() returned non-zero.\n"); diff --git a/drivers/net/lapbether.c b/drivers/net/lapbether.c index e282847a9..5f8cb5cca 100644 --- a/drivers/net/lapbether.c +++ b/drivers/net/lapbether.c @@ -407,7 +407,7 @@ static int lapbeth_close(struct device *dev) return 0; } -__initfunc(static int lapbeth_dev_init(struct device *dev)) +static int lapbeth_dev_init(struct device *dev) { return 0; } diff --git a/drivers/net/sdla_fr.c b/drivers/net/sdla_fr.c index d2dc0eb90..95f1ae739 100644 --- a/drivers/net/sdla_fr.c +++ b/drivers/net/sdla_fr.c @@ -10,6 +10,49 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * ============================================================================ +* +* Jun 29, 1997 Alan Cox o Hacked up vendor source 1.0.3 to remove +* C++ style comments, and a massive security +* hole (the UDP management junk). +* +* May 29, 1997 Jaspreet Singh o Fixed major Flow Control Problem +* With multiple boards a problem was seen where +* the second board always stopped transmitting +* packet after running for a while. The code +* got into a stage where the interrupts were +* disabled and dev->tbusy was set to 1. +* This caused the If_send() routine to get into +* the if clause for set_bit(0,dev->tbusy) +* forever. +* The code got into this stage due to an +* interrupt occuring within the if clause for +* set_bit(0,dev->tbusy). Since an interrupt +* disables furhter transmit interrupt and +* makes dev->tbusy = 0, this effect was undone +* by making dev->tbusy = 1 in the if clause. +* The Fix checks to see if Transmit interrupts +* are disabled then do not make dev->tbusy = 1 +* Introduced a global variable: int_occur and +* added tx_int_enabled in the wan_device +* structure. +* May 21, 1997 Jaspreet Singh o Fixed UDP Management for multiple +* boards. +* +* Apr 25, 1997 Farhan Thawar o added UDP Management stuff +* o fixed bug in if_send() and tx_intr() to +* sleep and wakeup all devices +* Mar 11, 1997 Farhan Thawar Version 3.1.1 +* o fixed (+1) bug in fr508_rx_intr() +* o changed if_send() to return 0 if +* wandev.critical() is true +* o free socket buffer in if_send() if +* returning 0 +* o added tx_intr() routine +* Jan 30, 1997 Gene Kozin Version 3.1.0 +* o implemented exec() entry point +* o fixed a bug causing driver configured as +* a FR switch to be stuck in WAN_DISCONNECTED +* mode * Jan 02, 1997 Gene Kozin Initial version. *****************************************************************************/ @@ -22,12 +65,13 @@ #include <linux/errno.h> /* return codes */ #include <linux/string.h> /* inline memset(), etc. */ #include <linux/malloc.h> /* kmalloc(), kfree() */ -#include <linux/router.h> /* WAN router definitions */ +#include <linux/wanrouter.h> /* WAN router definitions */ #include <linux/wanpipe.h> /* WANPIPE common user API definitions */ #include <linux/if_arp.h> /* ARPHRD_* defines */ #include <linux/init.h> /* __initfunc et al. */ #include <asm/byteorder.h> /* htons(), etc. */ #include <asm/io.h> /* for inb(), outb(), etc. */ +#include <asm/uaccess.h> #define _GNUC_ #include <linux/sdla_fr.h> /* frame relay firmware API definitions */ @@ -67,6 +111,9 @@ typedef struct dlci_status unsigned char state PACKED; } dlci_status_t; +static char TracingEnabled; +/* variable for checking interrupts within the ISR routine */ +static int int_occur = 0; /****** Function Prototypes *************************************************/ /* WAN link driver entry points. These are called by the WAN router module. */ @@ -75,14 +122,16 @@ static int new_if (wan_device_t* wandev, struct device* dev, wanif_conf_t* conf); static int del_if (wan_device_t* wandev, struct device* dev); +/* WANPIPE-specific entry points */ +static int wpf_exec (struct sdla* card, void* u_cmd, void* u_data); + /* Network device interface */ static int if_init (struct device* dev); static int if_open (struct device* dev); static int if_close (struct device* dev); static int if_header (struct sk_buff* skb, struct device* dev, unsigned short type, void* daddr, void* saddr, unsigned len); -static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr, - struct sk_buff* skb); +static int if_rebuild_hdr (struct sk_buff* skb); static int if_send (struct sk_buff* skb, struct device* dev); static struct enet_statistics* if_stats (struct device* dev); @@ -103,6 +152,8 @@ static int fr_configure (sdla_t* card, fr_conf_t *conf); static int fr_set_intr_mode (sdla_t* card, unsigned mode, unsigned mtu); static int fr_comm_enable (sdla_t* card); static int fr_comm_disable (sdla_t* card); +static int fr_get_err_stats (sdla_t* card); +static int fr_get_stats (sdla_t* card); static int fr_add_dlci (sdla_t* card, int dlci, int num); static int fr_activate_dlci (sdla_t* card, int dlci, int num); static int fr_issue_isf (sdla_t* card, int isf); @@ -120,7 +171,6 @@ static void set_chan_state (struct device* dev, int state); static struct device* find_channel (sdla_t* card, unsigned dlci); static int is_tx_ready (sdla_t* card); static unsigned int dec_to_uint (unsigned char* str, int len); -static unsigned int hex_to_uint (unsigned char* str, int len); /****** Public Functions ****************************************************/ @@ -198,7 +248,7 @@ __initfunc(int wpf_init (sdla_t* card, wandev_conf_t* conf)) u.cfg.n392 = 3; u.cfg.n393 = 4; u.cfg.kbps = conf->bps / 1000; - u.cfg.cir_fwd = max(min(u.cfg.kbps, 512), 1); + u.cfg.cir_fwd = 16; u.cfg.cir_bwd = u.cfg.bc_fwd = u.cfg.bc_bwd = u.cfg.cir_fwd; u.cfg.options = 0x0081; /* direct Rx, no CIR check */ switch (conf->u.fr.signalling) @@ -214,7 +264,7 @@ __initfunc(int wpf_init (sdla_t* card, wandev_conf_t* conf)) { u.cfg.station = 1; /* switch emulation mode */ card->u.f.node_dlci = conf->u.fr.dlci ? conf->u.fr.dlci : 16; - card->u.f.dlci_num = max(min(conf->u.fr.dlci_num, 1), 100); + card->u.f.dlci_num = min(max(conf->u.fr.dlci_num, 1), 100); } if (conf->clocking == WANOPT_INTERNAL) u.cfg.port |= 0x0001 @@ -270,11 +320,14 @@ __initfunc(int wpf_init (sdla_t* card, wandev_conf_t* conf)) card->wandev.clocking = conf->clocking; card->wandev.station = conf->station; card->poll = &wpf_poll; + card->exec = &wpf_exec; card->wandev.update = &update; card->wandev.new_if = &new_if; card->wandev.del_if = &del_if; card->wandev.state = WAN_DISCONNECTED; - return 0; + card->wandev.udp_port = conf->udp_port; + TracingEnabled = '0'; + return 0; } /******* WAN Device Driver Entry Points *************************************/ @@ -284,9 +337,22 @@ __initfunc(int wpf_init (sdla_t* card, wandev_conf_t* conf)) */ static int update (wan_device_t* wandev) { -/* - sdla_t* card = wandev->private; -*/ + sdla_t* card; + + /* sanity checks */ + if ((wandev == NULL) || (wandev->private == NULL)) + return -EFAULT + ; + if (wandev->state == WAN_UNCONFIGURED) + return -ENODEV + ; + if (test_and_set_bit(0, (void*)&wandev->critical)) + return -EAGAIN + ; + card = wandev->private; + fr_get_err_stats(card); + fr_get_stats(card); + wandev->critical = 0; return 0; } @@ -377,6 +443,43 @@ static int del_if (wan_device_t* wandev, struct device* dev) return 0; } +/****** WANPIPE-specific entry points ***************************************/ + +/*============================================================================ + * Execute adapter interface command. + */ +static int wpf_exec (struct sdla* card, void* u_cmd, void* u_data) +{ + fr_mbox_t* mbox = card->mbox; + int retry = MAX_CMD_RETRY; + int err, len; + fr_cmd_t cmd; + + if(copy_from_user((void*)&cmd, u_cmd, sizeof(cmd))) + return -EFAULT; + /* execute command */ + do + { + memcpy(&mbox->cmd, &cmd, sizeof(cmd)); + if (cmd.length) + if(copy_from_user((void*)&mbox->data, u_data, cmd.length)) + return -EFAULT; + if (sdla_exec(mbox)) + err = mbox->cmd.result; + else + return -EIO; + } + while (err && retry-- && fr_event(card, err, mbox)); + + /* return result */ + if(copy_to_user(u_cmd, (void*)&mbox->cmd, sizeof(fr_cmd_t))) + return -EFAULT; + len = mbox->cmd.length; + if (len && u_data && copy_to_user(u_data, (void*)&mbox->data, len)) + return -EFAULT; + return 0; +} + /****** Network Device Interface ********************************************/ /*============================================================================ @@ -416,6 +519,9 @@ static int if_init (struct device* dev) dev->mem_start = wandev->maddr; dev->mem_end = wandev->maddr + wandev->msize - 1; + /* Set transmit buffer queue length */ + dev->tx_queue_len = 30; + /* Initialize socket buffers */ for (i = 0; i < DEV_NUMBUFFS; ++i) skb_queue_head_init(&dev->buffs[i]) @@ -451,12 +557,14 @@ static int if_open (struct device* dev) err = -EIO; goto done; } + wanpipe_set_state(card, WAN_CONNECTED); + if (card->wandev.station == WANOPT_CPE) { /* CPE: issue full status enquiry */ fr_issue_isf(card, FR_ISF_FSE); } - else /* Switch: activate DLCI(s) */ + else /* FR switch: activate DLCI(s) */ { fr_add_dlci(card, card->u.f.node_dlci, card->u.f.dlci_num) @@ -465,7 +573,6 @@ static int if_open (struct device* dev) card->u.f.node_dlci, card->u.f.dlci_num) ; } - wanpipe_set_state(card, WAN_CONNECTED); } dev->mtu = min(dev->mtu, card->wandev.mtu - FR_HEADER_LEN); dev->interrupt = 0; @@ -538,14 +645,13 @@ static int if_header (struct sk_buff* skb, struct device* dev, * Return: 1 physical address resolved. * 0 physical address not resolved */ -static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr, - struct sk_buff* skb) +static int if_rebuild_hdr (struct sk_buff* skb) { - fr_channel_t* chan = dev->priv; + fr_channel_t* chan = skb->dev->priv; sdla_t* card = chan->card; printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n", - card->devname, dev->name) + card->devname, skb->dev->name) ; return 1; } @@ -570,10 +676,11 @@ static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr, */ static int if_send (struct sk_buff* skb, struct device* dev) { - fr_channel_t* chan = dev->priv; - sdla_t* card = chan->card; - int retry = 0; - + fr_channel_t *chan = dev->priv; + sdla_t *card = chan->card; + int retry=0, err; + struct device *dev2; + if (test_and_set_bit(0, (void*)&card->wandev.critical)) { #ifdef _DEBUG_ @@ -581,7 +688,8 @@ static int if_send (struct sk_buff* skb, struct device* dev) card->devname) ; #endif - return 1; + dev_kfree_skb(skb, FREE_WRITE); + return 0; } if (test_and_set_bit(0, (void*)&dev->tbusy)) @@ -592,23 +700,55 @@ static int if_send (struct sk_buff* skb, struct device* dev) ; #endif ++chan->ifstats.collisions; + ++card->wandev.stats.collisions; + retry = 1; + if(card->wandev.tx_int_enabled) + { + for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) + { + dev2->tbusy = 1; + } + } + } + else if (card->wandev.state != WAN_CONNECTED) + { + ++chan->ifstats.tx_dropped; + ++card->wandev.stats.tx_dropped; + } + else if (chan->state != WAN_CONNECTED) + { + update_chan_state(dev); + ++chan->ifstats.tx_dropped; + ++card->wandev.stats.tx_dropped; + } + else if (!is_tx_ready(card)) + { + retry = 1; + if(card->wandev.tx_int_enabled) + { + for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) + { + dev2->tbusy = 1; + } + } } - else if ((card->wandev.state != WAN_CONNECTED) || - (chan->state != WAN_CONNECTED)) - ++chan->ifstats.tx_dropped - ; - else if (!is_tx_ready(card)) - retry = 1 - ; else { - int err = (card->hw.fwid == SFID_FR508) ? + err = (card->hw.fwid == SFID_FR508) ? fr508_send(card, chan->dlci, 0, skb->len, skb->data) : fr502_send(card, chan->dlci, 0, skb->len, skb->data) ; - if (err) ++chan->ifstats.tx_errors; - else ++chan->ifstats.tx_packets; + if (err) + { + ++chan->ifstats.tx_errors; + ++card->wandev.stats.tx_errors; + } + else + { + ++chan->ifstats.tx_packets; + ++card->wandev.stats.tx_packets; + } } if (!retry) { @@ -619,6 +759,7 @@ static int if_send (struct sk_buff* skb, struct device* dev) return retry; } + /*============================================================================ * Get ethernet-style interface statistics. * Return a pointer to struct enet_statistics. @@ -663,7 +804,14 @@ static void fr508_isr (sdla_t* card) { fr508_flags_t* flags = card->flags; fr_buf_ctl_t* bctl; - + + if(int_occur){ +#ifdef _DEBUG_ + printk(KERN_INFO "%s:Interrupt Occurred within an ISR\n",card->devname); +#endif + return; + } + int_occur=1; switch (flags->iflag) { case 0x01: /* receive interrupt */ @@ -681,6 +829,7 @@ static void fr508_isr (sdla_t* card) default: spur_intr(card); } + int_occur = 0; flags->iflag = 0; } @@ -690,13 +839,14 @@ static void fr508_isr (sdla_t* card) static void fr502_rx_intr (sdla_t* card) { fr_mbox_t* mbox = card->rxmb; - struct sk_buff* skb; - struct device* dev; - fr_channel_t* chan; + struct sk_buff *skb; + struct device *dev; + fr_channel_t *chan; unsigned dlci, len; void* buf; - + sdla_mapmem(&card->hw, FR502_RX_VECTOR); + dlci = mbox->cmd.dlci; len = mbox->cmd.length; @@ -741,13 +891,14 @@ static void fr502_rx_intr (sdla_t* card) /* can't decapsulate packet */ dev_kfree_skb(skb, FREE_READ); ++chan->ifstats.rx_errors; + ++card->wandev.stats.rx_errors; } else { netif_rx(skb); ++chan->ifstats.rx_packets; + ++card->wandev.stats.rx_packets; } - rx_done: sdla_mapmem(&card->hw, FR_MB_VECTOR); } @@ -763,7 +914,7 @@ static void fr508_rx_intr (sdla_t* card) fr_channel_t* chan; unsigned dlci, len, offs; void* buf; - + if (frbuf->flag != 0x01) { printk(KERN_INFO "%s: corrupted Rx buffer @ 0x%X!\n", @@ -804,9 +955,9 @@ static void fr508_rx_intr (sdla_t* card) } /* Copy data to the socket buffer */ - if ((offs + len) > card->u.f.rx_top) + if ((offs + len) > card->u.f.rx_top + 1) { - unsigned tmp = card->u.f.rx_top - offs; + unsigned tmp = card->u.f.rx_top - offs + 1; buf = skb_put(skb, tmp); sdla_peek(&card->hw, offs, buf, tmp); @@ -814,8 +965,7 @@ static void fr508_rx_intr (sdla_t* card) len -= tmp; } buf = skb_put(skb, len); - sdla_peek(&card->hw, offs, buf, len); - + sdla_peek(&card->hw, offs, buf, len); /* Decapsulate packet and pass it up the protocol stack */ skb->dev = dev; buf = skb_pull(skb, 1); /* remove hardware header */ @@ -824,13 +974,14 @@ static void fr508_rx_intr (sdla_t* card) /* can't decapsulate packet */ dev_kfree_skb(skb, FREE_READ); ++chan->ifstats.rx_errors; + ++card->wandev.stats.rx_errors; } else { netif_rx(skb); ++chan->ifstats.rx_packets; + ++card->wandev.stats.rx_packets; } - rx_done: /* Release buffer element and calculate a pointer to the next one */ frbuf->flag = 0; @@ -848,7 +999,17 @@ rx_done: */ static void tx_intr (sdla_t* card) { + struct device* dev = card->wandev.dev; + + for (; dev; dev = dev->slave) { + if( !dev || !dev->start ) continue; + dev->tbusy = 0; + dev_tint(dev); + } + card->wandev.tx_int_enabled = 0; +/* printk(KERN_INFO "%s: transmit interrupt!\n", card->devname); +*/ } /*============================================================================ @@ -877,8 +1038,14 @@ static void spur_intr (sdla_t* card) */ static void wpf_poll (sdla_t* card) { - fr502_flags_t* flags = card->flags; + static unsigned long last_poll; + fr502_flags_t* flags; + if ((jiffies - last_poll) < HZ) + return + ; + + flags = card->flags; if (flags->event) { fr_mbox_t* mbox = card->mbox; @@ -889,6 +1056,7 @@ static void wpf_poll (sdla_t* card) err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; if (err) fr_event(card, err, mbox); } + last_poll = jiffies; } /****** Frame Relay Firmware-Specific Functions *****************************/ @@ -1025,6 +1193,65 @@ static int fr_comm_disable (sdla_t* card) } /*============================================================================ + * Get communications error statistics. + */ +static int fr_get_err_stats (sdla_t* card) +{ + fr_mbox_t* mbox = card->mbox; + int retry = MAX_CMD_RETRY; + int err; + + do + { + memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); + mbox->cmd.command = FR_READ_ERROR_STATS; + err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; + } + while (err && retry-- && fr_event(card, err, mbox)); + + if (!err) + { + fr_comm_stat_t* stats = (void*)mbox->data; + + card->wandev.stats.rx_over_errors = stats->rx_overruns; + card->wandev.stats.rx_crc_errors = stats->rx_bad_crc; + card->wandev.stats.rx_missed_errors = stats->rx_aborts; + card->wandev.stats.rx_length_errors = stats->rx_too_long; + card->wandev.stats.tx_aborted_errors = stats->tx_aborts; + } + return err; +} + +/*============================================================================ + * Get statistics. + */ +static int fr_get_stats (sdla_t* card) +{ + fr_mbox_t* mbox = card->mbox; + int retry = MAX_CMD_RETRY; + int err; + + do + { + memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); + mbox->cmd.command = FR_READ_STATISTICS; + err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; + } + while (err && retry-- && fr_event(card, err, mbox)); + + if (!err) + { + fr_link_stat_t* stats = (void*)mbox->data; + + card->wandev.stats.rx_frame_errors = stats->rx_bad_format; + card->wandev.stats.rx_dropped = + stats->rx_dropped + stats->rx_dropped2 + ; + } + return err; +} + +/*============================================================================ * Add DLCI(s) (Access Node only!). */ static int fr_add_dlci (sdla_t* card, int dlci, int num) @@ -1363,6 +1590,7 @@ static int is_tx_ready (sdla_t* card) if (sb & 0x02) return 1; flags->imask |= 0x02; + card->wandev.tx_int_enabled = 1; } else { @@ -1389,27 +1617,4 @@ static unsigned int dec_to_uint (unsigned char* str, int len) return val; } -/*============================================================================ - * Convert hex string to unsigned integer. - * If len != 0 then only 'len' characters of the string are conferted. - */ -static unsigned int hex_to_uint (unsigned char* str, int len) -{ - unsigned val, ch; - - if (!len) len = strlen(str); - for (val = 0; len; ++str, --len) - { - ch = *str; - if (is_digit(ch)) - val = (val << 4) + (ch - (unsigned)'0') - ; - else if (is_hex_digit(ch)) - val = (val << 4) + ((ch & 0xDF) - (unsigned)'A' + 10) - ; - else break; - } - return val; -} - /****** End *****************************************************************/ diff --git a/drivers/net/sdla_ppp.c b/drivers/net/sdla_ppp.c index c5c5ecc55..fa636040b 100644 --- a/drivers/net/sdla_ppp.c +++ b/drivers/net/sdla_ppp.c @@ -10,6 +10,25 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * ============================================================================ +* Jun 29, 1997 Alan Cox o Dumped the idiot UDP management system. +* +* May 22, 1997 Jaspreet Singh o Added change in the PPP_SET_CONFIG command for +* 508 card to reflect changes in the new +* ppp508.sfm for supporting:continous transmission +* of Configure-Request packets without receiving a +* reply +* OR-ed 0x300 to conf_flags +* o Changed connect_tmout from 900 to 0 +* May 21, 1997 Jaspreet Singh o Fixed UDP Management for multiple boards +* Apr 25, 1997 Farhan Thawar o added UDP Management stuff +* Mar 11, 1997 Farhan Thawar Version 3.1.1 +* o fixed (+1) bug in rx_intr() +* o changed if_send() to return 0 if +* wandev.critical() is true +* o free socket buffer in if_send() if +* returning 0 +* Jan 15, 1997 Gene Kozin Version 3.1.0 +* o implemented exec() entry point * Jan 06, 1997 Gene Kozin Initial version. *****************************************************************************/ @@ -22,11 +41,12 @@ #include <linux/errno.h> /* return codes */ #include <linux/string.h> /* inline memset(), etc. */ #include <linux/malloc.h> /* kmalloc(), kfree() */ -#include <linux/router.h> /* WAN router definitions */ +#include <linux/wanrouter.h> /* WAN router definitions */ #include <linux/wanpipe.h> /* WANPIPE common user API definitions */ #include <linux/if_arp.h> /* ARPHRD_* defines */ #include <linux/init.h> /* __initfunc et al. */ #include <asm/byteorder.h> /* htons(), etc. */ +#include <asm/uaccess.h> #define _GNUC_ #include <linux/sdla_ppp.h> /* PPP firmware API definitions */ @@ -57,14 +77,16 @@ static int new_if (wan_device_t* wandev, struct device* dev, wanif_conf_t* conf); static int del_if (wan_device_t* wandev, struct device* dev); +/* WANPIPE-specific entry points */ +static int wpp_exec (struct sdla* card, void* u_cmd, void* u_data); + /* Network device interface */ static int if_init (struct device* dev); static int if_open (struct device* dev); static int if_close (struct device* dev); static int if_header (struct sk_buff* skb, struct device* dev, unsigned short type, void* daddr, void* saddr, unsigned len); -static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr, - struct sk_buff* skb); +static int if_rebuild_hdr (struct sk_buff* skb); static int if_send (struct sk_buff* skb, struct device* dev); static struct enet_statistics* if_stats (struct device* dev); @@ -74,6 +96,7 @@ static int ppp_configure (sdla_t* card, void* data); static int ppp_set_intr_mode (sdla_t* card, unsigned mode); static int ppp_comm_enable (sdla_t* card); static int ppp_comm_disable (sdla_t* card); +static int ppp_get_err_stats (sdla_t* card); static int ppp_send (sdla_t* card, void* data, unsigned len, unsigned proto); static int ppp_error (sdla_t *card, int err, ppp_mbox_t* mb); @@ -94,6 +117,7 @@ static int config508 (sdla_t* card); static void show_disc_cause (sdla_t* card, unsigned cause); static unsigned char bps_to_speed_code (unsigned long bps); +static char TracingEnabled; /****** Public Functions ****************************************************/ /*============================================================================ @@ -163,10 +187,13 @@ __initfunc(int wpp_init (sdla_t* card, wandev_conf_t* conf)) card->wandev.station = conf->station; card->isr = &wpp_isr; card->poll = &wpp_poll; + card->exec = &wpp_exec; card->wandev.update = &update; card->wandev.new_if = &new_if; card->wandev.del_if = &del_if; card->wandev.state = WAN_DISCONNECTED; + card->wandev.udp_port = conf->udp_port; + TracingEnabled = '0'; return 0; } @@ -177,9 +204,22 @@ __initfunc(int wpp_init (sdla_t* card, wandev_conf_t* conf)) */ static int update (wan_device_t* wandev) { -/* - sdla_t* card = wandev->private; -*/ + sdla_t* card; + + /* sanity checks */ + if ((wandev == NULL) || (wandev->private == NULL)) + return -EFAULT + ; + if (wandev->state == WAN_UNCONFIGURED) + return -ENODEV + ; + if (test_and_set_bit(0, (void*)&wandev->critical)) + return -EAGAIN + ; + card = wandev->private; + + ppp_get_err_stats(card); + wandev->critical = 0; return 0; } @@ -228,6 +268,38 @@ static int del_if (wan_device_t* wandev, struct device* dev) return 0; } +/****** WANPIPE-specific entry points ***************************************/ + +/*============================================================================ + * Execute adapter interface command. + */ +static int wpp_exec (struct sdla* card, void* u_cmd, void* u_data) +{ + ppp_mbox_t* mbox = card->mbox; + int len; + + if(copy_from_user((void*)&mbox->cmd, u_cmd, sizeof(ppp_cmd_t))) + return -EFAULT; + len = mbox->cmd.length; + if (len) + { + if(copy_from_user((void*)&mbox->data, u_data, len)) + return -EFAULT; + } + + /* execute command */ + if (!sdla_exec(mbox)) + return -EIO; + + /* return result */ + if(copy_to_user(u_cmd, (void*)&mbox->cmd, sizeof(ppp_cmd_t))) + return -EFAULT; + len = mbox->cmd.length; + if (len && u_data && copy_to_user(u_data, (void*)&mbox->data, len)) + return -EFAULT; + return 0; +} + /****** Network Device Interface ********************************************/ /*============================================================================ @@ -264,6 +336,9 @@ static int if_init (struct device* dev) dev->mem_start = wandev->maddr; dev->mem_end = wandev->maddr + wandev->msize - 1; + /* Set transmit buffer queue length */ + dev->tx_queue_len = 30; + /* Initialize socket buffers */ for (i = 0; i < DEV_NUMBUFFS; ++i) skb_queue_head_init(&dev->buffs[i]) @@ -408,13 +483,12 @@ static int if_header (struct sk_buff* skb, struct device* dev, * Return: 1 physical address resolved. * 0 physical address not resolved */ -static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr, - struct sk_buff* skb) +static int if_rebuild_hdr (struct sk_buff* skb) { - sdla_t* card = dev->priv; + sdla_t* card = skb->dev->priv; printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n", - card->devname, dev->name) + card->devname, skb->dev->name) ; return 1; } @@ -460,8 +534,7 @@ static int if_send (struct sk_buff* skb, struct device* dev) #endif ++card->wandev.stats.collisions; retry = 1; - } - else if (card->wandev.state != WAN_CONNECTED) + } else if (card->wandev.state != WAN_CONNECTED) ++card->wandev.stats.tx_dropped ; else if (!skb->protocol) @@ -489,6 +562,7 @@ static int if_send (struct sk_buff* skb, struct device* dev) * Get ethernet-style interface statistics. * Return a pointer to struct enet_statistics. */ + static struct enet_statistics* if_stats (struct device* dev) { sdla_t* card = dev->priv; @@ -599,6 +673,31 @@ static int ppp_comm_disable (sdla_t* card) } /*============================================================================ + * Get communications error statistics. + */ +static int ppp_get_err_stats (sdla_t* card) +{ + ppp_mbox_t* mb = card->mbox; + int err; + + memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); + mb->cmd.command = PPP_READ_ERROR_STATS; + err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; + if (err == CMD_OK) + { + ppp_err_stats_t* stats = (void*)mb->data; + + card->wandev.stats.rx_over_errors = stats->rx_overrun; + card->wandev.stats.rx_crc_errors = stats->rx_bad_crc; + card->wandev.stats.rx_missed_errors = stats->rx_abort; + card->wandev.stats.rx_length_errors = stats->rx_lost; + card->wandev.stats.tx_aborted_errors = stats->tx_abort; + } + else ppp_error(card, err, mb); + return err; +} + +/*============================================================================ * Send packet. * Return: 0 - o.k. * 1 - no transmit buffers available @@ -701,7 +800,7 @@ static void rx_intr (sdla_t* card) struct sk_buff* skb; unsigned len; void* buf; - + if (rxbuf->flag != 0x01) { printk(KERN_INFO "%s: corrupted Rx buffer @ 0x%X!\n", @@ -738,9 +837,9 @@ static void rx_intr (sdla_t* card) { unsigned addr = rxbuf->buf.ptr; - if ((addr + len) > card->u.p.rx_top) + if ((addr + len) > card->u.p.rx_top + 1) { - unsigned tmp = card->u.p.rx_top - addr; + unsigned tmp = card->u.p.rx_top - addr + 1; buf = skb_put(skb, tmp); sdla_peek(&card->hw, addr, buf, tmp); @@ -751,8 +850,8 @@ static void rx_intr (sdla_t* card) sdla_peek(&card->hw, addr, buf, len); } - /* Decapsulate packet and pass it up the protocol stack */ - switch (rxbuf->proto) + /* Decapsulate packet */ + switch (rxbuf->proto) { case 0x00: skb->protocol = htons(ETH_P_IP); @@ -762,10 +861,11 @@ static void rx_intr (sdla_t* card) skb->protocol = htons(ETH_P_IPX); break; } + + /* Pass it up the protocol stack */ skb->dev = dev; netif_rx(skb); ++card->wandev.stats.rx_packets; - rx_done: /* Release buffer element and calculate a pointer to the next one */ rxbuf->flag = (card->hw.fwid == SFID_PPP502) ? 0xFF : 0x00; @@ -891,13 +991,14 @@ static int config502 (sdla_t* card) cfg.auth_wait_tmr = 300; cfg.mdm_fail_tmr = 5; cfg.dtr_drop_tmr = 1; - cfg.connect_tmout = 900; + cfg.connect_tmout = 0; /* changed it from 900 */ cfg.conf_retry = 10; cfg.term_retry = 2; cfg.fail_retry = 5; cfg.auth_retry = 10; cfg.ip_options = 0x80; cfg.ipx_options = 0xA0; + cfg.conf_flags |= 0x0E; /* cfg.ip_local = dev->pa_addr; cfg.ip_remote = dev->pa_dstaddr; @@ -921,6 +1022,7 @@ static int config508 (sdla_t* card) if (card->wandev.interface == WANOPT_RS232) cfg.conf_flags |= 0x0020; ; + cfg.conf_flags |= 0x300; /*send Configure-Request packets forever*/ cfg.txbuf_percent = 60; /* % of Tx bufs */ cfg.mtu_local = card->wandev.mtu; cfg.mtu_remote = card->wandev.mtu; @@ -929,7 +1031,7 @@ static int config508 (sdla_t* card) cfg.auth_wait_tmr = 300; cfg.mdm_fail_tmr = 5; cfg.dtr_drop_tmr = 1; - cfg.connect_tmout = 900; + cfg.connect_tmout = 0; /* changed it from 900 */ cfg.conf_retry = 10; cfg.term_retry = 2; cfg.fail_retry = 5; diff --git a/drivers/net/sdla_x25.c b/drivers/net/sdla_x25.c index 3296c2819..77dfc43b0 100644 --- a/drivers/net/sdla_x25.c +++ b/drivers/net/sdla_x25.c @@ -10,6 +10,17 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * ============================================================================ +* Mar 11, 1997 Farhan Thawar Version 3.1.1 +* o added support for V35 +* o changed if_send() to return 0 if +* wandev.critical() is true +* o free socket buffer in if_send() if +* returning 0 +* o added support for single '@' address to +* accept all incoming calls +* o fixed bug in set_chan_state() to disconnect +* Jan 15, 1997 Gene Kozin Version 3.1.0 +* o implemented exec() entry point * Jan 07, 1997 Gene Kozin Initial version. *****************************************************************************/ @@ -22,10 +33,11 @@ #include <linux/errno.h> /* return codes */ #include <linux/string.h> /* inline memset(), etc. */ #include <linux/malloc.h> /* kmalloc(), kfree() */ -#include <linux/router.h> /* WAN router definitions */ +#include <linux/wanrouter.h> /* WAN router definitions */ #include <linux/wanpipe.h> /* WANPIPE common user API definitions */ #include <linux/init.h> /* __initfunc et al. */ #include <asm/byteorder.h> /* htons(), etc. */ +#include <asm/uaccess.h> /* copy_from_user, etc */ #define _GNUC_ #include <linux/sdla_x25.h> /* X.25 firmware API definitions */ @@ -90,14 +102,16 @@ static int new_if (wan_device_t* wandev, struct device* dev, wanif_conf_t* conf); static int del_if (wan_device_t* wandev, struct device* dev); +/* WANPIPE-specific entry points */ +static int wpx_exec (struct sdla* card, void* u_cmd, void* u_data); + /* Network device interface */ static int if_init (struct device* dev); static int if_open (struct device* dev); static int if_close (struct device* dev); static int if_header (struct sk_buff* skb, struct device* dev, unsigned short type, void* daddr, void* saddr, unsigned len); -static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr, - struct sk_buff* skb); +static int if_rebuild_hdr (struct sk_buff* skb); static int if_send (struct sk_buff* skb, struct device* dev); static struct enet_statistics* if_stats (struct device* dev); @@ -118,6 +132,8 @@ static void poll_active (sdla_t* card); /* X.25 firmware interface functions */ static int x25_get_version (sdla_t* card, char* str); static int x25_configure (sdla_t* card, TX25Config* conf); +static int x25_get_err_stats (sdla_t* card); +static int x25_get_stats (sdla_t* card); static int x25_set_intr_mode (sdla_t* card, int mode); static int x25_close_hdlc (sdla_t* card); static int x25_open_hdlc (sdla_t* card); @@ -235,7 +251,9 @@ __initfunc(int wpx_init (sdla_t* card, wandev_conf_t* conf)) { u.cfg.station = 0; /* DCE mode */ } - + if (conf->interface != WANOPT_RS232 ) { + u.cfg.hdlcOptions |= 0x80; /* V35 mode */ + } /* adjust MTU */ if (!conf->mtu || (conf->mtu >= 1024)) card->wandev.mtu = 1024 @@ -299,6 +317,7 @@ __initfunc(int wpx_init (sdla_t* card, wandev_conf_t* conf)) card->wandev.station = conf->station; card->isr = &wpx_isr; card->poll = &wpx_poll; + card->exec = &wpx_exec; card->wandev.update = &update; card->wandev.new_if = &new_if; card->wandev.del_if = &del_if; @@ -313,9 +332,23 @@ __initfunc(int wpx_init (sdla_t* card, wandev_conf_t* conf)) */ static int update (wan_device_t* wandev) { -/* - sdla_t* card = wandev->private; -*/ + sdla_t* card; + + /* sanity checks */ + if ((wandev == NULL) || (wandev->private == NULL)) + return -EFAULT + ; + if (wandev->state == WAN_UNCONFIGURED) + return -ENODEV + ; + if (test_and_set_bit(0, (void*)&wandev->critical)) + return -EAGAIN + ; + card = wandev->private; + + x25_get_err_stats(card); + x25_get_stats(card); + wandev->critical = 0; return 0; } @@ -412,6 +445,45 @@ static int del_if (wan_device_t* wandev, struct device* dev) return 0; } +/****** WANPIPE-specific entry points ***************************************/ + +/*============================================================================ + * Execute adapter interface command. + */ +static int wpx_exec (struct sdla* card, void* u_cmd, void* u_data) +{ + TX25Mbox* mbox = card->mbox; + int retry = MAX_CMD_RETRY; + int err, len; + TX25Cmd cmd; + + if(copy_from_user((void*)&cmd, u_cmd, sizeof(cmd))) + return -EFAULT; + + /* execute command */ + do + { + memcpy(&mbox->cmd, &cmd, sizeof(cmd)); + if (cmd.length) + if(copy_from_user((void*)&mbox->data, u_data, cmd.length)) + return -EFAULT; + if (sdla_exec(mbox)) + err = mbox->cmd.result; + else + return -EIO; + } + while (err && retry-- && x25_error(card, err, cmd.command, cmd.lcn)); + + /* return result */ + if(copy_to_user(u_cmd, (void*)&mbox->cmd, sizeof(TX25Cmd))) + return -EFAULT; + len = mbox->cmd.length; + if (len && u_data) + if(copy_to_user(u_data, (void*)&mbox->data, len)) + return -EFAULT; + return 0; +} + /****** Network Device Interface ********************************************/ /*============================================================================ @@ -555,14 +627,13 @@ static int if_header (struct sk_buff* skb, struct device* dev, * Return: 1 physical address resolved. * 0 physical address not resolved */ -static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr, - struct sk_buff* skb) +static int if_rebuild_hdr (struct sk_buff* skb) { - x25_channel_t* chan = dev->priv; + x25_channel_t* chan = skb->dev->priv; sdla_t* card = chan->card; printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n", - card->devname, dev->name) + card->devname, skb->dev->name) ; return 1; } @@ -588,7 +659,7 @@ static int if_send (struct sk_buff* skb, struct device* dev) { x25_channel_t* chan = dev->priv; sdla_t* card = chan->card; - int retry = 0, queued = 0; + int queued = 0; if (test_and_set_bit(0, (void*)&card->wandev.critical)) { @@ -597,7 +668,8 @@ static int if_send (struct sk_buff* skb, struct device* dev) card->devname) ; #endif - return 1; + dev_kfree_skb(skb, FREE_WRITE); + return 0; } if (test_and_set_bit(0, (void*)&dev->tbusy)) @@ -608,7 +680,10 @@ static int if_send (struct sk_buff* skb, struct device* dev) ; #endif ++chan->ifstats.collisions; - retry = 1; + ++card->wandev.stats.collisions; + dev_kfree_skb(skb, FREE_WRITE); + card->wandev.critical = 0; + return 0; } else if (chan->protocol && (chan->protocol != skb->protocol)) { @@ -646,13 +721,13 @@ static int if_send (struct sk_buff* skb, struct device* dev) ++chan->ifstats.tx_dropped; } - if (!retry && !queued) + if (!queued) { dev_kfree_skb(skb, FREE_WRITE); dev->tbusy = 0; } card->wandev.critical = 0; - return retry; + return 0; } /*============================================================================ @@ -996,6 +1071,62 @@ static int x25_configure (sdla_t* card, TX25Config* conf) } /*============================================================================ + * Get communications error statistics. + */ +static int x25_get_err_stats (sdla_t* card) +{ + TX25Mbox* mbox = card->mbox; + int retry = MAX_CMD_RETRY; + int err; + + do + { + memset(&mbox->cmd, 0, sizeof(TX25Cmd)); + mbox->cmd.command = X25_HDLC_READ_COMM_ERR; + err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; + } while (err && retry-- && + x25_error(card, err, X25_HDLC_READ_COMM_ERR, 0)) + ; + if (!err) + { + THdlcCommErr* stats = (void*)mbox->data; + + card->wandev.stats.rx_over_errors = stats->rxOverrun; + card->wandev.stats.rx_crc_errors = stats->rxBadCrc; + card->wandev.stats.rx_missed_errors = stats->rxAborted; + card->wandev.stats.tx_aborted_errors = stats->txAborted; + } + return err; +} + +/*============================================================================ + * Get protocol statistics. + */ +static int x25_get_stats (sdla_t* card) +{ + TX25Mbox* mbox = card->mbox; + int retry = MAX_CMD_RETRY; + int err; + + do + { + memset(&mbox->cmd, 0, sizeof(TX25Cmd)); + mbox->cmd.command = X25_READ_STATISTICS; + err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; + } while (err && retry-- && + x25_error(card, err, X25_READ_STATISTICS, 0)) + ; + if (!err) + { + TX25Stats* stats = (void*)mbox->data; + + card->wandev.stats.rx_packets = stats->rxData; + card->wandev.stats.tx_packets = stats->rxData; + } + return err; +} + +/*============================================================================ * Close HDLC link. */ static int x25_close_hdlc (sdla_t* card) @@ -1478,6 +1609,10 @@ static int incomming_call (sdla_t* card, int cmd, int lcn, TX25Mbox* mb) if (strcmp(info->src, chan->addr) == 0) break ; + // If just an '@' is specified, accept all incomming calls + if (strcmp(chan->addr, "") == 0) + break + ; } if (dev == NULL) @@ -1755,9 +1890,10 @@ static void set_chan_state (struct device* dev, int state) printk (KERN_INFO "%s: interface %s disconnected!\n", card->devname, dev->name) ; - if (chan->svc) - *(unsigned short*)dev->dev_addr = 0 - ; + if (chan->svc) { + *(unsigned short*)dev->dev_addr = 0; + chan->lcn = 0; + } break; } chan->state = state; diff --git a/drivers/net/sdlamain.c b/drivers/net/sdlamain.c index 75d7df944..eeb0b40f3 100644 --- a/drivers/net/sdlamain.c +++ b/drivers/net/sdlamain.c @@ -26,9 +26,10 @@ #include <linux/module.h> /* support for loadable modules */ #include <linux/ioport.h> /* request_region(), release_region() */ #include <linux/tqueue.h> /* for kernel task queues */ -#include <linux/router.h> /* WAN router definitions */ +#include <linux/wanrouter.h> /* WAN router definitions */ #include <linux/wanpipe.h> /* WANPIPE common user API definitions */ -#include <asm/segment.h> /* kernel <-> user copy */ +#include <asm/uaccess.h> /* kernel <-> user copy */ + /****** Defines & Macros ****************************************************/ @@ -404,20 +405,13 @@ static int ioctl_dump (sdla_t* card, sdla_dump_t* u_dump) unsigned long oldvec; /* DPM window vector */ int err = 0; - if ((u_dump == NULL) || - verify_area(VERIFY_READ, u_dump, sizeof(sdla_dump_t))) - return -EFAULT - ; - memcpy_fromfs((void*)&dump, (void*)u_dump, sizeof(sdla_dump_t)); + if(copy_from_user((void*)&dump, (void*)u_dump, sizeof(sdla_dump_t))) + return -EFAULT; + if ((dump.magic != WANPIPE_MAGIC) || (dump.offset + dump.length > card->hw.memory)) - return -EINVAL - ; - if ((dump.ptr == NULL) || - verify_area(VERIFY_WRITE, dump.ptr, dump.length)) - return -EFAULT - ; - + return -EINVAL; + winsize = card->hw.dpmsize; cli(); /* >>> critical section start <<< */ oldvec = card->hw.vector; @@ -433,9 +427,12 @@ static int ioctl_dump (sdla_t* card, sdla_dump_t* u_dump) err = -EIO; break; } - memcpy_tofs((void*)(dump.ptr), - (void*)(card->hw.dpmbase + pos), len) - ; + /* FIXME::: COPY TO KERNEL BUFFER FIRST ?? */ + sti(); /* Not ideal but tough we have to do this */ + if(copy_to_user((void*)(dump.ptr), + (void*)(card->hw.dpmbase + pos), len)) + return -EFAULT; + cli(); dump.length -= len; dump.offset += len; (char*)dump.ptr += len; @@ -456,16 +453,12 @@ static int ioctl_exec (sdla_t* card, sdla_exec_t* u_exec) sdla_exec_t exec; if (card->exec == NULL) - return -ENODEV - ; - if ((u_exec == NULL) || - verify_area(VERIFY_READ, u_exec, sizeof(sdla_exec_t))) - return -EFAULT - ; - memcpy_fromfs((void*)&exec, (void*)u_exec, sizeof(sdla_exec_t)); + return -ENODEV; + + if(copy_from_user((void*)&exec, (void*)u_exec, sizeof(sdla_exec_t))) + return -EFAULT; if ((exec.magic != WANPIPE_MAGIC) || (exec.cmd == NULL)) - return -EINVAL - ; + return -EINVAL; return card->exec(card, exec.cmd, exec.data); } diff --git a/drivers/net/shaper.c b/drivers/net/shaper.c index a40f16599..fff408c42 100644 --- a/drivers/net/shaper.c +++ b/drivers/net/shaper.c @@ -70,11 +70,11 @@ #include <linux/if_arp.h> #include <linux/init.h> #include <net/dst.h> -#include "shaper.h" +#include <linux/if_shaper.h> int sh_debug; /* Debug flag */ -#define SHAPER_BANNER "CymruNet Traffic Shaper BETA 0.03 for Linux 2.1\n" +#define SHAPER_BANNER "CymruNet Traffic Shaper BETA 0.04 for Linux 2.1\n" /* * Locking @@ -426,17 +426,26 @@ static int shaper_header(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len) { struct shaper *sh=dev->priv; + int v; if(sh_debug) printk("Shaper header\n"); - return sh->hard_header(skb,sh->dev,type,daddr,saddr,len); + skb->dev=sh->dev; + v=sh->hard_header(skb,sh->dev,type,daddr,saddr,len); + skb->dev=dev; + return v; } static int shaper_rebuild_header(struct sk_buff *skb) { struct shaper *sh=skb->dev->priv; + struct device *dev=skb->dev; + int v; if(sh_debug) printk("Shaper rebuild header\n"); - return sh->rebuild_header(skb); + skb->dev=sh->dev; + v=sh->rebuild_header(skb); + skb->dev=dev; + return v; } static int shaper_cache(struct dst_entry *dst, struct neighbour *neigh, struct hh_cache *hh) @@ -483,6 +492,7 @@ static int shaper_attach(struct device *shdev, struct shaper *sh, struct device else shdev->rebuild_header = NULL; +#if 0 if(dev->hard_header_cache) { sh->hard_header_cache = dev->hard_header_cache; @@ -500,6 +510,10 @@ static int shaper_attach(struct device *shdev, struct shaper *sh, struct device } else shdev->header_cache_update= NULL; +#else + shdev->header_cache_update = NULL; + shdev->hard_header_cache = NULL; +#endif shdev->hard_header_len=dev->hard_header_len; shdev->type=dev->type; diff --git a/drivers/net/strip.c b/drivers/net/strip.c index 32c02b4f0..0104f6dc9 100644 --- a/drivers/net/strip.c +++ b/drivers/net/strip.c @@ -14,53 +14,62 @@ * for kernel-based devices like TTY. It interfaces between a * raw TTY, and the kernel's INET protocol layers (via DDI). * - * Version: @(#)strip.c 0.9.8 June 1996 + * Version: @(#)strip.c 1.2 February 1997 * * Author: Stuart Cheshire <cheshire@cs.stanford.edu> * - * Fixes: v0.9 12th Feb 1996. + * Fixes: v0.9 12th Feb 1996 (SC) * New byte stuffing (2+6 run-length encoding) * New watchdog timer task * New Protocol key (SIP0) * - * v0.9.1 3rd March 1996 + * v0.9.1 3rd March 1996 (SC) * Changed to dynamic device allocation -- no more compile * time (or boot time) limit on the number of STRIP devices. * - * v0.9.2 13th March 1996 + * v0.9.2 13th March 1996 (SC) * Uses arp cache lookups (but doesn't send arp packets yet) * - * v0.9.3 17th April 1996 + * v0.9.3 17th April 1996 (SC) * Fixed bug where STR_ERROR flag was getting set unneccessarily + * (causing otherwise good packets to be unneccessarily dropped) * - * v0.9.4 27th April 1996 + * v0.9.4 27th April 1996 (SC) * First attempt at using "&COMMAND" Starmode AT commands * - * v0.9.5 29th May 1996 + * v0.9.5 29th May 1996 (SC) * First attempt at sending (unicast) ARP packets * - * v0.9.6 5th June 1996 - * Elliot put "message level" tags in every "printk" statement + * v0.9.6 5th June 1996 (Elliot) + * Put "message level" tags in every "printk" statement * - * v0.9.7 13th June 1996 - * Added support for the /proc fs (laik) + * v0.9.7 13th June 1996 (laik) + * Added support for the /proc fs * - * v0.9.8 July 1996 - * Added packet logging (Mema) - */ - -/* - * Undefine this symbol if you don't have PROC_NET_STRIP_STATUS - * defined in include/linux/proc_fs.h + * v0.9.8 July 1996 (Mema) + * Added packet logging + * + * v1.0 November 1996 (SC) + * Fixed (severe) memory leaks in the /proc fs code + * Fixed race conditions in the logging code + * + * v1.1 January 1997 (SC) + * Deleted packet logging (use tcpdump instead) + * Added support for Metricom Firmware v204 features + * (like message checksums) + * + * v1.2 January 1997 (SC) + * Put portables list back in */ -#define DO_PROC_NET_STRIP_STATUS 1 - -/* - * Define this symbol if you want to enable STRIP packet tracing. - */ +#ifdef MODULE +static const char StripVersion[] = "1.2-STUART.CHESHIRE-MODULAR"; +#else +static const char StripVersion[] = "1.2-STUART.CHESHIRE"; +#endif -#define DO_PROC_NET_STRIP_TRACE 0 +#define TICKLE_TIMERS 0 +#define EXT_COUNTERS 1 /************************************************************************/ @@ -146,13 +155,19 @@ typedef struct __u8 c[24]; } MetricomAddressString; +/* Encapsulation can expand packet of size x to 65/64x + 1 + * Sent packet looks like "<CR>*<address>*<key><encaps payload><CR>" + * 1 1 1-18 1 4 ? 1 + * eg. <CR>*0000-1234*SIP0<encaps payload><CR> + * We allow 31 bytes for the stars, the key, the address and the <CR>s + */ +#define STRIP_ENCAP_SIZE(X) (32 + (X)*65L/64L) + /* - * Note: A Metricom packet looks like this: *<address>*<key><payload><CR> - * eg. *0000-1234*SIP0<payload><CR> - * A STRIP_Header is never really sent over the radio, but making a dummy header - * for internal use within the kernel that looks like an Ethernet header makes - * certain other software happier. For example, tcpdump already understands - * Ethernet headers. + * A STRIP_Header is never really sent over the radio, but making a dummy + * header for internal use within the kernel that looks like an Ethernet + * header makes certain other software happier. For example, tcpdump + * already understands Ethernet headers. */ typedef struct @@ -162,83 +177,20 @@ typedef struct unsigned short protocol; /* The protocol type, using Ethernet codes */ } STRIP_Header; -typedef struct GeographicLocation -{ - char s[18]; -} GeographicLocation; - -typedef enum { - NodeValid = 0x1, - NodeHasWAN = 0x2, - NodeIsRouter = 0x4 -} NodeType; - -typedef struct MetricomNode -{ - NodeType type; /* Some flags about the type of node */ - GeographicLocation gl; /* The location of the node. */ - MetricomAddress addr; /* The metricom address of this node */ - int poll_latency; /* The latency to poll that node ? */ - int rssi; /* The Receiver Signal Strength Indicator */ - struct MetricomNode *next; /* The next node */ -} MetricomNode; - -enum { FALSE = 0, TRUE = 1 }; - -/* - * Holds the packet signature for an IP packet. - */ typedef struct { - IPaddr src; - /* Data is stored in the following field in network byte order. */ - __u16 id; -} IPSignature; + char c[60]; +} MetricomNode; -/* - * Holds the packet signature for an ARP packet. - */ +#define NODE_TABLE_SIZE 32 typedef struct { - IPaddr src; - /* Data is stored in the following field in network byte order. */ - __u16 op; -} ARPSignature; - -/* - * Holds the signature of a packet. - */ -typedef union -{ - IPSignature ip_sig; - ARPSignature arp_sig; - __u8 print_sig[6]; -} PacketSignature; - -typedef enum { - EntrySend = 0, - EntryReceive = 1 -} LogEntry; - -/* Structure for Packet Logging */ -typedef struct stripLog -{ - LogEntry entry_type; - u_long seqNum; - int packet_type; - PacketSignature sig; - MetricomAddress src; - MetricomAddress dest; - struct timeval timeStamp; - u_long rawSize; - u_long stripSize; - u_long slipSize; - u_long valid; -} StripLog; + struct timeval timestamp; + int num_nodes; + MetricomNode node[NODE_TABLE_SIZE]; +} MetricomNodeTable; -#define ENTRY_TYPE_TO_STRING(X) ((X) ? "r" : "s") - -#define BOOLEAN_TO_STRING(X) ((X) ? "true" : "false") +enum { FALSE = 0, TRUE = 1 }; /* * Holds the radio's firmware version. @@ -246,7 +198,7 @@ typedef struct stripLog typedef struct { char c[50]; -} MetricomFirmwareVersion; +} FirmwareVersion; /* * Holds the radio's serial number. @@ -254,7 +206,7 @@ typedef struct typedef struct { char c[18]; -} MetricomSerialNumber; +} SerialNumber; /* * Holds the radio's battery voltage. @@ -262,7 +214,19 @@ typedef struct typedef struct { char c[11]; -} MetricomBatteryVoltage; +} BatteryVoltage; + +typedef struct +{ + char c[8]; +} char8; + +enum +{ + NoStructure = 0, /* Really old firmware */ + StructuredMessages = 1, /* Parsable AT response msgs */ + ChecksummedMessages = 2 /* Parsable AT response msgs with checksums */ +} FirmwareLevel; struct strip { @@ -292,6 +256,25 @@ struct strip unsigned long tx_dropped; /* When MTU change */ unsigned long rx_over_errors; /* Frame bigger then STRIP buf. */ + unsigned long pps_timer; /* Timer to determine pps */ + unsigned long rx_pps_count; /* Counter to determine pps */ + unsigned long tx_pps_count; /* Counter to determine pps */ + unsigned long sx_pps_count; /* Counter to determine pps */ + unsigned long rx_average_pps; /* rx packets per second * 8 */ + unsigned long tx_average_pps; /* tx packets per second * 8 */ + unsigned long sx_average_pps; /* sent packets per second * 8 */ + +#ifdef EXT_COUNTERS + unsigned long rx_bytes; /* total received bytes */ + unsigned long tx_bytes; /* total received bytes */ + unsigned long rx_rbytes; /* bytes thru radio i/f */ + unsigned long tx_rbytes; /* bytes thru radio i/f */ + unsigned long rx_sbytes; /* tot bytes thru serial i/f */ + unsigned long tx_sbytes; /* tot bytes thru serial i/f */ + unsigned long rx_ebytes; /* tot stat/err bytes */ + unsigned long tx_ebytes; /* tot stat/err bytes */ +#endif + /* * Internal variables. */ @@ -300,52 +283,142 @@ struct strip struct strip **referrer; /* The pointer that points to us*/ int discard; /* Set if serial error */ int working; /* Is radio working correctly? */ - int structured_messages; /* Parsable AT response msgs? */ + int firmware_level; /* Message structuring level */ + int next_command; /* Next periodic command */ int mtu; /* Our mtu (to spot changes!) */ long watchdog_doprobe; /* Next time to test the radio */ long watchdog_doreset; /* Time to do next reset */ long gratuitous_arp; /* Time to send next ARP refresh*/ long arp_interval; /* Next ARP interval */ struct timer_list idle_timer; /* For periodic wakeup calls */ - MetricomNode *neighbor_list; /* The list of neighbor nodes */ - int neighbor_list_locked; /* Indicates the list is locked */ - MetricomFirmwareVersion firmware_version; /* The radio's firmware version */ - MetricomSerialNumber serial_number; /* The radio's serial number */ - MetricomBatteryVoltage battery_voltage; /* The radio's battery voltage */ + MetricomAddress true_dev_addr; /* True address of radio */ + int manual_dev_addr; /* Hack: See note below */ + + FirmwareVersion firmware_version; /* The radio's firmware version */ + SerialNumber serial_number; /* The radio's serial number */ + BatteryVoltage battery_voltage; /* The radio's battery voltage */ /* * Other useful structures. */ struct tty_struct *tty; /* ptr to TTY structure */ - char if_name[8]; /* Dynamically generated name */ + char8 if_name; /* Dynamically generated name */ struct device dev; /* Our device structure */ /* - * Packet Logging Structures. + * Neighbour radio records */ - u_long num_sent; - u_long num_received; - - int next_entry; /* The index of the oldest packet; */ - /* Also the next to be logged. */ - StripLog packetLog[610]; + MetricomNodeTable portables; + MetricomNodeTable poletops; }; +/* + * Note: manual_dev_addr hack + * + * It is not possible to change the hardware address of a Metricom radio, + * or to send packets with a user-specified hardware source address, thus + * trying to manually set a hardware source address is a questionable + * thing to do. However, if the user *does* manually set the hardware + * source address of a STRIP interface, then the kernel will believe it, + * and use it in certain places. For example, the hardware address listed + * by ifconfig will be the manual address, not the true one. + * (Both addresses are listed in /proc/net/strip.) + * Also, ARP packets will be sent out giving the user-specified address as + * the source address, not the real address. This is dangerous, because + * it means you won't receive any replies -- the ARP replies will go to + * the specified address, which will be some other radio. The case where + * this is useful is when that other radio is also connected to the same + * machine. This allows you to connect a pair of radios to one machine, + * and to use one exclusively for inbound traffic, and the other + * exclusively for outbound traffic. Pretty neat, huh? + * + * Here's the full procedure to set this up: + * + * 1. "slattach" two interfaces, e.g. st0 for outgoing packets, + * and st1 for incoming packets + * + * 2. "ifconfig" st0 (outbound radio) to have the hardware address + * which is the real hardware address of st1 (inbound radio). + * Now when it sends out packets, it will masquerade as st1, and + * replies will be sent to that radio, which is exactly what we want. + * + * 3. Set the route table entry ("route add default ..." or + * "route add -net ...", as appropriate) to send packets via the st0 + * interface (outbound radio). Do not add any route which sends packets + * out via the st1 interface -- that radio is for inbound traffic only. + * + * 4. "ifconfig" st1 (inbound radio) to have hardware address zero. + * This tells the STRIP driver to "shut down" that interface and not + * send any packets through it. In particular, it stops sending the + * periodic gratuitous ARP packets that a STRIP interface normally sends. + * Also, when packets arrive on that interface, it will search the + * interface list to see if there is another interface who's manual + * hardware address matches its own real address (i.e. st0 in this + * example) and if so it will transfer ownership of the skbuff to + * that interface, so that it looks to the kernel as if the packet + * arrived on that interface. This is necessary because when the + * kernel sends an ARP packet on st0, it expects to get a reply on + * st0, and if it sees the reply come from st1 then it will ignore + * it (to be accurate, it puts the entry in the ARP table, but + * labelled in such a way that st0 can't use it). + * + * Thanks to Petros Maniatis for coming up with the idea of splitting + * inbound and outbound traffic between two interfaces, which turned + * out to be really easy to implement, even if it is a bit of a hack. + * + * Having set a manual address on an interface, you can restore it + * to automatic operation (where the address is automatically kept + * consistent with the real address of the radio) by setting a manual + * address of all ones, e.g. "ifconfig st0 hw strip FFFFFFFFFFFF" + * This 'turns off' manual override mode for the device address. + * + * Note: The IEEE 802 headers reported in tcpdump will show the *real* + * radio addresses the packets were sent and received from, so that you + * can see what is really going on with packets, and which interfaces + * they are really going through. + */ + /************************************************************************/ /* Constants */ -#ifdef MODULE -static const char StripVersion[] = "0.9.8-STUART.CHESHIRE-MODULAR"; -#else -static const char StripVersion[] = "0.9.8-STUART.CHESHIRE"; -#endif +/* + * CommandString1 works on all radios + * Other CommandStrings are only used with firmware that provides structured responses. + * + * ats319=1 Enables Info message for node additions and deletions + * ats319=2 Enables Info message for a new best node + * ats319=4 Enables checksums + * ats319=8 Enables ACK messages + */ + +static const int MaxCommandStringLength = 32; +static const int CompatibilityCommand = 1; -static const char TickleString1[] = "***&COMMAND*ATS305?\r"; -static const char TickleString2[] = "***&COMMAND*ATS305?\r\r" - "*&COMMAND*ATS300?\r\r*&COMMAND*ATS325?\r\r*&COMMAND*AT~I2 nn\r\r"; +static const char CommandString0[] = "*&COMMAND*ATS319=7"; /* Turn on checksums & info messages */ +static const char CommandString1[] = "*&COMMAND*ATS305?"; /* Query radio name */ +static const char CommandString2[] = "*&COMMAND*ATS325?"; /* Query battery voltage */ +static const char CommandString3[] = "*&COMMAND*ATS300?"; /* Query version information */ +static const char CommandString4[] = "*&COMMAND*ATS311?"; /* Query poletop list */ +static const char CommandString5[] = "*&COMMAND*AT~LA"; /* Query portables list */ +typedef struct { const char *string; long length; } StringDescriptor; + +static const StringDescriptor CommandString[] = + { + { CommandString0, sizeof(CommandString0)-1 }, + { CommandString1, sizeof(CommandString1)-1 }, + { CommandString2, sizeof(CommandString2)-1 }, + { CommandString3, sizeof(CommandString3)-1 }, + { CommandString4, sizeof(CommandString4)-1 }, + { CommandString5, sizeof(CommandString5)-1 } + }; + +#define GOT_ALL_RADIO_INFO(S) \ + ((S)->firmware_version.c[0] && \ + (S)->battery_voltage.c[0] && \ + memcmp(&(S)->true_dev_addr, zero_address.c, sizeof(zero_address))) static const char hextable[16] = "0123456789ABCDEF"; @@ -354,26 +427,27 @@ static const MetricomAddress broadcast_address = { { 0xFF,0xFF,0xFF,0xFF,0xFF,0x static const MetricomKey SIP0Key = { { "SIP0" } }; static const MetricomKey ARP0Key = { { "ARP0" } }; -static const MetricomKey ERR_Key = { { "ERR_" } }; static const MetricomKey ATR_Key = { { "ATR " } }; +static const MetricomKey ACK_Key = { { "ACK_" } }; +static const MetricomKey INF_Key = { { "INF_" } }; +static const MetricomKey ERR_Key = { { "ERR_" } }; static const long MaxARPInterval = 60 * HZ; /* One minute */ /* - * Maximum Starmode packet length (including starmode address) is 1183 bytes. - * Allowing 32 bytes for header, and 65/64 expansion for STRIP encoding, - * that translates to a maximum payload MTU of 1132. + * Maximum Starmode packet length is 1183 bytes. Allowing 4 bytes for + * protocol key, 4 bytes for checksum, one byte for CR, and 65/64 expansion + * for STRIP encoding, that translates to a maximum payload MTU of 1155. + * Note: A standard NFS 1K data packet is a total of 0x480 (1152) bytes + * long, including IP header, UDP header, and NFS header. Setting the STRIP + * MTU to 1152 allows us to send default sized NFS packets without fragmentation. */ -static const unsigned short MAX_STRIP_MTU = 1132; -static const unsigned short DEFAULT_STRIP_MTU = 1024; +static const unsigned short MAX_SEND_MTU = 1152; +static const unsigned short MAX_RECV_MTU = 1500; /* Hoping for Ethernet sized packets in the future! */ +static const unsigned short DEFAULT_STRIP_MTU = 1152; static const int STRIP_MAGIC = 0x5303; static const long LongTime = 0x7FFFFFFF; -static const int STRIP_NODE_LEN = 64; -static const char STRIP_PORTABLE_CHAR = 'P'; -static const char STRIP_ROUTER_CHAR = 'r'; -static const int STRIP_PROC_BUFFER_SIZE = 4096; -static const int STRIP_LOG_INT_SIZE = 10; /************************************************************************/ /* Global variables */ @@ -384,10 +458,18 @@ static struct strip *struct_strip_list = NULL; /************************************************************************/ /* Macros */ +/* Returns TRUE if text T begins with prefix P */ +#define has_prefix(T,P) (!strncmp((T), (P), sizeof(P)-1)) + +/* Returns TRUE if text T of length L is equal to string S */ +#define text_equal(T,L,S) (((L) == sizeof(S)-1) && !strncmp((T), (S), sizeof(S)-1)) + #define READHEX(X) ((X)>='0' && (X)<='9' ? (X)-'0' : \ (X)>='a' && (X)<='f' ? (X)-'a'+10 : \ (X)>='A' && (X)<='F' ? (X)-'A'+10 : 0 ) +#define READHEX16(X) ((__u16)(READHEX(X))) + #define READDEC(X) ((X)>='0' && (X)<='9' ? (X)-'0' : 0) #define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) @@ -395,17 +477,6 @@ static struct strip *struct_strip_list = NULL; #define ELEMENTS_OF(X) (sizeof(X) / sizeof((X)[0])) #define ARRAY_END(X) (&((X)[ELEMENTS_OF(X)])) -/* Encapsulation can expand packet of size x to 65/64x + 1 */ -/* Sent packet looks like "*<address>*<key><encaps payload><CR>" */ -/* 1 1-18 1 4 ? 1 */ -/* We allow 31 bytes for the stars, the key, the address and the <CR> */ -#define STRIP_ENCAP_SIZE(X) (32 + (X)*65L/64L) - -#define IS_RADIO_ADDRESS(p) ( \ - isdigit((p)[0]) && isdigit((p)[1]) && isdigit((p)[2]) && isdigit((p)[3]) && \ - (p)[4] == '-' && \ - isdigit((p)[5]) && isdigit((p)[6]) && isdigit((p)[7]) && isdigit((p)[8]) ) - #define JIFFIE_TO_SEC(X) ((X) / HZ) @@ -605,7 +676,15 @@ static __u8 *StuffData(__u8 *src, __u32 length, __u8 *dst, __u8 **code_ptr_ptr) } /* else, we only have one so far, so switch to Stuff_Diff code */ code = Stuff_Diff; - /* and fall through to Stuff_Diff case below */ + /* and fall through to Stuff_Diff case below + * Note cunning cleverness here: case Stuff_Diff compares + * the current character with the previous two to see if it + * has a run of three the same. Won't this be an error if + * there aren't two previous characters stored to compare with? + * No. Because we know the current character is *not* the same + * as the previous one, the first test below will necessarily + * fail and the send half of the "if" won't be executed. + */ /* Stuff_Diff: We have at least two *different* bytes encoded */ case Stuff_Diff: @@ -639,10 +718,10 @@ static __u8 *StuffData(__u8 *src, __u32 length, __u8 *dst, __u8 **code_ptr_ptr) src++; /* Consume the byte */ break; } - if (count == Stuff_MaxCount) - { - StuffData_FinishBlock(code + count); - } + if (count == Stuff_MaxCount) + { + StuffData_FinishBlock(code + count); + } } if (code == Stuff_NoCode) { @@ -758,14 +837,21 @@ static __u8 *UnStuffData(__u8 *src, __u8 *end, __u8 *dst, __u32 dst_length) * Convert a string to a Metricom Address. */ -static void string_to_radio_address(MetricomAddress *addr, __u8 *p) +#define IS_RADIO_ADDRESS(p) ( \ + isdigit((p)[0]) && isdigit((p)[1]) && isdigit((p)[2]) && isdigit((p)[3]) && \ + (p)[4] == '-' && \ + isdigit((p)[5]) && isdigit((p)[6]) && isdigit((p)[7]) && isdigit((p)[8]) ) + +static int string_to_radio_address(MetricomAddress *addr, __u8 *p) { + if (!IS_RADIO_ADDRESS(p)) return(1); addr->c[0] = 0; addr->c[1] = 0; addr->c[2] = READHEX(p[0]) << 4 | READHEX(p[1]); addr->c[3] = READHEX(p[2]) << 4 | READHEX(p[3]); addr->c[4] = READHEX(p[5]) << 4 | READHEX(p[6]); addr->c[5] = READHEX(p[7]) << 4 | READHEX(p[8]); + return(0); } /* @@ -780,19 +866,18 @@ static __u8 *radio_address_to_string(const MetricomAddress *addr, MetricomAddres /* * Note: Must make sure sx_size is big enough to receive a stuffed - * MAX_STRIP_MTU packet. Additionally, we also want to ensure that it's + * MAX_RECV_MTU packet. Additionally, we also want to ensure that it's * big enough to receive a large radio neighbour list (currently 4K). */ static int allocate_buffers(struct strip *strip_info) { struct device *dev = &strip_info->dev; - int stuffedlen = STRIP_ENCAP_SIZE(dev->mtu); - int sx_size = MAX(stuffedlen, 4096); - int tx_size = stuffedlen + sizeof(TickleString2); - __u8 *r = kmalloc(MAX_STRIP_MTU, GFP_ATOMIC); - __u8 *s = kmalloc(sx_size, GFP_ATOMIC); - __u8 *t = kmalloc(tx_size, GFP_ATOMIC); + int sx_size = MAX(STRIP_ENCAP_SIZE(MAX_RECV_MTU), 4096); + int tx_size = STRIP_ENCAP_SIZE(dev->mtu) + MaxCommandStringLength; + __u8 *r = kmalloc(MAX_RECV_MTU, GFP_ATOMIC); + __u8 *s = kmalloc(sx_size, GFP_ATOMIC); + __u8 *t = kmalloc(tx_size, GFP_ATOMIC); if (r && s && t) { strip_info->rx_buff = r; @@ -824,10 +909,10 @@ static void strip_changedmtu(struct strip *strip_info) unsigned char *otbuff = strip_info->tx_buff; InterruptStatus intstat; - if (dev->mtu > MAX_STRIP_MTU) + if (dev->mtu > MAX_SEND_MTU) { printk(KERN_ERR "%s: MTU exceeds maximum allowable (%d), MTU change cancelled.\n", - strip_info->dev.name, MAX_STRIP_MTU); + strip_info->dev.name, MAX_SEND_MTU); dev->mtu = old_mtu; return; } @@ -856,9 +941,8 @@ static void strip_changedmtu(struct strip *strip_info) memcpy(strip_info->sx_buff, osbuff, strip_info->sx_count); else { - strip_info->sx_count = 0; + strip_info->discard = strip_info->sx_count; strip_info->rx_over_errors++; - strip_info->discard = 1; } } @@ -889,7 +973,7 @@ static void strip_unlock(struct strip *strip_info) /* * Set the time to go off in one second. */ - strip_info->idle_timer.expires = jiffies + HZ; + strip_info->idle_timer.expires = jiffies + 1*HZ; add_timer(&strip_info->idle_timer); if (!test_and_clear_bit(0, (void *)&strip_info->dev.tbusy)) printk(KERN_ERR "%s: trying to unlock already unlocked device!\n", @@ -900,8 +984,6 @@ static void strip_unlock(struct strip *strip_info) /************************************************************************/ /* Callback routines for exporting information through /proc */ -#if DO_PROC_NET_STRIP_STATUS | DO_PROC_NET_STRIP_TRACE - /* * This function updates the total amount of data printed so far. It then * determines if the amount of data printed into a buffer has reached the @@ -912,29 +994,29 @@ static void strip_unlock(struct strip *strip_info) */ static int shift_buffer(char *buffer, int requested_offset, int requested_len, - int *total, int *slop, char **buf) + int *total, int *slop, char **buf) { int printed; /* printk(KERN_DEBUG "shift: buffer: %d o: %d l: %d t: %d buf: %d\n", - (int) buffer, requested_offset, requested_len, *total, - (int) *buf); */ + (int) buffer, requested_offset, requested_len, *total, + (int) *buf); */ printed = *buf - buffer; if (*total + printed <= requested_offset) { - *total += printed; - *buf = buffer; + *total += printed; + *buf = buffer; } else { - if (*total < requested_offset) { - *slop = requested_offset - *total; - } - *total = requested_offset + printed - *slop; + if (*total < requested_offset) { + *slop = requested_offset - *total; + } + *total = requested_offset + printed - *slop; } if (*total > requested_offset + requested_len) { - return 1; + return 1; } else { - return 0; + return 0; } } @@ -945,12 +1027,12 @@ shift_buffer(char *buffer, int requested_offset, int requested_len, */ static int calc_start_len(char *buffer, char **start, int requested_offset, - int requested_len, int total, char *buf) + int requested_len, int total, char *buf) { int return_len, buffer_len; buffer_len = buf - buffer; - if (buffer_len >= STRIP_PROC_BUFFER_SIZE - 1) { + if (buffer_len >= 4095) { printk(KERN_ERR "STRIP: exceeded /proc buffer size\n"); } @@ -960,20 +1042,16 @@ calc_start_len(char *buffer, char **start, int requested_offset, */ return_len = total - requested_offset; if (return_len < 0) { - return_len = 0; + return_len = 0; } *start = buf - return_len; if (return_len > requested_len) { - return_len = requested_len; + return_len = requested_len; } /* printk(KERN_DEBUG "return_len: %d\n", return_len); */ return return_len; } -#endif DO_PROC_NET_STRIP_STATUS | DO_PROC_NET_STRIP_TRACE - -#if DO_PROC_NET_STRIP_STATUS - /* * If the time is in the near future, time_delta prints the number of * seconds to go into the buffer and returns the address of the buffer. @@ -991,537 +1069,171 @@ static char *time_delta(char buffer[], long time) return(buffer); } -/* - * This function prints radio status information into the specified - * buffer. - */ -static int -sprintf_status_info(char *buffer, struct strip *strip_info) -{ - char temp_buffer[32]; - MetricomAddressString addr_string; - char *buf; - - buf = buffer; - buf += sprintf(buf, "Interface name\t\t%s\n", strip_info->if_name); - buf += sprintf(buf, " Radio working:\t\t%s\n", - strip_info->working && - (long)jiffies - strip_info->watchdog_doreset < 0 ? "Yes" : "No"); - (void) radio_address_to_string((MetricomAddress *) - &strip_info->dev.dev_addr, - &addr_string); - buf += sprintf(buf, " Device address:\t%s\n", addr_string.c); - buf += sprintf(buf, " Firmware version:\t%s\n", - !strip_info->working ? "Unknown" : - !strip_info->structured_messages ? "Should be upgraded" : - strip_info->firmware_version.c); - buf += sprintf(buf, " Serial number:\t\t%s\n", strip_info->serial_number.c); - buf += sprintf(buf, " Battery voltage:\t%s\n", strip_info->battery_voltage.c); - buf += sprintf(buf, " Transmit queue (bytes):%d\n", strip_info->tx_left); - buf += sprintf(buf, " Next watchdog probe:\t%s\n", - time_delta(temp_buffer, strip_info->watchdog_doprobe)); - buf += sprintf(buf, " Next watchdog reset:\t%s\n", - time_delta(temp_buffer, strip_info->watchdog_doreset)); - buf += sprintf(buf, " Next gratuitous ARP:\t%s\n", - time_delta(temp_buffer, strip_info->gratuitous_arp)); - buf += sprintf(buf, " Next ARP interval:\t%ld seconds\n", - JIFFIE_TO_SEC(strip_info->arp_interval)); - return buf - buffer; -} - -static int -sprintf_portables(char *buffer, struct strip *strip_info) -{ - - MetricomAddressString addr_string; - MetricomNode *node; - char *buf; - - buf = buffer; - buf += sprintf(buf, " portables: name\t\tpoll_latency\tsignal strength\n"); - for (node = strip_info->neighbor_list; node != NULL; - node = node->next) { - if (!(node->type & NodeValid)) { - break; - } - if (node->type & NodeHasWAN) { - continue; - } - (void) radio_address_to_string(&node->addr, &addr_string); - buf += sprintf(buf, " %s\t\t\t\t%d\t\t%d\n", - addr_string.c, node->poll_latency, node->rssi); - } - return buf - buffer; -} - -static int -sprintf_poletops(char *buffer, struct strip *strip_info) -{ - MetricomNode *node; - char *buf; - - buf = buffer; - buf += sprintf(buf, " poletops: GPS\t\t\tpoll_latency\tsignal strength\n"); - for (node = strip_info->neighbor_list; - node != NULL; node = node->next) { - if (!(node->type & NodeValid)) { - break; - } - if (!(node->type & NodeHasWAN)) { - continue; - } - buf += sprintf(buf, " %s\t\t\t%d\t\t%d\n", - node->gl.s, node->poll_latency, node->rssi); - } - return buf - buffer; -} - -/* - * This function is exports status information from the STRIP driver through - * the /proc file system. /proc filesystem should be fixed: - * 1) slow (sprintfs here, a memory copy in the proc that calls this one) - * 2) length of buffer not passed - * 3) dummy isn't client data set when the callback was registered - * 4) poorly documented (this function is called until the requested amount - * of data is returned, buffer is only 4K long, dummy is the permissions - * of the file (?), the proc_dir_entry passed to proc_net_register must - * be kmalloc-ed) - */ - -static int -strip_get_status_info(char *buffer, char **start, off_t requested_offset, - int requested_len, int dummy) +static int sprintf_neighbours(char *buffer, MetricomNodeTable *table, char *title) { - char *buf; - int total = 0, slop = 0, len_exceeded; - InterruptStatus i_status; - struct strip *strip_info; - - buf = buffer; - buf += sprintf(buf, "strip_version: %s\n", StripVersion); - - i_status = DisableInterrupts(); - strip_info = struct_strip_list; - RestoreInterrupts(i_status); - - while (strip_info != NULL) { - i_status = DisableInterrupts(); - buf += sprintf_status_info(buf, strip_info); - RestoreInterrupts(i_status); - len_exceeded = shift_buffer(buffer, requested_offset, requested_len, - &total, &slop, &buf); - if (len_exceeded) { - goto done; - } - strip_info->neighbor_list_locked = TRUE; - buf += sprintf_portables(buf, strip_info); - strip_info->neighbor_list_locked = FALSE; - len_exceeded = shift_buffer(buffer, requested_offset, requested_len, - &total, &slop, &buf); - if (len_exceeded) { - goto done; - } - strip_info->neighbor_list_locked = TRUE; - buf += sprintf_poletops(buf, strip_info); - strip_info->neighbor_list_locked = FALSE; - len_exceeded = shift_buffer(buffer, requested_offset, requested_len, - &total, &slop, &buf); - if (len_exceeded) { - goto done; - } - strip_info = strip_info->next; - } -done: - return calc_start_len(buffer, start, requested_offset, requested_len, - total, buf); + /* We wrap this in a do/while loop, so if the table changes */ + /* while we're reading it, we just go around and try again. */ + struct timeval t; + char *ptr; + do + { + int i; + t = table->timestamp; + ptr = buffer; + if (table->num_nodes) ptr += sprintf(ptr, "\n %s\n", title); + for (i=0; i<table->num_nodes; i++) + { + InterruptStatus intstat = DisableInterrupts(); + MetricomNode node = table->node[i]; + RestoreInterrupts(intstat); + ptr += sprintf(ptr, " %s\n", node.c); + } + } while (table->timestamp.tv_sec != t.tv_sec || table->timestamp.tv_usec != t.tv_usec); + return ptr - buffer; } -#endif DO_PROC_NET_STRIP_STATUS - -#if DO_PROC_NET_STRIP_TRACE - /* - * Convert an Ethernet protocol to a string - * Returns the number of characters printed. + * This function prints radio status information into the specified buffer. + * I think the buffer size is 4K, so this routine should never print more + * than 4K of data into it. With the maximum of 32 portables and 32 poletops + * reported, the routine outputs 3107 bytes into the buffer. */ - -static int protocol_to_string(int protocol, __u8 *p) -{ - int printed; - - switch (protocol) { - case ETH_P_IP: - printed = sprintf(p, "IP"); - break; - case ETH_P_ARP: - printed = sprintf(p, "ARP"); - break; - default: - printed = sprintf(p, "%d", protocol); - } - return printed; -} - static int -sprintf_log_entry(char *buffer, struct strip *strip_info, int packet_index) +sprintf_status_info(char *buffer, struct strip *strip_info) { - StripLog *entry; + char temp[32]; + char *p = buffer; MetricomAddressString addr_string; - __u8 sig_buf[24], *s; - char *buf, proto_buf[10]; - - entry = &strip_info->packetLog[packet_index]; - if (!entry->valid) { - return 0; - } - buf = buffer; - buf += sprintf(buf, "%-4s %s %7lu ", strip_info->if_name, - ENTRY_TYPE_TO_STRING(entry->entry_type), entry->seqNum); - (void) protocol_to_string(entry->packet_type, proto_buf); - buf += sprintf(buf, "%-4s", proto_buf); - s = entry->sig.print_sig; - sprintf(sig_buf, "%d.%d.%d.%d.%d.%d", s[0], s[1], s[2], s[3], s[4], s[5]); - buf += sprintf(buf, "%-24s", sig_buf); - (void) radio_address_to_string((MetricomAddress *) &entry->src, - &addr_string); - buf += sprintf(buf, "%-10s", addr_string.c); - (void) radio_address_to_string((MetricomAddress *) &entry->dest, - &addr_string); - buf += sprintf(buf, "%-10s", addr_string.c); - buf += sprintf(buf, "%8d %6d %5lu %6lu %5lu\n", entry->timeStamp.tv_sec, - entry->timeStamp.tv_usec, entry->rawSize, - entry->stripSize, entry->slipSize); - return buf - buffer; -} - -/* - * This function exports trace information from the STRIP driver through the - * /proc file system. - */ -static int -strip_get_trace_info(char *buffer, char **start, off_t requested_offset, - int requested_len, int dummy) -{ - char *buf; - int len_exceeded, total = 0, slop = 0, packet_index, oldest; - InterruptStatus i_status; - struct strip *strip_info; - - buf = buffer; - buf += sprintf(buf, "if s/r seqnum t signature "); - buf += sprintf(buf, - "src dest sec usec raw strip slip\n"); - - i_status = DisableInterrupts(); - strip_info = struct_strip_list; - oldest = strip_info->next_entry; - RestoreInterrupts(i_status); - - /* - * If we disable interrupts for this entire loop, - * characters from the serial port could be lost, - * so we only disable interrupts when accessing - * a log entry. If more than STRIP_LOG_INT_SIZE - * packets are logged before the first entry is - * printed, then some of the entries could be - * printed out of order. - */ - while (strip_info != NULL) { - for (packet_index = oldest + STRIP_LOG_INT_SIZE; - packet_index != oldest; - packet_index = (packet_index + 1) % - ELEMENTS_OF(strip_info->packetLog)) { - i_status = DisableInterrupts(); - buf += sprintf_log_entry(buf, strip_info, packet_index); - RestoreInterrupts(i_status); - len_exceeded = shift_buffer(buffer, requested_offset, - requested_len, &total, &slop, &buf); - if (len_exceeded) { - goto done; - } - } - strip_info = strip_info->next; - } -done: - return calc_start_len(buffer, start, requested_offset, requested_len, - total, buf); -} + /* First, we must copy all of our data to a safe place, */ + /* in case a serial interrupt comes in and changes it. */ + InterruptStatus intstat = DisableInterrupts(); + int tx_left = strip_info->tx_left; + unsigned long rx_average_pps = strip_info->rx_average_pps; + unsigned long tx_average_pps = strip_info->tx_average_pps; + unsigned long sx_average_pps = strip_info->sx_average_pps; + int working = strip_info->working; + int firmware_level = strip_info->firmware_level; + long watchdog_doprobe = strip_info->watchdog_doprobe; + long watchdog_doreset = strip_info->watchdog_doreset; + long gratuitous_arp = strip_info->gratuitous_arp; + long arp_interval = strip_info->arp_interval; + FirmwareVersion firmware_version = strip_info->firmware_version; + SerialNumber serial_number = strip_info->serial_number; + BatteryVoltage battery_voltage = strip_info->battery_voltage; + char8 if_name = strip_info->if_name; + MetricomAddress true_dev_addr = strip_info->true_dev_addr; + MetricomAddress dev_dev_addr = *(MetricomAddress*)strip_info->dev.dev_addr; + int manual_dev_addr = strip_info->manual_dev_addr; +#ifdef EXT_COUNTERS + unsigned long rx_bytes = strip_info->rx_bytes; + unsigned long tx_bytes = strip_info->tx_bytes; + unsigned long rx_rbytes = strip_info->rx_rbytes; + unsigned long tx_rbytes = strip_info->tx_rbytes; + unsigned long rx_sbytes = strip_info->rx_sbytes; + unsigned long tx_sbytes = strip_info->tx_sbytes; + unsigned long rx_ebytes = strip_info->rx_ebytes; + unsigned long tx_ebytes = strip_info->tx_ebytes; +#endif + RestoreInterrupts(intstat); -static int slip_len(unsigned char *data, int len) -{ - static const unsigned char SLIP_END=0300; /* indicates end of SLIP frame */ - static const unsigned char SLIP_ESC=0333; /* indicates SLIP byte stuffing */ - int count = len; - while (--len >= 0) + p += sprintf(p, "\nInterface name\t\t%s\n", if_name.c); + p += sprintf(p, " Radio working:\t\t%s\n", working ? "Yes" : "No"); + radio_address_to_string(&true_dev_addr, &addr_string); + p += sprintf(p, " Radio address:\t\t%s\n", addr_string.c); + if (manual_dev_addr) { - if (*data == SLIP_END || *data == SLIP_ESC) count++; - data++; - } - return(count); -} - -/* Copied from kernel/sched.c */ -static void jiffiestotimeval(unsigned long jiffies, struct timeval *value) -{ - value->tv_usec = (jiffies % HZ) * (1000000.0 / HZ); - value->tv_sec = jiffies / HZ; - return; -} - -/* - * This function logs a packet. - * A pointer to the packet itself is passed so that some of the data can be - * used to compute a signature. The pointer should point the the - * part of the packet following the STRIP_header. - */ - -static void packet_log(struct strip *strip_info, __u8 *packet, - LogEntry entry_type, STRIP_Header *hdr, - int raw_size, int strip_size, int slip_size) -{ - StripLog *entry; - struct iphdr *iphdr; - struct arphdr *arphdr; - - entry = &strip_info->packetLog[strip_info->next_entry]; - if (entry_type == EntrySend) { - entry->seqNum = strip_info->num_sent++; + radio_address_to_string(&dev_dev_addr, &addr_string); + p += sprintf(p, " Device address:\t%s\n", addr_string.c); + } + p += sprintf(p, " Firmware version:\t%s", !working ? "Unknown" : + !firmware_level ? "Should be upgraded" : + firmware_version.c); + if (firmware_level >= ChecksummedMessages) p += sprintf(p, " (Checksums Enabled)"); + p += sprintf(p, "\n"); + p += sprintf(p, " Serial number:\t\t%s\n", serial_number.c); + p += sprintf(p, " Battery voltage:\t%s\n", battery_voltage.c); + p += sprintf(p, " Transmit queue (bytes):%d\n", tx_left); + p += sprintf(p, " Receive packet rate: %ld packets per second\n", rx_average_pps / 8); + p += sprintf(p, " Transmit packet rate: %ld packets per second\n", tx_average_pps / 8); + p += sprintf(p, " Sent packet rate: %ld packets per second\n", sx_average_pps / 8); + p += sprintf(p, " Next watchdog probe:\t%s\n", time_delta(temp, watchdog_doprobe)); + p += sprintf(p, " Next watchdog reset:\t%s\n", time_delta(temp, watchdog_doreset)); + p += sprintf(p, " Next gratuitous ARP:\t"); + + if (!memcmp(strip_info->dev.dev_addr, zero_address.c, sizeof(zero_address))) + p += sprintf(p, "Disabled\n"); + else + { + p += sprintf(p, "%s\n", time_delta(temp, gratuitous_arp)); + p += sprintf(p, " Next ARP interval:\t%ld seconds\n", JIFFIE_TO_SEC(arp_interval)); } - else { - entry->seqNum = strip_info->num_received++; - } - entry->entry_type = entry_type; - entry->packet_type = ntohs(hdr->protocol); - switch (entry->packet_type) { - case ETH_P_IP: - /* - * The signature for IP is the sender's ip address and - * the identification field. - */ - iphdr = (struct iphdr *) packet; - entry->sig.ip_sig.id = iphdr->id; - entry->sig.ip_sig.src.l = iphdr->saddr; - break; - case ETH_P_ARP: - /* - * The signature for ARP is the sender's ip address and - * the operation. - */ - arphdr = (struct arphdr *) packet; - entry->sig.arp_sig.op = arphdr->ar_op; - memcpy(&entry->sig.arp_sig.src.l, packet + 8 + arphdr->ar_hln, - sizeof(entry->sig.arp_sig.src.l)); - entry->sig.arp_sig.src.l = entry->sig.arp_sig.src.l; - break; - default: - printk(KERN_DEBUG "STRIP: packet_log: unknown packet type: %d\n", - entry->packet_type); - break; - } - memcpy(&entry->src, &hdr->src_addr, sizeof(MetricomAddress)); - memcpy(&entry->dest, &hdr->dst_addr, sizeof(MetricomAddress)); - - jiffiestotimeval(jiffies, &(entry->timeStamp)); - entry->rawSize = raw_size; - entry->stripSize = strip_size; - entry->slipSize = slip_size; - entry->valid = 1; - - strip_info->next_entry = (strip_info->next_entry + 1) % - ELEMENTS_OF(strip_info->packetLog); -} -#endif DO_PROC_NET_STRIP_TRACE + if (working) + { +#ifdef EXT_COUNTERS + p += sprintf(p, "\n"); + p += sprintf(p, " Total bytes: \trx:\t%lu\ttx:\t%lu\n", rx_bytes, tx_bytes); + p += sprintf(p, " thru radio: \trx:\t%lu\ttx:\t%lu\n", rx_rbytes, tx_rbytes); + p += sprintf(p, " thru serial port: \trx:\t%lu\ttx:\t%lu\n", rx_sbytes, tx_sbytes); + p += sprintf(p, " Total stat/err bytes:\trx:\t%lu\ttx:\t%lu\n", rx_ebytes, tx_ebytes); +#endif + p += sprintf_neighbours(p, &strip_info->poletops, "Poletops:"); + p += sprintf_neighbours(p, &strip_info->portables, "Portables:"); + } -/* - * This function parses the response to the ATS300? command, - * extracting the radio version and serial number. - */ -static void get_radio_version(struct strip *strip_info, __u8 *ptr, __u8 *end) -{ - __u8 *p, *value_begin, *value_end; - int len; - - /* Determine the beginning of the second line of the payload */ - p = ptr; - while (p < end && *p != 10) p++; - if (p >= end) return; - p++; - value_begin = p; - - /* Determine the end of line */ - while (p < end && *p != 10) p++; - if (p >= end) return; - value_end = p; - p++; - - len = value_end - value_begin; - len = MIN(len, sizeof(MetricomFirmwareVersion) - 1); - sprintf(strip_info->firmware_version.c, "%.*s", len, value_begin); - - /* Look for the first colon */ - while (p < end && *p != ':') p++; - if (p >= end) return; - /* Skip over the space */ - p += 2; - len = sizeof(MetricomSerialNumber) - 1; - if (p + len <= end) { - sprintf(strip_info->serial_number.c, "%.*s", len, p); - } - else { - printk(KERN_ERR "STRIP: radio serial number shorter (%d) than expected (%d)\n", - end - p, len); - } + return p - buffer; } /* - * This function parses the response to the ATS325? command, - * extracting the radio battery voltage. + * This function is exports status information from the STRIP driver through + * the /proc file system. */ -static void get_radio_voltage(struct strip *strip_info, __u8 *ptr, __u8 *end) -{ - int len; - len = sizeof(MetricomBatteryVoltage) - 1; - if (ptr + len <= end) { - sprintf(strip_info->battery_voltage.c, "%.*s", len, ptr); - } - else { - printk(KERN_ERR "STRIP: radio voltage string shorter (%d) than expected (%d)\n", - end - ptr, len); - } -} - -/* - * This function parses the response to the AT~I2 command, - * which gives the names of the radio's nearest neighbors. - * It relies on the format of the response. - */ -static void get_radio_neighbors(struct strip *strip_info, __u8 *ptr, __u8 *end) +static int get_status_info(char *buffer, char **start, off_t req_offset, int req_len, int dummy) { - __u8 *p, *line_begin; - int num_nodes_reported, num_nodes_counted; - MetricomNode *node, *last; - - /* Check if someone is reading the list */ - if (strip_info->neighbor_list_locked) { - return; - } - - /* Determine the number of Nodes */ - p = ptr; - num_nodes_reported = simple_strtoul(p, NULL, 10); - /* printk(KERN_DEBUG "num_nodes: %d\n", num_nodes_reported); */ - - /* Determine the beginning of the next line */ - while (p < end && *p != 10) p++; - if (p >= end) return; - p++; + int total = 0, slop = 0; + struct strip *strip_info = struct_strip_list; + char *buf = buffer; - /* - * The node list should never be empty because we allocate one empty - * node when the strip_info is allocated. The nodes which were allocated - * when the number of neighbors was high but are no longer needed because - * there aren't as many neighbors any more are marked invalid. Invalid nodes - * are kept at the end of the list. - */ - node = strip_info->neighbor_list; - last = node; - if (node == NULL) { - DumpData("Neighbor list is NULL:", strip_info, p, end); - return; - } - line_begin = p; - num_nodes_counted = 0; - while (line_begin < end) { - /* Check to see if the format is what we expect. */ - if ((line_begin + STRIP_NODE_LEN) > end) { - printk(KERN_ERR "STRIP: radio neighbor node string shorter (%d) than expected (%d)\n", - end - line_begin, STRIP_NODE_LEN); - break; - } - - /* Get a node */ - if (node == NULL) { - node = kmalloc(sizeof(MetricomNode), GFP_ATOMIC); - node->next = NULL; - } - node->type = NodeValid; - - /* Fill the node in */ - - /* Determine if it has a GPS location and fill it in if it does. */ - p = line_begin; - /* printk(KERN_DEBUG "node: %64s\n", p); */ - if (p[0] != STRIP_PORTABLE_CHAR) { - node->type |= NodeHasWAN; - sprintf(node->gl.s, "%.*s", (int) sizeof(GeographicLocation) - 1, p); - } - - /* Determine if it is a router */ - p = line_begin + 18; - if (p[0] == STRIP_ROUTER_CHAR) { - node->type |= NodeIsRouter; - } - - /* Could be a radio address or some weird poletop address. */ - p = line_begin + 20; - /* printk(KERN_DEBUG "before addr: %6s\n", p); */ - string_to_radio_address(&node->addr, p); - /* radio_address_to_string(&node->addr, addr_string); - printk(KERN_DEBUG "after addr: %s\n", addr_string); */ - - if (IS_RADIO_ADDRESS(p)) { - string_to_radio_address(&node->addr, p); - } - else { - memset(&node->addr, 0, sizeof(MetricomAddress)); - } - - /* Get the poll latency. %$#!@ simple_strtoul can't skip white space */ - p = line_begin + 41; - while (isspace(*p) && (p < end)) { - p++; - } - node->poll_latency = simple_strtoul(p, NULL, 10); - - /* Get the signal strength. simple_strtoul doesn't do minus signs */ - p = line_begin + 60; - node->rssi = -simple_strtoul(p, NULL, 10); - - if (last != node) { - last->next = node; - last = node; - } - node = node->next; - line_begin += STRIP_NODE_LEN; - num_nodes_counted++; - } - - /* invalidate all remaining nodes */ - for (;node != NULL; node = node->next) { - node->type &= ~NodeValid; - } + buf += sprintf(buf, "strip_version: %s\n", StripVersion); + if (shift_buffer(buffer, req_offset, req_len, &total, &slop, &buf)) goto exit; - /* - * If the number of nodes reported is different - * from the number counted, might need to up the number - * requested. - */ - if (num_nodes_reported != num_nodes_counted) { - printk(KERN_DEBUG "nodes reported: %d \tnodes counted: %d\n", - num_nodes_reported, num_nodes_counted); - } + while (strip_info != NULL) + { + buf += sprintf_status_info(buf, strip_info); + if (shift_buffer(buffer, req_offset, req_len, &total, &slop, &buf)) break; + strip_info = strip_info->next; + } + exit: + return(calc_start_len(buffer, start, req_offset, req_len, total, buf)); } +static const char proc_strip_status_name[] = "strip"; +static struct proc_dir_entry proc_strip_get_status_info = +{ + PROC_NET_STRIP_STATUS, /* unsigned short low_ino */ + sizeof(proc_strip_status_name)-1, /* unsigned short namelen */ + proc_strip_status_name, /* const char *name */ + S_IFREG | S_IRUGO, /* mode_t mode */ + 1, /* nlink_t nlink */ + 0, 0, 0, /* uid_t uid, gid_t gid, unsigned long size */ + &proc_net_inode_operations, /* struct inode_operations * ops */ + &get_status_info, /* int (*get_info)(...) */ + NULL, /* void (*fill_inode)(struct inode *); */ + NULL, NULL, NULL, /* struct proc_dir_entry *next, *parent, *subdir; */ + NULL /* void *data; */ +}; + /************************************************************************/ /* Sending routines */ +#define InitString "ate0q1dt**starmode" + static void ResetRadio(struct strip *strip_info) -{ - static const char InitString[] = "\rat\r\rate0q1dt**starmode\r\r**"; +{ + static const char s[] = "\r" InitString "\r**"; /* If the radio isn't working anymore, we should clear the old status information. */ if (strip_info->working) @@ -1530,14 +1242,32 @@ static void ResetRadio(struct strip *strip_info) strip_info->firmware_version.c[0] = '\0'; strip_info->serial_number.c[0] = '\0'; strip_info->battery_voltage.c[0] = '\0'; + strip_info->portables.num_nodes = 0; + do_gettimeofday(&strip_info->portables.timestamp); + strip_info->poletops.num_nodes = 0; + do_gettimeofday(&strip_info->poletops.timestamp); } + + strip_info->pps_timer = jiffies; + strip_info->rx_pps_count = 0; + strip_info->tx_pps_count = 0; + strip_info->sx_pps_count = 0; + strip_info->rx_average_pps = 0; + strip_info->tx_average_pps = 0; + strip_info->sx_average_pps = 0; + /* Mark radio address as unknown */ - *(MetricomAddress*)&strip_info->dev.dev_addr = zero_address; + *(MetricomAddress*)&strip_info->true_dev_addr = zero_address; + if (!strip_info->manual_dev_addr) *(MetricomAddress*)strip_info->dev.dev_addr = zero_address; strip_info->working = FALSE; - strip_info->structured_messages = FALSE; + strip_info->firmware_level = NoStructure; + strip_info->next_command = CompatibilityCommand; strip_info->watchdog_doprobe = jiffies + 10 * HZ; strip_info->watchdog_doreset = jiffies + 1 * HZ; - strip_info->tty->driver.write(strip_info->tty, 0, (char *)InitString, sizeof(InitString)-1); + strip_info->tty->driver.write(strip_info->tty, 0, (char *)s, sizeof(s)-1); +#ifdef EXT_COUNTERS + strip_info->tx_ebytes += sizeof(s) - 1; +#endif } /* @@ -1573,6 +1303,9 @@ static void strip_write_some_more(struct tty_struct *tty) int num_written = tty->driver.write(tty, 0, strip_info->tx_head, strip_info->tx_left); strip_info->tx_left -= num_written; strip_info->tx_head += num_written; +#ifdef EXT_COUNTERS + strip_info->tx_sbytes += num_written; +#endif RestoreInterrupts(intstat); } else /* Else start transmission of another packet */ @@ -1583,12 +1316,21 @@ static void strip_write_some_more(struct tty_struct *tty) } } -static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_info, struct sk_buff *skb) +static __u8 *add_checksum(__u8 *buffer, __u8 *end) { -#if DO_PROC_NET_STRIP_TRACE - unsigned char *start_ptr; -#endif DO_PROC_NET_STRIP_TRACE + __u16 sum = 0; + __u8 *p = buffer; + while (p < end) sum += *p++; + end[3] = hextable[sum & 0xF]; sum >>= 4; + end[2] = hextable[sum & 0xF]; sum >>= 4; + end[1] = hextable[sum & 0xF]; sum >>= 4; + end[0] = hextable[sum & 0xF]; + return(end+4); +} +static unsigned char *strip_make_packet(unsigned char *buffer, struct strip *strip_info, struct sk_buff *skb) +{ + __u8 *ptr = buffer; __u8 *stuffstate = NULL; STRIP_Header *header = (STRIP_Header *)skb->data; MetricomAddress haddr = header->dst_addr; @@ -1603,7 +1345,6 @@ static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_ { printk(KERN_ERR "%s: strip_make_packet: Unknown packet type 0x%04X\n", strip_info->dev.name, ntohs(header->protocol)); - strip_info->tx_dropped++; return(NULL); } @@ -1611,7 +1352,6 @@ static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_ { printk(KERN_ERR "%s: Dropping oversized transmit packet: %d bytes\n", strip_info->dev.name, len); - strip_info->tx_dropped++; return(NULL); } @@ -1629,6 +1369,12 @@ static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_ } } + /* + * If we're sending to ourselves, discard the packet. + * (Metricom radios choke if they try to send a packet to their own address.) + */ + if (!memcmp(haddr.c, strip_info->true_dev_addr.c, sizeof(haddr))) + return(NULL); *ptr++ = '*'; *ptr++ = hextable[haddr.c[2] >> 4]; *ptr++ = hextable[haddr.c[2] & 0xF]; @@ -1645,17 +1391,9 @@ static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_ *ptr++ = key.c[2]; *ptr++ = key.c[3]; -#if DO_PROC_NET_STRIP_TRACE - start_ptr = ptr; -#endif DO_PROC_NET_STRIP_TRACE - ptr = StuffData(skb->data + sizeof(STRIP_Header), len, ptr, &stuffstate); -#if DO_PROC_NET_STRIP_TRACE - packet_log(strip_info, skb->data + sizeof(STRIP_Header), EntrySend, - header, len, ptr-start_ptr, - slip_len(skb->data + sizeof(STRIP_Header), len)); -#endif DO_PROC_NET_STRIP_TRACE + if (strip_info->firmware_level >= ChecksummedMessages) ptr = add_checksum(buffer+1, ptr); *ptr++ = 0x0D; return(ptr); @@ -1664,57 +1402,108 @@ static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_ static void strip_send(struct strip *strip_info, struct sk_buff *skb) { unsigned char *ptr = strip_info->tx_buff; + int doreset = (long)jiffies - strip_info->watchdog_doreset >= 0; + int doprobe = (long)jiffies - strip_info->watchdog_doprobe >= 0 && !doreset; - /* If we have a packet, encapsulate it and put it in the buffer */ + /* + * 1. If we have a packet, encapsulate it and put it in the buffer + */ if (skb) { - ptr = strip_make_packet(ptr, strip_info, skb); - /* If error, unlock and return */ - if (!ptr) { strip_unlock(strip_info); return; } - strip_info->tx_packets++; /* Count another successful packet */ - /*DumpData("Sending:", strip_info, strip_info->tx_buff, ptr);*/ - /*HexDump("Sending", strip_info, strip_info->tx_buff, ptr);*/ - } - - /* Set up the strip_info ready to send the data */ - strip_info->tx_head = strip_info->tx_buff; - strip_info->tx_left = ptr - strip_info->tx_buff; - strip_info->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); - - /* If watchdog has expired, reset the radio */ - if ((long)jiffies - strip_info->watchdog_doreset >= 0) - { - ResetRadio(strip_info); - return; - /* Note: if there's a packet to send, strip_write_some_more - will do it after the reset has finished */ + char *newptr = strip_make_packet(ptr, strip_info, skb); + strip_info->tx_pps_count++; + if (!newptr) strip_info->tx_dropped++; + else + { + ptr = newptr; + strip_info->sx_pps_count++; + strip_info->tx_packets++; /* Count another successful packet */ +#ifdef EXT_COUNTERS + strip_info->tx_bytes += skb->len; + strip_info->tx_rbytes += ptr - strip_info->tx_buff; +#endif + /*DumpData("Sending:", strip_info, strip_info->tx_buff, ptr);*/ + /*HexDump("Sending", strip_info, strip_info->tx_buff, ptr);*/ + } } - /* No reset. - * If it is time for another tickle, tack it on the end of the packet + /* + * 2. If it is time for another tickle, tack it on, after the packet */ - if ((long)jiffies - strip_info->watchdog_doprobe >= 0) + if (doprobe) { - /* Send tickle to make radio protest */ - /*printk(KERN_INFO "%s: Routine radio test.\n", strip_info->dev.name);*/ - const char *TickleString = TickleString1; - int length = sizeof(TickleString1)-1; - if (strip_info->structured_messages) + StringDescriptor ts = CommandString[strip_info->next_command]; +#if TICKLE_TIMERS { - TickleString = TickleString2; - length = sizeof(TickleString2)-1; + struct timeval tv; + do_gettimeofday(&tv); + printk(KERN_INFO "**** Sending tickle string %d at %02d.%06d\n", + strip_info->next_command, tv.tv_sec % 100, tv.tv_usec); } - memcpy(ptr, TickleString, length); - strip_info->tx_left += length; +#endif + if (ptr == strip_info->tx_buff) *ptr++ = 0x0D; + + *ptr++ = '*'; /* First send "**" to provoke an error message */ + *ptr++ = '*'; + + /* Then add the command */ + memcpy(ptr, ts.string, ts.length); + + /* Add a checksum ? */ + if (strip_info->firmware_level < ChecksummedMessages) ptr += ts.length; + else ptr = add_checksum(ptr, ptr + ts.length); + + *ptr++ = 0x0D; /* Terminate the command with a <CR> */ + + /* Cycle to next periodic command? */ + if (strip_info->firmware_level >= StructuredMessages) + if (++strip_info->next_command >= ELEMENTS_OF(CommandString)) + strip_info->next_command = 0; +#ifdef EXT_COUNTERS + strip_info->tx_ebytes += ts.length; +#endif strip_info->watchdog_doprobe = jiffies + 10 * HZ; strip_info->watchdog_doreset = jiffies + 1 * HZ; + /*printk(KERN_INFO "%s: Routine radio test.\n", strip_info->dev.name);*/ } /* - * If it is time for a periodic ARP, queue one up to be sent + * 3. Set up the strip_info ready to send the data (if any). + */ + strip_info->tx_head = strip_info->tx_buff; + strip_info->tx_left = ptr - strip_info->tx_buff; + strip_info->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); + + /* + * 4. Debugging check to make sure we're not overflowing the buffer. + */ + if (strip_info->tx_size - strip_info->tx_left < 20) + printk(KERN_ERR "%s: Sending%5d bytes;%5d bytes free.\n", strip_info->dev.name, + strip_info->tx_left, strip_info->tx_size - strip_info->tx_left); + + /* + * 5. If watchdog has expired, reset the radio. Note: if there's data waiting in + * the buffer, strip_write_some_more will send it after the reset has finished + */ + if (doreset) { ResetRadio(strip_info); return; } + + /* + * 6. If it is time for a periodic ARP, queue one up to be sent. + * We only do this if: + * 1. The radio is working + * 2. It's time to send another periodic ARP + * 3. We really know what our address is (and it is not manually set to zero) + * 4. We have a designated broadcast address configured + * If we queue up an ARP packet when we don't have a designated broadcast + * address configured, then the packet will just have to be discarded in + * strip_make_packet. This is not fatal, but it causes misleading information + * to be displayed in tcpdump. tcpdump will report that periodic APRs are + * being sent, when in fact they are not, because they are all being dropped + * in the strip_make_packet routine. */ if (strip_info->working && (long)jiffies - strip_info->gratuitous_arp >= 0 && - memcmp(strip_info->dev.dev_addr, zero_address.c, sizeof(zero_address))) + memcmp(strip_info->dev.dev_addr, zero_address.c, sizeof(zero_address)) && + *strip_info->dev.broadcast!=0xFF) { /*printk(KERN_INFO "%s: Sending gratuitous ARP with interval %ld\n", strip_info->dev.name, strip_info->arp_interval / HZ);*/ @@ -1722,16 +1511,18 @@ static void strip_send(struct strip *strip_info, struct sk_buff *skb) strip_info->arp_interval *= 2; if (strip_info->arp_interval > MaxARPInterval) strip_info->arp_interval = MaxARPInterval; - arp_send(ARPOP_REPLY, ETH_P_ARP, strip_info->dev.pa_addr, - &strip_info->dev, strip_info->dev.pa_addr, - NULL, strip_info->dev.dev_addr, NULL); + arp_send(ARPOP_REPLY, ETH_P_ARP, + strip_info->dev.pa_addr, /* Target address of ARP packet is our address */ + &strip_info->dev, /* Device to send packet on */ + strip_info->dev.pa_addr, /* Source IP address this ARP packet comes from */ + NULL, /* Destination HW address is NULL (broadcast it) */ + strip_info->dev.dev_addr, /* Source HW address is our HW address */ + strip_info->dev.dev_addr); /* Target HW address is our HW address (redundant) */ } - if (strip_info->tx_size - strip_info->tx_left < 20) - printk(KERN_ERR "%s: Sending%5d bytes;%5d bytes free.\n", strip_info->dev.name, - strip_info->tx_left, strip_info->tx_size - strip_info->tx_left); - - /* All ready. Start the transmission */ + /* + * 7. All ready. Start the transmission + */ strip_write_some_more(strip_info->tty); } @@ -1752,6 +1543,33 @@ static int strip_xmit(struct sk_buff *skb, struct device *dev) if (strip_info->mtu != strip_info->dev.mtu) strip_changedmtu(strip_info); + if (jiffies - strip_info->pps_timer > HZ) + { + unsigned long t = jiffies - strip_info->pps_timer; + unsigned long rx_pps_count = (strip_info->rx_pps_count * HZ * 8 + t/2) / t; + unsigned long tx_pps_count = (strip_info->tx_pps_count * HZ * 8 + t/2) / t; + unsigned long sx_pps_count = (strip_info->sx_pps_count * HZ * 8 + t/2) / t; + + strip_info->pps_timer = jiffies; + strip_info->rx_pps_count = 0; + strip_info->tx_pps_count = 0; + strip_info->sx_pps_count = 0; + + strip_info->rx_average_pps = (strip_info->rx_average_pps + rx_pps_count + 1) / 2; + strip_info->tx_average_pps = (strip_info->tx_average_pps + tx_pps_count + 1) / 2; + strip_info->sx_average_pps = (strip_info->sx_average_pps + sx_pps_count + 1) / 2; + + if (rx_pps_count / 8 >= 10) + printk(KERN_INFO "%s: WARNING: Receiving %ld packets per second.\n", + strip_info->dev.name, rx_pps_count / 8); + if (tx_pps_count / 8 >= 10) + printk(KERN_INFO "%s: WARNING: Tx %ld packets per second.\n", + strip_info->dev.name, tx_pps_count / 8); + if (sx_pps_count / 8 >= 10) + printk(KERN_INFO "%s: WARNING: Sending %ld packets per second.\n", + strip_info->dev.name, sx_pps_count / 8); + } + strip_send(strip_info, skb); if (skb) dev_kfree_skb(skb, FREE_WRITE); @@ -1759,6 +1577,17 @@ static int strip_xmit(struct sk_buff *skb, struct device *dev) } /* + * IdleTask periodically calls strip_xmit, so even when we have no IP packets + * to send for an extended period of time, the watchdog processing still gets + * done to ensure that the radio stays in Starmode + */ + +static void strip_IdleTask(unsigned long parameter) +{ + strip_xmit(NULL, (struct device *)parameter); +} + +/* * Create the MAC header for an arbitrary protocol layer * * saddr!=NULL means use this specific address (n/a for Metricom) @@ -1772,19 +1601,20 @@ static int strip_xmit(struct sk_buff *skb, struct device *dev) static int strip_header(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len) { + struct strip *strip_info = (struct strip *)(dev->priv); STRIP_Header *header = (STRIP_Header *)skb_push(skb, sizeof(STRIP_Header)); /*printk(KERN_INFO "%s: strip_header 0x%04X %s\n", dev->name, type, type == ETH_P_IP ? "IP" : type == ETH_P_ARP ? "ARP" : "");*/ - memcpy(header->src_addr.c, dev->dev_addr, dev->addr_len); + header->src_addr = strip_info->true_dev_addr; header->protocol = htons(type); /*HexDump("strip_header", (struct strip *)(dev->priv), skb->data, skb->data + skb->len);*/ if (!daddr) return(-dev->hard_header_len); - memcpy(header->dst_addr.c, daddr, dev->addr_len); + header->dst_addr = *(MetricomAddress*)daddr; return(dev->hard_header_len); } @@ -1811,43 +1641,130 @@ static int strip_rebuild_header(struct sk_buff *skb) #endif } + +/************************************************************************/ +/* Receiving routines */ + +static int strip_receive_room(struct tty_struct *tty) +{ + return 0x10000; /* We can handle an infinite amount of data. :-) */ +} + /* - * IdleTask periodically calls strip_xmit, so even when we have no IP packets - * to send for an extended period of time, the watchdog processing still gets - * done to ensure that the radio stays in Starmode + * This function parses the response to the ATS300? command, + * extracting the radio version and serial number. */ - -static void strip_IdleTask(unsigned long parameter) +static void get_radio_version(struct strip *strip_info, __u8 *ptr, __u8 *end) { - strip_xmit(NULL, (struct device *)parameter); + __u8 *p, *value_begin, *value_end; + int len; + + /* Determine the beginning of the second line of the payload */ + p = ptr; + while (p < end && *p != 10) p++; + if (p >= end) return; + p++; + value_begin = p; + + /* Determine the end of line */ + while (p < end && *p != 10) p++; + if (p >= end) return; + value_end = p; + p++; + + len = value_end - value_begin; + len = MIN(len, sizeof(FirmwareVersion) - 1); + if (strip_info->firmware_version.c[0] == 0) + printk(KERN_INFO "%s: Radio Firmware: %.*s\n", + strip_info->dev.name, len, value_begin); + sprintf(strip_info->firmware_version.c, "%.*s", len, value_begin); + + /* Look for the first colon */ + while (p < end && *p != ':') p++; + if (p >= end) return; + /* Skip over the space */ + p += 2; + len = sizeof(SerialNumber) - 1; + if (p + len <= end) { + sprintf(strip_info->serial_number.c, "%.*s", len, p); + } + else { + printk(KERN_DEBUG "STRIP: radio serial number shorter (%d) than expected (%d)\n", + end - p, len); + } } +/* + * This function parses the response to the ATS325? command, + * extracting the radio battery voltage. + */ +static void get_radio_voltage(struct strip *strip_info, __u8 *ptr, __u8 *end) +{ + int len; -/************************************************************************/ -/* Receiving routines */ + len = sizeof(BatteryVoltage) - 1; + if (ptr + len <= end) { + sprintf(strip_info->battery_voltage.c, "%.*s", len, ptr); + } + else { + printk(KERN_DEBUG "STRIP: radio voltage string shorter (%d) than expected (%d)\n", + end - ptr, len); + } +} -static int strip_receive_room(struct tty_struct *tty) +/* + * This function parses the responses to the AT~LA and ATS311 commands, + * which list the radio's neighbours. + */ +static void get_radio_neighbours(MetricomNodeTable *table, __u8 *ptr, __u8 *end) { - return 0x10000; /* We can handle an infinite amount of data. :-) */ + table->num_nodes = 0; + while (ptr < end && table->num_nodes < NODE_TABLE_SIZE) + { + MetricomNode *node = &table->node[table->num_nodes++]; + char *dst = node->c, *limit = dst + sizeof(*node) - 1; + while (ptr < end && *ptr <= 32) ptr++; + while (ptr < end && dst < limit && *ptr != 10) *dst++ = *ptr++; + *dst++ = 0; + while (ptr < end && ptr[-1] != 10) ptr++; + } + do_gettimeofday(&table->timestamp); } -static void get_radio_address(struct strip *strip_info, __u8 *p) +static int get_radio_address(struct strip *strip_info, __u8 *p) { MetricomAddress addr; - string_to_radio_address(&addr, p); + if (string_to_radio_address(&addr, p)) return(1); /* See if our radio address has changed */ - if (memcmp(strip_info->dev.dev_addr, addr.c, sizeof(addr))) + if (memcmp(strip_info->true_dev_addr.c, addr.c, sizeof(addr))) { MetricomAddressString addr_string; radio_address_to_string(&addr, &addr_string); - printk(KERN_INFO "%s: My radio address = %s\n", strip_info->dev.name, addr_string.c); - memcpy(strip_info->dev.dev_addr, addr.c, sizeof(addr)); + printk(KERN_INFO "%s: Radio address = %s\n", strip_info->dev.name, addr_string.c); + strip_info->true_dev_addr = addr; + if (!strip_info->manual_dev_addr) *(MetricomAddress*)strip_info->dev.dev_addr = addr; /* Give the radio a few seconds to get its head straight, then send an arp */ - strip_info->gratuitous_arp = jiffies + 6 * HZ; + strip_info->gratuitous_arp = jiffies + 15 * HZ; strip_info->arp_interval = 1 * HZ; } + return(0); +} + +static int verify_checksum(struct strip *strip_info) +{ + __u8 *p = strip_info->sx_buff; + __u8 *end = strip_info->sx_buff + strip_info->sx_count - 4; + u_short sum = (READHEX16(end[0]) << 12) | (READHEX16(end[1]) << 8) | + (READHEX16(end[2]) << 4) | (READHEX16(end[3])); + while (p < end) sum -= *p++; + if (sum == 0 && strip_info->firmware_level == StructuredMessages) + { + strip_info->firmware_level = ChecksummedMessages; + printk(KERN_INFO "%s: Radio provides message checksums\n", strip_info->dev.name); + } + return(sum == 0); } static void RecvErr(char *msg, struct strip *strip_info) @@ -1860,114 +1777,156 @@ static void RecvErr(char *msg, struct strip *strip_info) static void RecvErr_Message(struct strip *strip_info, __u8 *sendername, const __u8 *msg) { - static const char ERR_001[] = "001"; /* Not in StarMode! */ - static const char ERR_002[] = "002"; /* Remap handle */ - static const char ERR_003[] = "003"; /* Can't resolve name */ - static const char ERR_004[] = "004"; /* Name too small or missing */ - static const char ERR_005[] = "005"; /* Bad count specification */ - static const char ERR_006[] = "006"; /* Header too big */ - static const char ERR_007[] = "007"; /* Body too big */ - static const char ERR_008[] = "008"; /* Bad character in name */ - static const char ERR_009[] = "009"; /* No count or line terminator */ - - if (!strncmp(msg, ERR_001, sizeof(ERR_001)-1)) + if (has_prefix(msg, "001")) /* Not in StarMode! */ { RecvErr("Error Msg:", strip_info); printk(KERN_INFO "%s: Radio %s is not in StarMode\n", strip_info->dev.name, sendername); } - else if (!strncmp(msg, ERR_002, sizeof(ERR_002)-1)) + + else if (has_prefix(msg, "002")) /* Remap handle */ { - RecvErr("Error Msg:", strip_info); -#ifdef notyet /*Kernel doesn't have scanf!*/ - int handle; - __u8 newname[64]; - sscanf(msg, "ERR_002 Remap handle &%d to name %s", &handle, newname); - printk(KERN_INFO "%s: Radio name %s is handle %d\n", - strip_info->dev.name, newname, handle); -#endif + /* We ignore "Remap handle" messages for now */ } - else if (!strncmp(msg, ERR_003, sizeof(ERR_003)-1)) + + else if (has_prefix(msg, "003")) /* Can't resolve name */ { RecvErr("Error Msg:", strip_info); printk(KERN_INFO "%s: Destination radio name is unknown\n", strip_info->dev.name); } - else if (!strncmp(msg, ERR_004, sizeof(ERR_004)-1)) + + else if (has_prefix(msg, "004")) /* Name too small or missing */ { strip_info->watchdog_doreset = jiffies + LongTime; +#if TICKLE_TIMERS + { + struct timeval tv; + do_gettimeofday(&tv); + printk(KERN_INFO "**** Got ERR_004 response at %02d.%06d\n", + tv.tv_sec % 100, tv.tv_usec); + } +#endif if (!strip_info->working) { strip_info->working = TRUE; - printk(KERN_INFO "%s: Radio now in starmode\n", - strip_info->dev.name); + printk(KERN_INFO "%s: Radio now in starmode\n", strip_info->dev.name); /* * If the radio has just entered a working state, we should do our first * probe ASAP, so that we find out our radio address etc. without delay. */ strip_info->watchdog_doprobe = jiffies; } - if (!strip_info->structured_messages && sendername) + if (strip_info->firmware_level == NoStructure && sendername) + { + strip_info->firmware_level = StructuredMessages; + strip_info->next_command = 0; /* Try to enable checksums ASAP */ + printk(KERN_INFO "%s: Radio provides structured messages\n", strip_info->dev.name); + } + if (strip_info->firmware_level >= StructuredMessages) { - strip_info->structured_messages = TRUE; - printk(KERN_INFO "%s: Radio provides structured messages\n", - strip_info->dev.name); + verify_checksum(strip_info); + /* + * If the radio has structured messages but we don't yet have all our information about it, we should do + * probes without delay, until we have gathered all the information + */ + if (!GOT_ALL_RADIO_INFO(strip_info)) strip_info->watchdog_doprobe = jiffies; } } - else if (!strncmp(msg, ERR_005, sizeof(ERR_005)-1)) + + else if (has_prefix(msg, "005")) /* Bad count specification */ RecvErr("Error Msg:", strip_info); - else if (!strncmp(msg, ERR_006, sizeof(ERR_006)-1)) + + else if (has_prefix(msg, "006")) /* Header too big */ RecvErr("Error Msg:", strip_info); - else if (!strncmp(msg, ERR_007, sizeof(ERR_007)-1)) + + else if (has_prefix(msg, "007")) /* Body too big */ { - /* - * Note: This error knocks the radio back into - * command mode. - */ RecvErr("Error Msg:", strip_info); - printk(KERN_ERR "%s: Error! Packet size too big for radio.", + printk(KERN_ERR "%s: Error! Packet size too big for radio.\n", strip_info->dev.name); - strip_info->watchdog_doreset = jiffies; /* Do reset ASAP */ } - else if (!strncmp(msg, ERR_008, sizeof(ERR_008)-1)) + + else if (has_prefix(msg, "008")) /* Bad character in name */ { RecvErr("Error Msg:", strip_info); printk(KERN_ERR "%s: Radio name contains illegal character\n", strip_info->dev.name); } - else if (!strncmp(msg, ERR_009, sizeof(ERR_009)-1)) + + else if (has_prefix(msg, "009")) /* No count or line terminator */ + RecvErr("Error Msg:", strip_info); + + else if (has_prefix(msg, "010")) /* Invalid checksum */ RecvErr("Error Msg:", strip_info); + + else if (has_prefix(msg, "011")) /* Checksum didn't match */ + RecvErr("Error Msg:", strip_info); + + else if (has_prefix(msg, "012")) /* Failed to transmit packet */ + RecvErr("Error Msg:", strip_info); + else RecvErr("Error Msg:", strip_info); } static void process_AT_response(struct strip *strip_info, __u8 *ptr, __u8 *end) { - static const char ATS305[] = "ATS305?"; - static const char ATS300[] = "ATS300?"; - static const char ATS325[] = "ATS325?"; - static const char ATI2[] = "AT~I2 nn"; - - /* Skip to the first newline character */ __u8 *p = ptr; - while (p < end && *p != 10) p++; - if (p >= end) return; - p++; + while (p < end && p[-1] != 10) p++; /* Skip past first newline character */ + /* Now ptr points to the AT command, and p points to the text of the response. */ - if (!strncmp(ptr, ATS305, sizeof(ATS305)-1)) +#if TICKLE_TIMERS { - if (IS_RADIO_ADDRESS(p)) get_radio_address(strip_info, p); - } - else if (!strncmp(ptr, ATS300, sizeof(ATS300)-1)) { - get_radio_version(strip_info, p, end); - } - else if (!strncmp(ptr, ATS325, sizeof(ATS325)-1)) { - get_radio_voltage(strip_info, p, end); + struct timeval tv; + do_gettimeofday(&tv); + printk(KERN_INFO "**** Got AT response %.7s at %02d.%06d\n", + ptr, tv.tv_sec % 100, tv.tv_usec); } - else if (!strncmp(ptr, ATI2, sizeof(ATI2)-1)) { - get_radio_neighbors(strip_info, p, end); +#endif + + if (has_prefix(ptr, "ATS300?" )) get_radio_version(strip_info, p, end); + else if (has_prefix(ptr, "ATS305?" )) get_radio_address(strip_info, p); + else if (has_prefix(ptr, "ATS311?" )) get_radio_neighbours(&strip_info->poletops, p, end); + else if (has_prefix(ptr, "ATS319=7")) verify_checksum(strip_info); + else if (has_prefix(ptr, "ATS325?" )) get_radio_voltage(strip_info, p, end); + else if (has_prefix(ptr, "AT~LA" )) get_radio_neighbours(&strip_info->portables, p, end); + else RecvErr("Unknown AT Response:", strip_info); +} + +static void process_ACK(struct strip *strip_info, __u8 *ptr, __u8 *end) +{ + /* Currently we don't do anything with ACKs from the radio */ +} + +static void process_Info(struct strip *strip_info, __u8 *ptr, __u8 *end) +{ + if (ptr+16 > end) RecvErr("Bad Info Msg:", strip_info); +} + +static struct device *get_strip_dev(struct strip *strip_info) +{ + /* If our hardware address is *manually set* to zero, and we know our */ + /* real radio hardware address, try to find another strip device that has been */ + /* manually set to that address that we can 'transfer ownership' of this packet to */ + if (strip_info->manual_dev_addr && + !memcmp(strip_info->dev.dev_addr, zero_address.c, sizeof(zero_address)) && + memcmp(&strip_info->true_dev_addr, zero_address.c, sizeof(zero_address))) + { + struct device *dev = dev_base; + while (dev) + { + if (dev->type == strip_info->dev.type && + !memcmp(dev->dev_addr, &strip_info->true_dev_addr, sizeof(MetricomAddress))) + { + printk(KERN_INFO "%s: Transferred packet ownership to %s.\n", + strip_info->dev.name, dev->name); + return(dev); + } + dev = dev->next; + } } - else RecvErr("Unknown AT Response:", strip_info); + return(&strip_info->dev); } /* @@ -1979,14 +1938,14 @@ static void deliver_packet(struct strip *strip_info, STRIP_Header *header, __u16 struct sk_buff *skb = dev_alloc_skb(sizeof(STRIP_Header) + packetlen); if (!skb) { - printk(KERN_INFO "%s: memory squeeze, dropping packet.\n", strip_info->dev.name); + printk(KERN_ERR "%s: memory squeeze, dropping packet.\n", strip_info->dev.name); strip_info->rx_dropped++; } else { memcpy(skb_put(skb, sizeof(STRIP_Header)), header, sizeof(STRIP_Header)); memcpy(skb_put(skb, packetlen), strip_info->rx_buff, packetlen); - skb->dev = &strip_info->dev; + skb->dev = get_strip_dev(strip_info); skb->protocol = header->protocol; skb->mac.raw = skb->data; @@ -1997,6 +1956,10 @@ static void deliver_packet(struct strip *strip_info, STRIP_Header *header, __u16 /* Finally, hand the packet up to the next layer (e.g. IP or ARP, etc.) */ strip_info->rx_packets++; + strip_info->rx_pps_count++; +#ifdef EXT_COUNTERS + strip_info->rx_bytes += packetlen; +#endif netif_rx(skb); } } @@ -2005,10 +1968,6 @@ static void process_IP_packet(struct strip *strip_info, STRIP_Header *header, __ { __u16 packetlen; -#if DO_PROC_NET_STRIP_TRACE - __u8 *start_ptr = ptr; -#endif DO_PROC_NET_STRIP_TRACE - /* Decode start of the IP packet header */ ptr = UnStuffData(ptr, end, strip_info->rx_buff, 4); if (!ptr) @@ -2019,9 +1978,9 @@ static void process_IP_packet(struct strip *strip_info, STRIP_Header *header, __ packetlen = ((__u16)strip_info->rx_buff[2] << 8) | strip_info->rx_buff[3]; - if (packetlen > MAX_STRIP_MTU) + if (packetlen > MAX_RECV_MTU) { - printk(KERN_ERR "%s: Dropping oversized receive packet: %d bytes\n", + printk(KERN_INFO "%s: Dropping oversized received IP packet: %d bytes\n", strip_info->dev.name, packetlen); strip_info->rx_dropped++; return; @@ -2045,11 +2004,6 @@ static void process_IP_packet(struct strip *strip_info, STRIP_Header *header, __ header->protocol = htons(ETH_P_IP); -#if DO_PROC_NET_STRIP_TRACE - packet_log(strip_info, strip_info->rx_buff, EntryReceive, header, - packetlen, end-start_ptr, slip_len(strip_info->rx_buff, packetlen)); -#endif DO_PROC_NET_STRIP_TRACE - deliver_packet(strip_info, header, packetlen); } @@ -2058,10 +2012,6 @@ static void process_ARP_packet(struct strip *strip_info, STRIP_Header *header, _ __u16 packetlen; struct arphdr *arphdr = (struct arphdr *)strip_info->rx_buff; -#if DO_PROC_NET_STRIP_TRACE - __u8 *start_ptr = ptr; -#endif DO_PROC_NET_STRIP_TRACE - /* Decode start of the ARP packet */ ptr = UnStuffData(ptr, end, strip_info->rx_buff, 8); if (!ptr) @@ -2072,9 +2022,9 @@ static void process_ARP_packet(struct strip *strip_info, STRIP_Header *header, _ packetlen = 8 + (arphdr->ar_hln + arphdr->ar_pln) * 2; - if (packetlen > MAX_STRIP_MTU) + if (packetlen > MAX_RECV_MTU) { - printk(KERN_ERR "%s: Dropping oversized receive packet: %d bytes\n", + printk(KERN_INFO "%s: Dropping oversized received ARP packet: %d bytes\n", strip_info->dev.name, packetlen); strip_info->rx_dropped++; return; @@ -2100,15 +2050,47 @@ static void process_ARP_packet(struct strip *strip_info, STRIP_Header *header, _ header->protocol = htons(ETH_P_ARP); -#if DO_PROC_NET_STRIP_TRACE - packet_log(strip_info, strip_info->rx_buff, EntryReceive, header, - packetlen, end-start_ptr, slip_len(strip_info->rx_buff, packetlen)); -#endif DO_PROC_NET_STRIP_TRACE - deliver_packet(strip_info, header, packetlen); } -static void process_packet(struct strip *strip_info) +/* + * process_text_message processes a <CR>-terminated block of data received + * from the radio that doesn't begin with a '*' character. All normal + * Starmode communication messages with the radio begin with a '*', + * so any text that does not indicates a serial port error, a radio that + * is in Hayes command mode instead of Starmode, or a radio with really + * old firmware that doesn't frame its Starmode responses properly. + */ +static void process_text_message(struct strip *strip_info) +{ + __u8 *msg = strip_info->sx_buff; + int len = strip_info->sx_count; + + /* Check for anything that looks like it might be our radio name */ + /* (This is here for backwards compatibility with old firmware) */ + if (len == 9 && get_radio_address(strip_info, msg) == 0) return; + + if (text_equal(msg, len, "OK" )) return; /* Ignore 'OK' responses from prior commands */ + if (text_equal(msg, len, "ERROR" )) return; /* Ignore 'ERROR' messages */ + if (text_equal(msg, len, InitString)) return; /* Ignore character echo back from the radio */ + + /* Catch other error messages */ + /* (This is here for backwards compatibility with old firmware) */ + if (has_prefix(msg, "ERR_")) { RecvErr_Message(strip_info, NULL, &msg[4]); return; } + + RecvErr("No initial *", strip_info); +} + +/* + * process_message processes a <CR>-terminated block of data received + * from the radio. If the radio is not in Starmode or has old firmware, + * it may be a line of text in response to an AT command. Ideally, with + * a current radio that's properly in Starmode, all data received should + * be properly framed and checksummed radio message blocks, containing + * either a starmode packet, or a other communication from the radio + * firmware, like "INF_" Info messages and &COMMAND responses. + */ +static void process_message(struct strip *strip_info) { STRIP_Header header = { zero_address, zero_address, 0 }; __u8 *ptr = strip_info->sx_buff; @@ -2116,29 +2098,11 @@ static void process_packet(struct strip *strip_info) __u8 sendername[32], *sptr = sendername; MetricomKey key; - /* Ignore 'OK' responses from prior commands */ - if (strip_info->sx_count == 2 && ptr[0] == 'O' && ptr[1] == 'K') return; - - /* Check for anything that looks like it might be our radio name: dddd-dddd */ - /* (This is here for backwards compatibility with old firmware) */ - if (strip_info->sx_count == 9 && IS_RADIO_ADDRESS(ptr)) - { - get_radio_address(strip_info, ptr); - return; - } - /*HexDump("Receiving", strip_info, ptr, end);*/ /* Check for start of address marker, and then skip over it */ - if (*ptr != '*') - { - /* Catch other error messages */ - if (ptr[0] == 'E' && ptr[1] == 'R' && ptr[2] == 'R' && ptr[3] == '_') - RecvErr_Message(strip_info, NULL, &ptr[4]); - else RecvErr("No initial *", strip_info); - return; - } - ptr++; /* Skip the initial '*' */ + if (*ptr == '*') ptr++; + else { process_text_message(strip_info); return; } /* Copy out the return address */ while (ptr < end && *ptr != '*' && sptr < ARRAY_END(sendername)-1) *sptr++ = *ptr++; @@ -2156,55 +2120,85 @@ static void process_packet(struct strip *strip_info) /* (This is here for backwards compatibility with old firmware) */ if (!strcmp(sendername, "&COMMAND")) { - strip_info->structured_messages = FALSE; + strip_info->firmware_level = NoStructure; + strip_info->next_command = CompatibilityCommand; return; } - if (ptr+4 >= end) + if (ptr+4 > end) { RecvErr("No proto key", strip_info); return; } - /*printk(KERN_INFO "%s: Got packet from \"%s\".\n", strip_info->dev.name, sendername);*/ - - /* - * Fill in (pseudo) source and destination addresses in the packet. - * We assume that the destination address was our address (the radio does not - * tell us this). If the radio supplies a source address, then we use it. - */ - memcpy(&header.dst_addr, strip_info->dev.dev_addr, sizeof(MetricomAddress)); - if (IS_RADIO_ADDRESS(sendername)) string_to_radio_address(&header.src_addr, sendername); - /* Get the protocol key out of the buffer */ key.c[0] = *ptr++; key.c[1] = *ptr++; key.c[2] = *ptr++; key.c[3] = *ptr++; - if (key.l == SIP0Key.l) process_IP_packet(strip_info, &header, ptr, end); - else if (key.l == ARP0Key.l) process_ARP_packet(strip_info, &header, ptr, end); - else if (key.l == ATR_Key.l) process_AT_response(strip_info, ptr, end); - else if (key.l == ERR_Key.l) RecvErr_Message(strip_info, sendername, ptr); - else /* RecvErr("Unrecognized protocol key", strip_info); */ - - /* Note, this "else" block is temporary, until Metricom fix their */ - /* packet corruption bug */ + /* If we're using checksums, verify the checksum at the end of the packet */ + if (strip_info->firmware_level >= ChecksummedMessages) { - RecvErr("Unrecognized protocol key (retrying)", strip_info); - ptr -= 3; /* Back up and try again */ - key.c[0] = *ptr++; - key.c[1] = *ptr++; - key.c[2] = *ptr++; - key.c[3] = *ptr++; - if (key.l == SIP0Key.l) process_IP_packet(strip_info, &header, ptr, end); - else if (key.l == ARP0Key.l) process_ARP_packet(strip_info, &header, ptr, end); - else if (key.l == ATR_Key.l) process_AT_response(strip_info, ptr, end); - else if (key.l == ERR_Key.l) RecvErr_Message(strip_info, sendername, ptr); - else RecvErr("Unrecognized protocol key", strip_info); + end -= 4; /* Chop the last four bytes off the packet (they're the checksum) */ + if (ptr > end) + { + RecvErr("Missing Checksum", strip_info); + return; + } + if (!verify_checksum(strip_info)) + { + RecvErr("Bad Checksum", strip_info); + return; + } } + + /*printk(KERN_INFO "%s: Got packet from \"%s\".\n", strip_info->dev.name, sendername);*/ + + /* + * Fill in (pseudo) source and destination addresses in the packet. + * We assume that the destination address was our address (the radio does not + * tell us this). If the radio supplies a source address, then we use it. + */ + header.dst_addr = strip_info->true_dev_addr; + string_to_radio_address(&header.src_addr, sendername); + +#ifdef EXT_COUNTERS + if (key.l == SIP0Key.l) { + strip_info->rx_rbytes += (end - ptr); + process_IP_packet(strip_info, &header, ptr, end); + } else if (key.l == ARP0Key.l) { + strip_info->rx_rbytes += (end - ptr); + process_ARP_packet(strip_info, &header, ptr, end); + } else if (key.l == ATR_Key.l) { + strip_info->rx_ebytes += (end - ptr); + process_AT_response(strip_info, ptr, end); + } else if (key.l == ACK_Key.l) { + strip_info->rx_ebytes += (end - ptr); + process_ACK(strip_info, ptr, end); + } else if (key.l == INF_Key.l) { + strip_info->rx_ebytes += (end - ptr); + process_Info(strip_info, ptr, end); + } else if (key.l == ERR_Key.l) { + strip_info->rx_ebytes += (end - ptr); + RecvErr_Message(strip_info, sendername, ptr); + } else RecvErr("Unrecognized protocol key", strip_info); +#else + if (key.l == SIP0Key.l) process_IP_packet (strip_info, &header, ptr, end); + else if (key.l == ARP0Key.l) process_ARP_packet (strip_info, &header, ptr, end); + else if (key.l == ATR_Key.l) process_AT_response(strip_info, ptr, end); + else if (key.l == ACK_Key.l) process_ACK (strip_info, ptr, end); + else if (key.l == INF_Key.l) process_Info (strip_info, ptr, end); + else if (key.l == ERR_Key.l) RecvErr_Message (strip_info, sendername, ptr); + else RecvErr("Unrecognized protocol key", strip_info); +#endif } +#define TTYERROR(X) ((X) == TTY_BREAK ? "Break" : \ + (X) == TTY_FRAME ? "Framing Error" : \ + (X) == TTY_PARITY ? "Parity Error" : \ + (X) == TTY_OVERRUN ? "Hardware Overrun" : "Unknown Error") + /* * Handle the 'receiver data ready' interrupt. * This function is called by the 'tty_io' module in the kernel when @@ -2229,17 +2223,23 @@ strip_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int { struct timeval tv; do_gettimeofday(&tv); - printk(KERN_INFO "**** strip_receive_buf: %3d bytes at %d.%06d\n", + printk(KERN_INFO "**** strip_receive_buf: %3d bytes at %02d.%06d\n", count, tv.tv_sec % 100, tv.tv_usec); } #endif +#ifdef EXT_COUNTERS + strip_info->rx_sbytes += count; +#endif + /* Read the characters out of the buffer */ while (cp < end) { + if (fp && *fp) printk(KERN_INFO "%s: %s on serial port\n", strip_info->dev.name, TTYERROR(*fp)); if (fp && *fp++ && !strip_info->discard) /* If there's a serial error, record it */ { - strip_info->discard = 1; + /* If we have some characters in the buffer, discard them */ + strip_info->discard = strip_info->sx_count; strip_info->rx_errors++; } @@ -2249,21 +2249,23 @@ strip_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int if (*cp == 0x0D) /* If end of packet, decide what to do with it */ { if (strip_info->sx_count > 3000) - printk(KERN_INFO "Cut a %d byte packet (%d bytes remaining)%s\n", - strip_info->sx_count, end-cp-1, + printk(KERN_INFO "%s: Cut a %d byte packet (%d bytes remaining)%s\n", + strip_info->dev.name, strip_info->sx_count, end-cp-1, strip_info->discard ? " (discarded)" : ""); if (strip_info->sx_count > strip_info->sx_size) { - strip_info->discard = 1; strip_info->rx_over_errors++; printk(KERN_INFO "%s: sx_buff overflow (%d bytes total)\n", strip_info->dev.name, strip_info->sx_count); } - if (!strip_info->discard) process_packet(strip_info); + else if (strip_info->discard) + printk(KERN_INFO "%s: Discarding bad packet (%d/%d)\n", + strip_info->dev.name, strip_info->discard, strip_info->sx_count); + else process_message(strip_info); strip_info->discard = 0; strip_info->sx_count = 0; } - else if (!strip_info->discard) /* If we're not discarding, store the character */ + else { /* Make sure we have space in the buffer */ if (strip_info->sx_count < strip_info->sx_size) @@ -2279,9 +2281,28 @@ strip_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int /************************************************************************/ /* General control routines */ -static int strip_set_dev_mac_address(struct device *dev, void *addr) +static int set_mac_address(struct strip *strip_info, MetricomAddress *addr) { - return -1; /* You cannot override a Metricom radio's address */ + /* + * We're using a manually specified address if the address is set + * to anything other than all ones. Setting the address to all ones + * disables manual mode and goes back to automatic address determination + * (tracking the true address that the radio has). + */ + strip_info->manual_dev_addr = memcmp(addr->c, broadcast_address.c, sizeof(broadcast_address)); + if (strip_info->manual_dev_addr) + *(MetricomAddress*)strip_info->dev.dev_addr = *addr; + else *(MetricomAddress*)strip_info->dev.dev_addr = strip_info->true_dev_addr; + return 0; +} + +static int dev_set_mac_address(struct device *dev, void *addr) +{ + struct strip *strip_info = (struct strip *)(dev->priv); + struct sockaddr *sa = addr; + printk(KERN_INFO "%s: strip_set_dev_mac_address called\n", dev->name); + set_mac_address(strip_info, (MetricomAddress *)sa->sa_data); + return 0; } static struct net_device_stats *strip_get_stats(struct device *dev) @@ -2341,7 +2362,8 @@ static int strip_open_low(struct device *dev) strip_info->discard = 0; strip_info->working = FALSE; - strip_info->structured_messages = FALSE; + strip_info->firmware_level = NoStructure; + strip_info->next_command = CompatibilityCommand; strip_info->sx_count = 0; strip_info->tx_left = 0; @@ -2442,7 +2464,7 @@ static int strip_dev_init(struct device *dev) dev->rebuild_header = strip_rebuild_header; /* dev->type_trans unused */ /* dev->set_multicast_list unused */ - dev->set_mac_address = strip_set_dev_mac_address; + dev->set_mac_address = dev_set_mac_address; /* dev->do_ioctl unused */ /* dev->set_config unused */ dev->get_stats = strip_get_stats; @@ -2455,19 +2477,10 @@ static int strip_dev_init(struct device *dev) static void strip_free(struct strip *strip_info) { - MetricomNode *node, *free; - *(strip_info->referrer) = strip_info->next; if (strip_info->next) strip_info->next->referrer = strip_info->referrer; strip_info->magic = 0; - - for (node = strip_info->neighbor_list; node != NULL; ) - { - free = node; - node = node->next; - kfree(free); - } kfree(strip_info); } @@ -2522,13 +2535,9 @@ static struct strip *strip_alloc(void) strip_info->idle_timer.data = (long)&strip_info->dev; strip_info->idle_timer.function = strip_IdleTask; - strip_info->neighbor_list = kmalloc(sizeof(MetricomNode), GFP_KERNEL); - strip_info->neighbor_list->type = 0; - strip_info->neighbor_list->next = NULL; - /* Note: strip_info->if_name is currently 8 characters long */ - sprintf(strip_info->if_name, "st%d", channel_id); - strip_info->dev.name = strip_info->if_name; + sprintf(strip_info->if_name.c, "st%d", channel_id); + strip_info->dev.name = strip_info->if_name.c; strip_info->dev.base_addr = channel_id; strip_info->dev.priv = (void*)strip_info; strip_info->dev.next = NULL; @@ -2598,6 +2607,9 @@ static int strip_open(struct tty_struct *tty) #ifdef MODULE MOD_INC_USE_COUNT; #endif + + printk(KERN_INFO "STRIP: device \"%s\" activated\n", strip_info->if_name.c); + /* * Done. We have linked the TTY line to a channel. */ @@ -2627,6 +2639,7 @@ static void strip_close(struct tty_struct *tty) tty->disc_data = 0; strip_info->tty = NULL; + printk(KERN_INFO "STRIP: device \"%s\" closed down\n", strip_info->if_name.c); strip_free(strip_info); tty->disc_data = NULL; #ifdef MODULE @@ -2657,12 +2670,17 @@ static int strip_ioctl(struct tty_struct *tty, struct file *file, err = verify_area(VERIFY_WRITE, (void*)arg, 16); if (err) return -err; - copy_to_user((void*)arg, strip_info->dev.name, - strlen(strip_info->dev.name) + 1); - return 0; + return copy_to_user((void*)arg, strip_info->dev.name, + strlen(strip_info->dev.name) + 1)?-EFAULT:0; case SIOCSIFHWADDR: - return -EINVAL; + { + MetricomAddress addr; + printk(KERN_INFO "%s: SIOCSIFHWADDR\n", strip_info->dev.name); + if(copy_from_user(&addr, (void*)arg, sizeof(MetricomAddress))) + return -EFAULT; + return(set_mac_address(strip_info, &addr)); + } /* * Allow stty to read, but not set, the serial port @@ -2683,32 +2701,6 @@ static int strip_ioctl(struct tty_struct *tty, struct file *file, /* Initialization */ /* - * Registers with the /proc file system to create different /proc/net files. - */ - -static int strip_proc_net_register(unsigned short type, char *file_name, - int (*get_info)(char *, char **, off_t, int, int)) -{ - struct proc_dir_entry *strip_entry; - - strip_entry = kmalloc(sizeof(struct proc_dir_entry), GFP_ATOMIC); - - memset(strip_entry, 0, sizeof(struct proc_dir_entry)); - strip_entry->low_ino = type; - strip_entry->namelen = strlen(file_name); - strip_entry->name = file_name; - strip_entry->mode = S_IFREG | S_IRUGO; - strip_entry->nlink = 1; - strip_entry->uid = 0; - strip_entry->gid = 0; - strip_entry->size = 0; - strip_entry->ops = &proc_net_inode_operations; - strip_entry->get_info = get_info; - - return proc_net_register(strip_entry); -} - -/* * Initialize the STRIP driver. * This routine is called at boot time, to bootstrap the multi-channel * STRIP driver @@ -2722,7 +2714,7 @@ int strip_init_ctrl_dev(struct device *dummy) static struct tty_ldisc strip_ldisc; int status; - printk("STRIP: version %s (unlimited channels)\n", StripVersion); + printk(KERN_INFO "STRIP: Version %s (unlimited channels)\n", StripVersion); /* * Fill in our line protocol discipline, and register it @@ -2747,24 +2739,12 @@ int strip_init_ctrl_dev(struct device *dummy) } /* - * Register the status and trace files with /proc + * Register the status file with /proc */ - -#if DO_PROC_NET_STRIP_STATUS - if (strip_proc_net_register(PROC_NET_STRIP_STATUS, "strip_status", - &strip_get_status_info) != 0) + if (proc_net_register(&proc_strip_get_status_info) != 0) { - printk(KERN_ERR "strip: status strip_proc_net_register() failed.\n"); + printk(KERN_ERR "strip: status proc_net_register() failed.\n"); } -#endif - -#if DO_PROC_NET_STRIP_TRACE - if (strip_proc_net_register(PROC_NET_STRIP_TRACE, "strip_trace", - &strip_get_trace_info) != 0) - { - printk(KERN_ERR "strip: trace strip_proc_net_register() failed.\n"); - } -#endif #ifdef MODULE return status; @@ -2794,16 +2774,12 @@ void cleanup_module(void) while (struct_strip_list) strip_free(struct_strip_list); - /* Unregister with the /proc/net files here. */ - -#if DO_PROC_NET_STRIP_TRACE - proc_net_unregister(PROC_NET_STRIP_TRACE); -#endif -#if DO_PROC_NET_STRIP_STATUS + /* Unregister with the /proc/net file here. */ proc_net_unregister(PROC_NET_STRIP_STATUS); -#endif if ((i = tty_register_ldisc(N_STRIP, NULL))) printk(KERN_ERR "STRIP: can't unregister line discipline (err = %d)\n", i); + + printk(KERN_INFO "STRIP: Module Unloaded\n"); } #endif /* MODULE */ diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c index 7299b0f4b..dca243c4f 100644 --- a/drivers/net/sunhme.c +++ b/drivers/net/sunhme.c @@ -946,7 +946,8 @@ static void happy_meal_init_rings(struct happy_meal *hp, int from_irq) /* Because we reserve afterwards. */ skb_put(skb, (ETH_FRAME_LEN + RX_OFFSET)); - hb->happy_meal_rxd[i].rx_addr = (unsigned int) skb->data; + hb->happy_meal_rxd[i].rx_addr = + (u32) ((unsigned long)skb->data); skb_reserve(skb, RX_OFFSET); hb->happy_meal_rxd[i].rx_flags = (RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16)); @@ -1615,7 +1616,8 @@ static inline void happy_meal_rx(struct happy_meal *hp, struct device *dev, /* Return it to the Happy meal. */ drop_it: hp->net_stats.rx_dropped++; - this->rx_addr = (unsigned int) hp->rx_skbs[elem]->data; + this->rx_addr = + (u32) ((unsigned long)hp->rx_skbs[elem]->data); this->rx_flags = (RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16)); goto next; @@ -1634,7 +1636,8 @@ static inline void happy_meal_rx(struct happy_meal *hp, struct device *dev, hp->rx_skbs[elem] = new_skb; new_skb->dev = dev; skb_put(new_skb, (ETH_FRAME_LEN + RX_OFFSET)); - rxbase[elem].rx_addr = (unsigned int) new_skb->data; + rxbase[elem].rx_addr = + (u32) ((unsigned long)new_skb->data); skb_reserve(new_skb, RX_OFFSET); rxbase[elem].rx_flags = (RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16)); @@ -1655,7 +1658,8 @@ static inline void happy_meal_rx(struct happy_meal *hp, struct device *dev, memcpy(copy_skb->data, skb->data, len); /* Reuse original ring buffer. */ - rxbase[elem].rx_addr = (unsigned int) skb->data; + rxbase[elem].rx_addr = + (u32) ((unsigned long)skb->data); rxbase[elem].rx_flags = (RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16)); @@ -1913,7 +1917,8 @@ static int happy_meal_start_xmit(struct sk_buff *skb, struct device *dev) SXD(("SX<l[%d]e[%d]>", len, entry)); hp->tx_skbs[entry] = skb; - hp->happy_block->happy_meal_txd[entry].tx_addr = (unsigned int) skb->data; + hp->happy_block->happy_meal_txd[entry].tx_addr = + (u32) ((unsigned long)skb->data); hp->happy_block->happy_meal_txd[entry].tx_flags = (TXFLAG_OWN | TXFLAG_SOP | TXFLAG_EOP | (len & TXFLAG_SIZE)); hp->tx_new = NEXT_TX(entry); @@ -2087,6 +2092,8 @@ static inline int happy_meal_ether_init(struct device *dev, struct linux_sbus_de printk("\n"); hp = (struct happy_meal *) dev->priv; + memset(hp, 0, sizeof(*hp)); + hp->happy_sbus_dev = sdev; if(sdev->num_registers != 5) { diff --git a/drivers/net/sunqe.c b/drivers/net/sunqe.c index 18e758c80..6b417e34b 100644 --- a/drivers/net/sunqe.c +++ b/drivers/net/sunqe.c @@ -152,8 +152,8 @@ static void qe_init_rings(struct sunqe *qep, int from_irq) skb_put(skb, ETH_FRAME_LEN); skb_reserve(skb, 34); - /* FIX FOR ULTRA */ - qb->qe_rxd[i].rx_addr = (unsigned int) skb->data; + qb->qe_rxd[i].rx_addr = + (unsigned int) ((unsigned long)skb->data); qb->qe_rxd[i].rx_flags = (RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH)); } @@ -491,7 +491,8 @@ static inline void qe_rx(struct sunqe *qep) drop_it: /* Return it to the QE. */ qep->net_stats.rx_dropped++; - this->rx_addr = (unsigned int) qep->rx_skbs[elem]->data; + this->rx_addr = + (unsigned int) ((unsigned long)qep->rx_skbs[elem]->data); this->rx_flags = (RXD_OWN | (RX_BUF_ALLOC_SIZE & RXD_LENGTH)); goto next; @@ -512,8 +513,8 @@ static inline void qe_rx(struct sunqe *qep) skb_put(new_skb, ETH_FRAME_LEN); skb_reserve(new_skb, 34); - /* FIX FOR ULTRA */ - rxbase[elem].rx_addr = (unsigned int) new_skb->data; + rxbase[elem].rx_addr = + (unsigned int) ((unsigned long)new_skb->data); rxbase[elem].rx_flags = (RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH)); @@ -533,7 +534,8 @@ static inline void qe_rx(struct sunqe *qep) eth_copy_and_sum(copy_skb, (unsigned char *)skb->data, len, 0); /* Reuse original ring buffer. */ - rxbase[elem].rx_addr = (unsigned int) skb->data; + rxbase[elem].rx_addr = + (unsigned int) ((unsigned long)skb->data); rxbase[elem].rx_flags = (RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH)); diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c new file mode 100644 index 000000000..d3d9d0e1c --- /dev/null +++ b/drivers/net/tlan.c @@ -0,0 +1,2309 @@ +/******************************************************************** + * + * Linux ThunderLAN Driver + * + * tlan.c + * by James Banks, james.banks@caldera.com + * + * (C) 1997 Caldera, Inc. + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + ** This file is best viewed/edited with tabstop=4 and colums>=132. + * + ** Useful (if not required) reading: + * + * Texas Instruments, ThunderLAN Programmer's Guide, + * TI Literature Number SPWU013A + * available in PDF format from www.ti.com + * National Semiconductor, DP83840A Data Sheet + * available in PDF format from www.national.com + * Microchip Technology, 24C01A/02A/04A Data Sheet + * available in PDF format from www.microchip.com + * + ********************************************************************/ + + +#include <linux/module.h> + + +#include "tlan.h" + + +#include <linux/bios32.h> +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/etherdevice.h> + + + + +#ifdef MODULE + static struct device *TLanDevices = NULL; + static int TLanDevicesInstalled = 0; +#endif + static int debug = 0; + static u8 *TLanPadBuffer; + static char TLanSignature[] = "TLAN"; + static int TLanVersionMajor = 0; + static int TLanVersionMinor = 27; + + static TLanPciId TLanDeviceList[] = { + { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10, "Compaq Netelligent 10" }, + { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10_100, "Compaq Netelligent 10/100" }, + { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETFLEX_3_INTEGRATED, "Compaq Integrated NetFlex-3" }, + { 0, 0, NULL } /* End of List */ + }; + + + static int TLan_MiiReadReg(u16, u16, u16, u16 *); + static void TLan_MiiSendData( u16, u32, unsigned ); + static void TLan_MiiSync(u16); + static void TLan_MiiWriteReg(u16, u16, u16, u16); + + + +/***************************************************************************** +****************************************************************************** + + ThunderLAN Driver MII Routines + + These routines are based on the information in Chap. 2 of the + "ThunderLAN Programmer's Guide", pp. 15-24. + +****************************************************************************** +*****************************************************************************/ + + + /************************************************************************* + * TLan_MiiReadReg + * + * Returns: 0 if ack received ok, 1 otherwise. + * Parms: base_port The base IO port of the adapter in question. + * dev The address of the PHY to be queried. + * reg The register whose contents are to be + * retreived. + * val A pointer to a variable to store the retrieved + * value. + * + * This function uses the TLAN's MII bus to retreive the contents of a + * given register on a PHY. It sends the appropriate info and then + * reads the 16-bit register value from the MII bus via the TLAN SIO + * register. + * + ************************************************************************/ + + int TLan_MiiReadReg(u16 base_port, u16 dev, u16 reg, u16 *val) + { + u8 nack; + u16 sio, tmp; + u32 i; + int err; + + err = FALSE; + outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR); + sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; + + cli(); + + TLan_MiiSync(base_port); + + TLan_ClearBit(TLAN_NET_SIO_MINTEN, sio); /* Disable PHY ints */ + + TLan_MiiSendData( base_port, 0x1, 2 ); /* Start ( 01b ) */ + TLan_MiiSendData( base_port, 0x2, 2 ); /* Read ( 10b ) */ + TLan_MiiSendData( base_port, dev, 5 ); /* Device # */ + TLan_MiiSendData( base_port, reg, 5 ); /* Register # */ + + + TLan_ClearBit(TLAN_NET_SIO_MTXEN, sio); /* Change direction */ + + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Clock through Idle bit */ + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Use this to wait 300ns */ + + nack = TLan_GetBit(TLAN_NET_SIO_MDATA, sio); /* Check for ACK */ + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); /* Finish ACK clock cycle */ + if (nack) { /* No ACK, so fake it */ + for (i = 0; i < 16; i++) { + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); + } + tmp = 0xffff; + err = TRUE; + } else { /* ACKed, so read data */ + for (tmp = 0, i = 0x8000; i; i >>= 1) { + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); + if (TLan_GetBit(TLAN_NET_SIO_MDATA, sio)) + tmp |= i; + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); + } + } + + + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Idle cycle */ + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); + + TLan_SetBit(TLAN_NET_SIO_MINTEN, sio); /* Enable PHY ints */ + + *val = tmp; + + sti(); + + return err; + + } /* TLan_MiiReadReg */ + + + + + /************************************************************************* + * TLan_MiiSendData + * + * Returns: Nothing + * Parms: base_port The base IO port of the adapter in question. + * dev The address of the PHY to be queried. + * data The value to be placed on the MII bus. + * num_bits The number of bits in data that are to be + * placed on the MII bus. + * + * This function sends on sequence of bits on the MII configuration + * bus. + * + ************************************************************************/ + + void TLan_MiiSendData( u16 base_port, u32 data, unsigned num_bits ) + { + u16 sio; + u32 i; + + if ( num_bits == 0 ) + return; + + outw( TLAN_NET_SIO, base_port + TLAN_DIO_ADR ); + sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; + TLan_SetBit( TLAN_NET_SIO_MTXEN, sio ); + + for ( i = ( 0x1 << ( num_bits - 1 ) ); i; i >>= 1 ) { + TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); + TLan_GetBit( TLAN_NET_SIO_MCLK, sio ); + if ( data & i ) + TLan_SetBit( TLAN_NET_SIO_MDATA, sio ); + else + TLan_ClearBit( TLAN_NET_SIO_MDATA, sio ); + TLan_SetBit( TLAN_NET_SIO_MCLK, sio ); + TLan_GetBit( TLAN_NET_SIO_MCLK, sio ); + } + + } /* TLan_MiiSendData */ + + + + + /************************************************************************* + * TLan_MiiSync + * + * Returns: Nothing + * Parms: base_port The base IO port of the adapter in question. + * + * This functions syncs all PHYs in terms of the MII configuration bus. + * + ************************************************************************/ + + void TLan_MiiSync( u16 base_port ) + { + int i; + u16 sio; + + outw( TLAN_NET_SIO, base_port + TLAN_DIO_ADR ); + sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; + + TLan_ClearBit( TLAN_NET_SIO_MTXEN, sio ); + for ( i = 0; i < 32; i++ ) { + TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); + TLan_SetBit( TLAN_NET_SIO_MCLK, sio ); + } + + } /* TLan_MiiSync */ + + + + + /************************************************************************* + * TLan_MiiWriteReg + * + * Returns: Nothing + * Parms: base_port The base IO port of the adapter in question. + * dev The address of the PHY to be written to. + * reg The register whose contents are to be + * written. + * val The value to be written to the register. + * + * This function uses the TLAN's MII bus to write the contents of a + * given register on a PHY. It sends the appropriate info and then + * writes the 16-bit register value from the MII configuration bus + * via the TLAN SIO register. + * + ************************************************************************/ + + void TLan_MiiWriteReg(u16 base_port, u16 dev, u16 reg, u16 val) + { + u16 sio; + + outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR); + sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; + + cli(); + + TLan_MiiSync( base_port ); + + TLan_ClearBit( TLAN_NET_SIO_MINTEN, sio ); + + TLan_MiiSendData( base_port, 0x1, 2 ); /* Start ( 01b ) */ + TLan_MiiSendData( base_port, 0x1, 2 ); /* Write ( 01b ) */ + TLan_MiiSendData( base_port, dev, 5 ); /* Device # */ + TLan_MiiSendData( base_port, reg, 5 ); /* Register # */ + + TLan_MiiSendData( base_port, 0x2, 2 ); /* Send ACK */ + TLan_MiiSendData( base_port, val, 16 ); /* Send Data */ + + TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); /* Idle cycle */ + TLan_SetBit( TLAN_NET_SIO_MCLK, sio ); + + TLan_SetBit( TLAN_NET_SIO_MINTEN, sio ); + + sti(); + + } /* TLan_MiiWriteReg */ + + + + +/***************************************************************************** +****************************************************************************** + + ThunderLAN Driver PHY Layer Routines + + The TLAN chip can drive any number of PHYs (physical devices). Rather + than having lots of 'if' or '#ifdef' statements, I have created a + second driver layer for the PHYs. Each PHY can be identified from its + id in registers 2 and 3, and can be given a Check and Service routine + that will be called when the adapter is reset and when the adapter + receives a Network Status interrupt, respectively. + +****************************************************************************** +*****************************************************************************/ + + static int TLan_PhyNop( struct device * ); + static void TLan_PhyPrint( struct device * ); + static void TLan_PhySelect( struct device * ); + static int TLan_PhyInternalCheck( struct device * ); + static int TLan_PhyInternalService( struct device * ); + static int TLan_PhyDp83840aCheck( struct device * ); + + + + + static TLanPhyIdEntry TLanPhyIdTable[] = { + { 0x4000, 0x5014, &TLan_PhyInternalCheck, &TLan_PhyInternalService, TLAN_PHY_ACTIVITY }, + { 0x4000, 0x5015, &TLan_PhyInternalCheck, &TLan_PhyInternalService, TLAN_PHY_ACTIVITY }, + { 0x2000, 0x5C01, &TLan_PhyDp83840aCheck, &TLan_PhyNop, TLAN_PHY_ACTIVITY | TLAN_PHY_AUTONEG }, + { 0x0000, 0x0000, NULL, NULL, 0 } + }; + + + + + /************************************************************************* + * TLan_PhyPrint + * + * Returns: Nothing + * Parms: dev A pointer to the device structure of the adapter + * which the desired PHY is located. + * + * This function prints the registers a PHY. + * + ************************************************************************/ + + void TLan_PhyPrint( struct device *dev ) + { + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u16 i, data0, data1, data2, data3, phy; + u32 io; + + phy = priv->phyAddr; + io = dev->base_addr; + + if ( ( phy > 0 ) || ( phy <= TLAN_PHY_MAX_ADDR ) ) { + printk( "TLAN: Device %s, PHY 0x%02x.\n", dev->name, phy ); + printk( "TLAN: Off. +0 +1 +2 +3 \n" ); + for ( i = 0; i < 0x20; i+= 4 ) { + TLan_MiiReadReg( io, phy, i, &data0 ); + TLan_MiiReadReg( io, phy, i + 1, &data1 ); + TLan_MiiReadReg( io, phy, i + 2, &data2 ); + TLan_MiiReadReg( io, phy, i + 3, &data3 ); + printk( "TLAN: 0x%02x 0x%04hx 0x%04hx 0x%04hx 0x%04hx\n", i, data0, data1, data2, data3 ); + } + } else { + printk( "TLAN: Device %s, PHY 0x%02x (Invalid).\n", dev->name, phy ); + } + + } /* TLan_PhyPrint */ + + + + + /************************************************************************* + * TLan_PhySelect + * + * Returns: Nothing + * Parms: dev A pointer to the device structure of the adapter + * for which the PHY needs determined. + * + * This function decides which PHY amoung those attached to the TLAN chip + * is to be used. The TLAN chip can be attached to multiple PHYs, and + * the driver needs to decide which one to talk to. Currently this + * routine picks the PHY with the lowest address as the internal PHY + * address is 0x1F, the highest possible. This strategy assumes that + * there can be only one other PHY, and, if it exists, it is the one to + * be used. If token ring PHYs are ever supported, this routine will + * become a little more interesting... + * + ************************************************************************/ + + void TLan_PhySelect( struct device *dev ) + { + int err; + int phy; + int entry; + u16 id_hi; + u16 id_lo; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u16 val; + + priv->phyCheck = &TLan_PhyNop; // Make absolutely sure these aren't NULL + priv->phyService = &TLan_PhyNop; + + for ( phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++ ) { + err = TLan_MiiReadReg( dev->base_addr, phy, 0, &val ); + if ( ! err ) { + TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_HI, &id_hi ); + TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_LO, &id_lo ); + entry = 0; + while ( ( TLanPhyIdTable[entry].idHi != 0 ) && ( TLanPhyIdTable[entry].idLo != 0 ) ) { + if ( ( TLanPhyIdTable[entry].idHi == id_hi ) && ( TLanPhyIdTable[entry].idLo == id_lo ) ) { + priv->phyAddr = phy; + priv->phyEntry = entry; + priv->phyCheck = TLanPhyIdTable[entry].check; + priv->phyService = TLanPhyIdTable[entry].service; + priv->phyFlags = TLanPhyIdTable[entry].flags; + break; + } + entry++; + } + break; + } + } + + } /* TLan_PhySelect */ + + + + + /************************************************************************* + * TLan_PhyNop + * + * Returns: Nothing + * Parms: dev A pointer to a device structure. + * + * This function does nothing and is meant as a stand-in for when + * a Check or Service function would be meaningless. + * + ************************************************************************/ + + int TLan_PhyNop( struct device *dev ) + { + dev = NULL; + return 0; + + } /* TLan_PhyNop */ + + + + + /************************************************************************* + * TLan_PhyInternalCheck + * + * Returns: Nothing + * Parms: dev A pointer to a device structure of the adapter + * holding the PHY to be checked. + * + * This function resets the internal PHY on a TLAN chip. See Chap. 7, + * "Physical Interface (PHY)" of "ThunderLAN Programmer's Guide" + * + ************************************************************************/ + + int TLan_PhyInternalCheck( struct device *dev ) + { + u16 gen_ctl; + int i; + u32 io; + u16 phy; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u16 value; + + io = dev->base_addr; + phy = priv->phyAddr; + + TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl ); + if ( gen_ctl & MII_GC_PDOWN ) { + TLan_MiiSync( io ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK ); + for ( i = 0; i < 500000; i++ ) + SLOW_DOWN_IO; + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK ); + TLan_MiiSync( io ); + } + + TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value ); + while ( value & MII_GC_RESET ) + TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value ); + + // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX ); + // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 ); + + for ( i = 0; i < 500000; i++ ) + SLOW_DOWN_IO; + + // Read Possible Latched Link Status + TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); + // Read Real Link Status + TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); + if ( value & MII_GS_LINK ) { + priv->phyOnline = 1; + TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK ); + } else { + priv->phyOnline = 0; + TLan_DioWrite8( io, TLAN_LED_REG, 0 ); + } + + // Enable Interrupts + TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value ); + value |= TLAN_TC_INTEN; + TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value ); + + return 0; + + } /* TLanPhyInternalCheck */ + + + + + /************************************************************************* + * TLan_PhyInternalService + * + * Returns: Nothing + * Parms: dev A pointer to a device structure of the adapter + * holding the PHY to be serviced. + * + * This function services an interrupt generated by the internal PHY. + * It can turn on/off the link LED. See Chap. 7, + * "Physical Interface (PHY)" of "ThunderLAN Programmer's Guide". + * + ************************************************************************/ + + int TLan_PhyInternalService( struct device *dev ) + { + u16 tlphy_sts; + u16 gen_sts; + u16 an_exp; + u32 io; + u16 phy; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + + io = dev->base_addr; + phy = priv->phyAddr; + + TLan_MiiReadReg( io, phy, TLAN_TLPHY_STS, &tlphy_sts ); + TLan_MiiReadReg( io, phy, MII_GEN_STS, &gen_sts ); + TLan_MiiReadReg( io, phy, MII_AN_EXP, &an_exp ); + if ( gen_sts & MII_GS_LINK ) { + priv->phyOnline = 1; + TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK ); + } else { + priv->phyOnline = 0; + TLan_DioWrite8( io, TLAN_LED_REG, 0 ); + } + + return 0; + + } /* TLan_PhyInternalService */ + + + + + /************************************************************************* + * TLan_PhyDp83840aCheck + * + * Returns: Nothing + * Parms: dev A pointer to a device structure of the adapter + * holding the PHY to be reset. + * + * This function resets a National Semiconductor DP83840A 10/100 Mb/s + * PHY device. See National Semiconductor's data sheet for more info. + * This PHY is used on Compaq Netelligent 10/100 cards. + * + ************************************************************************/ + + static int TLan_PhyDp83840aCheck( struct device *dev ) + { + u16 gen_ctl; + int i; + u32 io; + u16 phy; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u16 value; + + io = dev->base_addr; + phy = priv->phyAddr; + + TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl ); + if ( gen_ctl & MII_GC_PDOWN ) { + TLan_MiiSync( io ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK ); + for ( i = 0; i < 500000; i++ ) + SLOW_DOWN_IO; + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK ); + TLan_MiiSync( io ); + } + + TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value ); + while ( value & MII_GC_RESET ) + TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value ); + + // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX ); + // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1000 ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1200 ); + + for ( i = 0; i < 50000; i++ ) + SLOW_DOWN_IO; + +/* + // Read Possible Latched Link Status + TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); + // Read Real Link Status + TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); + if ( value & MII_GS_LINK ) { + priv->phyOnline = 1; + TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK ); + } else { + priv->phyOnline = 0; + TLan_DioWrite8( io, TLAN_LED_REG, 0 ); + } + + // Enable Interrupts + TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value ); + value |= TLAN_TC_INTEN; + TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value ); +*/ + priv->phyOnline = 1; + + return 0; + + } /* TLan_PhyDp83840aCheck */ + + + + +/***************************************************************************** +****************************************************************************** + + ThunderLAN Driver Adapter Related Routines + +****************************************************************************** +*****************************************************************************/ + + + + static void TLan_ResetLists( struct device * ); + static void TLan_PrintDio( u16 ); + static void TLan_PrintList( TLanList *, char *, int ); + static void TLan_ReadAndClearStats( struct device *, int ); + static int TLan_Reset( struct device * ); + static void TLan_SetMac( struct device *, int areg, char *mac ); + + + + + /************************************************************************* + * TLan_ResetLists + * + * Returns: Nothing + * Parms: dev The device structure with the list stuctures to + * be reset. + * + * This routine sets the variables associated with managing the TLAN + * lists to their initial values. + * + ************************************************************************/ + + void TLan_ResetLists( struct device *dev ) + { + int i; + TLanList *list; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + + priv->txHead = 0; + priv->txTail = 0; + for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) { + list = priv->txList + i; + list->cStat = TLAN_CSTAT_UNUSED; + list->buffer[0].address = virt_to_bus( priv->txBuffer + ( i * TLAN_MAX_FRAME_SIZE ) ); + list->buffer[2].count = 0; + list->buffer[2].address = 0; + } + + priv->rxHead = 0; + priv->rxTail = TLAN_NUM_RX_LISTS - 1; + for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) { + list = priv->rxList + i; + list->cStat = TLAN_CSTAT_READY; + list->frameSize = TLAN_MAX_FRAME_SIZE; + list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER; + list->buffer[0].address = virt_to_bus( priv->rxBuffer + ( i * TLAN_MAX_FRAME_SIZE ) ); + list->buffer[1].count = 0; + list->buffer[1].address = 0; + if ( i < TLAN_NUM_RX_LISTS - 1 ) + list->forward = virt_to_bus( list + 1 ); + else + list->forward = 0; + } + + } /* TLan_ResetLists */ + + + + + /************************************************************************* + * TLan_PrintDio + * + * Returns: Nothing + * Parms: io_base Base IO port of the device of which to print + * DIO registers. + * + * This function prints out all the the internal (DIO) registers of a + * TLAN chip. + * + ************************************************************************/ + + void TLan_PrintDio( u16 io_base ) + { + u32 data0, data1; + int i; + + printk( "TLAN: Contents of internal registers for io base 0x%04hx.\n", io_base ); + printk( "TLAN: Off. +0 +4\n" ); + for ( i = 0; i < 0x4C; i+= 8 ) { + data0 = TLan_DioRead32( io_base, i ); + data1 = TLan_DioRead32( io_base, i + 0x4 ); + printk( "TLAN: 0x%02x 0x%08x 0x%08x\n", i, data0, data1 ); + } + + } /* TLan_PrintDio */ + + + + + /************************************************************************* + * TLan_PrintList + * + * Returns: Nothing + * Parms: list A pointer to the TLanList structure to be printed. + * type A string to designate type of list, "Rx" or "Tx". + * num The index of the list. + * + * This function prints out the contents of the list pointed to by the + * list parameter. + * + ************************************************************************/ + + void TLan_PrintList( TLanList *list, char *type, int num) + { + int i; + + printk( "TLAN: %s List %d at 0x%08x\n", type, num, (u32) list ); + printk( "TLAN: Forward = 0x%08x\n", list->forward ); + printk( "TLAN: CSTAT = 0x%04hx\n", list->cStat ); + printk( "TLAN: Frame Size = 0x%04hx\n", list->frameSize ); + for ( i = 0; i < 10; i++ ) { + printk( "TLAN: Buffer[%d].count, addr = 0x%08x, 0x%08x\n", i, list->buffer[i].count, list->buffer[i].address ); + } + + } /* TLan_PrintList */ + + + + + /************************************************************************* + * TLan_ReadAndClearStats + * + * Returns: Nothing + * Parms: dev Pointer to device structure of adapter to which + * to read stats. + * record Flag indicating whether to add + * + * This functions reads all the internal status registers of the TLAN + * chip, which clears them as a side effect. It then either adds the + * values to the device's status struct, or discards them, depending + * on whether record is TLAN_RECORD (!=0) or TLAN_IGNORE (==0). + * + ************************************************************************/ + + void TLan_ReadAndClearStats( struct device *dev, int record ) + { + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u32 tx_good, tx_under; + u32 rx_good, rx_over; + u32 def_tx, crc, code; + u32 multi_col, single_col; + u32 excess_col, late_col, loss; + + outw( TLAN_GOOD_TX_FRMS, dev->base_addr + TLAN_DIO_ADR ); + tx_good = inb( dev->base_addr + TLAN_DIO_DATA ); + tx_good += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8; + tx_good += inb( dev->base_addr + TLAN_DIO_DATA + 2 ) << 16; + tx_under = inb( dev->base_addr + TLAN_DIO_DATA + 3 ); + + outw( TLAN_GOOD_RX_FRMS, dev->base_addr + TLAN_DIO_ADR ); + rx_good = inb( dev->base_addr + TLAN_DIO_DATA ); + rx_good += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8; + rx_good += inb( dev->base_addr + TLAN_DIO_DATA + 2 ) << 16; + rx_over = inb( dev->base_addr + TLAN_DIO_DATA + 3 ); + + outw( TLAN_DEFERRED_TX, dev->base_addr + TLAN_DIO_ADR ); + def_tx = inb( dev->base_addr + TLAN_DIO_DATA ); + def_tx += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8; + crc = inb( dev->base_addr + TLAN_DIO_DATA + 2 ); + code = inb( dev->base_addr + TLAN_DIO_DATA + 3 ); + + outw( TLAN_MULTICOL_FRMS, dev->base_addr + TLAN_DIO_ADR ); + multi_col = inb( dev->base_addr + TLAN_DIO_DATA ); + multi_col += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8; + single_col = inb( dev->base_addr + TLAN_DIO_DATA + 2 ); + single_col += inb( dev->base_addr + TLAN_DIO_DATA + 3 ) << 8; + + outw( TLAN_EXCESSCOL_FRMS, dev->base_addr + TLAN_DIO_ADR ); + excess_col = inb( dev->base_addr + TLAN_DIO_DATA ); + late_col = inb( dev->base_addr + TLAN_DIO_DATA + 1 ); + loss = inb( dev->base_addr + TLAN_DIO_DATA + 2 ); + + if ( record ) { + priv->stats.rx_packets += rx_good; + priv->stats.rx_errors += rx_over + crc + code; + priv->stats.tx_packets += tx_good; + priv->stats.tx_errors += tx_under + loss; + priv->stats.collisions += multi_col + single_col + excess_col + late_col; + + priv->stats.rx_over_errors += rx_over; + priv->stats.rx_crc_errors += crc; + priv->stats.rx_frame_errors += code; + + priv->stats.tx_aborted_errors += tx_under; + priv->stats.tx_carrier_errors += loss; + } + + } /* TLan_ReadAndClearStats */ + + + + + /************************************************************************* + * TLan_Reset + * + * Returns: 0 + * Parms: dev Pointer to device structure of adapter to be + * reset. + * + * This function resets the adapter and it's physical device. See + * Chap. 3, pp. 9-10 of the "ThunderLAN Programmer's Guide" for details. + * The routine tries to implement what is detailed there, though + * adjustments have been made. + * + ************************************************************************/ + + int TLan_Reset( struct device *dev ) + { + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + int i; + u32 data; + u8 data8; + + // 1. Assume Stat registers have been dealt with. + // 2. Assert reset bit. + data = inl(dev->base_addr + TLAN_HOST_CMD); + data |= TLAN_HC_AD_RST; + outl(data, dev->base_addr + TLAN_HOST_CMD); + // 3. Turn off interrupts. + data = inl(dev->base_addr + TLAN_HOST_CMD); + data |= TLAN_HC_INT_OFF; + outl(data, dev->base_addr + TLAN_HOST_CMD); + // 4. Setup AREGs and HASHs. + for ( i = TLAN_AREG_0; i <= TLAN_HASH_2; i += 4 ) + TLan_DioWrite32( dev->base_addr, (u16) i, 0 ); + // 5. Setup NetConfig register. + outw(TLAN_NET_CONFIG, dev->base_addr + TLAN_DIO_ADR); + outw(TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN, dev->base_addr + TLAN_DIO_DATA); + // 6. Setup BSIZE register. + // Accept defaults, 0x22, for now. + // 7. Setup TX commit in Acommit. + // Allow it to manage itself. + // 8. Load Ld_Tmr in HOST_CMD. + // I don't know what this value should be. I'll try 3. + outl( TLAN_HC_LD_TMR | 0x0, dev->base_addr + TLAN_HOST_CMD ); + // 9. Load Ld_Thr in HOST_CMD. + // Try 1 for now. + outl( TLAN_HC_LD_THR | 0x1, dev->base_addr + TLAN_HOST_CMD ); + // 10. Unreset the MII by setting NMRST (in NetSio) to 1. + outw( TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR ); + TLan_SetBit( TLAN_NET_SIO_NMRST, dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO ); + // 10a. Other. + // 12. Setup the NetMask register. + TLan_DioWrite8( dev->base_addr, TLAN_INT_DIS, TLAN_ID_TX_EOC | TLAN_ID_RX_EOC ); + //TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP | TLAN_NET_CMD_DUPLEX | TLAN_NET_CMD_CAF ); + TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP ); + // 11. Initialize PHYs. + TLan_PhySelect( dev ); + (*priv->phyCheck)( dev ); + if ( debug >= 1 ) + TLan_PhyPrint( dev ); + + //data8 = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5 | TLAN_NET_MASK_MASK6 | TLAN_NET_MASK_MASK7; + data8 = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5 | TLAN_NET_MASK_MASK7; + TLan_DioWrite8( dev->base_addr, TLAN_NET_MASK, data8 ); + TLan_DioWrite16( dev->base_addr, TLAN_MAX_RX, 1550 ); + /* + * 13. Turn on interrupts to host. + * I don't want any interrupts, yet. + */ + /* + data = inl(base_port + TLAN_HOST_CMD); + data |= TLAN_HC_INT_ON; + outl(data, base_port + TLAN_HOST_CMD); + */ + + return 0; + + } /* TLan_Reset */ + + + + + /************************************************************************* + * TLan_SetMac + * + * Returns: Nothing + * Parms: dev Pointer to device structure of adapter on which to + * change the AREG. + * areg The AREG to set the address in (0 - 3). + * mac A pointer to an array of chars. Each element + * stores one byte of the address. IE, it isn't + * in ascii. + * + * This function transfers a MAC address to one of the TLAN AREGs + * (address registers). The TLAN chip locks the register on writing to + * offset 0 and unlocks the register after writing to offset 5. If NULL + * is passed in mac, then the AREG is filled with 0's. + * + ************************************************************************/ + + void TLan_SetMac( struct device *dev, int areg, char *mac ) + { + int i; + + areg *= 6; + + if ( mac != NULL ) { + for ( i = 0; i < 6; i++ ) + TLan_DioWrite8( dev->base_addr, TLAN_AREG_0 + areg + i, mac[i] ); + } else { + for ( i = 0; i < 6; i++ ) + TLan_DioWrite8( dev->base_addr, TLAN_AREG_0 + areg + i, 0 ); + } + + } /* TLan_SetMac */ + + + + +/***************************************************************************** +****************************************************************************** + + ThunderLAN Driver Eeprom routines + + The Compaq Netelligent 10 and 10/100 cards use a Microchip 24C02A EEPROM. + These functions are based on information in Microchip's data sheet. I + don't know how well this functions will work with other EEPROMs. + +****************************************************************************** +*****************************************************************************/ + + static void TLan_EeSendStart( u16 ); + static int TLan_EeSendByte( u16, u8, int ); + static void TLan_EeReceiveByte( u16, u8 *, int ); + static int TLan_EeReadByte( u16, u8, u8 * ); + + + + + /************************************************************************* + * TLan_EeSendStart + * + * Returns: Nothing + * Parms: io_base The IO port base address for the TLAN device + * with the EEPROM to use. + * + * This function sends a start cycle to an EEPROM attached to a TLAN + * chip. + * + ************************************************************************/ + + void TLan_EeSendStart( u16 io_base ) + { + u16 sio; + + outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR ); + sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO; + + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); + TLan_SetBit( TLAN_NET_SIO_ETXEN, sio ); + // + // FIXME: Do I really need these SLOW_DOWN_IOs? + // + SLOW_DOWN_IO; + TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); + SLOW_DOWN_IO; + TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); + SLOW_DOWN_IO; + + } /* TLan_EeSendStart */ + + + + + /************************************************************************* + * TLan_EeSendByte + * + * Returns: If the correct ack was received, 0, otherwise 1 + * Parms: io_base The IO port base address for the TLAN device + * with the EEPROM to use. + * data The 8 bits of information to send to the + * EEPROM. + * stop If TLAN_EEPROM_STOP is passed, a stop cycle is + * sent after the byte is sent after the ack is + * read. + * + * This function sends a byte on the serial EEPROM line, driving the + * clock to send each bit. The function then reverses transmission + * direction and reads an acknowledge bit. + * + ************************************************************************/ + + int TLan_EeSendByte( u16 io_base, u8 data, int stop ) + { + int err; + u8 place; + u16 sio; + + outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR ); + sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO; + + // Assume clock is low, tx is enabled; + for ( place = 0x80; place; place >>= 1 ) { + if ( place & data ) + TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); + else + TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); + } + TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio ); + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + err = TLan_GetBit( TLAN_NET_SIO_EDATA, sio ); + TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_SetBit( TLAN_NET_SIO_ETXEN, sio ); + + if ( ( ! err ) && stop ) { + TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // STOP, raise data while clock is high + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); + } + + return ( err ); + + } /* TLan_EeSendByte */ + + + + + /************************************************************************* + * TLan_EeReceiveByte + * + * Returns: Nothing + * Parms: io_base The IO port base address for the TLAN device + * with the EEPROM to use. + * data An address to a char to hold the data sent + * from the EEPROM. + * stop If TLAN_EEPROM_STOP is passed, a stop cycle is + * sent after the byte is received, and no ack is + * sent. + * + * This function receives 8 bits of data from the EEPROM over the serial + * link. It then sends and ack bit, or no ack and a stop bit. This + * function is used to retrieve data after the address of a byte in the + * EEPROM has been sent. + * + ************************************************************************/ + + void TLan_EeReceiveByte( u16 io_base, u8 *data, int stop ) + { + u8 place; + u16 sio; + + outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR ); + sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO; + *data = 0; + + // Assume clock is low, tx is enabled; + TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio ); + for ( place = 0x80; place; place >>= 1 ) { + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + if ( TLan_GetBit( TLAN_NET_SIO_EDATA, sio ) ) + *data |= place; + TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); + } + + TLan_SetBit( TLAN_NET_SIO_ETXEN, sio ); + if ( ! stop ) { + TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // Ack = 0 + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); + } else { + TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); // No ack = 1 (?) + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // STOP, raise data while clock is high + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); + } + + } /* TLan_EeReceiveByte */ + + + + + /************************************************************************* + * TLan_EeReadByte + * + * Returns: No error = 0, else, the stage at which the error occured. + * Parms: io_base The IO port base address for the TLAN device + * with the EEPROM to use. + * ee_addr The address of the byte in the EEPROM whose + * contents are to be retrieved. + * data An address to a char to hold the data obtained + * from the EEPROM. + * + * This function reads a byte of information from an byte cell in the + * EEPROM. + * + ************************************************************************/ + + int TLan_EeReadByte( u16 io_base, u8 ee_addr, u8 *data ) + { + int err; + + cli(); + + TLan_EeSendStart( io_base ); + err = TLan_EeSendByte( io_base, 0xA0, TLAN_EEPROM_ACK ); + if (err) + return 1; + err = TLan_EeSendByte( io_base, ee_addr, TLAN_EEPROM_ACK ); + if (err) + return 2; + TLan_EeSendStart( io_base ); + err = TLan_EeSendByte( io_base, 0xA1, TLAN_EEPROM_ACK ); + if (err) + return 3; + TLan_EeReceiveByte( io_base, data, TLAN_EEPROM_STOP ); + + sti(); + + return 0; + + } /* TLan_EeReadByte */ + + + + +/***************************************************************************** +****************************************************************************** + + ThunderLAN Driver Interrupt Vectors and Table + + Please see Chap. 4, "Interrupt Handling" of the + "ThunderLAN Programmer's Guide" for more informations on handling + interrupts generated by TLAN based adapters. + +****************************************************************************** +*****************************************************************************/ + + static u32 TLan_HandleInvalid( struct device *, u16 ); + static u32 TLan_HandleTxEOF( struct device *, u16 ); + static u32 TLan_HandleStatOverflow( struct device *, u16 ); + static u32 TLan_HandleRxEOF( struct device *, u16 ); + static u32 TLan_HandleDummy( struct device *, u16 ); + static u32 TLan_HandleTxEOC( struct device *, u16 ); + static u32 TLan_HandleStatusCheck( struct device *, u16 ); + static u32 TLan_HandleRxEOC( struct device *, u16 ); + + + + + typedef u32 (TLanIntVectorFunc)( struct device *, u16 ); + + + + + /************************************************************************* + * TLan_HandleInvalid + * + * Returns: 0 + * Parms: dev Device assigned the IRQ that was raised. + * host_int The contents of the HOST_INT port. + * + * This function handles invalid interrupts. This should never happen + * unless some other adapter is trying to use the IRQ line assigned to + * the device. + * + ************************************************************************/ + + u32 TLan_HandleInvalid( struct device *dev, u16 host_int ) + { + host_int = 0; + // printk( "TLAN: Invalid interrupt on %s.\n", dev->name ); + return 0; + + } /* TLan_HandleInvalid */ + + + /************************************************************************* + * TLan_HandleTxEOF + * + * Returns: 1 + * Parms: dev Device assigned the IRQ that was raised. + * host_int The contents of the HOST_INT port. + * + * This function handles Tx EOF interrupts which are raised by the + * adapter when it has completed sending the contents of a buffer. If + * detemines which list/buffer was completed and resets it. If the + * buffer was the last in the channel (EOC), then the function checks + * to see if another buffer is ready to send, and if so, sends a Tx Go + * command. Finally, the driver activates/continues the activity LED. + * + ************************************************************************/ + + u32 TLan_HandleTxEOF( struct device *dev, u16 host_int ) + { + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + int eoc = 0; + TLanList *head_list; + u32 ack = 1; + + // printk( "TLAN: Handling Tx EOF\n" ); + host_int = 0; + head_list = priv->txList + priv->txHead; + if ( head_list->cStat & TLAN_CSTAT_EOC ) + eoc = 1; + if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) { + printk( "TLAN: Received interrupt for uncompleted TX frame.\n" ); + } + // printk( "Ack %d CSTAT=%hx\n", priv->txHead, head_list->cStat ); + head_list->cStat = TLAN_CSTAT_UNUSED; + dev->tbusy = 0; + priv->txHead++; + if ( priv->txHead >= TLAN_NUM_TX_LISTS ) + priv->txHead = 0; + if ( eoc ) { + // printk( "TLAN: Handling Tx EOC\n" ); + head_list = priv->txList + priv->txHead; + if ( ( head_list->cStat & TLAN_CSTAT_READY ) == TLAN_CSTAT_READY ) { + outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM ); + ack |= TLAN_HC_GO; + } else { + priv->txInProgress = 0; + } + } + TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT ); + if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) { + if ( priv->timerSetAt == 0 ) { + // printk("TxEOF Starting timer...\n"); + priv->timerSetAt = jiffies; + priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY; + priv->timerType = TLAN_TIMER_ACT; + add_timer( &priv->timer ); + } else if ( priv->timerType == TLAN_TIMER_ACT ) { + priv->timerSetAt = jiffies; + // printk("TxEOF continuing timer...\n"); + } + } + + return ack; + + } /* TLan_HandleTxEOF */ + + + + + /************************************************************************* + * TLan_HandleStatOverflow + * + * Returns: 1 + * Parms: dev Device assigned the IRQ that was raised. + * host_int The contents of the HOST_INT port. + * + * This function handles the Statistics Overflow interrupt which means + * that one or more of the TLAN statistics registers has reached 1/2 + * capacity and needs to be read. + * + ************************************************************************/ + + u32 TLan_HandleStatOverflow( struct device *dev, u16 host_int ) + { + host_int = 0; + TLan_ReadAndClearStats( dev, TLAN_RECORD ); + + return 1; + + } /* TLan_HandleStatOverflow */ + + + + + /************************************************************************* + * TLan_HandleRxEOF + * + * Returns: 1 + * Parms: dev Device assigned the IRQ that was raised. + * host_int The contents of the HOST_INT port. + * + * This function handles the Rx EOF interrupt which indicates a frame + * has been received by the adapter from the net and the frame has been + * transferred to memory. The function determines the bounce buffer + * the frame has been loaded into, creates a new sk_buff big enough to + * hold the frame, and sends it to protocol stack. It then resets the + * used buffer and appends it to the end of the list. If the frame was + * the last in the Rx channel (EOC), the function restarts the receive + * channel by sending an Rx Go command to the adapter. Then it activates/ + * continues the the activity LED. + * + ************************************************************************/ + + u32 TLan_HandleRxEOF( struct device *dev, u16 host_int ) + { + u32 ack = 1; + int eoc = 0; + u8 *head_buffer; + TLanList *head_list; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + struct sk_buff *skb; + TLanList *tail_list; + void *t; + + // printk( "TLAN: Handling Rx EOF Head=%d Tail=%d\n", priv->rxHead, priv->rxTail ); + host_int = 0; + head_list = priv->rxList + priv->rxHead; + tail_list = priv->rxList + priv->rxTail; + if ( head_list->cStat & TLAN_CSTAT_EOC ) + eoc = 1; + if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) { + printk( "TLAN: Received interrupt for uncompleted RX frame.\n" ); + } else { + skb = dev_alloc_skb( head_list->frameSize + 7 ); + if ( skb == NULL ) { + printk( "TLAN: Couldn't allocate memory for received data.\n" ); + } else { + head_buffer = priv->rxBuffer + ( priv->rxHead * TLAN_MAX_FRAME_SIZE ); + skb->dev = dev; + skb_reserve( skb, 2 ); + t = (void *) skb_put( skb, head_list->frameSize ); + // printk( " %hd %p %p\n", head_list->frameSize, skb->data, t ); + memcpy( t, head_buffer, head_list->frameSize ); + skb->protocol = eth_type_trans( skb, dev ); + netif_rx( skb ); + } + } + head_list->forward = 0; + head_list->frameSize = TLAN_MAX_FRAME_SIZE; + head_list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER; + tail_list->forward = virt_to_bus( head_list ); + priv->rxHead++; + if ( priv->rxHead >= TLAN_NUM_RX_LISTS ) + priv->rxHead = 0; + priv->rxTail++; + if ( priv->rxTail >= TLAN_NUM_RX_LISTS ) + priv->rxTail = 0; + if ( eoc ) { + // printk( "TLAN: Handling Rx EOC Head=%d Tail=%d\n", priv->rxHead, priv->rxTail ); + head_list = priv->rxList + priv->rxHead; + outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM ); + ack |= TLAN_HC_GO | TLAN_HC_RT; + priv->rxEocCount++; + } + TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT ); + if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) { + if ( priv->timerSetAt == 0 ) { + // printk("RxEOF Starting timer...\n"); + priv->timerSetAt = jiffies; + priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY; + priv->timerType = TLAN_TIMER_ACT; + add_timer( &priv->timer ); + } else if ( priv->timerType == TLAN_TIMER_ACT ) { + // printk("RxEOF tarting continuing timer...\n"); + priv->timerSetAt = jiffies; + } + } + dev->last_rx = jiffies; + + return ack; + + } /* TLan_HandleRxEOF */ + + + + + /************************************************************************* + * TLan_HandleDummy + * + * Returns: 1 + * Parms: dev Device assigned the IRQ that was raised. + * host_int The contents of the HOST_INT port. + * + * This function handles the Dummy interrupt, which is raised whenever + * a test interrupt is generated by setting the Req_Int bit of HOST_CMD + * to 1. + * + ************************************************************************/ + + u32 TLan_HandleDummy( struct device *dev, u16 host_int ) + { + host_int = 0; + printk( "TLAN: Dummy interrupt on %s.\n", dev->name ); + return 1; + + } /* TLan_HandleDummy */ + + + + + /************************************************************************* + * TLan_HandleTxEOC + * + * Returns: 1 + * Parms: dev Device assigned the IRQ that was raised. + * host_int The contents of the HOST_INT port. + * + * This driver is structured to determine EOC occurances by reading the + * CSTAT member of the list structure. Tx EOC interrupts are disabled + * via the DIO INTDIS register. + * + ************************************************************************/ + + u32 TLan_HandleTxEOC( struct device *dev, u16 host_int ) + { + host_int = 0; + printk( "TLAN: Tx EOC interrupt on %s.\n", dev->name ); + return 1; + + } /* TLan_HandleTxEOC */ + + + + + /************************************************************************* + * TLan_HandleStatusCheck + * + * Returns: 0 if Adapter check, 1 if Network Status check. + * Parms: dev Device assigned the IRQ that was raised. + * host_int The contents of the HOST_INT port. + * + * This function handles Adapter Check/Network Status interrupts + * generated by the adapter. It checks the vector in the HOST_INT + * register to determine if it is an Adapter Check interrupt. If so, + * it resets the adapter. Otherwise it clears the status registers + * and services the PHY. + * + ************************************************************************/ + + u32 TLan_HandleStatusCheck( struct device *dev, u16 host_int ) + { + u32 ack; + u32 error; + u8 net_sts; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + + ack = 1; + if ( host_int & TLAN_HI_IV_MASK ) { + error = inl( dev->base_addr + TLAN_CH_PARM ); + printk( "TLAN: Adaptor Check on device %s err = 0x%x\n", dev->name, error ); + TLan_ReadAndClearStats( dev, TLAN_RECORD ); + outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD ); + TLan_ResetLists( dev ); + TLan_Reset( dev ); + dev->tbusy = 0; + TLan_SetMac( dev, 0, dev->dev_addr ); + if ( priv->timerType == 0 ) { + if ( priv->phyFlags & TLAN_PHY_AUTONEG ) { + priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY; + priv->timerSetAt = jiffies; + priv->timerType = TLAN_TIMER_LINK; + add_timer( &priv->timer ); + } else { + //printk( " RX GO---->\n" ); + outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM ); + outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD ); + } + } + ack = 0; + } else { + net_sts = TLan_DioRead8( dev->base_addr, TLAN_NET_STS ); + if ( net_sts ) + TLan_DioWrite8( dev->base_addr, TLAN_NET_STS, net_sts ); + if ( net_sts & TLAN_NET_STS_MIRQ ) + (*priv->phyService)( dev ); + + TLAN_DBG( 1, "TLAN: Status Check! %s Net_Sts=%x\n", dev->name, (unsigned) net_sts ); + } + + return ack; + + } /* TLan_HandleStatusCheck */ + + + + + /************************************************************************* + * TLan_HandleRxEOC + * + * Returns: 1 + * Parms: dev Device assigned the IRQ that was raised. + * host_int The contents of the HOST_INT port. + * + * This driver is structured to determine EOC occurances by reading the + * CSTAT member of the list structure. Rx EOC interrupts are disabled + * via the DIO INTDIS register. + * + ************************************************************************/ + + u32 TLan_HandleRxEOC( struct device *dev, u16 host_int ) + { + host_int = 0; + printk( "TLAN: Rx EOC interrupt on %s.\n", dev->name ); + return 1; + + } /* TLan_HandleRxEOC */ + + + + static TLanIntVectorFunc *TLanIntVector[TLAN_INT_NUMBER_OF_INTS] = { + TLan_HandleInvalid, + TLan_HandleTxEOF, + TLan_HandleStatOverflow, + TLan_HandleRxEOF, + TLan_HandleDummy, + TLan_HandleTxEOC, + TLan_HandleStatusCheck, + TLan_HandleRxEOC + }; + + + + +/***************************************************************************** +****************************************************************************** + + ThunderLAN Driver Timer Function + +****************************************************************************** +*****************************************************************************/ + + static void TLan_Timer( unsigned long ); + + + + + /************************************************************************* + * TLan_Timer + * + * Returns: Nothing + * Parms: data A value given to add timer when add_timer was + * called. + * + * This function handles timed functionality for the TLAN driver. The + * two current timer uses are for delaying for autonegotionation and + * driving the ACT LED. + * - Autonegotiation requires being allowed about 2 1/2 seconds before + * attempting to transmit a packet. It would be a very bad thing + * to hang the kernel this long, so the driver doesn't allow + * transmission 'til after this time, for certain PHYs. It would + * be much nicer if all PHYs were interrupt-capable like the + * internal PHY. + * - The ACT LED, which shows adapter activity, is driven by the driver, + * and so must be left on for a short period to power up the LED so + * it can be seen. This delay can be changed by changing the + * TLAN_TIMER_ACT_DELAY in tlan.h, if desired. 10 jiffies produces a + * slightly sluggish response. + * + ************************************************************************/ + + void TLan_Timer( unsigned long data ) + { + struct device *dev = (struct device *) data; + u16 gen_sts; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + + // printk( "TLAN: %s Entered Timer, type = %d\n", dev->name, priv->timerType ); + + switch ( priv->timerType ) { + case TLAN_TIMER_LINK: + TLan_MiiReadReg( dev->base_addr, priv->phyAddr, MII_GEN_STS, &gen_sts ); + if ( gen_sts & MII_GS_LINK ) { + priv->phyOnline = 1; + outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM ); + outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD ); + priv->timerSetAt = 0; + priv->timerType = 0; + } else { + priv->timer.expires = jiffies + ( TLAN_TIMER_LINK_DELAY * 2 ); + add_timer( &priv->timer ); + } + break; + case TLAN_TIMER_ACT: + if ( jiffies - priv->timerSetAt >= TLAN_TIMER_ACT_DELAY ) { + TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK ); + priv->timerSetAt = 0; + priv->timerType = 0; + } else { + priv->timer.expires = priv->timerSetAt + TLAN_TIMER_ACT_DELAY; + add_timer( &priv->timer ); + } + break; + default: + break; + } + + } /* TLan_Timer */ + + + + +/***************************************************************************** +****************************************************************************** + + ThunderLAN Driver Primary Functions + + These functions are more or less common to all Linux network drivers. + +****************************************************************************** +*****************************************************************************/ + + static int TLan_PciProbe( u8 *, u8 *, u8 *, u8 *, u32 *, u32 * ); + static int TLan_Init( struct device * ); + static int TLan_Open(struct device *dev); + static int TLan_StartTx(struct sk_buff *, struct device *); + static void TLan_HandleInterrupt(int, void *, struct pt_regs *); + static int TLan_Close(struct device *); + static struct enet_statistics *TLan_GetStats( struct device * ); + static void TLan_SetMulticastList( struct device * ); + + +#ifdef MODULE + + /************************************************************************* + * init_module + * + * Returns: 0 if module installed ok, non-zero if not. + * Parms: None + * + * This function begins the setup of the driver creating a pad buffer, + * finding all TLAN devices (matching TLanDeviceList entries), and + * creating and initializing a device structure for each adapter. + * + ************************************************************************/ + + extern int init_module(void) + { + int failed, found; + size_t dev_size; + struct device *dev; + TLanPrivateInfo *priv; + u8 bus, dfn, irq, rev; + u32 io_base, dl_ix; + + printk("TLAN driver, v%d.%d, (C) 1997 Caldera, Inc.\n", + TLanVersionMajor, + TLanVersionMinor + ); + + TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, GFP_KERNEL | GFP_DMA ); + if ( TLanPadBuffer == NULL ) { + printk( "TLAN: Could not allocate memory for pad buffer.\n" ); + return -ENOMEM; + } + memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE ); + + dev_size = sizeof(struct device) + sizeof(TLanPrivateInfo); + while ( ( found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix ) ) ) { + dev = (struct device *) kmalloc( dev_size, GFP_KERNEL ); + if ( dev == NULL ) { + printk( "TLAN: Could not allocate memory for device.\n" ); + continue; + } + memset( dev, 0, dev_size ); + + dev->priv = priv = ( (void *) dev ) + sizeof(struct device); + dev->name = priv->devName; + strcpy( priv->devName, " " ); + dev->base_addr = io_base; + dev->irq = irq; + dev->init = TLan_Init; + + priv->pciBus = bus; + priv->pciDeviceFn = dfn; + priv->pciRevision = rev; + priv->pciEntry = dl_ix; + + ether_setup( dev ); + + failed = register_netdev( dev ); + + if ( failed ) { + printk( "TLAN: Could not register network device. Freeing struct.\n" ); + kfree( dev ); + } else { + priv->nextDevice = TLanDevices; + TLanDevices = dev; + TLanDevicesInstalled++; + printk("TLAN: %s irq=%2d io=%04x, %s\n", dev->name, (int) irq, io_base, TLanDeviceList[dl_ix].deviceName ); + } + } + + // printk( "TLAN: Found %d device(s).\n", TLanDevicesInstalled ); + + return ( ( TLanDevicesInstalled >= 0 ) ? 0 : -ENODEV ); + + } /* init_module */ + + + + + /************************************************************************* + * cleanup_module + * + * Returns: Nothing + * Parms: None + * + * Goes through the TLanDevices list and frees the device structs and + * memory associated with each device (lists and buffers). It also + * ureserves the IO port regions associated with this device. + * + ************************************************************************/ + + extern void cleanup_module(void) + { + struct device *dev; + TLanPrivateInfo *priv; + + while (TLanDevicesInstalled) { + dev = TLanDevices; + priv = (TLanPrivateInfo *) dev->priv; + if ( priv->dmaStorage ) + kfree( priv->dmaStorage ); + release_region( dev->base_addr, 0x10 ); + unregister_netdev( dev ); + TLanDevices = priv->nextDevice; + kfree( dev ); + TLanDevicesInstalled--; + } + kfree( TLanPadBuffer ); + + } /* cleanup_module */ + + +#else /* MODULE */ + + + + + /************************************************************************* + * tlan_probe + * + * Returns: 0 on success, error code on error + * Parms: dev device struct to use if adapter is found. + * + * The name is lower case to fit in with all the rest of the + * netcard_probe names. This function looks for a/another TLan based + * adapter, setting it up with the provided device struct if one is + * found. + * + ************************************************************************/ + + extern int tlan_probe( struct device *dev ) + { + static int pad_allocated = 0; + int found; + TLanPrivateInfo *priv; + u8 bus, dfn, irq, rev; + u32 io_base, dl_ix; + + found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix ); + TLAN_DBG( 1, "TLAN: Probing...\n" ); + if ( found ) { + dev->priv = kmalloc( sizeof(TLanPrivateInfo), GFP_KERNEL ); + if ( dev->priv == NULL ) { + printk( "TLAN: Could not allocate memory for device.\n" ); + } + memset( dev->priv, 0, sizeof(TLanPrivateInfo) ); + priv = (TLanPrivateInfo *) dev->priv; + + dev->name = priv->devName; + strcpy( priv->devName, " " ); + + dev = init_etherdev( dev, sizeof(TLanPrivateInfo) ); + + dev->base_addr = io_base; + dev->irq = irq; + + priv->pciBus = bus; + priv->pciDeviceFn = dfn; + priv->pciRevision = rev; + priv->pciEntry = dl_ix; + + if ( ! pad_allocated ) { + TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, GFP_KERNEL | GFP_DMA ); + if ( TLanPadBuffer == NULL ) { + printk( "TLAN: Could not allocate memory for pad buffer.\n" ); + } else { + pad_allocated = 1; + memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE ); + } + } + printk("TLAN %d.%d: %s irq=%2d io=%04x, %s\n", TLanVersionMajor, + TLanVersionMinor, + dev->name, + (int) irq, + io_base, + TLanDeviceList[dl_ix].deviceName ); + TLan_Init( dev ); + } + + return ( ( found ) ? 0 : -ENODEV ); + + } /* tlan_probe */ + + +#endif /* MODULE */ + + + + + /************************************************************************* + * TLan_PciProbe + * + * Returns: 1 if another TLAN card was found, 0 if not. + * Parms: pci_bus The PCI bus the card was found on. + * pci_dfn The PCI whatever the card was found at. + * pci_irq The IRQ of the found adapter. + * pci_rev The revision of the adapter. + * pci_io_base The first IO port used by the adapter. + * dl_ix The index in the device list of the adapter. + * + * This function searches for an adapter with PCI vendor and device + * IDs matching those in the TLanDeviceList. The function 'remembers' + * the last device it found, and so finds a new device (if anymore are + * to be found) each time the function is called. It then looks up + * pertinent PCI info and returns it to the caller. + * + ************************************************************************/ + + int TLan_PciProbe( u8 *pci_bus, u8 *pci_dfn, u8 *pci_irq, u8 *pci_rev, u32 *pci_io_base, u32 *dl_ix ) + { + static int dl_index = 0; + static int pci_index = 0; + + int not_found; + u8 pci_latency; + u16 pci_command; + + + if ( ! pcibios_present() ) { + printk( "TLAN: PCI Bios not present.\n" ); + return 0; + } + + for (; TLanDeviceList[dl_index].vendorId != 0; dl_index++) { + not_found = pcibios_find_device( TLanDeviceList[dl_index].vendorId, + TLanDeviceList[dl_index].deviceId, + pci_index, + pci_bus, + pci_dfn + ); + if ( ! not_found ) { + TLAN_DBG( 1, "TLAN: found: Vendor Id = 0x%hx, Device Id = 0x%hx\n", + TLanDeviceList[dl_index].vendorId, + TLanDeviceList[dl_index].deviceId + ); + + pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_REVISION_ID, pci_rev); + pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_INTERRUPT_LINE, pci_irq); + pcibios_read_config_word ( *pci_bus, *pci_dfn, PCI_COMMAND, &pci_command); + pcibios_read_config_dword( *pci_bus, *pci_dfn, PCI_BASE_ADDRESS_0, pci_io_base); + pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_LATENCY_TIMER, &pci_latency); + + if (pci_latency < 0x10) { + pcibios_write_config_byte( *pci_bus, *pci_dfn, PCI_LATENCY_TIMER, 0xff); + TLAN_DBG( 1, "TLAN: Setting latency timer to max.\n"); + } + + if ((pci_command & PCI_COMMAND_IO) && (*pci_io_base & 0x3)) { + *pci_io_base &= PCI_BASE_ADDRESS_IO_MASK; + TLAN_DBG( 1, "TLAN: IO mapping is available at %x.\n", *pci_io_base); + } else { + *pci_io_base = 0; + printk("TLAN: IO mapping not available, ignoring device.\n"); + } + + if (pci_command & PCI_COMMAND_MASTER) { + TLAN_DBG( 1, "TLAN: Bus mastering is active.\n"); + } + + pci_index++; + + if ( *pci_io_base ) { + *dl_ix = dl_index; + return 1; + } + + } else { + pci_index = 0; + } + } + + return 0; + + } /* TLan_PciProbe */ + + + + + /************************************************************************* + * TLan_Init + * + * Returns: 0 on success, error code otherwise. + * Parms: dev The structure of the device to be init'ed. + * + * This function completes the initialization of the device structure + * and driver. It reserves the IO addresses, allocates memory for the + * lists and bounce buffers, retrieves the MAC address from the eeprom + * and assignes the device's methods. + * + ************************************************************************/ + + int TLan_Init( struct device *dev ) + { + int dma_size; + int err; + int i; + TLanPrivateInfo *priv; + + priv = (TLanPrivateInfo *) dev->priv; + + err = check_region( dev->base_addr, 0x10 ); + if ( err ) { + printk( "TLAN: %s: Io port region 0x%lx size 0x%x in use.\n", dev->name, dev->base_addr, 0x10 ); + return -EIO; + } + request_region( dev->base_addr, 0x10, TLanSignature ); + + dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS ) * ( sizeof(TLanList) + TLAN_MAX_FRAME_SIZE ); + priv->dmaStorage = kmalloc( dma_size, GFP_KERNEL | GFP_DMA ); + if ( priv->dmaStorage == NULL ) { + printk( "TLAN: Could not allocate lists and buffers for %s.\n", dev->name ); + return -ENOMEM; + } + memset( priv->dmaStorage, 0, dma_size ); + priv->rxList = (TLanList *) ( ( ( (u32) priv->dmaStorage ) + 7 ) & 0xFFFFFFF8 ); + priv->txList = priv->rxList + TLAN_NUM_RX_LISTS; + priv->rxBuffer = (u8 *) ( priv->txList + TLAN_NUM_TX_LISTS ); + priv->txBuffer = priv->rxBuffer + ( TLAN_NUM_RX_LISTS * TLAN_MAX_FRAME_SIZE ); + + err = 0; + for ( i = 0; i < 6 ; i++ ) + err |= TLan_EeReadByte( dev->base_addr, (u8) 0x83 + i, (u8 *) &dev->dev_addr[i] ); + if ( err ) + printk( "TLAN: %s: Error reading MAC Address from eeprom: %d\n", dev->name, err ); + dev->addr_len = 6; + + dev->open = &TLan_Open; + dev->hard_start_xmit = &TLan_StartTx; + dev->stop = &TLan_Close; + dev->get_stats = &TLan_GetStats; + dev->set_multicast_list = &TLan_SetMulticastList; + + return 0; + + } /* TLan_Init */ + + + + + /************************************************************************* + * TLan_Open + * + * Returns: 0 on success, error code otherwise. + * Parms: dev Structure of device to be opened. + * + * This routine puts the driver and TLAN adapter in a state where it is + * ready to send and receive packets. It allocates the IRQ, resets and + * brings the adapter out of reset, and allows interrupts. It also + * delays the startup for autonegotiation or sends a Rx GO command to + * the adapter, as appropriate. + * + ************************************************************************/ + + int TLan_Open( struct device *dev ) + { + int err; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + + err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev ); + if ( err ) { + printk( "TLAN: %s: IRQ %d already in use.\n", dev->name, dev->irq ); + return -EAGAIN; + } + + MOD_INC_USE_COUNT; + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + /* NOTE: It might not be necessary to read the stats before a + reset if you don't care what the values are. + */ + TLan_ResetLists( dev ); + TLan_ReadAndClearStats( dev, TLAN_IGNORE ); + TLan_Reset( dev ); + TLan_SetMac( dev, 0, dev->dev_addr ); + outb( ( TLAN_HC_INT_ON >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 ); + if ( debug >= 1 ) + outb( ( TLAN_HC_REQ_INT >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 ); + + init_timer( &priv->timer ); + priv->timer.data = (unsigned long) dev; + priv->timer.function = &TLan_Timer; + if ( priv->phyFlags & TLAN_PHY_AUTONEG ) { + priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY; + priv->timerSetAt = jiffies; + priv->timerType = TLAN_TIMER_LINK; + add_timer( &priv->timer ); + } else { + TLAN_DBG( 1, "TLAN: RX GO\n" ); + outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM ); + outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD ); + } + + TLAN_DBG( 1, "TLAN: Device %s opened.\n", dev->name ); + + return 0; + + } /* TLan_Open */ + + + + + /************************************************************************* + * TLan_StartTx + * + * Returns: 0 on success, non-zero on failure. + * Parms: skb A pointer to the sk_buff containing the frame to + * be sent. + * dev The device to send the data on. + * + * This function adds a frame to the Tx list to be sent ASAP. First it + * verifies that the adapter is ready and there is room in the queue. + * Then it sets up the next available list, copies the frame to the + * corresponding buffer. If the adapter Tx channel is idle, it gives + * adapter a Tx Go command on the list, otherwise it sets the forward + * address of the previous list to point to this one. Then it frees + * the sk_buff. + * + ************************************************************************/ + + int TLan_StartTx( struct sk_buff *skb, struct device *dev ) + { + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + TLanList *tail_list; + u8 *tail_buffer; + int pad; + + // printk( "Entering StartTx\n" ); + if ( ! priv->phyOnline ) { + dev_kfree_skb( skb, FREE_WRITE ); + return 0; + } + + tail_list = priv->txList + priv->txTail; + if ( tail_list->cStat != TLAN_CSTAT_UNUSED ) { + // printk( "TLAN: %s TX is busy, Head=%d Tail=%d\n", dev->name, priv->txHead, priv->txTail ); + dev->tbusy = 1; + // printk( "TLAN: Tx is busy.\n"); + priv->txBusyCount++; + return 1; + } + tail_list->forward = 0; + tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE ); + memcpy( tail_buffer, skb->data, skb->len ); + pad = TLAN_MIN_FRAME_SIZE - skb->len; + if ( pad > 0 ) { + tail_list->frameSize = (u16) skb->len + pad; + tail_list->buffer[0].count = (u32) skb->len; + tail_list->buffer[1].count = TLAN_LAST_BUFFER | (u32) pad; + tail_list->buffer[1].address = virt_to_bus( TLanPadBuffer ); + } else { + tail_list->frameSize = (u16) skb->len; + tail_list->buffer[0].count = TLAN_LAST_BUFFER | (u32) skb->len; + tail_list->buffer[1].count = 0; + tail_list->buffer[1].address = 0; + } + // are we transferring? + cli(); + tail_list->cStat = TLAN_CSTAT_READY; + if ( ! priv->txInProgress ) { + priv->txInProgress = 1; + outw( 0x4, dev->base_addr + TLAN_HOST_INT ); + // printk("TLAN: Sending GO for 0%d\n", priv->txTail ); + outl( virt_to_bus( tail_list ), dev->base_addr + TLAN_CH_PARM ); + outl( TLAN_HC_GO | TLAN_HC_ACK, dev->base_addr + TLAN_HOST_CMD ); + } else { + // printk("TLAN: Adding 0%d\n", priv->txTail ); + // Assign previous list to point to this one. If previous has + // already been read, the EOC check in the TX EOF interrupt handler + // will start another transfer. + if ( priv->txTail == 0 ) + ( priv->txList + ( TLAN_NUM_TX_LISTS - 1 ) )->forward = virt_to_bus( tail_list ); + else + ( priv->txList + ( priv->txTail - 1 ) )->forward = virt_to_bus( tail_list ); + } + sti(); + priv->txTail++; + if ( priv->txTail >= TLAN_NUM_TX_LISTS ) + priv->txTail = 0; + + dev_kfree_skb( skb, FREE_WRITE ); + + dev->trans_start = jiffies; + // printk( "Leaving StartTx\n" ); + return 0; + + } /* TLan_StartTx */ + + + + + /************************************************************************* + * TLan_HandleInterrupt + * + * Returns: Nothing + * Parms: irq The line on which the interrupt occurred. + * dev_id A pointer to the device assigned to this irq line. + * regs ??? + * + * This function handles an interrupt generated by its assigned TLAN + * adapter. The function deactivates interrupts on its adapter, records + * the type of interrupt, executes the appropriate subhandler, and + * acknowdges the interrupt to the adapter (thus re-enabling adapter + * interrupts. + * + ************************************************************************/ + + void TLan_HandleInterrupt(int irq, void *dev_id, struct pt_regs *regs) + { + u32 ack; + struct device *dev; + u32 host_cmd; + u16 host_int; + int type; + + dev = (struct device *) dev_id; + + if ( dev->interrupt ) + TLAN_DBG( 1, "TLAN: Re-entering interrupt handler for %s: %d.\n" , dev->name, dev->interrupt ); + dev->interrupt++; + + cli(); + + host_int = inw( dev->base_addr + TLAN_HOST_INT ); + outw( host_int, dev->base_addr + TLAN_HOST_INT ); // Deactivate Ints + + type = ( host_int & TLAN_HI_IT_MASK ) >> 2; + + ack = TLanIntVector[type]( dev, host_int ); + + sti(); + + if ( ack ) { + host_cmd = TLAN_HC_ACK | ack | ( type << 18 ); + outl( host_cmd, dev->base_addr + TLAN_HOST_CMD ); + } + + dev->interrupt--; + + } /* TLan_HandleInterrupts */ + + + + + /************************************************************************* + * TLan_Close + * + * Returns: An error code. + * Parms: dev The device structure of the device to close. + * + * This function shuts down the adapter. It records any stats, puts + * the adapter into reset state, deactivates its time as needed, and + * frees the irq it is using. + * + ************************************************************************/ + + int TLan_Close(struct device *dev) + { + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + + dev->start = 0; + dev->tbusy = 1; + + TLan_ReadAndClearStats( dev, TLAN_RECORD ); + outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD ); + if ( priv->timerSetAt != 0 ) + del_timer( &priv->timer ); + free_irq( dev->irq, dev ); + TLAN_DBG( 1, "TLAN: Device %s closed.\n", dev->name ); + + MOD_DEC_USE_COUNT; + + return 0; + + } /* TLan_Close */ + + + + + /************************************************************************* + * TLan_GetStats + * + * Returns: A pointer to the device's statistics structure. + * Parms: dev The device structure to return the stats for. + * + * This function updates the devices statistics by reading the TLAN + * chip's onboard registers. Then it returns the address of the + * statistics structure. + * + ************************************************************************/ + + struct enet_statistics *TLan_GetStats( struct device *dev ) + { + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + int i; + + /* Should only read stats if open ? */ + TLan_ReadAndClearStats( dev, TLAN_RECORD ); + printk( "TLAN: %s Rx EOC Count: %d\n", dev->name, priv->rxEocCount ); + printk( "TLAN: %s Tx Busy Count: %d\n", dev->name, priv->txBusyCount ); + // printk( "TLAN: Got stats for %s.\n", dev->name ); + TLan_PrintDio( dev->base_addr ); + TLan_PhyPrint( dev ); + for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) + TLan_PrintList( priv->rxList + i, "RX", i ); + for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) + TLan_PrintList( priv->txList + i, "TX", i ); + + return ( &( (TLanPrivateInfo *) dev->priv )->stats ); + + } /* TLan_GetStats */ + + + + + /************************************************************************* + * TLan_SetMulticastList + * + * Returns: Nothing + * Parms: dev The device structure to set the multicast list for. + * + * This function sets the TLAN adaptor to various receive modes. If the + * IFF_PROMISC flag is set, promiscuous mode is acitviated. Otherwise, + * promiscuous mode is turned off. If the IFF_ALLMULTI flag is set, then + * the hash table is set to receive all group addresses. Otherwise, the + * first three multicast addresses are stored in AREG_1-3, and the rest + * are selected via the hash table, as necessary. + * + ************************************************************************/ + + void TLan_SetMulticastList( struct device *dev ) + { + struct dev_mc_list *dmi = dev->mc_list; + u32 hash1 = 0; + u32 hash2 = 0; + int i; + u32 offset; + u8 tmp; + + if ( dev->flags & IFF_PROMISC ) { + tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD ); + TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, tmp | TLAN_NET_CMD_CAF ); + } else { + tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD ); + TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, tmp & ~TLAN_NET_CMD_CAF ); + if ( dev->flags & IFF_ALLMULTI ) { + for ( i = 0; i < 3; i++ ) + TLan_SetMac( dev, i + 1, NULL ); + TLan_DioWrite32( dev->base_addr, TLAN_HASH_1, 0xFFFFFFFF ); + TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, 0xFFFFFFFF ); + } else { + for ( i = 0; i < dev->mc_count; i++ ) { + if ( i < 3 ) { + TLan_SetMac( dev, i + 1, (char *) &dmi->dmi_addr ); + } else { + offset = TLan_HashFunc( (u8 *) &dmi->dmi_addr ); + if ( offset < 32 ) + hash1 |= ( 1 << offset ); + else + hash2 |= ( 1 << ( offset - 32 ) ); + } + dmi = dmi->next; + } + for ( ; i < 3; i++ ) + TLan_SetMac( dev, i + 1, NULL ); + TLan_DioWrite32( dev->base_addr, TLAN_HASH_1, hash1 ); + TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, hash2 ); + } + } + + } /* TLan_SetRxMode */ diff --git a/drivers/net/tlan.h b/drivers/net/tlan.h new file mode 100644 index 000000000..9919fd275 --- /dev/null +++ b/drivers/net/tlan.h @@ -0,0 +1,485 @@ +/******************************************************************** + * + * Linux ThunderLAN Driver + * + * tlan.h + * by James Banks, james.banks@caldera.com + * + * (C) 1997 Caldera, Inc. + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + ** This file is best viewed/edited with tabstop=4, colums>=132 + * + ********************************************************************/ + +#include <asm/io.h> +#include <asm/types.h> +#include <linux/netdevice.h> + + + + /***************************************************************** + * TLan Definitions + * + ****************************************************************/ + + #define FALSE 0 + #define TRUE 1 + + #define TLAN_MIN_FRAME_SIZE 64 + #define TLAN_MAX_FRAME_SIZE 1600 + + #define TLAN_NUM_RX_LISTS 4 + #define TLAN_NUM_TX_LISTS 8 + + #define TLAN_IGNORE 0 + #define TLAN_RECORD 1 + + #define TLAN_DBG(lvl, format, args...) if ( debug >= lvl ) printk( format, ##args ); + + + + + /***************************************************************** + * Device Identification Definitions + * + ****************************************************************/ + + /* NOTE: These should be moved to pci.h someday */ + #define PCI_DEVICE_ID_NETELLIGENT_10 0xAE34 + #define PCI_DEVICE_ID_NETELLIGENT_10_100 0xAE32 + #define PCI_DEVICE_ID_NETFLEX_3_INTEGRATED 0xAE35 + + + typedef struct tlan_pci_id { + u16 vendorId; + u16 deviceId; + char *deviceName; + } TLanPciId; + + + + + /***************************************************************** + * Rx/Tx List Definitions + * + ****************************************************************/ + + #define TLAN_BUFFERS_PER_LIST 10 + #define TLAN_LAST_BUFFER 0x80000000 + #define TLAN_CSTAT_UNUSED 0x8000 + #define TLAN_CSTAT_FRM_CMP 0x4000 + #define TLAN_CSTAT_READY 0x3000 + #define TLAN_CSTAT_EOC 0x0800 + #define TLAN_CSTAT_RX_ERROR 0x0400 + #define TLAN_CSTAT_PASS_CRC 0x0200 + #define TLAN_CSTAT_DP_PR 0x0100 + + + typedef struct tlan_buffer_ref_tag { + u32 count; + u32 address; + } TLanBufferRef; + + + typedef struct tlan_list_tag { + u32 forward; + u16 cStat; + u16 frameSize; + TLanBufferRef buffer[TLAN_BUFFERS_PER_LIST]; + } TLanList; + + + typedef u8 TLanBuffer[TLAN_MAX_FRAME_SIZE]; + + + + + /***************************************************************** + * PHY definitions + * + ****************************************************************/ + + #define TLAN_PHY_MAX_ADDR 0x1F + #define TLAN_PHY_INTERNAL 0x1F + + #define TLAN_PHY_ACTIVITY 0x00000001 + #define TLAN_PHY_AUTONEG 0x00000002 + + + typedef int (TLanPhyFunc)( struct device * ); + + + typedef struct tlan_phy_id_entry_tag { + u16 idHi; + u16 idLo; + TLanPhyFunc *check; + TLanPhyFunc *service; + u32 flags; + } TLanPhyIdEntry; + + + + + /***************************************************************** + * TLAN Private Information Structure + * + ****************************************************************/ + + typedef struct tlan_private_tag { + struct device *nextDevice; + void *dmaStorage; + u8 *padBuffer; + TLanList *rxList; + u8 *rxBuffer; + u32 rxHead; + u32 rxTail; + u32 rxEocCount; + TLanList *txList; + u8 *txBuffer; + u32 txHead; + u32 txInProgress; + u32 txTail; + u32 txBusyCount; + u32 phyAddr; + u32 phyEntry; + u32 phyOnline; + u32 phyFlags; + TLanPhyFunc *phyCheck; + TLanPhyFunc *phyService; + u32 timerSetAt; + u32 timerType; + struct timer_list timer; + struct enet_statistics stats; + u32 pciEntry; + u8 pciRevision; + u8 pciBus; + u8 pciDeviceFn; + char devName[8]; + } TLanPrivateInfo; + + + + + /***************************************************************** + * TLan Driver Timer Definitions + * + ****************************************************************/ + + #define TLAN_TIMER_LINK 1 + #define TLAN_TIMER_ACT 2 + + #define TLAN_TIMER_LINK_DELAY 230 + #define TLAN_TIMER_ACT_DELAY 10 + + + + + /***************************************************************** + * TLan Driver Eeprom Definitions + * + ****************************************************************/ + + #define TLAN_EEPROM_ACK 0 + #define TLAN_EEPROM_STOP 1 + + + + + /***************************************************************** + * Host Register Offsets and Contents + * + ****************************************************************/ + + #define TLAN_HOST_CMD 0x00 + #define TLAN_HC_GO 0x80000000 + #define TLAN_HC_STOP 0x40000000 + #define TLAN_HC_ACK 0x20000000 + #define TLAN_HC_CS_MASK 0x1FE00000 + #define TLAN_HC_EOC 0x00100000 + #define TLAN_HC_RT 0x00080000 + #define TLAN_HC_NES 0x00040000 + #define TLAN_HC_AD_RST 0x00008000 + #define TLAN_HC_LD_TMR 0x00004000 + #define TLAN_HC_LD_THR 0x00002000 + #define TLAN_HC_REQ_INT 0x00001000 + #define TLAN_HC_INT_OFF 0x00000800 + #define TLAN_HC_INT_ON 0x00000400 + #define TLAN_HC_AC_MASK 0x000000FF + #define TLAN_CH_PARM 0x04 + #define TLAN_DIO_ADR 0x08 + #define TLAN_DA_ADR_INC 0x8000 + #define TLAN_DA_RAM_ADR 0x4000 + #define TLAN_HOST_INT 0x0A + #define TLAN_HI_IV_MASK 0x1FE0 + #define TLAN_HI_IT_MASK 0x001C + #define TLAN_DIO_DATA 0x0C + + +/* ThunderLAN Internal Register DIO Offsets */ + +#define TLAN_NET_CMD 0x00 +#define TLAN_NET_CMD_NRESET 0x80 +#define TLAN_NET_CMD_NWRAP 0x40 +#define TLAN_NET_CMD_CSF 0x20 +#define TLAN_NET_CMD_CAF 0x10 +#define TLAN_NET_CMD_NOBRX 0x08 +#define TLAN_NET_CMD_DUPLEX 0x04 +#define TLAN_NET_CMD_TRFRAM 0x02 +#define TLAN_NET_CMD_TXPACE 0x01 +#define TLAN_NET_SIO 0x01 +#define TLAN_NET_SIO_MINTEN 0x80 +#define TLAN_NET_SIO_ECLOK 0x40 +#define TLAN_NET_SIO_ETXEN 0x20 +#define TLAN_NET_SIO_EDATA 0x10 +#define TLAN_NET_SIO_NMRST 0x08 +#define TLAN_NET_SIO_MCLK 0x04 +#define TLAN_NET_SIO_MTXEN 0x02 +#define TLAN_NET_SIO_MDATA 0x01 +#define TLAN_NET_STS 0x02 +#define TLAN_NET_STS_MIRQ 0x80 +#define TLAN_NET_STS_HBEAT 0x40 +#define TLAN_NET_STS_TXSTOP 0x20 +#define TLAN_NET_STS_RXSTOP 0x10 +#define TLAN_NET_STS_RSRVD 0x0F +#define TLAN_NET_MASK 0x03 +#define TLAN_NET_MASK_MASK7 0x80 +#define TLAN_NET_MASK_MASK6 0x40 +#define TLAN_NET_MASK_MASK5 0x20 +#define TLAN_NET_MASK_MASK4 0x10 +#define TLAN_NET_MASK_RSRVD 0x0F +#define TLAN_NET_CONFIG 0x04 +#define TLAN_NET_CFG_RCLK 0x8000 +#define TLAN_NET_CFG_TCLK 0x4000 +#define TLAN_NET_CFG_BIT 0x2000 +#define TLAN_NET_CFG_RXCRC 0x1000 +#define TLAN_NET_CFG_PEF 0x0800 +#define TLAN_NET_CFG_1FRAG 0x0400 +#define TLAN_NET_CFG_1CHAN 0x0200 +#define TLAN_NET_CFG_MTEST 0x0100 +#define TLAN_NET_CFG_PHY_EN 0x0080 +#define TLAN_NET_CFG_MSMASK 0x007F +#define TLAN_MAN_TEST 0x06 +#define TLAN_DEF_VENDOR_ID 0x08 +#define TLAN_DEF_DEVICE_ID 0x0A +#define TLAN_DEF_REVISION 0x0C +#define TLAN_DEF_SUBCLASS 0x0D +#define TLAN_DEF_MIN_LAT 0x0E +#define TLAN_DEF_MAX_LAT 0x0F +#define TLAN_AREG_0 0x10 +#define TLAN_AREG_1 0x16 +#define TLAN_AREG_2 0x1C +#define TLAN_AREG_3 0x22 +#define TLAN_HASH_1 0x28 +#define TLAN_HASH_2 0x2C +#define TLAN_GOOD_TX_FRMS 0x30 +#define TLAN_TX_UNDERUNS 0x33 +#define TLAN_GOOD_RX_FRMS 0x34 +#define TLAN_RX_OVERRUNS 0x37 +#define TLAN_DEFERRED_TX 0x38 +#define TLAN_CRC_ERRORS 0x3A +#define TLAN_CODE_ERRORS 0x3B +#define TLAN_MULTICOL_FRMS 0x3C +#define TLAN_SINGLECOL_FRMS 0x3E +#define TLAN_EXCESSCOL_FRMS 0x40 +#define TLAN_LATE_COLS 0x41 +#define TLAN_CARRIER_LOSS 0x42 +#define TLAN_ACOMMIT 0x43 +#define TLAN_LED_REG 0x44 +#define TLAN_LED_ACT 0x10 +#define TLAN_LED_LINK 0x01 +#define TLAN_BSIZE_REG 0x45 +#define TLAN_MAX_RX 0x46 +#define TLAN_INT_DIS 0x48 +#define TLAN_ID_TX_EOC 0x04 +#define TLAN_ID_RX_EOF 0x02 +#define TLAN_ID_RX_EOC 0x01 + + + +/* ThunderLAN Interrupt Codes */ + +#define TLAN_INT_NUMBER_OF_INTS 8 + +#define TLAN_INT_NONE 0x0000 +#define TLAN_INT_TX_EOF 0x0001 +#define TLAN_INT_STAT_OVERFLOW 0x0002 +#define TLAN_INT_RX_EOF 0x0003 +#define TLAN_INT_DUMMY 0x0004 +#define TLAN_INT_TX_EOC 0x0005 +#define TLAN_INT_STATUS_CHECK 0x0006 +#define TLAN_INT_RX_EOC 0x0007 + + + +/* ThunderLAN MII Registers */ + +/* Generic MII/PHY Registers */ + +#define MII_GEN_CTL 0x00 +#define MII_GC_RESET 0x8000 +#define MII_GC_LOOPBK 0x4000 +#define MII_GC_SPEEDSEL 0x2000 +#define MII_GC_AUTOENB 0x1000 +#define MII_GC_PDOWN 0x0800 +#define MII_GC_ISOLATE 0x0400 +#define MII_GC_AUTORSRT 0x0200 +#define MII_GC_DUPLEX 0x0100 +#define MII_GC_COLTEST 0x0080 +#define MII_GC_RESERVED 0x007F +#define MII_GEN_STS 0x01 +#define MII_GS_100BT4 0x8000 +#define MII_GS_100BTXFD 0x4000 +#define MII_GS_100BTXHD 0x2000 +#define MII_GS_10BTFD 0x1000 +#define MII_GS_10BTHD 0x0800 +#define MII_GS_RESERVED 0x07C0 +#define MII_GS_AUTOCMPLT 0x0020 +#define MII_GS_RFLT 0x0010 +#define MII_GS_AUTONEG 0x0008 +#define MII_GS_LINK 0x0004 +#define MII_GS_JABBER 0x0002 +#define MII_GS_EXTCAP 0x0001 +#define MII_GEN_ID_HI 0x02 +#define MII_GEN_ID_LO 0x03 +#define MII_GIL_OUI 0xFC00 +#define MII_GIL_MODEL 0x03F0 +#define MII_GIL_REVISION 0x000F +#define MII_AN_ADV 0x04 +#define MII_AN_LPA 0x05 +#define MII_AN_EXP 0x06 + +/* ThunderLAN Specific MII/PHY Registers */ + +#define TLAN_TLPHY_ID 0x10 +#define TLAN_TLPHY_CTL 0x11 +#define TLAN_TC_IGLINK 0x8000 +#define TLAN_TC_SWAPOL 0x4000 +#define TLAN_TC_AUISEL 0x2000 +#define TLAN_TC_SQEEN 0x1000 +#define TLAN_TC_MTEST 0x0800 +#define TLAN_TC_RESERVED 0x07F8 +#define TLAN_TC_NFEW 0x0004 +#define TLAN_TC_INTEN 0x0002 +#define TLAN_TC_TINT 0x0001 +#define TLAN_TLPHY_STS 0x12 +#define TLAN_TS_MINT 0x8000 +#define TLAN_TS_PHOK 0x4000 +#define TLAN_TS_POLOK 0x2000 +#define TLAN_TS_TPENERGY 0x1000 +#define TLAN_TS_RESERVED 0x0FFF + + + +/* Routines to access internal registers. */ + +inline u8 TLan_DioRead8(u16 base_addr, u16 internal_addr) +{ + outw(internal_addr, base_addr + TLAN_DIO_ADR); + return (inb((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x3))); + +} /* TLan_DioRead8 */ + + + + +inline u16 TLan_DioRead16(u16 base_addr, u16 internal_addr) +{ + outw(internal_addr, base_addr + TLAN_DIO_ADR); + return (inw((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x2))); + +} /* TLan_DioRead16 */ + + + + +inline u32 TLan_DioRead32(u16 base_addr, u16 internal_addr) +{ + outw(internal_addr, base_addr + TLAN_DIO_ADR); + return (inl(base_addr + TLAN_DIO_DATA)); + +} /* TLan_DioRead32 */ + + + + +inline void TLan_DioWrite8(u16 base_addr, u16 internal_addr, u8 data) +{ + outw(internal_addr, base_addr + TLAN_DIO_ADR); + outb(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x3)); + +} + + + + +inline void TLan_DioWrite16(u16 base_addr, u16 internal_addr, u16 data) +{ + outw(internal_addr, base_addr + TLAN_DIO_ADR); + outw(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2)); + +} + + + + +inline void TLan_DioWrite32(u16 base_addr, u16 internal_addr, u32 data) +{ + outw(internal_addr, base_addr + TLAN_DIO_ADR); + outl(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2)); + +} + + + + +inline void TLan_ClearBit(u8 bit, u16 port) +{ + outb_p(inb_p(port) & ~bit, port); +} + + + + +inline int TLan_GetBit(u8 bit, u16 port) +{ + return ((int) (inb_p(port) & bit)); +} + + + + +inline void TLan_SetBit(u8 bit, u16 port) +{ + outb_p(inb_p(port) | bit, port); +} + + +inline u32 xor( u32 a, u32 b ) +{ + return ( ( a && ! b ) || ( ! a && b ) ); +} +#define XOR8( a, b, c, d, e, f, g, h ) xor( a, xor( b, xor( c, xor( d, xor( e, xor( f, xor( g, h ) ) ) ) ) ) ) +#define DA( a, bit ) ( ( (u8) a[bit/8] ) & ( (u8) ( 1 << bit%8 ) ) ) + +inline u32 TLan_HashFunc( u8 *a ) +{ + u32 hash; + + hash = XOR8( DA(a,0), DA(a, 6), DA(a,12), DA(a,18), DA(a,24), DA(a,30), DA(a,36), DA(a,42) ); + hash |= XOR8( DA(a,1), DA(a, 7), DA(a,13), DA(a,19), DA(a,25), DA(a,31), DA(a,37), DA(a,43) ) << 1; + hash |= XOR8( DA(a,2), DA(a, 8), DA(a,14), DA(a,20), DA(a,26), DA(a,32), DA(a,38), DA(a,44) ) << 2; + hash |= XOR8( DA(a,3), DA(a, 9), DA(a,15), DA(a,21), DA(a,27), DA(a,33), DA(a,39), DA(a,45) ) << 3; + hash |= XOR8( DA(a,4), DA(a,10), DA(a,16), DA(a,22), DA(a,28), DA(a,34), DA(a,40), DA(a,46) ) << 4; + hash |= XOR8( DA(a,5), DA(a,11), DA(a,17), DA(a,23), DA(a,29), DA(a,35), DA(a,41), DA(a,47) ) << 5; + + return hash; + +} + + + + +#endif diff --git a/drivers/net/tulip.c b/drivers/net/tulip.c index f87beba00..03b7fc10c 100644 --- a/drivers/net/tulip.c +++ b/drivers/net/tulip.c @@ -1078,6 +1078,7 @@ tulip_rx(struct device *dev) skb->protocol = eth_type_trans(skb,dev); netif_rx(skb); lp->stats.rx_packets++; + lp->stats.rx_bytes+=skb->len; } lp->rx_ring[entry].status = TRING_OWN; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 7118164b6..16dcfafa0 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -204,7 +204,7 @@ struct pci_dev_info dev_info[] = { DEVICE( VIA, VIA_82C586_1, "VT 82C586 Apollo VP-1"), DEVICE( VIA, VIA_82C576, "VT 82C576 3V"), DEVICE( VIA, VIA_82C585, "VT 82C585VP Apollo VP-1"), - DEVICE( VIA, VIA_82C586_0, "VT 82C586 Apollo VP-1"), + DEVICE( VIA, VIA_82C586, "VT 82C586 Apollo VP-1"), DEVICE( VIA, VIA_82C416, "VT 82C416MV"), DEVICE( VORTEX, VORTEX_GDT60x0, "GDT 60x0"), DEVICE( VORTEX, VORTEX_GDT6000B,"GDT 6000b"), diff --git a/drivers/pnp/parport_procfs.c b/drivers/pnp/parport_procfs.c index 77574991b..7adfac2da 100644 --- a/drivers/pnp/parport_procfs.c +++ b/drivers/pnp/parport_procfs.c @@ -1,4 +1,4 @@ -/* $Id: parport_procfs.c,v 1.3.2.6 1997/04/16 21:30:38 phil Exp $ +/* $Id: parport_procfs.c,v 1.2 1997/06/03 07:28:11 ralf Exp $ * Parallel port /proc interface code. * * Authors: David Campbell <campbell@tirian.che.curtin.edu.au> @@ -13,7 +13,6 @@ #include <asm/io.h> #include <asm/dma.h> -#include <linux/config.h> #include <linux/delay.h> #include <linux/errno.h> #include <linux/ioport.h> diff --git a/drivers/sbus/char/bwtwo.c b/drivers/sbus/char/bwtwo.c index ae81e1260..615fef33d 100644 --- a/drivers/sbus/char/bwtwo.c +++ b/drivers/sbus/char/bwtwo.c @@ -1,4 +1,4 @@ -/* $Id: bwtwo.c,v 1.16 1997/06/04 08:27:26 davem Exp $ +/* $Id: bwtwo.c,v 1.18 1997/07/17 02:21:43 davem Exp $ * bwtwo.c: bwtwo console driver * * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) @@ -74,7 +74,8 @@ static int bwtwo_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, long base, fbinfo_t *fb) { - uint size, map_offset, r; + uint size, r; + unsigned long map_offset; int map_size; map_size = size = vma->vm_end - vma->vm_start; @@ -91,9 +92,10 @@ bwtwo_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, map_offset = get_phys ((unsigned long) fb->base); r = io_remap_page_range (vma->vm_start, map_offset, map_size, vma->vm_page_prot, fb->space); - if (r) return -EAGAIN; - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + if (r) + return -EAGAIN; + + vma->vm_dentry = dget(file->f_dentry); return 0; } diff --git a/drivers/sbus/char/cgfourteen.c b/drivers/sbus/char/cgfourteen.c index 2cb4c21c9..40fb9fd70 100644 --- a/drivers/sbus/char/cgfourteen.c +++ b/drivers/sbus/char/cgfourteen.c @@ -1,4 +1,4 @@ -/* $Id: cgfourteen.c,v 1.22 1997/06/04 08:27:27 davem Exp $ +/* $Id: cgfourteen.c,v 1.24 1997/07/17 02:21:44 davem Exp $ * cgfourteen.c: Sun SparcStation console support. * * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx) @@ -166,7 +166,7 @@ cg14_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, long base, fbinfo_t *fb) { uint size, page, r, map_size; - uint map_offset = 0; + unsigned long map_offset = 0; uint ram_size = fb->info.cg14.ramsize; printk ("RAMSIZE=%d\n", ram_size); @@ -269,11 +269,12 @@ cg14_mmap (struct inode *inode, struct file *file, map_offset, map_size, vma->vm_page_prot, fb->space); - if (r) return -EAGAIN; + if (r) + return -EAGAIN; page += map_size; } - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + + vma->vm_dentry = dget(file->f_dentry); return 0; } diff --git a/drivers/sbus/char/cgsix.c b/drivers/sbus/char/cgsix.c index e53fcf09e..825d49b4a 100644 --- a/drivers/sbus/char/cgsix.c +++ b/drivers/sbus/char/cgsix.c @@ -1,4 +1,4 @@ -/* $Id: cgsix.c,v 1.30 1997/06/04 08:27:28 davem Exp $ +/* $Id: cgsix.c,v 1.35 1997/07/17 02:21:45 davem Exp $ * cgsix.c: cgsix frame buffer driver * * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) @@ -233,12 +233,33 @@ cg6_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, long base, fbinfo_t *fb) { uint size, page, r, map_size; - uint map_offset = 0; - + unsigned long map_offset = 0; + size = vma->vm_end - vma->vm_start; if (vma->vm_offset & ~PAGE_MASK) return -ENXIO; +#ifdef __sparc_v9__ + /* Try to align RAM */ +#define ALIGNMENT 0x80000 + map_offset = vma->vm_offset + size; + if (vma->vm_offset <= CG6_RAM && map_offset >= CG6_RAM + fb->type.fb_size) { + struct vm_area_struct *vmm = find_vma(current->mm, vma->vm_start); + int alignment = ALIGNMENT - ((vma->vm_start + CG6_RAM - vma->vm_offset) & (ALIGNMENT - 1)); + int sz = 0, fbsz; + + if (alignment == ALIGNMENT) alignment = 0; + fbsz = ((fb->type.fb_size + ALIGNMENT - 1) & ~(ALIGNMENT - 1)); + if (map_offset < CG6_RAM + fbsz) + sz = fbsz - map_offset + CG6_RAM; + if ((sz || alignment) && (!vmm || vmm->vm_start >= vma->vm_end + sz + alignment)) { + vma->vm_start += alignment; + vma->vm_end += alignment + sz; + } + } +#undef ALIGNMENT +#endif + /* To stop the swapper from even considering these pages */ vma->vm_flags |= FB_MMAP_VM_FLAGS; @@ -247,7 +268,7 @@ cg6_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, switch (vma->vm_offset+page){ case CG6_TEC: map_size = PAGE_SIZE; - map_offset = get_phys ((unsigned long)fb->info.cg6.tec); + map_offset = get_phys ((unsigned long)fb->info.cg6.tec) & PAGE_MASK; break; case CG6_FBC: map_size = PAGE_SIZE; @@ -259,18 +280,25 @@ cg6_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, break; case CG6_THC: map_size = PAGE_SIZE; - map_offset = get_phys ((unsigned long)fb->info.cg6.thc); + map_offset = get_phys ((unsigned long)fb->info.cg6.thc) & PAGE_MASK; break; case CG6_BTREGS: map_size = PAGE_SIZE; map_offset = get_phys ((unsigned long)fb->info.cg6.bt); break; + + /* For Ultra, make sure the following two are right. + * The above two happen to work out (for example FBC and + * TEC will get mapped by one I/O page mapping because + * of the 8192 byte page size, same for FHC/THC. -DaveM + */ + case CG6_DHC: - map_size = PAGE_SIZE * 40; + map_size = /* PAGE_SIZE * 40 */ (4096 * 40); map_offset = get_phys ((unsigned long)fb->info.cg6.dhc); break; case CG6_ROM: - map_size = PAGE_SIZE * 16; + map_size = /* PAGE_SIZE * 16 */ (4096 * 16); map_offset = get_phys ((unsigned long)fb->info.cg6.rom); break; case CG6_RAM: @@ -293,11 +321,12 @@ cg6_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, map_offset, map_size, vma->vm_page_prot, fb->space); - if (r) return -EAGAIN; + if (r) + return -EAGAIN; page += map_size; } - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + + vma->vm_dentry = dget(file->f_dentry); return 0; } @@ -449,14 +478,22 @@ __initfunc(void cg6_setup (fbinfo_t *fb, int slot, u32 cg6, int cg6_io)) sizeof (struct bt_regs), "cgsix_dac", cg6_io, 0); cg6info->fhc = sparc_alloc_io (cg6+CG6_FHC_OFFSET, 0, sizeof (int), "cgsix_fhc", cg6_io, 0); +#if PAGE_SHIFT <= 12 cg6info->thc = sparc_alloc_io (cg6+CG6_THC_OFFSET, 0, sizeof (struct cg6_thc), "cgsix_thc", cg6_io, 0); +#else + cg6info->thc = (struct cg6_thc *)(((char *)cg6info->fhc)+0x1000); +#endif + cg6info->fbc = sparc_alloc_io (cg6+CG6_FBC_OFFSET, 0, + 0x1000, "cgsix_fbc", cg6_io, 0); +#if PAGE_SHIFT <= 12 cg6info->tec = sparc_alloc_io (cg6+CG6_TEC_OFFSET, 0, sizeof (struct cg6_tec), "cgsix_tec", cg6_io, 0); +#else + cg6info->tec = (struct cg6_tec *)(((char *)cg6info->fbc)+0x1000); +#endif cg6info->dhc = sparc_alloc_io (cg6+CG6_DHC_OFFSET, 0, 0x40000, "cgsix_dhc", cg6_io, 0); - cg6info->fbc = sparc_alloc_io (cg6+CG6_FBC_OFFSET, 0, - 0x1000, "cgsix_fbc", cg6_io, 0); cg6info->rom = sparc_alloc_io (cg6+CG6_ROM_OFFSET, 0, 0x10000, "cgsix_rom", cg6_io, 0); if (!fb->base) { @@ -477,6 +514,10 @@ __initfunc(void cg6_setup (fbinfo_t *fb, int slot, u32 cg6, int cg6_io)) cg6info->bt->addr = 0x07 << 24; cg6info->bt->control = 0x00 << 24; +#ifdef __sparc_v9__ + printk("VA %016lx ", fb->base); +#endif + printk("TEC Rev %x ", (cg6info->thc->thc_misc >> CG6_THC_MISC_REV_SHIFT) & CG6_THC_MISC_REV_MASK); diff --git a/drivers/sbus/char/cgthree.c b/drivers/sbus/char/cgthree.c index 0e1446c0e..aea1bff32 100644 --- a/drivers/sbus/char/cgthree.c +++ b/drivers/sbus/char/cgthree.c @@ -1,4 +1,4 @@ -/* $Id: cgthree.c,v 1.21 1997/06/04 08:27:29 davem Exp $ +/* $Id: cgthree.c,v 1.23 1997/07/17 02:21:46 davem Exp $ * cgtree.c: cg3 frame buffer driver * * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) @@ -96,7 +96,7 @@ cg3_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, long base, fbinfo_t *fb) { uint size, page, r, map_size; - uint map_offset = 0; + unsigned long map_offset = 0; size = vma->vm_end - vma->vm_start; if (vma->vm_offset & ~PAGE_MASK) @@ -128,11 +128,12 @@ cg3_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, map_offset, map_size, vma->vm_page_prot, fb->space); - if (r) return -EAGAIN; + if (r) + return -EAGAIN; page += map_size; } - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + + vma->vm_dentry = dget(file->f_dentry); return 0; } diff --git a/drivers/sbus/char/creator.c b/drivers/sbus/char/creator.c index 4ff2caf31..086f9b40e 100644 --- a/drivers/sbus/char/creator.c +++ b/drivers/sbus/char/creator.c @@ -1,13 +1,13 @@ -/* - * creator.c: Linux/Sun Ultra Creator console support. - * - * Copyright (C) 1997 MIguel de Icaza (miguel@nuclecu.unam.mx) +/* $Id: creator.c,v 1.7 1997/07/17 02:21:47 davem Exp $ + * creator.c: Creator/Creator3D frame buffer driver * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ #include <linux/kd.h> #include <linux/tty.h> #include <linux/malloc.h> #include <linux/proc_fs.h> +#include <linux/config.h> #include <asm/sbus.h> #include <asm/io.h> @@ -20,31 +20,575 @@ #include <linux/selection.h> #include <linux/console_struct.h> #include "fb.h" +#include "cg_common.h" + +#define FFB_SFB8R_VOFF 0x00000000 +#define FFB_SFB8G_VOFF 0x00400000 +#define FFB_SFB8B_VOFF 0x00800000 +#define FFB_SFB8X_VOFF 0x00c00000 +#define FFB_SFB32_VOFF 0x01000000 +#define FFB_SFB64_VOFF 0x02000000 +#define FFB_FBC_REGS_VOFF 0x04000000 +#define FFB_BM_FBC_REGS_VOFF 0x04002000 +#define FFB_DFB8R_VOFF 0x04004000 +#define FFB_DFB8G_VOFF 0x04404000 +#define FFB_DFB8B_VOFF 0x04804000 +#define FFB_DFB8X_VOFF 0x04c04000 +#define FFB_DFB24_VOFF 0x05004000 +#define FFB_DFB32_VOFF 0x06004000 +#define FFB_DFB422A_VOFF 0x07004000 /* DFB 422 mode write to A */ +#define FFB_DFB422AD_VOFF 0x07804000 /* DFB 422 mode with line doubling */ +#define FFB_DFB24B_VOFF 0x08004000 /* DFB 24bit mode write to B */ +#define FFB_DFB422B_VOFF 0x09004000 /* DFB 422 mode write to B */ +#define FFB_DFB422BD_VOFF 0x09804000 /* DFB 422 mode with line doubling */ +#define FFB_SFB16Z_VOFF 0x0a004000 /* 16bit mode Z planes */ +#define FFB_SFB8Z_VOFF 0x0a404000 /* 8bit mode Z planes */ +#define FFB_SFB422_VOFF 0x0ac04000 /* SFB 422 mode write to A/B */ +#define FFB_SFB422D_VOFF 0x0b404000 /* SFB 422 mode with line doubling */ +#define FFB_FBC_KREGS_VOFF 0x0bc04000 +#define FFB_DAC_VOFF 0x0bc06000 +#define FFB_PROM_VOFF 0x0bc08000 +#define FFB_EXP_VOFF 0x0bc18000 + +#define FFB_SFB8R_POFF 0x04000000 +#define FFB_SFB8G_POFF 0x04400000 +#define FFB_SFB8B_POFF 0x04800000 +#define FFB_SFB8X_POFF 0x04c00000 +#define FFB_SFB32_POFF 0x05000000 +#define FFB_SFB64_POFF 0x06000000 +#define FFB_FBC_REGS_POFF 0x00600000 +#define FFB_BM_FBC_REGS_POFF 0x00600000 +#define FFB_DFB8R_POFF 0x01000000 +#define FFB_DFB8G_POFF 0x01400000 +#define FFB_DFB8B_POFF 0x01800000 +#define FFB_DFB8X_POFF 0x01c00000 +#define FFB_DFB24_POFF 0x02000000 +#define FFB_DFB32_POFF 0x03000000 +#define FFB_FBC_KREGS_POFF 0x00610000 +#define FFB_DAC_POFF 0x00400000 +#define FFB_PROM_POFF 0x00000000 +#define FFB_EXP_POFF 0x00200000 + +#define FFB_Y_BYTE_ADDR_SHIFT 11 +#define FFB_Y_ADDR_SHIFT 13 + +#define FFB_PPC_ACE_DISABLE 1 +#define FFB_PPC_ACE_AUX_ADD 3 +#define FFB_PPC_ACE_SHIFT 18 +#define FFB_PPC_DCE_DISABLE 2 +#define FFB_PPC_DCE_SHIFT 16 +#define FFB_PPC_ABE_DISABLE 2 +#define FFB_PPC_ABE_SHIFT 14 +#define FFB_PPC_VCE_DISABLE 1 +#define FFB_PPC_VCE_2D 2 +#define FFB_PPC_VCE_SHIFT 12 +#define FFB_PPC_APE_DISABLE 2 +#define FFB_PPC_APE_SHIFT 10 +#define FFB_PPC_CS_VARIABLE 2 +#define FFB_PPC_CS_SHIFT 0 + +#define FFB_FBC_WB_A 1 +#define FFB_FBC_WB_SHIFT 29 +#define FFB_FBC_PGE_MASK 3 +#define FFB_FBC_BE_SHIFT 4 +#define FFB_FBC_GE_SHIFT 2 +#define FFB_FBC_RE_SHIFT 0 + +#define FFB_ROP_NEW 0x83 +#define FFB_ROP_RGB_SHIFT 0 + +#define FFB_UCSR_FIFO_MASK 0x00000fff +#define FFB_UCSR_RP_BUSY 0x02000000 + +struct ffb_fbc { + u8 xxx1[0x200]; + volatile u32 ppc; + u8 xxx2[0x50]; + volatile u32 fbc; + volatile u32 rop; + u8 xxx3[0x34]; + volatile u32 pmask; + u8 xxx4[12]; + volatile u32 clip0min; + volatile u32 clip0max; + volatile u32 clip1min; + volatile u32 clip1max; + volatile u32 clip2min; + volatile u32 clip2max; + volatile u32 clip3min; + volatile u32 clip3max; + u8 xxx5[0x3c]; + volatile u32 unk1; + u8 xxx6[0x500]; + volatile u32 unk2; + u8 xxx7[0xfc]; + volatile u32 ucsr; +}; + +struct ffb_dac { + volatile u32 type; + volatile u32 value; + volatile u32 type2; + volatile u32 value2; +}; + +static void +ffb_restore_palette (fbinfo_t *fbinfo) +{ +} + +static void ffb_blitc(unsigned short, int, int); +static void ffb_setw(int, int, unsigned short, int); +static void ffb_cpyw(int, int, unsigned short *, int); +static void ffb_fill(int, int, int *); + +static struct { + unsigned long voff; + unsigned long poff; + unsigned long size; +} ffbmmap [] = { + { FFB_SFB8R_VOFF, FFB_SFB8R_POFF, 0x0400000 }, + { FFB_SFB8G_VOFF, FFB_SFB8G_POFF, 0x0400000 }, + { FFB_SFB8B_VOFF, FFB_SFB8B_POFF, 0x0400000 }, + { FFB_SFB8X_VOFF, FFB_SFB8X_POFF, 0x0400000 }, + { FFB_SFB32_VOFF, FFB_SFB32_POFF, 0x1000000 }, + { FFB_SFB64_VOFF, FFB_SFB64_POFF, 0x2000000 }, + { FFB_FBC_REGS_VOFF, FFB_FBC_REGS_POFF, 0x0002000 }, + { FFB_BM_FBC_REGS_VOFF, FFB_BM_FBC_REGS_POFF, 0x0002000 }, + { FFB_DFB8R_VOFF, FFB_DFB8R_POFF, 0x0400000 }, + { FFB_DFB8G_VOFF, FFB_DFB8G_POFF, 0x0400000 }, + { FFB_DFB8B_VOFF, FFB_DFB8B_POFF, 0x0400000 }, + { FFB_DFB8X_VOFF, FFB_DFB8X_POFF, 0x0400000 }, + { FFB_DFB24_VOFF, FFB_DFB24_POFF, 0x1000000 }, + { FFB_DFB32_VOFF, FFB_DFB32_POFF, 0x1000000 }, + { FFB_FBC_KREGS_VOFF, FFB_FBC_KREGS_POFF, 0x0002000 }, + { FFB_DAC_VOFF, FFB_DAC_POFF, 0x0002000 }, + { FFB_PROM_VOFF, FFB_PROM_POFF, 0x0010000 }, + { FFB_EXP_VOFF, FFB_EXP_POFF, 0x0002000 } +}; + +/* Ugh: X wants to mmap a bunch of cute stuff at the same time :-( */ +/* So, we just mmap the things that are being asked for */ +static int +ffb_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, + long base, fbinfo_t *fb) +{ + uint size, page, r, map_size; + unsigned long map_offset = 0; + int i; + + size = vma->vm_end - vma->vm_start; + if (vma->vm_offset & ~PAGE_MASK) + return -ENXIO; + + /* Try to align RAM */ +#define ALIGNMENT 0x400000 + map_offset = vma->vm_offset + size; + if (vma->vm_offset < FFB_FBC_REGS_VOFF) { + struct vm_area_struct *vmm = find_vma(current->mm, vma->vm_start); + int alignment = ALIGNMENT - ((vma->vm_start - vma->vm_offset) & (ALIGNMENT - 1)); + + if (alignment == ALIGNMENT) alignment = 0; + if (alignment && (!vmm || vmm->vm_start >= vma->vm_end + alignment)) { + vma->vm_start += alignment; + vma->vm_end += alignment; + } + } +#undef ALIGNMENT + + /* To stop the swapper from even considering these pages */ + vma->vm_flags |= FB_MMAP_VM_FLAGS; + + /* Each page, see which map applies */ + for (page = 0; page < size; ){ + map_size = 0; + for (i = 0; i < sizeof (ffbmmap) / sizeof (ffbmmap[0]); i++) + if (ffbmmap[i].voff == vma->vm_offset+page) { + map_size = ffbmmap[i].size; + map_offset = fb->info.ffb.physbase + ffbmmap[i].poff; + } + + if (!map_size){ + page += PAGE_SIZE; + continue; + } + if (page + map_size > size) + map_size = size - page; + r = io_remap_page_range (vma->vm_start+page, + map_offset, + map_size, vma->vm_page_prot, 0); + if (r) + return -EAGAIN; + page += map_size; + } + + vma->vm_dentry = dget(file->f_dentry); + return 0; +} + +/* XXX write body of these two... */ +static inline int +ffb_wid_get (fbinfo_t *fb, struct fb_wid_list *wl) +{ + struct fb_wid_item *wi; + struct fb_wid_list wlt; + struct fb_wid_item wit[30]; + char *km = NULL; + int i, j; + u32 l; + int err; + +#ifdef CONFIG_SPARC32_COMPAT + if (current->tss.flags & SPARC_FLAG_32BIT) { + if (copy_from_user (&wlt, wl, 2 * sizeof (__u32)) || + __get_user ((long)wlt.wl_list, (((__u32 *)wl)+2))) + return -EFAULT; + } else +#endif + if (copy_from_user (&wlt, wl, sizeof (wlt))) + return -EFAULT; + if (wlt.wl_count <= 30) { + if (copy_from_user (wit, wlt.wl_list, wlt.wl_count * sizeof(*wi))) + return -EFAULT; + wi = wit; + } else if (wlt.wl_count > 120) + return -EINVAL; + else { + wi = (struct fb_wid_item *) km = kmalloc (wlt.wl_count * sizeof (*wi), GFP_KERNEL); + if (!wi) return -ENOMEM; + if (copy_from_user (wi, wlt.wl_list, wlt.wl_count * sizeof(*wi))) { + kfree (wi); + return -EFAULT; + } + } + for (i = 0; i < wlt.wl_count; i++, wi++) { + switch (wi->wi_type) { + case FB_WID_DBL_8: j = (wi->wi_index & 0xf) + 0x40; break; + case FB_WID_DBL_24: j = wi->wi_index & 0x3f; break; + default: return -EINVAL; + } + wi->wi_attrs = 0xffff; + for (j = 0; j < 32; j++) + wi->wi_values [j] = 0; + } + err = 0; + if (copy_to_user (wlt.wl_list, km ? km : (char *)wit, wlt.wl_count * sizeof (*wi))) + err = -EFAULT; + if (km) + kfree (km); + return err; +} + +static inline int +ffb_wid_put (fbinfo_t *fb, struct fb_wid_list *wl) +{ + struct fb_wid_item *wi; + struct fb_wid_list wlt; + struct fb_wid_item wit[30]; + char *km = NULL; + int i, j; + u32 l; + +#ifdef CONFIG_SPARC32_COMPAT + if (current->tss.flags & SPARC_FLAG_32BIT) { + if (copy_from_user (&wlt, wl, 2 * sizeof (__u32)) || + __get_user ((long)wlt.wl_list, (((__u32 *)wl)+2))) + return -EFAULT; + } else +#endif + if (copy_from_user (&wlt, wl, sizeof (wlt))) + return -EFAULT; + if (wlt.wl_count <= 30) { + if (copy_from_user (wit, wlt.wl_list, wlt.wl_count * sizeof(*wi))) + return -EFAULT; + wi = wit; + } else if (wlt.wl_count > 120) + return -EINVAL; + else { + wi = (struct fb_wid_item *) km = kmalloc (wlt.wl_count * sizeof (*wi), GFP_KERNEL); + if (!wi) return -ENOMEM; + if (copy_from_user (wi, wlt.wl_list, wlt.wl_count * sizeof(*wi))) { + kfree (wi); + return -EFAULT; + } + } + for (i = 0; i < wlt.wl_count; i++, wi++) { + switch (wi->wi_type) { + case FB_WID_DBL_8: j = (wi->wi_index & 0xf) + 0x40; break; + case FB_WID_DBL_24: j = wi->wi_index & 0x3f; break; + default: return -EINVAL; + } + /* x = wi->wi_values [j] */; + } + if (km) + kfree (km); + return 0; +} + +static inline void +ffb_curs_enable (fbinfo_t *fb, int enable) +{ + struct ffb_dac *dac = fb->info.ffb.dac; + + dac->type2 = 0x100; + if (fb->info.ffb.dac_rev <= 2) + dac->value2 = enable ? 3 : 0; + else + dac->value2 = enable ? 0 : 3; +} -__initfunc(void creator_setup (fbinfo_t *fb, int slot, int con_node, unsigned long creator, int creator_io)) -{ - uint bases [2]; - unsigned long *p; - - if (!creator) { - prom_getproperty (con_node, "address", (char *) &bases[0], 4); - prom_printf ("Bases: %x %x\n", bases [0], bases [1]); - p = (unsigned long *) creator = bases[0]; - fb->base = creator; - fb->base = 0xff168000; - } - - fb->type.fb_cmsize = 256; - fb->mmap = 0; - fb->loadcmap = 0; - fb->setcursor = 0; - fb->setcursormap = 0; - fb->setcurshape = 0; - fb->ioctl = 0; - fb->switch_from_graph = 0; - fb->postsetup = sun_cg_postsetup; - fb->reset = 0; - fb->blank = 0; - fb->unblank = 0; - fb->type.fb_depth = 8; +static void +ffb_setcursormap (fbinfo_t *fb, unsigned char *red, + unsigned char *green, + unsigned char *blue) +{ + struct ffb_dac *dac = fb->info.ffb.dac; + + ffb_curs_enable (fb, 0); + dac->type2 = 0x102; + dac->value2 = (red[0] | (green[0]<<8) | (blue[0]<<16)); + dac->value2 = (red[1] | (green[1]<<8) | (blue[1]<<16)); +} + +/* Set cursor shape */ +static void +ffb_setcurshape (fbinfo_t *fb) +{ + struct ffb_dac *dac = fb->info.ffb.dac; + int i, j; + + ffb_curs_enable (fb, 0); + for (j = 0; j < 2; j++) { + dac->type2 = j ? 0 : 0x80; + for (i = 0; i < 0x40; i++) { + if (fb->cursor.size.fbx <= 32) { + dac->value2 = fb->cursor.bits [j][i]; + dac->value2 = 0; + } else { + dac->value2 = fb->cursor.bits [j][2*i]; + dac->value2 = fb->cursor.bits [j][2*i+1]; + } + } + } +} + +/* Load cursor information */ +static void +ffb_setcursor (fbinfo_t *fb) +{ + struct ffb_dac *dac = fb->info.ffb.dac; + struct cg_cursor *c = &fb->cursor; + + dac->type2 = 0x104; +/* Should this be just 0x7ff?? Should I do some margin handling and setcurshape + in that case? */ + dac->value2 = (((c->cpos.fbx - c->chot.fbx) & 0xffff) << 16) + |((c->cpos.fby - c->chot.fby) & 0xffff); + ffb_curs_enable (fb, fb->cursor.enable); +} + +static void +ffb_blank (fbinfo_t *fb) +{ +/* XXX Write this */ +} + +static void +ffb_unblank (fbinfo_t *fb) +{ +/* XXX Write this */ +} + +static int ffb_clutstore (fbinfo_t *fb, int offset, int count) +{ + int i; + u32 *clut = fb->info.ffb.clut + offset; + struct ffb_dac *dac = fb->info.ffb.dac; + + dac->type = 0x2000 | offset; + for (i = 0; i < count; i++) + dac->value = *clut++; /* Feed the colors in :)) */ + return 0; +} + +static int ffb_clutpost (fbinfo_t *fb, struct fb_clut *fc) +{ + int i; + u32 *clut; + struct fb_clut fct; + u8 red[256], green[256], blue[256]; + +#ifdef CONFIG_SPARC32_COMPAT + if (current->tss.flags & SPARC_FLAG_32BIT) { + if (copy_from_user (&fct, fc, 3 * sizeof (__u32)) || + __get_user ((long)fct.red, &(((struct fb_clut32 *)fc)->red)) || + __get_user ((long)fct.green, &(((struct fb_clut32 *)fc)->green)) || + __get_user ((long)fct.blue, &(((struct fb_clut32 *)fc)->blue))) + return -EFAULT; + } else +#endif + if (copy_from_user (&fct, fc, sizeof (struct fb_clut))) + return -EFAULT; + i = fct.offset + fct.count; + if (fct.clutid || i <= 0 || i > 256) return -EINVAL; + if (copy_from_user (red, fct.red, fct.count) || + copy_from_user (green, fct.green, fct.count) || + copy_from_user (blue, fct.blue, fct.count)) + return -EFAULT; + clut = fb->info.ffb.clut + fct.offset; + for (i = 0; i < fct.count; i++) + *clut++ = ((red[i])|(green[i]<<8)|(blue[i]<<16)); + return ffb_clutstore (fb, fct.offset, fct.count); +} + +static int ffb_clutread (fbinfo_t *fb, struct fb_clut *fc) +{ +/* XXX write this */ + return 0; +} + +static void +ffb_loadcmap (fbinfo_t *fb, int index, int count) +{ + u32 *clut = fb->info.ffb.clut + index; + int i, j = count; + + for (i = index; j--; i++) + *clut++ = ((fb->color_map CM(i,0))) | + ((fb->color_map CM(i,1)) << 8) | + ((fb->color_map CM(i,2)) << 16); + ffb_clutstore (fb, index, count); +} + +/* Handle ffb-specific ioctls */ +static int +ffb_ioctl (struct inode *inode, struct file *file, unsigned cmd, unsigned long arg, fbinfo_t *fb) +{ + int i; + + switch (cmd) { + case FBIO_WID_GET: + return ffb_wid_get (fb, (struct fb_wid_list *)arg); + case FBIO_WID_PUT: + return ffb_wid_put (fb, (struct fb_wid_list *)arg); + case FFB_CLUTPOST: + return ffb_clutpost (fb, (struct fb_clut *)arg); + case FFB_CLUTREAD: + return ffb_clutread (fb, (struct fb_clut *)arg); + + default: + return -ENOSYS; + } +} + +void +ffb_reset (fbinfo_t *fb) +{ + struct ffb_info *ffb = &(fb->info.ffb); + int fifo; + + if (fb->setcursor) + sun_hw_hide_cursor (); + + while ((fifo = (ffb->fbc->ucsr & FFB_UCSR_FIFO_MASK)) < 8); + ffb->fbc->ppc = (FFB_PPC_ACE_DISABLE << FFB_PPC_ACE_SHIFT) | + (FFB_PPC_DCE_DISABLE << FFB_PPC_DCE_SHIFT) | + (FFB_PPC_ABE_DISABLE << FFB_PPC_ABE_SHIFT) | + (FFB_PPC_VCE_DISABLE << FFB_PPC_VCE_SHIFT) | + (FFB_PPC_APE_DISABLE << FFB_PPC_APE_SHIFT) | + (FFB_PPC_CS_VARIABLE << FFB_PPC_CS_SHIFT); + ffb->fbc->fbc = (FFB_FBC_WB_A << FFB_FBC_WB_SHIFT) | + (FFB_FBC_PGE_MASK << FFB_FBC_BE_SHIFT) | + (FFB_FBC_PGE_MASK << FFB_FBC_GE_SHIFT) | + (FFB_FBC_PGE_MASK << FFB_FBC_RE_SHIFT); + ffb->fbc->rop = (FFB_ROP_NEW << FFB_ROP_RGB_SHIFT); + ffb->fbc->pmask = 0x00ffffff; + while (ffb->fbc->ucsr & FFB_UCSR_RP_BUSY); +} + +__initfunc(static unsigned long ffb_postsetup (fbinfo_t *fb, unsigned long memory_start)) +{ + fb->info.ffb.clut = (u32 *)(memory_start); + fb->color_map = (u8 *)(memory_start+256*4+256); + return memory_start + 256*4 + 256*3; +} + +__initfunc(void creator_setup (fbinfo_t *fb, int slot, int ffb_node, unsigned long ffb, int ffb_io)) +{ + struct ffb_info *ffbinfo; + struct linux_prom64_registers regs[2*PROMREG_MAX]; + + if (prom_getproperty(ffb_node, "reg", (void *) regs, sizeof(regs)) <= 0) + return; + ffb = regs[0].phys_addr; + printk ("creator%d at 0x%016lx ", slot, ffb); + + fb->base = ffb; /* ??? */ + + /* Fill in parameters we left out */ + fb->type.fb_cmsize = 256; + fb->mmap = ffb_mmap; + fb->loadcmap = ffb_loadcmap; + fb->reset = ffb_reset; + fb->blank = ffb_blank; + fb->unblank = ffb_unblank; + fb->setcursor = ffb_setcursor; + fb->setcursormap = ffb_setcursormap; + fb->setcurshape = ffb_setcurshape; + fb->postsetup = ffb_postsetup; + fb->blitc = ffb_blitc; + fb->setw = ffb_setw; + fb->cpyw = ffb_cpyw; + fb->fill = ffb_fill; + fb->ioctl = ffb_ioctl; + fb->cursor.hwsize.fbx = 64; + fb->cursor.hwsize.fby = 64; + + ffbinfo = (struct ffb_info *) &fb->info.ffb; + + ffbinfo->physbase = ffb; + + ffbinfo->fbc = (struct ffb_fbc *)(ffb + FFB_FBC_REGS_POFF); + ffbinfo->dac = (struct ffb_dac *)(ffb + FFB_DAC_POFF); + + ffbinfo->dac->type = 0x8000; + ffbinfo->dac_rev = (ffbinfo->dac->value >> 0x1c); + + if (slot == sun_prom_console_id) + fb_restore_palette = ffb_restore_palette; + + /* Initialize Brooktree DAC */ + + printk("DAC %d\n", ffbinfo->dac_rev); + + if (slot && sun_prom_console_id == slot) + return; + + /* Reset the ffb */ + ffb_reset(fb); + + if (!slot) { + /* Enable Video */ + ffb_unblank(fb); + } else { + ffb_blank(fb); + } +} + +extern unsigned char vga_font[]; + +static void ffb_blitc(unsigned short charattr, int xoff, int yoff) +{ +} + +static void ffb_setw(int xoff, int yoff, unsigned short c, int count) +{ +} + +static void ffb_cpyw(int xoff, int yoff, unsigned short *p, int count) +{ +} + +static void ffb_fill(int attrib, int count, int *boxes) +{ } diff --git a/drivers/sbus/char/fb.h b/drivers/sbus/char/fb.h index 029eac81b..0aa9f2b48 100644 --- a/drivers/sbus/char/fb.h +++ b/drivers/sbus/char/fb.h @@ -1,4 +1,4 @@ -/* $Id: fb.h,v 1.26 1997/04/17 02:29:33 miguel Exp $ +/* $Id: fb.h,v 1.29 1997/07/15 09:48:48 jj Exp $ * fb.h: contains the definitions of the structures that various sun * frame buffer can use to do console driver stuff. * @@ -33,7 +33,7 @@ struct cg_cursor { struct fbcurpos chot; /* hot-spot */ struct fbcurpos size; /* size of mask & image fields */ struct fbcurpos hwsize; /* hw max size */ - int bits[2][32]; /* space for mask & image bits */ + int bits[2][128]; /* space for mask & image bits */ char color [6]; /* cursor colors */ }; @@ -57,6 +57,14 @@ struct tcx_info { int lowdepth; }; +struct ffb_info { + unsigned long physbase; + struct ffb_fbc *fbc; + struct ffb_dac *dac; + int dac_rev; + u32 *clut; +}; + struct leo_info { struct leo_cursor *cursor; struct leo_lc_ss0_krn *lc_ss0_krn; @@ -113,6 +121,7 @@ typedef struct fbinfo { struct cg14_info cg14; struct tcx_info tcx; struct leo_info leo; + struct ffb_info ffb; } info; /* per frame information */ int space; /* I/O space this card resides in */ int blanked; /* true if video blanked */ @@ -183,7 +192,7 @@ extern int con_height, con_linebytes; extern int ints_per_line; /* used in the mmap routines */ -extern unsigned int get_phys (unsigned long addr); +extern unsigned long get_phys (unsigned long addr); extern int get_iospace (unsigned long addr); extern void render_screen(void); diff --git a/drivers/sbus/char/leo.c b/drivers/sbus/char/leo.c index 61e646e9f..1b5d06e4b 100644 --- a/drivers/sbus/char/leo.c +++ b/drivers/sbus/char/leo.c @@ -1,7 +1,7 @@ -/* $Id: leo.c,v 1.18 1997/06/04 08:27:30 davem Exp $ +/* $Id: leo.c,v 1.21 1997/07/17 02:21:48 davem Exp $ * leo.c: SUNW,leo 24/8bit frame buffer driver * - * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) * Copyright (C) 1997 Michal Rehacek (Michal.Rehacek@st.mff.cuni.cz) */ @@ -143,7 +143,7 @@ leo_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, long base, fbinfo_t *fb) { uint size, page, r, map_size = 0; - uint map_offset = 0; + unsigned long map_offset = 0; size = vma->vm_end - vma->vm_start; if (vma->vm_offset & ~PAGE_MASK) @@ -218,11 +218,12 @@ leo_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, map_offset, map_size, vma->vm_page_prot, fb->space); - if (r) return -EAGAIN; + if (r) + return -EAGAIN; page += map_size; } - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + + vma->vm_dentry = dget(file->f_dentry); return 0; } @@ -376,7 +377,7 @@ static int leo_clutstore (fbinfo_t *fb, int clutid) return 0; } -static int leo_clutpost (fbinfo_t *fb, struct leo_clut *lc) +static int leo_clutpost (fbinfo_t *fb, struct fb_clut *lc) { int xlate = 0, i; u32 *clut; @@ -398,7 +399,7 @@ static int leo_clutpost (fbinfo_t *fb, struct leo_clut *lc) return leo_clutstore (fb, lc->clutid); } -static int leo_clutread (fbinfo_t *fb, struct leo_clut *lc) +static int leo_clutread (fbinfo_t *fb, struct fb_clut *lc) { int i; u32 u; @@ -463,29 +464,29 @@ leo_ioctl (struct inode *inode, struct file *file, unsigned cmd, unsigned long a if (i) return i; return leo_wid_put (fb, (struct fb_wid_list *)arg); case LEO_CLUTPOST: - i = verify_area (VERIFY_READ, (void *)arg, sizeof (struct leo_clut)); + i = verify_area (VERIFY_READ, (void *)arg, sizeof (struct fb_clut)); if (i) return i; - i = ((struct leo_clut *)arg)->offset + ((struct leo_clut *)arg)->count; + i = ((struct fb_clut *)arg)->offset + ((struct fb_clut *)arg)->count; if (i <= 0 || i > 256) return -EINVAL; - i = verify_area (VERIFY_READ, ((struct leo_clut *)arg)->red, ((struct leo_clut *)arg)->count); + i = verify_area (VERIFY_READ, ((struct fb_clut *)arg)->red, ((struct fb_clut *)arg)->count); if (i) return i; - i = verify_area (VERIFY_READ, ((struct leo_clut *)arg)->green, ((struct leo_clut *)arg)->count); + i = verify_area (VERIFY_READ, ((struct fb_clut *)arg)->green, ((struct fb_clut *)arg)->count); if (i) return i; - i = verify_area (VERIFY_READ, ((struct leo_clut *)arg)->blue, ((struct leo_clut *)arg)->count); + i = verify_area (VERIFY_READ, ((struct fb_clut *)arg)->blue, ((struct fb_clut *)arg)->count); if (i) return i; - return leo_clutpost (fb, (struct leo_clut *)arg); + return leo_clutpost (fb, (struct fb_clut *)arg); case LEO_CLUTREAD: - i = verify_area (VERIFY_READ, (void *)arg, sizeof (struct leo_clut)); + i = verify_area (VERIFY_READ, (void *)arg, sizeof (struct fb_clut)); if (i) return i; - i = ((struct leo_clut *)arg)->offset + ((struct leo_clut *)arg)->count; + i = ((struct fb_clut *)arg)->offset + ((struct fb_clut *)arg)->count; if (i <= 0 || i > 256) return -EINVAL; - i = verify_area (VERIFY_WRITE, ((struct leo_clut *)arg)->red, ((struct leo_clut *)arg)->count); + i = verify_area (VERIFY_WRITE, ((struct fb_clut *)arg)->red, ((struct fb_clut *)arg)->count); if (i) return i; - i = verify_area (VERIFY_WRITE, ((struct leo_clut *)arg)->green, ((struct leo_clut *)arg)->count); + i = verify_area (VERIFY_WRITE, ((struct fb_clut *)arg)->green, ((struct fb_clut *)arg)->count); if (i) return i; - i = verify_area (VERIFY_WRITE, ((struct leo_clut *)arg)->blue, ((struct leo_clut *)arg)->count); + i = verify_area (VERIFY_WRITE, ((struct fb_clut *)arg)->blue, ((struct fb_clut *)arg)->count); if (i) return i; - return leo_clutread (fb, (struct leo_clut *)arg); + return leo_clutread (fb, (struct fb_clut *)arg); default: return -ENOSYS; diff --git a/drivers/sbus/char/openprom.c b/drivers/sbus/char/openprom.c index 548abb601..75b950071 100644 --- a/drivers/sbus/char/openprom.c +++ b/drivers/sbus/char/openprom.c @@ -137,6 +137,7 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file, struct openpromio *opp; unsigned long flags; int bufsize, len, error = 0; + extern char saved_command_line[]; if (cmd == OPROMSETOPT) bufsize = getstrings((void *)arg, &opp); @@ -172,7 +173,7 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file, case OPROMNXTOPT: case OPROMNXTPROP: save_and_cli(flags); - buf = prom_nextprop(node, opp->oprom_array); + buf = prom_nextprop(node, opp->oprom_array, buffer); restore_flags(flags); len = strlen(buf); @@ -229,9 +230,7 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file, break; case OPROMGETBOOTARGS: - save_and_cli(flags); - buf = prom_getbootargs(); - restore_flags(flags); + buf = saved_command_line; len = strlen(buf); @@ -315,6 +314,7 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file, unsigned long flags; int error, node, len; char *str, *tmp; + char buffer[64]; switch (cmd) { case OPIOCGET: @@ -378,7 +378,7 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file, return error; save_and_cli(flags); - tmp = prom_nextprop(op.op_nodeid,str); + tmp = prom_nextprop(op.op_nodeid,str,buffer); restore_flags(flags); if (tmp) { diff --git a/drivers/sbus/char/suncons.c b/drivers/sbus/char/suncons.c index 1d3815dd3..cad446904 100644 --- a/drivers/sbus/char/suncons.c +++ b/drivers/sbus/char/suncons.c @@ -1,4 +1,4 @@ -/* $Id: suncons.c,v 1.63 1997/05/31 18:33:25 mj Exp $ +/* $Id: suncons.c,v 1.66 1997/07/15 09:48:47 jj Exp $ * * suncons.c: Sun SparcStation console support. * @@ -759,7 +759,7 @@ console_restore_palette (void) (*fb_restore_palette) (&fbinfo[0]); } -unsigned int +unsigned long get_phys (unsigned long addr) { return __get_phys(addr); @@ -828,10 +828,14 @@ __initfunc(static int creator_present (void)) { int root, n; +#ifdef __sparc_v9__ root = prom_getchild (prom_root_node); if ((n = prom_searchsiblings (root, "SUNW,ffb")) == 0) return 0; return n; +#else + return 0; +#endif } __initfunc(static void @@ -1108,7 +1112,6 @@ __initfunc(static int sparc_console_probe(void)) if (!card_found) card_found = cg14 = cg14_present (); if (!card_found){ - prom_printf ("Searching for a creator\n"); card_found = creator = creator_present (); } if (!card_found){ @@ -1172,7 +1175,7 @@ __initfunc(static int sparc_console_probe(void)) if (creator){ sparc_framebuffer_setup (!sbdprom, creator, FBTYPE_CREATOR, 0, 0, 0, prom_console_node == creator, - prom_getchild (prom_root_node)); + prom_root_node); } break; default: @@ -1641,57 +1644,67 @@ void memcpyw(unsigned short *to, unsigned short *from, unsigned int count) int sun_hw_scursor (struct fbcursor *cursor, fbinfo_t *fb) { - int op = cursor->set; + int op; int i, bytes = 0; + struct fbcursor f; + char red[2], green[2], blue[2]; + if (copy_from_user (&f, cursor, sizeof(struct fbcursor))) + return -EFAULT; + op = f.set; if (op & FB_CUR_SETSHAPE){ - if ((uint) cursor->size.fbx > fb->cursor.hwsize.fbx) + if ((uint) f.size.fbx > fb->cursor.hwsize.fbx) return -EINVAL; - if ((uint) cursor->size.fby > fb->cursor.hwsize.fby) + if ((uint) f.size.fby > fb->cursor.hwsize.fby) return -EINVAL; - bytes = (cursor->size.fby * 32)/8; - i = verify_area (VERIFY_READ, cursor->image, bytes); - if (i) return i; - i = verify_area (VERIFY_READ, cursor->mask, bytes); - if (i) return i; + if (f.size.fbx > 32) + bytes = f.size.fby << 3; + else + bytes = f.size.fby << 2; } if (op & FB_CUR_SETCMAP){ - if (cursor->cmap.index && cursor->cmap.count != 2) + if (f.cmap.index || f.cmap.count != 2) return -EINVAL; - i = verify_area (VERIFY_READ, cursor->cmap.red, 2); - if (i) return i; - i = verify_area (VERIFY_READ, cursor->cmap.green, 2); - if (i) return i; - i = verify_area (VERIFY_READ, cursor->cmap.blue, 2); - if (i) return i; - } - if (op & (FB_CUR_SETCUR | FB_CUR_SETPOS | FB_CUR_SETHOT)){ - if (op & FB_CUR_SETCUR) - fb->cursor.enable = cursor->enable; - if (op & FB_CUR_SETPOS) - fb->cursor.cpos = cursor->pos; - if (op & FB_CUR_SETHOT) - fb->cursor.chot = cursor->hot; - (*fb->setcursor) (fb); + if (copy_from_user (red, f.cmap.red, 2) || + copy_from_user (green, f.cmap.green, 2) || + copy_from_user (blue, f.cmap.blue, 2)) + return -EFAULT; } if (op & FB_CUR_SETCMAP) - (*fb->setcursormap) (fb, cursor->cmap.red, cursor->cmap.green, cursor->cmap.blue); + (*fb->setcursormap) (fb, red, green, blue); if (op & FB_CUR_SETSHAPE){ uint u; - fb->cursor.size = cursor->size; + fb->cursor.size = f.size; memset ((void *)&fb->cursor.bits, 0, sizeof (fb->cursor.bits)); - memcpy (fb->cursor.bits [0], cursor->mask, bytes); - memcpy (fb->cursor.bits [1], cursor->image, bytes); - u = ~0; - if (cursor->size.fbx < fb->cursor.hwsize.fbx) - u = ~(u >> cursor->size.fbx); - for (i = fb->cursor.size.fby - 1; i >= 0; i--) { - fb->cursor.bits [0][i] &= u; - fb->cursor.bits [1][i] &= fb->cursor.bits [0][i]; + if (copy_from_user (fb->cursor.bits [0], f.mask, bytes) || + copy_from_user (fb->cursor.bits [1], f.image, bytes)) + return -EFAULT; + if (f.size.fbx <= 32) { + u = ~(0xffffffff >> f.size.fbx); + for (i = fb->cursor.size.fby - 1; i >= 0; i--) { + fb->cursor.bits [0][i] &= u; + fb->cursor.bits [1][i] &= fb->cursor.bits [0][i]; + } + } else { + u = ~(0xffffffff >> (f.size.fbx - 32)); + for (i = fb->cursor.size.fby - 1; i >= 0; i--) { + fb->cursor.bits [0][2*i+1] &= u; + fb->cursor.bits [1][2*i] &= fb->cursor.bits [0][2*i]; + fb->cursor.bits [1][2*i+1] &= fb->cursor.bits [0][2*i+1]; + } } (*fb->setcurshape) (fb); } + if (op & (FB_CUR_SETCUR | FB_CUR_SETPOS | FB_CUR_SETHOT)){ + if (op & FB_CUR_SETCUR) + fb->cursor.enable = f.enable; + if (op & FB_CUR_SETPOS) + fb->cursor.cpos = f.pos; + if (op & FB_CUR_SETHOT) + fb->cursor.chot = f.hot; + (*fb->setcursor) (fb); + } return 0; } diff --git a/drivers/sbus/char/sunfb.c b/drivers/sbus/char/sunfb.c index 68856c9ee..885581975 100644 --- a/drivers/sbus/char/sunfb.c +++ b/drivers/sbus/char/sunfb.c @@ -1,4 +1,4 @@ -/* $Id: sunfb.c,v 1.23 1997/05/31 18:33:26 mj Exp $ +/* $Id: sunfb.c,v 1.26 1997/07/17 02:21:48 davem Exp $ * sunfb.c: Sun generic frame buffer support. * * Copyright (C) 1995, 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) @@ -132,7 +132,7 @@ fb_ioctl (struct inode *inode, struct file *file, uint cmd, unsigned long arg) if ((index < 0) || (index > 255)) return -EINVAL; if (index + count > 256) - count = 256 - cmap->index; + count = 256 - index; __get_user_ret(rp, &cmap->red, -EFAULT); __get_user_ret(gp, &cmap->green, -EFAULT); __get_user_ret(bp, &cmap->blue, -EFAULT); @@ -146,7 +146,7 @@ fb_ioctl (struct inode *inode, struct file *file, uint cmd, unsigned long arg) __put_user_ret(fb->color_map CM(i,2), bp, -EFAULT); rp++; gp++; bp++; } - (*fb->loadcmap)(fb, cmap->index, count); + (*fb->loadcmap)(fb, index, count); break; } @@ -164,13 +164,13 @@ fb_ioctl (struct inode *inode, struct file *file, uint cmd, unsigned long arg) if ((index < 0) || (index > 255)) return -EINVAL; if (index + count > 256) - count = 256 - cmap->index; + count = 256 - index; __get_user_ret(rp, &cmap->red, -EFAULT); __get_user_ret(gp, &cmap->green, -EFAULT); __get_user_ret(bp, &cmap->blue, -EFAULT); - if(verify_area (VERIFY_READ, rp, cmap->count)) return -EFAULT; - if(verify_area (VERIFY_READ, gp, cmap->count)) return -EFAULT; - if(verify_area (VERIFY_READ, bp, cmap->count)) return -EFAULT; + if(verify_area (VERIFY_READ, rp, count)) return -EFAULT; + if(verify_area (VERIFY_READ, gp, count)) return -EFAULT; + if(verify_area (VERIFY_READ, bp, count)) return -EFAULT; end = index + count; for (i = index; i < end; i++){ @@ -179,7 +179,7 @@ fb_ioctl (struct inode *inode, struct file *file, uint cmd, unsigned long arg) __get_user_ret(fb->color_map CM(i,2), bp, -EFAULT); rp++; gp++; bp++; } - (*fb->loadcmap)(fb, cmap->index, count); + (*fb->loadcmap)(fb, index, count); break; } @@ -275,7 +275,9 @@ fb_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma) int v; v = (*fb->mmap)(inode, file, vma, fb->base, fb); - if (v) return v; + if (v) + return v; + vma->vm_flags |= VM_IO; if (!fb->mmaped) { fb->mmaped = 1; if (!minor) { diff --git a/drivers/sbus/char/sunkbd.c b/drivers/sbus/char/sunkbd.c index 1ddaaecd4..54b09cd76 100644 --- a/drivers/sbus/char/sunkbd.c +++ b/drivers/sbus/char/sunkbd.c @@ -419,27 +419,27 @@ void sunkbd_inchar(unsigned char ch, struct pt_regs *regs) if(ch == SKBD_RESET) { kbd_reset_pending = 1; - return; + goto out; } if(ch == SKBD_LYOUT) { kbd_layout_pending = 1; - return; + goto out; } if(kbd_reset_pending) { sunkbd_type = ch; kbd_reset_pending = 0; if(ch == SUNKBD_TYPE4) send_cmd(SKBDCMD_GLAYOUT); - return; + goto out; } else if(kbd_layout_pending) { sunkbd_layout = ch; kbd_layout_pending = 0; - return; + goto out; } else if(ch == SKBD_ALLUP) { del_timer (&auto_repeat_timer); memset(key_down, 0, sizeof(key_down)); compute_shiftstate(); - return; + goto out; } #ifdef SKBD_DEBUG if(ch == 0x7f) @@ -456,11 +456,11 @@ void sunkbd_inchar(unsigned char ch, struct pt_regs *regs) } else { keycode = ch; } - add_keyboard_randomness(keycode); - mark_bh(KEYBOARD_BH); do_poke_blanked_console = 1; mark_bh(CONSOLE_BH); + add_keyboard_randomness(keycode); + kbd = kbd_table + fg_console; tty = ttytab[fg_console]; if((raw_mode = (kbd->kbdmode == VC_RAW))) { @@ -491,11 +491,11 @@ void sunkbd_inchar(unsigned char ch, struct pt_regs *regs) } if(raw_mode) - return; + goto out; if(kbd->kbdmode == VC_MEDIUMRAW) { put_queue(keycode + up_flag); - return; + goto out; } /* @@ -545,6 +545,8 @@ void sunkbd_inchar(unsigned char ch, struct pt_regs *regs) compute_shiftstate(); } } +out: + mark_bh(KEYBOARD_BH); } static void put_queue(int ch) diff --git a/drivers/sbus/char/sunmouse.c b/drivers/sbus/char/sunmouse.c index 371fb3004..789a332c0 100644 --- a/drivers/sbus/char/sunmouse.c +++ b/drivers/sbus/char/sunmouse.c @@ -171,7 +171,7 @@ void mouse_baud_detection(unsigned char c) ctr = 0; wait_for_synchron = 1; if(mouse_baud_changing == 1) { - printk("sunmouse: Successfully adjusted to %d baud.\n", + printk(KERN_DEBUG "sunmouse: Successfully adjusted to %d baud.\n", mouse_baud); mouse_baud_changing = 0; } diff --git a/drivers/sbus/char/sunserial.c b/drivers/sbus/char/sunserial.c index e89384a3a..c0e6ab71d 100644 --- a/drivers/sbus/char/sunserial.c +++ b/drivers/sbus/char/sunserial.c @@ -1,4 +1,4 @@ -/* $Id: sunserial.c,v 1.42 1997/05/26 20:10:20 davem Exp $ +/* $Id: sunserial.c,v 1.43 1997/07/05 09:53:23 davem Exp $ * serial.c: Serial port driver for the Sparc. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -348,10 +348,12 @@ static void batten_down_hatches(void) */ printk("\n"); flush_user_windows(); +#ifndef __sparc_v9__ if((((unsigned long)linux_dbvec)>=DEBUG_FIRSTVADDR) && (((unsigned long)linux_dbvec)<=DEBUG_LASTVADDR)) sp_enter_debugger(); else +#endif prom_cmdline(); /* XXX We want to notify the keyboard driver that all @@ -396,7 +398,9 @@ static _INLINE_ void rs_sched_event(struct sun_serial *info, mark_bh(SERIAL_BH); } +#ifndef __sparc_v9__ extern void breakpoint(void); /* For the KGDB frame character */ +#endif static _INLINE_ void receive_chars(struct sun_serial *info, struct pt_regs *regs) { @@ -453,6 +457,7 @@ static _INLINE_ void receive_chars(struct sun_serial *info, struct pt_regs *regs /* It is a 'keyboard interrupt' ;-) */ wake_up(&keypress_wait); } +#ifndef __sparc_v9__ /* Look for kgdb 'stop' character, consult the gdb * documentation for remote target debugging and * arch/sparc/kernel/sparc-stub.c to see how all this works. @@ -461,7 +466,7 @@ static _INLINE_ void receive_chars(struct sun_serial *info, struct pt_regs *regs breakpoint(); return; } - +#endif if(!tty) return; @@ -1867,7 +1872,7 @@ int rs_open(struct tty_struct *tty, struct file * filp) static void show_serial_version(void) { - char *revision = "$Revision: 1.42 $"; + char *revision = "$Revision: 1.2 $"; char *version, *p; version = strchr(revision, ' '); diff --git a/drivers/sbus/char/sunserial.h b/drivers/sbus/char/sunserial.h index b8ae23305..ae4260cad 100644 --- a/drivers/sbus/char/sunserial.h +++ b/drivers/sbus/char/sunserial.h @@ -1,4 +1,4 @@ -/* $Id: sunserial.h,v 1.9 1997/04/12 23:33:12 ecd Exp $ +/* $Id: sunserial.h,v 1.11 1997/07/08 10:17:23 davem Exp $ * serial.h: Definitions for the Sparc Zilog serial driver. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) diff --git a/drivers/sbus/char/tcx.c b/drivers/sbus/char/tcx.c index db66383ac..20c14a687 100644 --- a/drivers/sbus/char/tcx.c +++ b/drivers/sbus/char/tcx.c @@ -1,4 +1,4 @@ -/* $Id: tcx.c,v 1.15 1997/06/04 08:27:32 davem Exp $ +/* $Id: tcx.c,v 1.17 1997/07/17 02:21:50 davem Exp $ * tcx.c: SUNW,tcx 24/8bit frame buffer driver * * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -110,7 +110,8 @@ tcx_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, long base, fbinfo_t *fb) { uint size, page, r, map_size; - uint map_offset = 0, i; + unsigned long map_offset = 0; + uint i; long offsets[13] = { -1, TCX_RAM24BIT, TCX_UNK3, TCX_UNK4, -1, TCX_UNK6, TCX_UNK7, -1, -1, -1, TCX_UNK2, TCX_DHC, TCX_ALT }; @@ -168,11 +169,12 @@ tcx_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, map_offset, map_size, vma->vm_page_prot, fb->space); - if (r) return -EAGAIN; + if (r) + return -EAGAIN; page += map_size; } - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + + vma->vm_dentry = dget(file->f_dentry); return 0; } diff --git a/drivers/sbus/char/vfc_dev.c b/drivers/sbus/char/vfc_dev.c index 00cdfe35c..007174a82 100644 --- a/drivers/sbus/char/vfc_dev.c +++ b/drivers/sbus/char/vfc_dev.c @@ -577,11 +577,12 @@ static int vfc_mmap(struct inode *inode, struct file *file, if(vma->vm_offset & ~PAGE_MASK) return -ENXIO; vma->vm_flags |= VM_SHM | VM_LOCKED | VM_IO | VM_MAYREAD | VM_MAYWRITE | VM_MAYSHARE; map_offset=(unsigned int)dev->phys_regs; - ret=io_remap_page_range(vma->vm_start,map_offset,map_size, - vma->vm_page_prot, dev->which_io); - if(ret) return -EAGAIN; - vma->vm_inode=inode; - atomic_inc(&inode->i_count); + ret = io_remap_page_range(vma->vm_start,map_offset,map_size, + vma->vm_page_prot, dev->which_io); + if(ret) + return -EAGAIN; + + vma->vm_dentry = dget(file->f_dentry); return 0; } diff --git a/drivers/sbus/char/weitek.c b/drivers/sbus/char/weitek.c index d2ac4d135..7b7b1bd72 100644 --- a/drivers/sbus/char/weitek.c +++ b/drivers/sbus/char/weitek.c @@ -1,4 +1,4 @@ -/* $Id: weitek.c,v 1.12 1997/06/04 08:27:34 davem Exp $ +/* $Id: weitek.c,v 1.14 1997/07/17 02:21:53 davem Exp $ * weitek.c: Tadpole P9100/P9000 console driver * * Copyright (C) 1996 David Redman (djhr@tadpole.co.uk) @@ -41,7 +41,7 @@ weitek_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma, long base, fbinfo_t *fb) { unsigned int size, page, r, map_size; - unsigned int map_offset = 0; + unsigned long map_offset = 0; size = vma->vm_end - vma->vm_start; if (vma->vm_offset & ~PAGE_MASK) @@ -79,11 +79,12 @@ weitek_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma, map_offset, map_size, vma->vm_page_prot, fb->space); - if (r) return -EAGAIN; + if (r) + return -EAGAIN; page += map_size; } - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + + vma->vm_dentry = dget(file->f_dentry); return 0; } #endif diff --git a/drivers/scsi/BusLogic.h b/drivers/scsi/BusLogic.h index 7cd6ba436..515e84b64 100644 --- a/drivers/scsi/BusLogic.h +++ b/drivers/scsi/BusLogic.h @@ -27,6 +27,9 @@ */ +#include <linux/config.h> + + /* Define types for some of the structures that interface with the rest of the Linux Kernel and SCSI Subsystem. diff --git a/drivers/scsi/README.in2000 b/drivers/scsi/README.in2000 index 06d0fee22..861d6efb2 100644 --- a/drivers/scsi/README.in2000 +++ b/drivers/scsi/README.in2000 @@ -1,4 +1,16 @@ +UPDATE NEWS: version 1.31 - 6 Jul 97 + + Fixed a bug that caused incorrect SCSI status bytes to be + returned from commands sent to LUN's greater than 0. This + means that CDROM changers work now! Fixed a bug in the + handling of command-line arguments when loaded as a module. + Also put all the header data in in2000.h where it belongs. + There are no longer any differences between this driver in + the 2.1.xx source tree and the 2.0.xx tree, as of 2.0.31 + and 2.1.45 (or is it .46?) - this makes things much easier + for me... + UPDATE NEWS: version 1.30 - 14 Oct 96 Fixed a bug in the code that sets the transfer direction @@ -105,15 +117,10 @@ to see what happens: my tests showed little difference either way. There's also a define called 'DEFAULT_SX_PER'; this sets the data transfer speed for the asynchronous mode. I've put it at 500 ns despite the fact that the card could handle settings of 376 or -252, because I'm not really sure if certain devices or maybe bad -cables might have trouble at higher speeds. I couldn't find any -info in my various SCSI references that talk about this in language -I could understand, so decided to compromise with 500. This is still -faster than the old driver was set at (I think). Can someone explain -the significance of the bus transfer speed setting? Do devices on -the bus ever care what it is? Is cable quality a factor here? -Regardless, you can choose your own default through the command- -line with the 'period' keyword. +252, because higher speeds may be a problem with poor quality +cables or improper termination; 500 ns is a compromise. You can +choose your own default through the command-line with the +'period' keyword. ------------------------------------------------ diff --git a/drivers/scsi/advansys.h b/drivers/scsi/advansys.h index 6cf629921..eb3d65e65 100644 --- a/drivers/scsi/advansys.h +++ b/drivers/scsi/advansys.h @@ -1,4 +1,4 @@ -/* $Id: advansys.h,v 1997/05/28 00:23:06 bobf Exp bobf $ */ +/* $Id: advansys.h,v 1.6 1997/05/30 19:25:12 davem Exp $ */ /* * advansys.h - Linux Host Driver for AdvanSys SCSI Adapters diff --git a/drivers/scsi/amiga7xx.c b/drivers/scsi/amiga7xx.c index 6edd01320..f07d5e2cd 100644 --- a/drivers/scsi/amiga7xx.c +++ b/drivers/scsi/amiga7xx.c @@ -36,7 +36,7 @@ struct proc_dir_entry proc_scsi_amiga7xx = { int amiga7xx_detect(Scsi_Host_Template *tpnt) { static unsigned char called = 0; - int key; + int key, clock; int num = 0; unsigned long address; long long options; @@ -59,8 +59,7 @@ int amiga7xx_detect(Scsi_Host_Template *tpnt) clock = 50000000; /* 50MHz SCSI Clock */ ncr53c7xx_init(tpnt, 0, 710, (u32)(unsigned char *)(address + 0x40000), - 0, IRQ_AMIGA_PORTS, DMA_NONE, - options, clock); + 0, IRQ_AMIGA_PORTS, DMA_NONE, options, clock); zorro_config_board(key, 0); num++; @@ -74,16 +73,16 @@ int amiga7xx_detect(Scsi_Host_Template *tpnt) clock = 50000000; /* 50MHz SCSI Clock */ - ncr53c7xx_init(tpnt, 0, 710, (u32)(unsigned char *)ZTWO_VADDR(0xDD0040), - 0, IRQ_AMIGA_PORTS, DMA_NONE, - options, clock); + ncr53c7xx_init(tpnt, 0, 710, + (u32)(unsigned char *)ZTWO_VADDR(0xDD0040), + 0, IRQ_AMIGA_PORTS, DMA_NONE, options, clock); num++; } #endif #ifdef CONFIG_A4091_SCSI while ( (key = zorro_find(MANUF_COMMODORE, PROD_A4091, 0, 0)) || - (key = zorro_find(MANUF_COMMODORE2, PROD_A4091_2, 0, 0)) ) + (key = zorro_find(MANUF_COMMODORE2, PROD_A4091_2, 0, 0)) ) { cd = zorro_get_board(key); address = (unsigned long)kernel_map((unsigned long)cd->cd_BoardAddr, diff --git a/drivers/scsi/in2000.c b/drivers/scsi/in2000.c index 6f24da81c..d493fa167 100644 --- a/drivers/scsi/in2000.c +++ b/drivers/scsi/in2000.c @@ -104,7 +104,7 @@ * */ - +#include <linux/module.h> #include <asm/system.h> #include <linux/sched.h> @@ -115,46 +115,44 @@ #include <linux/ioport.h> #include <linux/blkdev.h> +#include <linux/blk.h> +#include <linux/stat.h> + #include "scsi.h" #include "sd.h" #include "hosts.h" -#include "in2000.h" - -#include <linux/blk.h> -#include <linux/stat.h> -#ifdef MODULE -#include <linux/module.h> -#endif -#define uchar unsigned char +#define IN2000_VERSION "1.31" +#define IN2000_DATE "06/July/1997" -#define IN2000_VERSION "1.30" -#define IN2000_DATE "14/Oct/1996" +/* + * Note - the following defines have been moved to 'in2000.h': + * + * PROC_INTERFACE + * PROC_STATISTICS + * SYNC_DEBUG + * DEBUGGING_ON + * DEBUG_DEFAULTS + * FAST_READ_IO + * FAST_WRITE_IO + * + */ -#define PROC_INTERFACE /* add code for /proc/scsi/in2000/xxx interface */ -#define SYNC_DEBUG /* extra info on sync negotiation printed */ -#define DEBUGGING_ON /* enable command-line debugging bitmask */ -#define DEBUG_DEFAULTS 0 /* default bitmask - change from command-line */ -#define FAST_READ_IO /* No problems with these on my machine */ -#define FAST_WRITE_IO +#include "in2000.h" -#ifdef DEBUGGING_ON -#define DB(f,a) if (hostdata->args & (f)) a; -#define CHECK_NULL(p,s) /* if (!(p)) {printk("\n"); while (1) printk("NP:%s\r",(s));} */ -#else -#define DB(f,a) -#define CHECK_NULL(p,s) -#endif /* - * setup_strings is an array of strings that define some of the operating - * parameters and settings for this driver. It is used unless a LILO - * or insmod command line has been specified, in which case those settings - * are combined with the ones here. The driver recognizes the following - * keywords (lower case required) and arguments: + * 'setup_strings' is a single string used to pass operating parameters and + * settings from the kernel/module command-line to the driver. 'setup_args[]' + * is an array of strings that define the compile-time default values for + * these settings. If Linux boots with a LILO or insmod command-line, those + * settings are combined with 'setup_args[]'. Note that LILO command-lines + * are prefixed with "in2000=" while insmod uses a "setup_strings=" prefix. + * The driver recognizes the following keywords (lower case required) and + * arguments: * * - ioport:addr -Where addr is IO address of a (usually ROM-less) card. * - noreset -No optional args. Prevents SCSI bus reset at boot time. @@ -179,13 +177,11 @@ * _must_ be a colon between a keyword and its numeric argument, with no * spaces. * - Keywords are separated by commas, no spaces, in the standard kernel - * command-line manner, except in the case of 'setup_strings[]' (see - * below), which is simply a C array of pointers to char. Each element - * in the array is a string comprising one keyword & argument. + * command-line manner. * - A keyword in the 'nth' comma-separated command-line member will overwrite - * the 'nth' element of setup_strings[]. A blank command-line member (in + * the 'nth' element of setup_args[]. A blank command-line member (in * other words, a comma with no preceding keyword) will _not_ overwrite - * the corresponding setup_strings[] element. + * the corresponding setup_args[] element. * * A few LILO examples (for insmod, use 'setup_strings' instead of 'in2000'): * - in2000=ioport:0x220,noreset @@ -194,334 +190,20 @@ * - in2000=proc:3 */ -static char *setup_strings[] = - {"","","","","","","","","","","",""}; +/* Normally, no defaults are specified... */ +static char *setup_args[] = + {"","","","","","","","",""}; -static struct Scsi_Host *instance_list = 0; +/* filled in by 'insmod' */ +static char *setup_strings = 0; -#ifdef PROC_INTERFACE -static unsigned long disc_allowed_total; -static unsigned long disc_taken_total; +#ifdef MODULE_PARM +MODULE_PARM(setup_strings, "s"); #endif -/* IN2000 io_port offsets */ -#define IO_WD_ASR 0x00 /* R - 3393 auxstat reg */ -#define ASR_INT 0x80 -#define ASR_LCI 0x40 -#define ASR_BSY 0x20 -#define ASR_CIP 0x10 -#define ASR_PE 0x02 -#define ASR_DBR 0x01 -#define IO_WD_ADDR 0x00 /* W - 3393 address reg */ -#define IO_WD_DATA 0x01 /* R/W - rest of 3393 regs */ -#define IO_FIFO 0x02 /* R/W - in2000 dual-port fifo (16 bits) */ -#define IN2000_FIFO_SIZE 2048 /* fifo capacity in bytes */ -#define IO_CARD_RESET 0x03 /* W - in2000 start master reset */ -#define IO_FIFO_COUNT 0x04 /* R - in2000 fifo counter */ -#define IO_FIFO_WRITE 0x05 /* W - clear fifo counter, start write */ -#define IO_FIFO_READ 0x07 /* W - start fifo read */ -#define IO_LED_OFF 0x08 /* W - turn off in2000 activity LED */ -#define IO_SWITCHES 0x08 /* R - read in2000 dip switch */ -#define SW_ADDR0 0x01 /* bit 0 = bit 0 of index to io addr */ -#define SW_ADDR1 0x02 /* bit 1 = bit 1 of index io addr */ -#define SW_DISINT 0x04 /* bit 2 true if ints disabled */ -#define SW_INT0 0x08 /* bit 3 = bit 0 of index to interrupt */ -#define SW_INT1 0x10 /* bit 4 = bit 1 of index to interrupt */ -#define SW_INT_SHIFT 3 /* shift right this amount to right justify int bits */ -#define SW_SYNC_DOS5 0x20 /* bit 5 used by Always BIOS */ -#define SW_FLOPPY 0x40 /* bit 6 true if floppy enabled */ -#define SW_BIT7 0x80 /* bit 7 hardwired true (ground) */ -#define IO_LED_ON 0x09 /* W - turn on in2000 activity LED */ -#define IO_HARDWARE 0x0a /* R - read in2000 hardware rev, stop reset */ -#define IO_INTR_MASK 0x0c /* W - in2000 interrupt mask reg */ -#define IMASK_WD 0x01 /* WD33c93 interrupt mask */ -#define IMASK_FIFO 0x02 /* FIFO interrupt mask */ - -/* wd register names */ -#define WD_OWN_ID 0x00 -#define WD_CONTROL 0x01 -#define WD_TIMEOUT_PERIOD 0x02 -#define WD_CDB_1 0x03 -#define WD_CDB_2 0x04 -#define WD_CDB_3 0x05 -#define WD_CDB_4 0x06 -#define WD_CDB_5 0x07 -#define WD_CDB_6 0x08 -#define WD_CDB_7 0x09 -#define WD_CDB_8 0x0a -#define WD_CDB_9 0x0b -#define WD_CDB_10 0x0c -#define WD_CDB_11 0x0d -#define WD_CDB_12 0x0e -#define WD_TARGET_LUN 0x0f -#define WD_COMMAND_PHASE 0x10 -#define WD_SYNCHRONOUS_TRANSFER 0x11 -#define WD_TRANSFER_COUNT_MSB 0x12 -#define WD_TRANSFER_COUNT 0x13 -#define WD_TRANSFER_COUNT_LSB 0x14 -#define WD_DESTINATION_ID 0x15 -#define WD_SOURCE_ID 0x16 -#define WD_SCSI_STATUS 0x17 -#define WD_COMMAND 0x18 -#define WD_DATA 0x19 -#define WD_QUEUE_TAG 0x1a -#define WD_AUXILIARY_STATUS 0x1f - -/* WD commands */ -#define WD_CMD_RESET 0x00 -#define WD_CMD_ABORT 0x01 -#define WD_CMD_ASSERT_ATN 0x02 -#define WD_CMD_NEGATE_ACK 0x03 -#define WD_CMD_DISCONNECT 0x04 -#define WD_CMD_RESELECT 0x05 -#define WD_CMD_SEL_ATN 0x06 -#define WD_CMD_SEL 0x07 -#define WD_CMD_SEL_ATN_XFER 0x08 -#define WD_CMD_SEL_XFER 0x09 -#define WD_CMD_RESEL_RECEIVE 0x0a -#define WD_CMD_RESEL_SEND 0x0b -#define WD_CMD_WAIT_SEL_RECEIVE 0x0c -#define WD_CMD_TRANS_ADDR 0x18 -#define WD_CMD_TRANS_INFO 0x20 -#define WD_CMD_TRANSFER_PAD 0x21 -#define WD_CMD_SBT_MODE 0x80 - -/* SCSI Bus Phases */ -#define PHS_DATA_OUT 0x00 -#define PHS_DATA_IN 0x01 -#define PHS_COMMAND 0x02 -#define PHS_STATUS 0x03 -#define PHS_MESS_OUT 0x06 -#define PHS_MESS_IN 0x07 - -/* Command Status Register definitions */ - - /* reset state interrupts */ -#define CSR_RESET 0x00 -#define CSR_RESET_AF 0x01 - - /* successful completion interrupts */ -#define CSR_RESELECT 0x10 -#define CSR_SELECT 0x11 -#define CSR_SEL_XFER_DONE 0x16 -#define CSR_XFER_DONE 0x18 - - /* paused or aborted interrupts */ -#define CSR_MSGIN 0x20 -#define CSR_SDP 0x21 -#define CSR_SEL_ABORT 0x22 -#define CSR_RESEL_ABORT 0x25 -#define CSR_RESEL_ABORT_AM 0x27 -#define CSR_ABORT 0x28 - - /* terminated interrupts */ -#define CSR_INVALID 0x40 -#define CSR_UNEXP_DISC 0x41 -#define CSR_TIMEOUT 0x42 -#define CSR_PARITY 0x43 -#define CSR_PARITY_ATN 0x44 -#define CSR_BAD_STATUS 0x45 -#define CSR_UNEXP 0x48 - - /* service required interrupts */ -#define CSR_RESEL 0x80 -#define CSR_RESEL_AM 0x81 -#define CSR_DISC 0x85 -#define CSR_SRV_REQ 0x88 - - /* Own ID/CDB Size register */ -#define OWNID_EAF 0x08 -#define OWNID_EHP 0x10 -#define OWNID_RAF 0x20 -#define OWNID_FS_8 0x00 -#define OWNID_FS_12 0x40 -#define OWNID_FS_16 0x80 - - /* Control register */ -#define CTRL_HSP 0x01 -#define CTRL_HA 0x02 -#define CTRL_IDI 0x04 -#define CTRL_EDI 0x08 -#define CTRL_HHP 0x10 -#define CTRL_POLLED 0x00 -#define CTRL_BURST 0x20 -#define CTRL_BUS 0x40 -#define CTRL_DMA 0x80 - - /* Timeout Period register */ -#define TIMEOUT_PERIOD_VALUE 20 /* results in 200 ms. */ - - /* Synchronous Transfer Register */ -#define STR_FSS 0x80 - - /* Destination ID register */ -#define DSTID_DPD 0x40 -#define DATA_OUT_DIR 0 -#define DATA_IN_DIR 1 -#define DSTID_SCC 0x80 - - /* Source ID register */ -#define SRCID_MASK 0x07 -#define SRCID_SIV 0x08 -#define SRCID_DSP 0x20 -#define SRCID_ES 0x40 -#define SRCID_ER 0x80 - - - -#define DEFAULT_SX_PER 500 /* (ns) fairly safe */ -#define DEFAULT_SX_OFF 0 /* aka async */ - -#define OPTIMUM_SX_PER 252 /* (ns) best we can do (mult-of-4) */ -#define OPTIMUM_SX_OFF 12 /* size of in2000 fifo */ - - -/* defines for hostdata->chip */ - -#define C_WD33C93 0 -#define C_WD33C93A 1 -#define C_WD33C93B 2 -#define C_UNKNOWN_CHIP 100 - -/* defines for hostdata->state */ - -#define S_UNCONNECTED 0 -#define S_SELECTING 1 -#define S_RUNNING_LEVEL2 2 -#define S_CONNECTED 3 -#define S_PRE_TMP_DISC 4 -#define S_PRE_CMP_DISC 5 - -/* defines for hostdata->fifo */ - -#define FI_FIFO_UNUSED 0 -#define FI_FIFO_READING 1 -#define FI_FIFO_WRITING 2 - -/* defines for hostdata->level2 */ -/* NOTE: only the first 3 are trustworthy at this point - - * having trouble when more than 1 device is reading/writing - * at the same time... - */ - -#define L2_NONE 0 /* no combination commands - we get lots of ints */ -#define L2_SELECT 1 /* start with SEL_ATN_XFER, but never resume it */ -#define L2_BASIC 2 /* resume after STATUS ints & RDP messages */ -#define L2_DATA 3 /* resume after DATA_IN/OUT ints */ -#define L2_MOST 4 /* resume after anything except a RESELECT int */ -#define L2_RESELECT 5 /* resume after everything, including RESELECT ints */ -#define L2_ALL 6 /* always resume */ - -/* defines for hostdata->disconnect */ - -#define DIS_NEVER 0 -#define DIS_ADAPTIVE 1 -#define DIS_ALWAYS 2 - -/* defines for hostdata->args */ - -#define DB_TEST 1<<0 -#define DB_FIFO 1<<1 -#define DB_QUEUE_COMMAND 1<<2 -#define DB_EXECUTE 1<<3 -#define DB_INTR 1<<4 -#define DB_TRANSFER 1<<5 -#define DB_MASK 0x3f -#define A_NO_SCSI_RESET 1<<15 - - -/* defines for hostdata->sync_xfer[] */ - -#define SS_UNSET 0 -#define SS_FIRST 1 -#define SS_WAITING 2 -#define SS_SET 3 - -/* defines for hostdata->proc */ - -#define PR_VERSION 1<<0 -#define PR_INFO 1<<1 -#define PR_TOTALS 1<<2 -#define PR_CONNECTED 1<<3 -#define PR_INPUTQ 1<<4 -#define PR_DISCQ 1<<5 -#define PR_TEST 1<<6 -#define PR_STOP 1<<7 - - -#define read1_io(a) (inb(hostdata->io_base+(a))) -#define read2_io(a) (inw(hostdata->io_base+(a))) -#define write1_io(b,a) (outb((b),hostdata->io_base+(a))) -#define write2_io(w,a) (outw((w),hostdata->io_base+(a))) - - -struct sx_period { - unsigned int period_ns; - uchar reg_value; - }; - - -struct IN2000_hostdata { - struct Scsi_Host *next; - uchar chip; /* what kind of wd33c93 chip? */ - uchar microcode; /* microcode rev if 'B' */ - unsigned short io_base; /* IO port base */ - unsigned int dip_switch; /* dip switch settings */ - unsigned int hrev; /* hardware revision of card */ - volatile uchar busy[8]; /* index = target, bit = lun */ - volatile Scsi_Cmnd *input_Q; /* commands waiting to be started */ - volatile Scsi_Cmnd *selecting; /* trying to select this command */ - volatile Scsi_Cmnd *connected; /* currently connected command */ - volatile Scsi_Cmnd *disconnected_Q;/* commands waiting for reconnect */ - uchar state; /* what we are currently doing */ - uchar fifo; /* what the FIFO is up to */ - uchar level2; /* extent to which Level-2 commands are used */ - uchar disconnect; /* disconnect/reselect policy */ - unsigned int args; /* set from command-line argument */ - uchar incoming_msg[8]; /* filled during message_in phase */ - int incoming_ptr; /* mainly used with EXTENDED messages */ - uchar outgoing_msg[8]; /* send this during next message_out */ - int outgoing_len; /* length of outgoing message */ - unsigned int default_sx_per; /* default transfer period for SCSI bus */ - uchar sync_xfer[8]; /* sync_xfer reg settings per target */ - uchar sync_stat[8]; /* status of sync negotiation per target */ - uchar sync_off; /* bit mask: don't use sync with these targets */ - uchar proc; /* bit mask: what's in proc output */ - }; - -/* These inline assembly defines are derived from a patch - * sent to me by Bill Earnest. He's done a lot of very - * valuable thinking, testing, and coding during his effort - * to squeeze more speed out of this driver. I really think - * that we are doing IO at close to the maximum now with - * the fifo. (And yes, insw uses 'edi' while outsw uses - * 'esi'. Thanks Bill!) - */ +static struct Scsi_Host *instance_list = 0; -#define FAST_READ2_IO() \ - __asm__ __volatile__ ("\n \ - cld \n \ - orl %%ecx, %%ecx \n \ - jz 1f \n \ - rep \n \ - insw %%dx \n \ -1: " \ - : "=D" (sp) /* output */ \ - : "d" (f), "D" (sp), "c" (i) /* input */ \ - : "edx", "ecx", "edi" ) /* trashed */ - -#define FAST_WRITE2_IO() \ - __asm__ __volatile__ ("\n \ - cld \n \ - orl %%ecx, %%ecx \n \ - jz 1f \n \ - rep \n \ - outsw %%dx \n \ -1: " \ - : "=S" (sp) /* output */ \ - : "d" (f), "S" (sp), "c" (i) /* input */ \ - : "edx", "ecx", "esi" ) /* trashed */ static inline uchar read_3393(struct IN2000_hostdata *hostdata, uchar reg_num) @@ -596,10 +278,10 @@ static int is_dir_out(Scsi_Cmnd *cmd) switch (cmd->cmnd[0]) { case WRITE_6: case WRITE_10: case WRITE_12: case WRITE_LONG: case WRITE_SAME: case WRITE_BUFFER: - case WRITE_VERIFY: case WRITE_VERIFY_12: + case WRITE_VERIFY: case WRITE_VERIFY_12: case COMPARE: case COPY: case COPY_VERIFY: case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW: - case SEARCH_EQUAL_12: case SEARCH_HIGH_12: case SEARCH_LOW_12: + case SEARCH_EQUAL_12: case SEARCH_HIGH_12: case SEARCH_LOW_12: case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE: case MODE_SELECT: case MODE_SELECT_10: case LOG_SELECT: case SEND_DIAGNOSTIC: case CHANGE_DEFINITION: case UPDATE_BLOCK: @@ -624,7 +306,7 @@ static struct sx_period sx_table[] = { {1000,0x00}, {0, 0} }; -int round_period(unsigned int period) +static int round_period(unsigned int period) { int x; @@ -657,7 +339,6 @@ struct IN2000_hostdata *hostdata; Scsi_Cmnd *tmp; unsigned long flags; - hostdata = (struct IN2000_hostdata *)cmd->host->hostdata; DB(DB_QUEUE_COMMAND,printk("Q-%d-%02x-%ld(",cmd->target,cmd->cmnd[0],cmd->pid)) @@ -675,7 +356,7 @@ DB(DB_QUEUE_COMMAND,printk("Q-%d-%02x-%ld(",cmd->target,cmd->cmnd[0],cmd->pid)) /* We use the Scsi_Pointer structure that's included with each command * as a scratchpad (as it's intended to be used!). The handy thing about * the SCp.xxx fields is that they're always associated with a given - * cmd, and are preserved across disconnect-reconnect. This means we + * cmd, and are preserved across disconnect-reselect. This means we * can pretty much ignore SAVE_POINTERS and RESTORE_POINTERS messages * if we keep all the critical pointers and counters in SCp: * - SCp.ptr is the pointer into the RAM buffer @@ -703,9 +384,24 @@ DB(DB_QUEUE_COMMAND,printk("Q-%d-%02x-%ld(",cmd->target,cmd->cmnd[0],cmd->pid)) /* We don't set SCp.phase here - that's done in in2000_execute() */ -/* Preset the command status to GOOD, since that's the normal case */ +/* WD docs state that at the conclusion of a "LEVEL2" command, the + * status byte can be retrieved from the LUN register. Apparently, + * this is the case only for *uninterrupted* LEVEL2 commands! If + * there are any unexpected phases entered, even if they are 100% + * legal (different devices may choose to do things differently), + * the LEVEL2 command sequence is exited. This often occurs prior + * to receiving the status byte, in which case the driver does a + * status phase interrupt and gets the status byte on its own. + * While such a command can then be "resumed" (ie restarted to + * finish up as a LEVEL2 command), the LUN register will NOT be + * a valid status byte at the command's conclusion, and we must + * use the byte obtained during the earlier interrupt. Here, we + * preset SCp.Status to an illegal value (0xff) so that when + * this command finally completes, we can tell where the actual + * status byte is stored. + */ - cmd->SCp.Status = GOOD; + cmd->SCp.Status = ILLEGAL_STATUS_BYTE; save_flags(flags); cli(); @@ -803,6 +499,10 @@ DB(DB_EXECUTE,printk(")EX-1 ")) else hostdata->input_Q = (Scsi_Cmnd *)cmd->host_scribble; +#ifdef PROC_STATISTICS + hostdata->cmd_cnt[cmd->target]++; +#endif + /* * Start the selection process */ @@ -860,8 +560,8 @@ DB(DB_EXECUTE,printk(")EX-1 ")) yes: cmd->SCp.phase = 1; -#ifdef PROC_INTERFACE - disc_allowed_total++; +#ifdef PROC_STATISTICS + hostdata->disc_allowed_cnt[cmd->target]++; #endif no: @@ -1094,7 +794,8 @@ int i; if (data_in_dir) { write1_io(0,IO_FIFO_READ); - if ((hostdata->level2 >= L2_DATA) || (cmd->SCp.phase == 0)) { + if ((hostdata->level2 >= L2_DATA) || + (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) { write_3393(hostdata,WD_COMMAND_PHASE,0x45); write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER); hostdata->state = S_RUNNING_LEVEL2; @@ -1111,7 +812,8 @@ int i; * write any bytes that don't make it at this stage. */ - if ((hostdata->level2 >= L2_DATA) || (cmd->SCp.phase == 0)) { + if ((hostdata->level2 >= L2_DATA) || + (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) { write_3393(hostdata,WD_COMMAND_PHASE,0x45); write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER); hostdata->state = S_RUNNING_LEVEL2; @@ -1177,6 +879,10 @@ unsigned short f; save_flags(flags); sti(); +#ifdef PROC_STATISTICS + hostdata->int_cnt++; +#endif + /* The IN2000 card has 2 interrupt sources OR'ed onto its IRQ line - the * WD3393 chip and the 2k fifo (which is actually a dual-port RAM combined * with a big logic array, so it's a little different than what you might @@ -1484,9 +1190,10 @@ DB(DB_INTR,printk("CMND-%02x,%ld",cmd->cmnd[0],cmd->pid)) case CSR_XFER_DONE|PHS_STATUS: case CSR_UNEXP |PHS_STATUS: case CSR_SRV_REQ |PHS_STATUS: -DB(DB_INTR,printk("STATUS")) +DB(DB_INTR,printk("STATUS=")) cmd->SCp.Status = read_1_byte(hostdata); +DB(DB_INTR,printk("%02x",cmd->SCp.Status)) if (hostdata->level2 >= L2_BASIC) { sr = read_3393(hostdata,WD_SCSI_STATUS); /* clear interrupt */ hostdata->state = S_RUNNING_LEVEL2; @@ -1494,7 +1201,6 @@ DB(DB_INTR,printk("STATUS")) write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER); } else { -DB(DB_INTR,printk("=%02x",cmd->SCp.Status)) hostdata->state = S_CONNECTED; } break; @@ -1665,15 +1371,16 @@ printk("sync_xfer=%02x",hostdata->sync_xfer[cmd->target]); DB(DB_INTR,printk("SX-DONE-%ld",cmd->pid)) cmd->SCp.Message = COMMAND_COMPLETE; lun = read_3393(hostdata,WD_TARGET_LUN); - if (cmd->SCp.Status == GOOD) - cmd->SCp.Status = lun; +DB(DB_INTR,printk(":%d.%d",cmd->SCp.Status,lun)) hostdata->connected = NULL; - if (cmd->cmnd[0] != REQUEST_SENSE) - cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); - else if (cmd->SCp.Status != GOOD) - cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); hostdata->busy[cmd->target] &= ~(1 << cmd->lun); hostdata->state = S_UNCONNECTED; + if (cmd->SCp.Status == ILLEGAL_STATUS_BYTE) + cmd->SCp.Status = lun; + if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD) + cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); + else + cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); cmd->scsi_done(cmd); /* We are no longer connected to a target - check to see if @@ -1755,10 +1462,10 @@ DB(DB_INTR,printk("UNEXP_DISC-%ld",cmd->pid)) hostdata->connected = NULL; hostdata->busy[cmd->target] &= ~(1 << cmd->lun); hostdata->state = S_UNCONNECTED; - if (cmd->cmnd[0] != REQUEST_SENSE) - cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); - else if (cmd->SCp.Status != GOOD) + if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD) cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); + else + cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); cmd->scsi_done(cmd); /* We are no longer connected to a target - check to see if @@ -1788,10 +1495,11 @@ DB(DB_INTR,printk("DISC-%ld",cmd->pid)) hostdata->connected = NULL; hostdata->busy[cmd->target] &= ~(1 << cmd->lun); hostdata->state = S_UNCONNECTED; - if (cmd->cmnd[0] != REQUEST_SENSE) - cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); - else if (cmd->SCp.Status != GOOD) +DB(DB_INTR,printk(":%d",cmd->SCp.Status)) + if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD) cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); + else + cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); cmd->scsi_done(cmd); break; case S_PRE_TMP_DISC: @@ -1801,8 +1509,8 @@ DB(DB_INTR,printk("DISC-%ld",cmd->pid)) hostdata->connected = NULL; hostdata->state = S_UNCONNECTED; -#ifdef PROC_INTERFACE - disc_taken_total++; +#ifdef PROC_STATISTICS + hostdata->disc_done_cnt[cmd->target]++; #endif break; @@ -2158,10 +1866,11 @@ unsigned long timeout; #define MAX_IN2000_HOSTS 3 -#define MAX_SETUP_STRINGS (sizeof(setup_strings) / sizeof(char *)) +#define MAX_SETUP_ARGS (sizeof(setup_args) / sizeof(char *)) #define SETUP_BUFFER_SIZE 200 static char setup_buffer[SETUP_BUFFER_SIZE]; -static char setup_used[MAX_SETUP_STRINGS]; +static char setup_used[MAX_SETUP_ARGS]; +static int done_setup = 0; void in2000_setup (char *str, int *ints) { @@ -2172,43 +1881,44 @@ char *p1,*p2; setup_buffer[SETUP_BUFFER_SIZE - 1] = '\0'; p1 = setup_buffer; i = 0; - while (*p1 && (i < MAX_SETUP_STRINGS)) { + while (*p1 && (i < MAX_SETUP_ARGS)) { p2 = strchr(p1, ','); if (p2) { *p2 = '\0'; if (p1 != p2) - setup_strings[i] = p1; + setup_args[i] = p1; p1 = p2 + 1; i++; } else { - setup_strings[i] = p1; + setup_args[i] = p1; break; } } - for (i=0; i<MAX_SETUP_STRINGS; i++) + for (i=0; i<MAX_SETUP_ARGS; i++) setup_used[i] = 0; + done_setup = 1; } -/* check_setup_strings() returns index if key found, 0 if not +/* check_setup_args() returns index if key found, 0 if not */ -static int check_setup_strings(char *key, int *flags, int *val, char *buf) +static int check_setup_args(char *key, int *flags, int *val, char *buf) { int x; char *cp; - for (x=0; x<MAX_SETUP_STRINGS; x++) { + for (x=0; x<MAX_SETUP_ARGS; x++) { if (setup_used[x]) continue; - if (!strncmp(setup_strings[x], key, strlen(key))) + if (!strncmp(setup_args[x], key, strlen(key))) break; } - if (x == MAX_SETUP_STRINGS) + if (x == MAX_SETUP_ARGS) return 0; setup_used[x] = 1; - cp = setup_strings[x] + strlen(key); + cp = setup_args[x] + strlen(key); *val = -1; if (*cp != ':') return ++x; @@ -2221,16 +1931,10 @@ char *cp; -struct proc_dir_entry proc_scsi_in2000 = { - PROC_SCSI_IN2000, 6, "in2000", - S_IFDIR | S_IRUGO | S_IXUGO, 2 - }; - - -/* As of the 2.1.x kernel series, memory-mapped hardware such - * as the IN2000 EPROM and dip switch must be accessed through - * special macros declared in 'asm/io.h'. We use readb() and - * readl() when reading from the card's BIOS area in in2000_detect(). +/* The "correct" (ie portable) way to access memory-mapped hardware + * such as the IN2000 EPROM and dip switch is through the use of + * special macros declared in 'asm/io.h'. We use readb() and readl() + * when reading from the card's BIOS area in in2000_detect(). */ static const unsigned int *bios_tab[] = { (unsigned int *)0xc8000, @@ -2253,6 +1957,7 @@ static const int int_tab[] = { 10 }; + int in2000_detect(Scsi_Host_Template * tpnt) { struct Scsi_Host *instance; @@ -2268,29 +1973,33 @@ int val; char buf[32]; /* Thanks to help from Bill Earnest, probing for IN2000 cards is a - * pretty straightforward and fool-proof operation. We do require - * that cards have their BIOS enabled, although I hope to be able - * to detect and use BIOS-less cards in the future. There are 3 + * pretty straightforward and fool-proof operation. There are 3 * possible locations for the IN2000 EPROM in memory space - if we * find a BIOS signature, we can read the dip switch settings from * the byte at BIOS+32 (shadowed in by logic on the card). From 2 * of the switch bits we get the card's address in IO space. There's * an image of the dip switch there, also, so we have a way to back- - * check that this really is an IN2000 card. Very nifty. - * - * There have been a couple of BIOS versions with different layouts - * for the obvious ID strings. We look for the 2 most common ones and - * hope that they cover all the cases... + * check that this really is an IN2000 card. Very nifty. Use the + * 'ioport:xx' command-line parameter if your BIOS EPROM is absent + * or disabled. */ + if (!done_setup && setup_strings) + in2000_setup(setup_strings,0); + detect_count = 0; for (bios = 0; bios_tab[bios]; bios++) { - if (check_setup_strings("ioport",&flags,&val,buf)) { + if (check_setup_args("ioport",&flags,&val,buf)) { base = val; switches = ~inb(base + IO_SWITCHES) & 0xff; printk("Forcing IN2000 detection at IOport 0x%x ",base); bios = 2; } +/* + * There have been a couple of BIOS versions with different layouts + * for the obvious ID strings. We look for the 2 most common ones and + * hope that they cover all the cases... + */ else if (readl(bios_tab[bios]+0x04) == 0x41564f4e || readl(bios_tab[bios]+0x0c) == 0x61776c41) { printk("Found IN2000 BIOS at 0x%x ",(unsigned int)bios_tab[bios]); @@ -2374,6 +2083,11 @@ char buf[32]; hostdata->busy[x] = 0; hostdata->sync_xfer[x] = calc_sync_xfer(DEFAULT_SX_PER/4,DEFAULT_SX_OFF); hostdata->sync_stat[x] = SS_UNSET; /* using default sync values */ +#ifdef PROC_STATISTICS + hostdata->cmd_cnt[x] = 0; + hostdata->disc_allowed_cnt[x] = 0; + hostdata->disc_done_cnt[x] = 0; +#endif } hostdata->input_Q = NULL; hostdata->selecting = NULL; @@ -2395,36 +2109,42 @@ char buf[32]; else hostdata->sync_off = 0xff; /* sync defaults to off */ - hostdata->proc = PR_VERSION|PR_INFO|PR_TOTALS| +#ifdef PROC_INTERFACE + hostdata->proc = PR_VERSION|PR_INFO|PR_STATISTICS| PR_CONNECTED|PR_INPUTQ|PR_DISCQ| PR_STOP; - -#ifdef PROC_INTERFACE - disc_allowed_total = 0; - disc_taken_total = 0; +#ifdef PROC_STATISTICS + hostdata->int_cnt = 0; +#endif #endif - if (check_setup_strings("nosync",&flags,&val,buf)) + if (check_setup_args("nosync",&flags,&val,buf)) hostdata->sync_off = val; - if (check_setup_strings("period",&flags,&val,buf)) + if (check_setup_args("period",&flags,&val,buf)) hostdata->default_sx_per = sx_table[round_period((unsigned int)val)].period_ns; - if (check_setup_strings("disconnect",&flags,&val,buf)) { + if (check_setup_args("disconnect",&flags,&val,buf)) { if ((val >= DIS_NEVER) && (val <= DIS_ALWAYS)) hostdata->disconnect = val; else hostdata->disconnect = DIS_ADAPTIVE; } - if (check_setup_strings("noreset",&flags,&val,buf)) + if (check_setup_args("noreset",&flags,&val,buf)) hostdata->args ^= A_NO_SCSI_RESET; - if (check_setup_strings("debug",&flags,&val,buf)) + if (check_setup_args("level2",&flags,&val,buf)) + hostdata->level2 = val; + + if (check_setup_args("debug",&flags,&val,buf)) hostdata->args = (val & DB_MASK); - if (check_setup_strings("proc",&flags,&val,buf)) +#ifdef PROC_INTERFACE + if (check_setup_args("proc",&flags,&val,buf)) hostdata->proc = val; +#endif + x = reset_hardware(instance,(hostdata->args & A_NO_SCSI_RESET)?RESET_CARD:RESET_CARD_AND_BUS); @@ -2450,9 +2170,9 @@ char buf[32]; (hostdata->chip==C_WD33C93B)?"WD33c93B":"unknown", hostdata->microcode); #ifdef DEBUGGING_ON - printk("setup_strings = "); - for (x=0; x<8; x++) - printk("%s,",setup_strings[x]); + printk("setup_args = "); + for (x=0; x<MAX_SETUP_ARGS; x++) + printk("%s,",setup_args[x]); printk("\n"); #endif if (hostdata->sync_off == 0xff) @@ -2496,23 +2216,18 @@ int size; iinfo[0] = 255; iinfo[1] = 63; iinfo[2] = disk->capacity / (iinfo[0] * iinfo[1]); - -/* This next little bit of code was intended to prevent the number of - * tracks from exceeding 1023. As Andries Brouwer (aeb@cwi.nl) pointed - * out in his "Large Disk HOWTO" (June 1996), this kind of DOS - * compatibility is pointless. And wasteful on disks larger than 8 gigs. - */ - -#if 0 - if (iinfo[2] > 1023) - iinfo[2] = 1023; -#endif - } return 0; } + +struct proc_dir_entry proc_scsi_in2000 = { + PROC_SCSI_IN2000, 6, "in2000", + S_IFDIR | S_IRUGO | S_IXUGO, 2 + }; + + int in2000_proc_info(char *buf, char **start, off_t off, int len, int hn, int in) { @@ -2576,6 +2291,10 @@ static int stop = 0; bp += 5; hd->proc = simple_strtoul(bp,NULL,0); } + else if (!strncmp(bp,"level2:",7)) { + bp += 7; + hd->level2 = simple_strtoul(bp,NULL,0); + } return len; } @@ -2594,12 +2313,38 @@ static int stop = 0; (hd->dip_switch & 0x40)?"Yes":"No", (hd->dip_switch & 0x20)?"Yes":"No"); strcat(bp,tbuf); + strcat(bp,"\nsync_xfer[] = "); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%02x",hd->sync_xfer[x]); + strcat(bp,tbuf); + } + strcat(bp,"\nsync_stat[] = "); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%02x",hd->sync_stat[x]); + strcat(bp,tbuf); + } } - if (hd->proc & PR_TOTALS) { - sprintf(tbuf,"\n%ld disc_allowed, %ld disc_taken", - disc_allowed_total,disc_taken_total); +#ifdef PROC_STATISTICS + if (hd->proc & PR_STATISTICS) { + strcat(bp,"\ncommands issued: "); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%ld",hd->cmd_cnt[x]); + strcat(bp,tbuf); + } + strcat(bp,"\ndisconnects allowed:"); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%ld",hd->disc_allowed_cnt[x]); + strcat(bp,tbuf); + } + strcat(bp,"\ndisconnects done: "); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%ld",hd->disc_done_cnt[x]); + strcat(bp,tbuf); + } + sprintf(tbuf,"\ninterrupts: \t%ld",hd->int_cnt); strcat(bp,tbuf); } +#endif if (hd->proc & PR_CONNECTED) { strcat(bp,"\nconnected: "); if (hd->connected) { diff --git a/drivers/scsi/in2000.h b/drivers/scsi/in2000.h index 4bae99bc1..bb75a08c5 100644 --- a/drivers/scsi/in2000.h +++ b/drivers/scsi/in2000.h @@ -2,7 +2,7 @@ * in2000.h - Linux device driver definitions for the * Always IN2000 ISA SCSI card. * - * IMPORTANT: This file is for version 1.30 - 14/Oct/1996 + * IMPORTANT: This file is for version 1.31 - 06/Jul/1997 * * Copyright (c) 1996 John Shifflett, GeoLog Consulting * john@geolog.com @@ -23,13 +23,366 @@ #ifndef IN2000_H #define IN2000_H -extern struct proc_dir_entry proc_scsi_in2000; +#include <asm/io.h> + +#define PROC_INTERFACE /* add code for /proc/scsi/in2000/xxx interface */ +#ifdef PROC_INTERFACE +#define PROC_STATISTICS /* add code for keeping various real time stats */ +#endif + +#define SYNC_DEBUG /* extra info on sync negotiation printed */ +#define DEBUGGING_ON /* enable command-line debugging bitmask */ +#define DEBUG_DEFAULTS 0 /* default bitmask - change from command-line */ + +#define FAST_READ_IO /* No problems with these on my machine */ +#define FAST_WRITE_IO + +#ifdef DEBUGGING_ON +#define DB(f,a) if (hostdata->args & (f)) a; +#define CHECK_NULL(p,s) /* if (!(p)) {printk("\n"); while (1) printk("NP:%s\r",(s));} */ +#else +#define DB(f,a) +#define CHECK_NULL(p,s) +#endif + +#define uchar unsigned char + +#define read1_io(a) (inb(hostdata->io_base+(a))) +#define read2_io(a) (inw(hostdata->io_base+(a))) +#define write1_io(b,a) (outb((b),hostdata->io_base+(a))) +#define write2_io(w,a) (outw((w),hostdata->io_base+(a))) + +/* These inline assembly defines are derived from a patch + * sent to me by Bill Earnest. He's done a lot of very + * valuable thinking, testing, and coding during his effort + * to squeeze more speed out of this driver. I really think + * that we are doing IO at close to the maximum now with + * the fifo. (And yes, insw uses 'edi' while outsw uses + * 'esi'. Thanks Bill!) + */ + +#define FAST_READ2_IO() \ + __asm__ __volatile__ ("\n \ + cld \n \ + orl %%ecx, %%ecx \n \ + jz 1f \n \ + rep \n \ + insw %%dx \n \ +1: " \ + : "=D" (sp) /* output */ \ + : "d" (f), "D" (sp), "c" (i) /* input */ \ + : "edx", "ecx", "edi" ) /* trashed */ + +#define FAST_WRITE2_IO() \ + __asm__ __volatile__ ("\n \ + cld \n \ + orl %%ecx, %%ecx \n \ + jz 1f \n \ + rep \n \ + outsw %%dx \n \ +1: " \ + : "=S" (sp) /* output */ \ + : "d" (f), "S" (sp), "c" (i) /* input */ \ + : "edx", "ecx", "esi" ) /* trashed */ + + +/* IN2000 io_port offsets */ +#define IO_WD_ASR 0x00 /* R - 3393 auxstat reg */ +#define ASR_INT 0x80 +#define ASR_LCI 0x40 +#define ASR_BSY 0x20 +#define ASR_CIP 0x10 +#define ASR_PE 0x02 +#define ASR_DBR 0x01 +#define IO_WD_ADDR 0x00 /* W - 3393 address reg */ +#define IO_WD_DATA 0x01 /* R/W - rest of 3393 regs */ +#define IO_FIFO 0x02 /* R/W - in2000 dual-port fifo (16 bits) */ +#define IN2000_FIFO_SIZE 2048 /* fifo capacity in bytes */ +#define IO_CARD_RESET 0x03 /* W - in2000 start master reset */ +#define IO_FIFO_COUNT 0x04 /* R - in2000 fifo counter */ +#define IO_FIFO_WRITE 0x05 /* W - clear fifo counter, start write */ +#define IO_FIFO_READ 0x07 /* W - start fifo read */ +#define IO_LED_OFF 0x08 /* W - turn off in2000 activity LED */ +#define IO_SWITCHES 0x08 /* R - read in2000 dip switch */ +#define SW_ADDR0 0x01 /* bit 0 = bit 0 of index to io addr */ +#define SW_ADDR1 0x02 /* bit 1 = bit 1 of index io addr */ +#define SW_DISINT 0x04 /* bit 2 true if ints disabled */ +#define SW_INT0 0x08 /* bit 3 = bit 0 of index to interrupt */ +#define SW_INT1 0x10 /* bit 4 = bit 1 of index to interrupt */ +#define SW_INT_SHIFT 3 /* shift right this amount to right justify int bits */ +#define SW_SYNC_DOS5 0x20 /* bit 5 used by Always BIOS */ +#define SW_FLOPPY 0x40 /* bit 6 true if floppy enabled */ +#define SW_BIT7 0x80 /* bit 7 hardwired true (ground) */ +#define IO_LED_ON 0x09 /* W - turn on in2000 activity LED */ +#define IO_HARDWARE 0x0a /* R - read in2000 hardware rev, stop reset */ +#define IO_INTR_MASK 0x0c /* W - in2000 interrupt mask reg */ +#define IMASK_WD 0x01 /* WD33c93 interrupt mask */ +#define IMASK_FIFO 0x02 /* FIFO interrupt mask */ + +/* wd register names */ +#define WD_OWN_ID 0x00 +#define WD_CONTROL 0x01 +#define WD_TIMEOUT_PERIOD 0x02 +#define WD_CDB_1 0x03 +#define WD_CDB_2 0x04 +#define WD_CDB_3 0x05 +#define WD_CDB_4 0x06 +#define WD_CDB_5 0x07 +#define WD_CDB_6 0x08 +#define WD_CDB_7 0x09 +#define WD_CDB_8 0x0a +#define WD_CDB_9 0x0b +#define WD_CDB_10 0x0c +#define WD_CDB_11 0x0d +#define WD_CDB_12 0x0e +#define WD_TARGET_LUN 0x0f +#define WD_COMMAND_PHASE 0x10 +#define WD_SYNCHRONOUS_TRANSFER 0x11 +#define WD_TRANSFER_COUNT_MSB 0x12 +#define WD_TRANSFER_COUNT 0x13 +#define WD_TRANSFER_COUNT_LSB 0x14 +#define WD_DESTINATION_ID 0x15 +#define WD_SOURCE_ID 0x16 +#define WD_SCSI_STATUS 0x17 +#define WD_COMMAND 0x18 +#define WD_DATA 0x19 +#define WD_QUEUE_TAG 0x1a +#define WD_AUXILIARY_STATUS 0x1f + +/* WD commands */ +#define WD_CMD_RESET 0x00 +#define WD_CMD_ABORT 0x01 +#define WD_CMD_ASSERT_ATN 0x02 +#define WD_CMD_NEGATE_ACK 0x03 +#define WD_CMD_DISCONNECT 0x04 +#define WD_CMD_RESELECT 0x05 +#define WD_CMD_SEL_ATN 0x06 +#define WD_CMD_SEL 0x07 +#define WD_CMD_SEL_ATN_XFER 0x08 +#define WD_CMD_SEL_XFER 0x09 +#define WD_CMD_RESEL_RECEIVE 0x0a +#define WD_CMD_RESEL_SEND 0x0b +#define WD_CMD_WAIT_SEL_RECEIVE 0x0c +#define WD_CMD_TRANS_ADDR 0x18 +#define WD_CMD_TRANS_INFO 0x20 +#define WD_CMD_TRANSFER_PAD 0x21 +#define WD_CMD_SBT_MODE 0x80 + +/* SCSI Bus Phases */ +#define PHS_DATA_OUT 0x00 +#define PHS_DATA_IN 0x01 +#define PHS_COMMAND 0x02 +#define PHS_STATUS 0x03 +#define PHS_MESS_OUT 0x06 +#define PHS_MESS_IN 0x07 + +/* Command Status Register definitions */ + + /* reset state interrupts */ +#define CSR_RESET 0x00 +#define CSR_RESET_AF 0x01 + + /* successful completion interrupts */ +#define CSR_RESELECT 0x10 +#define CSR_SELECT 0x11 +#define CSR_SEL_XFER_DONE 0x16 +#define CSR_XFER_DONE 0x18 + + /* paused or aborted interrupts */ +#define CSR_MSGIN 0x20 +#define CSR_SDP 0x21 +#define CSR_SEL_ABORT 0x22 +#define CSR_RESEL_ABORT 0x25 +#define CSR_RESEL_ABORT_AM 0x27 +#define CSR_ABORT 0x28 + + /* terminated interrupts */ +#define CSR_INVALID 0x40 +#define CSR_UNEXP_DISC 0x41 +#define CSR_TIMEOUT 0x42 +#define CSR_PARITY 0x43 +#define CSR_PARITY_ATN 0x44 +#define CSR_BAD_STATUS 0x45 +#define CSR_UNEXP 0x48 + + /* service required interrupts */ +#define CSR_RESEL 0x80 +#define CSR_RESEL_AM 0x81 +#define CSR_DISC 0x85 +#define CSR_SRV_REQ 0x88 + + /* Own ID/CDB Size register */ +#define OWNID_EAF 0x08 +#define OWNID_EHP 0x10 +#define OWNID_RAF 0x20 +#define OWNID_FS_8 0x00 +#define OWNID_FS_12 0x40 +#define OWNID_FS_16 0x80 + + /* Control register */ +#define CTRL_HSP 0x01 +#define CTRL_HA 0x02 +#define CTRL_IDI 0x04 +#define CTRL_EDI 0x08 +#define CTRL_HHP 0x10 +#define CTRL_POLLED 0x00 +#define CTRL_BURST 0x20 +#define CTRL_BUS 0x40 +#define CTRL_DMA 0x80 + + /* Timeout Period register */ +#define TIMEOUT_PERIOD_VALUE 20 /* results in 200 ms. */ + + /* Synchronous Transfer Register */ +#define STR_FSS 0x80 + + /* Destination ID register */ +#define DSTID_DPD 0x40 +#define DATA_OUT_DIR 0 +#define DATA_IN_DIR 1 +#define DSTID_SCC 0x80 + + /* Source ID register */ +#define SRCID_MASK 0x07 +#define SRCID_SIV 0x08 +#define SRCID_DSP 0x20 +#define SRCID_ES 0x40 +#define SRCID_ER 0x80 + + + +#define ILLEGAL_STATUS_BYTE 0xff + + +#define DEFAULT_SX_PER 500 /* (ns) fairly safe */ +#define DEFAULT_SX_OFF 0 /* aka async */ + +#define OPTIMUM_SX_PER 252 /* (ns) best we can do (mult-of-4) */ +#define OPTIMUM_SX_OFF 12 /* size of in2000 fifo */ + +struct sx_period { + unsigned int period_ns; + uchar reg_value; + }; + + +struct IN2000_hostdata { + struct Scsi_Host *next; + uchar chip; /* what kind of wd33c93 chip? */ + uchar microcode; /* microcode rev if 'B' */ + unsigned short io_base; /* IO port base */ + unsigned int dip_switch; /* dip switch settings */ + unsigned int hrev; /* hardware revision of card */ + volatile uchar busy[8]; /* index = target, bit = lun */ + volatile Scsi_Cmnd *input_Q; /* commands waiting to be started */ + volatile Scsi_Cmnd *selecting; /* trying to select this command */ + volatile Scsi_Cmnd *connected; /* currently connected command */ + volatile Scsi_Cmnd *disconnected_Q;/* commands waiting for reconnect */ + uchar state; /* what we are currently doing */ + uchar fifo; /* what the FIFO is up to */ + uchar level2; /* extent to which Level-2 commands are used */ + uchar disconnect; /* disconnect/reselect policy */ + unsigned int args; /* set from command-line argument */ + uchar incoming_msg[8]; /* filled during message_in phase */ + int incoming_ptr; /* mainly used with EXTENDED messages */ + uchar outgoing_msg[8]; /* send this during next message_out */ + int outgoing_len; /* length of outgoing message */ + unsigned int default_sx_per; /* default transfer period for SCSI bus */ + uchar sync_xfer[8]; /* sync_xfer reg settings per target */ + uchar sync_stat[8]; /* status of sync negotiation per target */ + uchar sync_off; /* bit mask: don't use sync with these targets */ +#ifdef PROC_INTERFACE + uchar proc; /* bit mask: what's in proc output */ +#ifdef PROC_STATISTICS + unsigned long cmd_cnt[8]; /* # of commands issued per target */ + unsigned long int_cnt; /* # of interrupts serviced */ + unsigned long disc_allowed_cnt[8]; /* # of disconnects allowed per target */ + unsigned long disc_done_cnt[8]; /* # of disconnects done per target*/ +#endif +#endif + }; + + +/* defines for hostdata->chip */ + +#define C_WD33C93 0 +#define C_WD33C93A 1 +#define C_WD33C93B 2 +#define C_UNKNOWN_CHIP 100 + +/* defines for hostdata->state */ + +#define S_UNCONNECTED 0 +#define S_SELECTING 1 +#define S_RUNNING_LEVEL2 2 +#define S_CONNECTED 3 +#define S_PRE_TMP_DISC 4 +#define S_PRE_CMP_DISC 5 + +/* defines for hostdata->fifo */ + +#define FI_FIFO_UNUSED 0 +#define FI_FIFO_READING 1 +#define FI_FIFO_WRITING 2 + +/* defines for hostdata->level2 */ +/* NOTE: only the first 3 are trustworthy at this point - + * having trouble when more than 1 device is reading/writing + * at the same time... + */ + +#define L2_NONE 0 /* no combination commands - we get lots of ints */ +#define L2_SELECT 1 /* start with SEL_ATN_XFER, but never resume it */ +#define L2_BASIC 2 /* resume after STATUS ints & RDP messages */ +#define L2_DATA 3 /* resume after DATA_IN/OUT ints */ +#define L2_MOST 4 /* resume after anything except a RESELECT int */ +#define L2_RESELECT 5 /* resume after everything, including RESELECT ints */ +#define L2_ALL 6 /* always resume */ + +/* defines for hostdata->disconnect */ + +#define DIS_NEVER 0 +#define DIS_ADAPTIVE 1 +#define DIS_ALWAYS 2 + +/* defines for hostdata->args */ + +#define DB_TEST 1<<0 +#define DB_FIFO 1<<1 +#define DB_QUEUE_COMMAND 1<<2 +#define DB_EXECUTE 1<<3 +#define DB_INTR 1<<4 +#define DB_TRANSFER 1<<5 +#define DB_MASK 0x3f + +#define A_NO_SCSI_RESET 1<<15 + + +/* defines for hostdata->sync_xfer[] */ + +#define SS_UNSET 0 +#define SS_FIRST 1 +#define SS_WAITING 2 +#define SS_SET 3 + +/* defines for hostdata->proc */ + +#define PR_VERSION 1<<0 +#define PR_INFO 1<<1 +#define PR_STATISTICS 1<<2 +#define PR_CONNECTED 1<<3 +#define PR_INPUTQ 1<<4 +#define PR_DISCQ 1<<5 +#define PR_TEST 1<<6 +#define PR_STOP 1<<7 + int in2000_detect(Scsi_Host_Template *); int in2000_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); int in2000_abort(Scsi_Cmnd *); void in2000_setup(char *, int *); int in2000_proc_info(char *, char **, off_t, int, int, int); +struct proc_dir_entry proc_scsi_in2000; int in2000_biosparam(struct scsi_disk *, kdev_t, int *); int in2000_reset(Scsi_Cmnd *, unsigned int); @@ -40,7 +393,7 @@ int in2000_reset(Scsi_Cmnd *, unsigned int); #define IN2000_HOST_ID 7 #define IN2000 { NULL, /* link pointer for modules */ \ - NULL, /* module pointer for modules */ \ + NULL, /* usage_count for modules */ \ &proc_scsi_in2000, /* pointer to /proc/scsi directory entry */ \ in2000_proc_info, /* pointer to proc info function */ \ "Always IN2000", /* device name */ \ diff --git a/drivers/scsi/ppa.c b/drivers/scsi/ppa.c index 581f280e8..e9bdcee9b 100644 --- a/drivers/scsi/ppa.c +++ b/drivers/scsi/ppa.c @@ -115,6 +115,8 @@ * [Curtin-1-08-STABLE] */ +#include <linux/config.h> + /* The following #define is to avoid a clash with hosts.c */ #define PPA_CODE 1 #include "ppa.h" diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c index 5e14ea594..b8620dfdc 100644 --- a/drivers/scsi/qlogicpti.c +++ b/drivers/scsi/qlogicpti.c @@ -653,7 +653,7 @@ __initfunc(int qlogicpti_detect(Scsi_Host_Template *tpnt)) } qpti_host->base = (unsigned char *)qregs; - qpti_host->io_port = (unsigned int) qregs; + qpti_host->io_port = (unsigned int) ((unsigned long)qregs); qpti_host->n_io_port = (unsigned char) qpti->qdev->reg_addrs[0].reg_size; @@ -805,7 +805,7 @@ static inline void cmd_frob(struct Command_Entry *cmd, Scsi_Cmnd *Cmnd, memset(cmd, 0, sizeof(struct Command_Entry)); cmd->hdr.entry_cnt = 1; cmd->hdr.entry_type = ENTRY_COMMAND; - cmd->handle = (u_int) Cmnd; /* magic mushroom */ + cmd->handle = (u_int) ((unsigned long)Cmnd); /* magic mushroom */ cmd->target_id = Cmnd->target; cmd->target_lun = Cmnd->lun; cmd->cdb_length = Cmnd->cmd_len; @@ -890,7 +890,7 @@ static inline u_int load_cmd(Scsi_Cmnd *Cmnd, struct Command_Entry *cmd, Cmnd->request_bufflen, qpti->qdev->my_bus)); - cmd->dataseg[0].d_base = (u_int) Cmnd->SCp.ptr; + cmd->dataseg[0].d_base = (u_int) ((unsigned long)Cmnd->SCp.ptr); cmd->dataseg[0].d_count = Cmnd->request_bufflen; cmd->segment_cnt = 1; } @@ -1062,7 +1062,7 @@ repeat: while(out_ptr != in_ptr) { sts = (struct Status_Entry *) &qpti->res_cpu[out_ptr]; out_ptr = NEXT_RES_PTR(out_ptr); - Cmnd = (Scsi_Cmnd *) sts->handle; /* but_to_virt?!?! */ + Cmnd = (Scsi_Cmnd *) ((unsigned long)sts->handle); if(sts->completion_status == CS_RESET_OCCURRED || sts->completion_status == CS_ABORTED || (sts->status_flags & STF_BUS_RESET)) @@ -1111,8 +1111,8 @@ int qlogicpti_abort(Scsi_Cmnd *Cmnd) qlogicpti_disable_irqs(qpti->qregs); param[0] = MBOX_ABORT; param[1] = (((u_short) Cmnd->target) << 8) | Cmnd->lun; - param[2] = ((unsigned int)Cmnd) >> 16; - param[3] = ((unsigned int)Cmnd) & 0xffff; + param[2] = ((unsigned int)((unsigned long)Cmnd)) >> 16; + param[3] = ((unsigned int)((unsigned long)Cmnd)) & 0xffff; if(qlogicpti_mbox_command(qpti, param, 0) || (param[0] != MBOX_COMMAND_COMPLETE)) { printk(KERN_EMERG "qlogicpti : scsi abort failure: %x\n", param[0]); diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index c9b8064b3..1f0080bd9 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -12,6 +12,7 @@ * Rik Faith <faith@cs.unc.edu> * Tommy Thorn <tthorn> * Thomas Wuensche <tw@fgb1.fgb.mw.tu-muenchen.de> + * Andrea Arcangeli <arcangeli@mbox.queen.it> * * Modified by Eric Youngdale eric@aib.com to * add scatter-gather, multiple outstanding request, and other @@ -65,7 +66,7 @@ #undef USE_STATIC_SCSI_MEMORY /* -static const char RCSid[] = "$Header: /vger/u4/cvs/linux/drivers/scsi/scsi.c,v 1.38 1997/01/19 23:07:18 davem Exp $"; +static const char RCSid[] = "$Header: /src/cvs/linux/drivers/scsi/scsi.c,v 1.1.1.1 1997/06/01 03:17:37 ralf Exp $"; */ @@ -3512,6 +3513,7 @@ int init_module(void) { void cleanup_module( void) { + timer_active &= ~(1 << SCSI_TIMER); #if CONFIG_PROC_FS proc_scsi_unregister(0, PROC_SCSI_SCSI); diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index eb9e7fec3..d83b94e26 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -488,7 +488,7 @@ static long sg_write(struct inode *inode,struct file *filp,const char *buf,unsig static unsigned int sg_poll(struct file *file, poll_table * wait) { - int dev = MINOR(file->f_inode->i_rdev); + int dev = MINOR(file->f_dentry->d_inode->i_rdev); struct scsi_generic *device = &scsi_generics[dev]; unsigned int mask = 0; diff --git a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c index a10e9545d..bc870dfea 100644 --- a/drivers/scsi/sr_ioctl.c +++ b/drivers/scsi/sr_ioctl.c @@ -1,3 +1,4 @@ +#include <linux/config.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> diff --git a/drivers/scsi/tmscsim.c b/drivers/scsi/tmscsim.c index f5ceb1371..3008f1b21 100644 --- a/drivers/scsi/tmscsim.c +++ b/drivers/scsi/tmscsim.c @@ -50,13 +50,8 @@ #include <linux/string.h> #include <linux/mm.h> #include <linux/config.h> - -#include <linux/version.h> -#if LINUX_VERSION_CODE < 66354 /* 1.3.50 */ -#include "../block/blk.h" -#else +#include <linux/init.h> #include <linux/blk.h> -#endif #include "scsi.h" #include "hosts.h" @@ -69,13 +64,10 @@ #define PCI_DEVICE_ID_AMD53C974 PCI_DEVICE_ID_AMD_SCSI - -#ifndef VERSION_ELF_1_2_13 struct proc_dir_entry proc_scsi_tmscsim ={ PROC_SCSI_DC390T, 7 ,"tmscsim", S_IFDIR | S_IRUGO | S_IXUGO, 2 }; -#endif static USHORT DC390_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB ); static void DC390_DataOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus); @@ -675,11 +667,7 @@ DoNextCmd( PACB pACB, PDCB pDCB ) * Description: * Return the disk geometry for the given SCSI device. ***********************************************************************/ -#ifdef VERSION_ELF_1_2_13 -int DC390_bios_param(Disk *disk, int devno, int geom[]) -#else int DC390_bios_param(Disk *disk, kdev_t devno, int geom[]) -#endif { int heads, sectors, cylinders; PACB pACB; @@ -1046,14 +1034,10 @@ void DC390_initDCB( PACB pACB, PDCB pDCB, PSCSICMD cmd ) ***********************************************************************/ void DC390_initSRB( PSRB psrb ) { -#ifndef VERSION_ELF_1_2_13 #ifdef DC390_DEBUG0 printk("DC390 init: %08lx %08lx,",(ULONG)psrb,(ULONG)virt_to_bus(psrb)); #endif psrb->PhysSRB = virt_to_bus( psrb ); -#else - psrb->PhysSRB = (ULONG) psrb; -#endif } @@ -1084,7 +1068,7 @@ void DC390_linkSRB( PACB pACB ) * Inputs : psh - pointer to this host adapter's structure * ***********************************************************************/ -void DC390_initACB( PSH psh, ULONG io_port, UCHAR Irq, USHORT index ) +__initfunc(void DC390_initACB( PSH psh, ULONG io_port, UCHAR Irq, USHORT index )) { PACB pACB; USHORT i; @@ -1098,7 +1082,6 @@ void DC390_initACB( PSH psh, ULONG io_port, UCHAR Irq, USHORT index ) pACB = (PACB) psh->hostdata; -#ifndef VERSION_ELF_1_2_13 psh->max_id = 8; #ifdef CONFIG_SCSI_MULTI_LUN if( eepromBuf[index][EE_MODE2] & LUN_CHECK ) @@ -1106,7 +1089,6 @@ void DC390_initACB( PSH psh, ULONG io_port, UCHAR Irq, USHORT index ) else #endif psh->max_lun = 1; -#endif pACB->max_id = 7; if( pACB->max_id == eepromBuf[index][EE_ADAPT_SCSI_ID] ) @@ -1155,7 +1137,7 @@ void DC390_initACB( PSH psh, ULONG io_port, UCHAR Irq, USHORT index ) * Inputs : psh - pointer to this host adapter's structure * ***********************************************************************/ -int DC390_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index ) +__initfunc(int DC390_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index)) { USHORT ioport; UCHAR bval; @@ -1179,11 +1161,7 @@ int DC390_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index ) if( !used_irq ) { -#ifdef VERSION_ELF_1_2_13 - if( request_irq(Irq, DC390_Interrupt, SA_INTERRUPT, "tmscsim")) -#else if( request_irq(Irq, DC390_Interrupt, SA_INTERRUPT, "tmscsim", NULL)) -#endif { printk("DC390: register IRQ error!\n"); return( -1 ); @@ -1533,8 +1511,8 @@ DC390_ToMech( USHORT Mechnum, USHORT BusDevFunNum ) * field of the pACB structure MUST have been set. ***********************************************************************/ -static int -DC390_init (PSHT psht, ULONG io_port, UCHAR Irq, USHORT index, USHORT MechNum) +__initfunc(static int +DC390_init (PSHT psht, ULONG io_port, UCHAR Irq, USHORT index, USHORT MechNum)) { PSH psh; PACB pACB; @@ -1614,8 +1592,8 @@ DC390_init (PSHT psht, ULONG io_port, UCHAR Irq, USHORT index, USHORT MechNum) * ***********************************************************************/ -int -DC390_detect(Scsi_Host_Template *psht) +__initfunc(int +DC390_detect(Scsi_Host_Template *psht)) { #ifdef FOR_PCI_OK UCHAR pci_bus, pci_device_fn; @@ -1626,19 +1604,13 @@ DC390_detect(Scsi_Host_Template *psht) UCHAR irq; UCHAR istatus; -#ifndef VERSION_ELF_1_2_13 UINT io_port; -#else - ULONG io_port; -#endif USHORT adaptCnt = 0; /* Number of boards detected */ USHORT pci_index = 0; /* Device index to PCI BIOS calls */ USHORT MechNum, BusDevFunNum; ULONG wlval; -#ifndef VERSION_ELF_1_2_13 psht->proc_dir = &proc_scsi_tmscsim; -#endif InitialTime = 1; pSHT_start = psht; @@ -1726,8 +1698,6 @@ DC390_detect(Scsi_Host_Template *psht) } -#ifndef VERSION_ELF_1_2_13 - /******************************************************************** * Function: tmscsim_set_info() * @@ -1848,7 +1818,6 @@ int tmscsim_proc_info(char *buffer, char **start, else return length; } -#endif /* VERSION_ELF_1_2_13 */ #ifdef MODULE @@ -1909,11 +1878,7 @@ int DC390_release(struct Scsi_Host *host) #ifdef DC390_DEBUG0 printk("DC390: Free IRQ %i.",host->irq); #endif -#ifndef VERSION_ELF_1_2_13 free_irq(host->irq,NULL); -#else - free_irq(host->irq); -#endif } } diff --git a/drivers/scsi/wd33c93.c b/drivers/scsi/wd33c93.c index 14cb0f37a..6d60d12ae 100644 --- a/drivers/scsi/wd33c93.c +++ b/drivers/scsi/wd33c93.c @@ -84,8 +84,9 @@ #include "scsi.h" #include "hosts.h" -#define WD33C93_VERSION "1.24" -#define WD33C93_DATE "29/Jan/1997" +#define WD33C93_VERSION "1.25" +#define WD33C93_DATE "09/Jul/1997" +/* NOTE: 1.25 for m68k is related to in2000-1.31 for x86 */ /* * Note - the following defines have been moved to 'wd33c93.h': @@ -103,11 +104,15 @@ -/* setup_strings is an array of strings that define some of the operating - * parameters and settings for this driver. It is used unless an amiboot - * or insmod command line has been specified, in which case those settings - * are combined with the ones here. The driver recognizes the following - * keywords (lower case required) and arguments: +/* + * 'setup_strings' is a single string used to pass operating parameters and + * settings from the kernel/module command-line to the driver. 'setup_args[]' + * is an array of strings that define the compile-time default values for + * these settings. If Linux boots with an amiboot or insmod command-line, + * those settings are combined with 'setup_args[]'. Note that amiboot + * command-lines are prefixed with "wd33c93=" while insmod uses a + * "setup_strings=" prefix. The driver recognizes the following keywords + * (lower case required) and arguments: * * - nosync:bitmask -bitmask is a byte where the 1st 7 bits correspond with * the 7 possible SCSI devices. Set a bit to negotiate for @@ -135,13 +140,11 @@ * _must_ be a colon between a keyword and its numeric argument, with no * spaces. * - Keywords are separated by commas, no spaces, in the standard kernel - * command-line manner, except in the case of 'setup_strings[]' (see - * below), which is simply a C array of pointers to char. Each element - * in the array is a string comprising one keyword & argument. + * command-line manner. * - A keyword in the 'nth' comma-separated command-line member will overwrite - * the 'nth' element of setup_strings[]. A blank command-line member (in + * the 'nth' element of setup_args[]. A blank command-line member (in * other words, a comma with no preceding keyword) will _not_ overwrite - * the corresponding setup_strings[] element. + * the corresponding setup_args[] element. * - If a keyword is used more than once, the first one applies to the first * SCSI host found, the second to the second card, etc, unless the 'next' * keyword is used to change the order. @@ -154,8 +157,16 @@ * - wd33c93=debug:0x1c */ -static char *setup_strings[] = - {"","","","","","","","","","","",""}; +/* Normally, no defaults are specified */ +static char *setup_args[] = + {"","","","","","","","",""}; + +/* filled in by 'insmod' */ +static char *setup_strings = 0; + +#ifdef MODULE_PARM +MODULE_PARM(setup_strings, "s"); +#endif static inline uchar read_wd33c93(wd33c93_regs *regp,uchar reg_num) @@ -319,8 +330,24 @@ int wd33c93_queuecommand (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) cmd->SCp.this_residual = cmd->request_bufflen; } - /* Preset the command status to GOOD, since that's the normal case */ - cmd->SCp.Status = GOOD; + /* WD docs state that at the conclusion of a "LEVEL2" command, the + * status byte can be retrieved from the LUN register. Apparently, + * this is the case only for *uninterrupted* LEVEL2 commands! If + * there are any unexpected phases entered, even if they are 100% + * legal (different devices may choose to do things differently), + * the LEVEL2 command sequence is exited. This often occurs prior + * to receiving the status byte, in which case the driver does a + * status phase interrupt and gets the status byte on its own. + * While such a command can then be "resumed" (ie restarted to + * finish up as a LEVEL2 command), the LUN register will NOT be + * a valid status byte at the command's conclusion, and we must + * use the byte obtained during the earlier interrupt. Here, we + * preset SCp.Status to an illegal value (0xff) so that when + * this command finally completes, we can tell where the actual + * status byte is stored. + */ + + cmd->SCp.Status = ILLEGAL_STATUS_BYTE; /* Add the cmd to the end of 'input_Q'. Note that REQUEST SENSE * commands are added to the head of the queue so that the desired @@ -641,7 +668,8 @@ use_transfer_pio: write_wd33c93(regp, WD_CONTROL, (CTRL_IDI | CTRL_EDI | CTRL_DMA)); /* write_wd33c93_count(regp, cmd->SCp.this_residual); */ - if ((hostdata->level2 >= L2_DATA) || (cmd->SCp.phase == 0)) { + if ((hostdata->level2 >= L2_DATA) || + (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) { write_wd33c93(regp, WD_COMMAND_PHASE, 0x45); write_wd33c93_cmd(regp, WD_CMD_SEL_ATN_XFER); hostdata->state = S_RUNNING_LEVEL2; @@ -808,8 +836,10 @@ void wd33c93_intr (struct Scsi_Host *instance) case CSR_XFER_DONE|PHS_STATUS: case CSR_UNEXP |PHS_STATUS: case CSR_SRV_REQ |PHS_STATUS: - DB(DB_INTR,printk("STATUS")); + DB(DB_INTR,printk("STATUS=")); + cmd->SCp.Status = read_1_byte(regp); + DB(DB_INTR,printk("%02x",cmd->SCp.Status)); if (hostdata->level2 >= L2_BASIC) { /* clear interrupt */ sr = read_wd33c93(regp, WD_SCSI_STATUS); @@ -817,7 +847,6 @@ void wd33c93_intr (struct Scsi_Host *instance) write_wd33c93(regp, WD_COMMAND_PHASE, 0x50); write_wd33c93_cmd(regp, WD_CMD_SEL_ATN_XFER); } else { - DB(DB_INTR, printk("=%02x", cmd->SCp.Status)); hostdata->state = S_CONNECTED; } break; @@ -999,21 +1028,22 @@ void wd33c93_intr (struct Scsi_Host *instance) DB(DB_INTR, printk("SX-DONE-%ld", cmd->pid)); cmd->SCp.Message = COMMAND_COMPLETE; lun = read_wd33c93(regp, WD_TARGET_LUN); - if (cmd->SCp.Status == GOOD) - cmd->SCp.Status = lun; + DB(DB_INTR,printk(":%d.%d",cmd->SCp.Status,lun)); hostdata->connected = NULL; - if (cmd->cmnd[0] != REQUEST_SENSE) - cmd->result = (cmd->SCp.Status | - (cmd->SCp.Message << 8)); - else if (cmd->SCp.Status != GOOD) - cmd->result = ((cmd->result & 0x00ffff) | - (DID_ERROR << 16)); hostdata->busy[cmd->target] &= ~(1 << cmd->lun); hostdata->state = S_UNCONNECTED; + if (cmd->SCp.Status == ILLEGAL_STATUS_BYTE) + cmd->SCp.Status = lun; + if (cmd->cmnd[0] == REQUEST_SENSE + && cmd->SCp.Status != GOOD) + cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); + else + cmd->result = cmd->SCp.Status | + (cmd->SCp.Message << 8); cmd->scsi_done(cmd); - /* We are no longer connected to a target - check to see if - * there are commands waiting to be executed. + /* We are no longer connected to a target - check to + * see if there are commands waiting to be executed. */ restore_flags(flags); wd33c93_execute(instance); @@ -1081,10 +1111,13 @@ void wd33c93_intr (struct Scsi_Host *instance) hostdata->connected = NULL; hostdata->busy[cmd->target] &= ~(1 << cmd->lun); hostdata->state = S_UNCONNECTED; - if (cmd->cmnd[0] != REQUEST_SENSE) - cmd->result = (cmd->SCp.Status | (cmd->SCp.Message << 8)); - else if (cmd->SCp.Status != GOOD) - cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); + if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD) + cmd->result = (cmd->result & 0x00ffff) | + (DID_ERROR << 16); + else + cmd->result = cmd->SCp.Status | + (cmd->SCp.Message << 8); + cmd->scsi_done(cmd); /* We are no longer connected to a target - check to see if @@ -1110,12 +1143,14 @@ void wd33c93_intr (struct Scsi_Host *instance) hostdata->connected = NULL; hostdata->busy[cmd->target] &= ~(1 << cmd->lun); hostdata->state = S_UNCONNECTED; - if (cmd->cmnd[0] != REQUEST_SENSE) - cmd->result = (cmd->SCp.Status | - (cmd->SCp.Message << 8)); - else if (cmd->SCp.Status != GOOD) - cmd->result = ((cmd->result & 0x00ffff) | - (DID_ERROR << 16)); + DB(DB_INTR,printk(":%d",cmd->SCp.Status)) + if (cmd->cmnd[0] == REQUEST_SENSE && + cmd->SCp.Status != GOOD) + cmd->result = (cmd->result & 0x00ffff) | + (DID_ERROR << 16); + else + cmd->result = cmd->SCp.Status | + (cmd->SCp.Message << 8); cmd->scsi_done(cmd); restore_flags(flags); break; @@ -1455,10 +1490,11 @@ int wd33c93_abort (Scsi_Cmnd *cmd) } #define MAX_WD33C93_HOSTS 4 -#define MAX_SETUP_STRINGS (sizeof(setup_strings) / sizeof(char *)) +#define MAX_SETUP_ARGS (sizeof(setup_args) / sizeof(char *)) #define SETUP_BUFFER_SIZE 200 static char setup_buffer[SETUP_BUFFER_SIZE]; -static char setup_used[MAX_SETUP_STRINGS]; +static char setup_used[MAX_SETUP_ARGS]; +static int done_setup = 0; void wd33c93_setup (char *str, int *ints) { @@ -1486,41 +1522,42 @@ void wd33c93_setup (char *str, int *ints) setup_buffer[SETUP_BUFFER_SIZE - 1] = '\0'; p1 = setup_buffer; i = 0; - while (*p1 && (i < MAX_SETUP_STRINGS)) { + while (*p1 && (i < MAX_SETUP_ARGS)) { p2 = strchr(p1, ','); if (p2) { *p2 = '\0'; if (p1 != p2) - setup_strings[i] = p1; + setup_args[i] = p1; p1 = p2 + 1; i++; } else { - setup_strings[i] = p1; + setup_args[i] = p1; break; } } - for (i = 0; i < MAX_SETUP_STRINGS; i++) + for (i = 0; i < MAX_SETUP_ARGS; i++) setup_used[i] = 0; + done_setup = 1; } -/* check_setup_strings() returns index if key found, 0 if not */ -static int check_setup_strings(char *key, int *flags, int *val, char *buf) +/* check_setup_args() returns index if key found, 0 if not */ +static int check_setup_args(char *key, int *flags, int *val, char *buf) { int x; char *cp; - for (x = 0; x < MAX_SETUP_STRINGS; x++) { + for (x = 0; x < MAX_SETUP_ARGS; x++) { if (setup_used[x]) continue; - if (!strncmp(setup_strings[x], key, strlen(key))) + if (!strncmp(setup_args[x], key, strlen(key))) break; - if (!strncmp(setup_strings[x], "next", strlen("next"))) + if (!strncmp(setup_args[x], "next", strlen("next"))) return 0; } - if (x == MAX_SETUP_STRINGS) + if (x == MAX_SETUP_ARGS) return 0; setup_used[x] = 1; - cp = setup_strings[x] + strlen(key); + cp = setup_args[x] + strlen(key); *val = -1; if (*cp != ':') return ++x; @@ -1535,12 +1572,16 @@ void wd33c93_init (struct Scsi_Host *instance, wd33c93_regs *regs, dma_setup_t setup, dma_stop_t stop, int clock_freq) { static int shown = 0; - struct WD33C93_hostdata *hostdata = INSTHOSTDATA(instance); + struct WD33C93_hostdata *hostdata; int i; int flags; int val; char buf[32]; + if (!done_setup && setup_strings) + wd33c93_setup(setup_strings,0); + hostdata = INSTHOSTDATA(instance); + hostdata->regp = regs; hostdata->clock_freq = clock_freq; hostdata->dma_setup = setup; @@ -1580,27 +1621,30 @@ void wd33c93_init (struct Scsi_Host *instance, wd33c93_regs *regs, #endif - if (check_setup_strings("nosync", &flags, &val, buf)) + if (check_setup_args("nosync",&flags,&val,buf)) hostdata->no_sync = val; - if (check_setup_strings("nodma",&flags,&val,buf)) + if (check_setup_args("nodma",&flags,&val,buf)) hostdata->no_dma = (val == -1) ? 1 : val; - if (check_setup_strings("period", &flags, &val, buf)) + if (check_setup_args("period",&flags,&val,buf)) hostdata->default_sx_per = sx_table[round_period((unsigned int)val)].period_ns; - if (check_setup_strings("disconnect", &flags, &val, buf)) { + if (check_setup_args("disconnect",&flags,&val,buf)) { if ((val >= DIS_NEVER) && (val <= DIS_ALWAYS)) hostdata->disconnect = val; else hostdata->disconnect = DIS_ADAPTIVE; } - if (check_setup_strings("debug", &flags, &val, buf)) + if (check_setup_args("level2",&flags,&val,buf)) + hostdata->level2 = val; + + if (check_setup_args("debug",&flags,&val,buf)) hostdata->args = val & DB_MASK; - if (check_setup_strings("clock", &flags, &val, buf)) { + if (check_setup_args("clock",&flags,&val,buf)) { if ((val > 7) && (val < 11)) val = WD33C93_FS_8_10; else if ((val > 11) && (val < 16)) @@ -1612,13 +1656,13 @@ void wd33c93_init (struct Scsi_Host *instance, wd33c93_regs *regs, hostdata->clock_freq = val; } - if ((i = check_setup_strings("next", &flags, &val, buf))) { + if ((i = check_setup_args("next",&flags,&val,buf))) { while (i) setup_used[--i] = 1; } #ifdef PROC_INTERFACE - if (check_setup_strings("proc", &flags, &val, buf)) + if (check_setup_args("proc",&flags,&val,buf)) hostdata->proc = val; #endif @@ -1635,9 +1679,9 @@ void wd33c93_init (struct Scsi_Host *instance, wd33c93_regs *regs, printk(" debugging=OFF\n"); #endif #if 0 - printk("wd33c93-%d: setup_strings=", instance->host_no); - for (i = 0; i < MAX_SETUP_STRINGS; i++) - printk("%s,", setup_strings[i]); + printk("wd33c93-%d: setup_args=", instance->host_no); + for (i = 0; i < MAX_SETUP_ARGS; i++) + printk("%s,", setup_args[i]); printk("\n"); printk("wd33c93-%d: debug_flags = %04x\n", instance->host_no, hostdata->args); @@ -1715,6 +1759,11 @@ int wd33c93_proc_info(char *buf, char **start, off_t off, int len, int hn, int i bp += 6; hd->no_dma = simple_strtoul(bp,NULL,0); } + else if (!strncmp(bp,"level2:",7)) { + bp += 7; + hd->level2 = simple_strtoul(bp,NULL,0); + } + return len; } @@ -1731,32 +1780,32 @@ int wd33c93_proc_info(char *buf, char **start, off_t off, int len, int hn, int i sprintf(tbuf,"\nclock_freq=%02x no_sync=%02x no_dma=%d", hd->clock_freq,hd->no_sync,hd->no_dma); strcat(bp,tbuf); - strcat(bp,"\nsync_xfer[] ="); - for (x=0; x<8; x++) { - sprintf(tbuf," %02x",hd->sync_xfer[x]); + strcat(bp,"\nsync_xfer[] = "); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%02x",hd->sync_xfer[x]); strcat(bp,tbuf); } - strcat(bp,"\nsync_stat[] ="); - for (x=0; x<8; x++) { - sprintf(tbuf," %02x",hd->sync_stat[x]); + strcat(bp,"\nsync_stat[] = "); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%02x",hd->sync_stat[x]); strcat(bp,tbuf); } } #ifdef PROC_STATISTICS if (hd->proc & PR_STATISTICS) { strcat(bp,"\ncommands issued: "); - for (x=0; x<8; x++) { - sprintf(tbuf," %ld",hd->cmd_cnt[x]); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%ld",hd->cmd_cnt[x]); strcat(bp,tbuf); } strcat(bp,"\ndisconnects allowed:"); - for (x=0; x<8; x++) { - sprintf(tbuf," %ld",hd->disc_allowed_cnt[x]); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%ld",hd->disc_allowed_cnt[x]); strcat(bp,tbuf); } strcat(bp,"\ndisconnects done: "); - for (x=0; x<8; x++) { - sprintf(tbuf," %ld",hd->disc_done_cnt[x]); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%ld",hd->disc_done_cnt[x]); strcat(bp,tbuf); } sprintf(tbuf, diff --git a/drivers/scsi/wd33c93.h b/drivers/scsi/wd33c93.h index 3b8b4cf3a..c0584037b 100644 --- a/drivers/scsi/wd33c93.h +++ b/drivers/scsi/wd33c93.h @@ -2,7 +2,7 @@ * wd33c93.h - Linux device driver definitions for the * Commodore Amiga A2091/590 SCSI controller card * - * IMPORTANT: This file is for version 1.24 - 29/Jan/1997 + * IMPORTANT: This file is for version 1.25 - 09/Jul/1997 * * Copyright (c) 1996 John Shifflett, GeoLog Consulting * john@geolog.com @@ -34,6 +34,8 @@ #define DEBUG_DEFAULTS 0 /* default debugging bitmask */ +#define ILLEGAL_STATUS_BYTE 0xff + #ifdef DEBUGGING_ON #define DB(f,a) if (hostdata->args & (f)) a; #else diff --git a/drivers/sound/Config.in b/drivers/sound/Config.in index 310bf01bf..d14653d45 100644 --- a/drivers/sound/Config.in +++ b/drivers/sound/Config.in @@ -1,15 +1,277 @@ -# -# Sound driver configuration -# -#-------- -# There is another confic script which is compatible with rest of -# the kernel. It can be activated by running 'make mkscript' in this -# directory. Please note that this is an _experimental_ feature which -# doesn't work with all cards (PSS, SM Wave, AudioTriX Pro, Maui). -#-------- -# -$MAKE -C drivers/sound config || exit 1 +bool 'ProAudioSpectrum 16 support' CONFIG_PAS +bool '100%% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' CONFIG_SB +bool 'Generic OPL2/OPL3 FM synthesizer support' CONFIG_ADLIB +bool 'Gravis Ultrasound support' CONFIG_GUS +bool 'MPU-401 support (NOT for SB16)' CONFIG_MPU401 +bool 'PSS (ECHO-ADI2111) support' CONFIG_PSS +bool '16 bit sampling option of GUS (_NOT_ GUS MAX)' CONFIG_GUS16 +bool 'GUS MAX support' CONFIG_GUSMAX +bool 'Microsoft Sound System support' CONFIG_MSS +bool 'Ensoniq SoundScape support' CONFIG_SSCAPE +bool 'MediaTrix AudioTrix Pro support' CONFIG_TRIX +bool 'Support for MAD16 and/or Mozart based cards' CONFIG_MAD16 +bool 'Support for Crystal CS4232 based (PnP) cards' CONFIG_CS4232 +bool 'Support for Turtle Beach Wave Front (Maui, Tropez) synthesizers' CONFIG_MAUI +bool 'FM synthesizer (YM3812/OPL-3) support' CONFIG_YM3812 + +if [ "$CONFIG_AEDSP16" = "y" ]; then +hex 'I/O base for Audio Excel DSP 16 220 or 240' AEDSP16_BASE 220 +fi + +if [ "$CONFIG_SB" = "y" ]; then +hex 'I/O base for SB Check from manual of the card' SBC_BASE 220 +fi + +if [ "$CONFIG_SB" = "y" ]; then +int 'Sound Blaster IRQ Check from manual of the card' SBC_IRQ 7 +fi + +if [ "$CONFIG_SB" = "y" ]; then +int 'Sound Blaster DMA 0, 1 or 3' SBC_DMA 1 +fi + +if [ "$CONFIG_SB" = "y" ]; then +int 'Sound Blaster 16 bit DMA (SB16, Jazz16, SMW) 5, 6 or 7 (use 1 for 8 bit cards)' SB_DMA2 5 +fi + +if [ "$CONFIG_SB" = "y" ]; then +hex 'MPU401 I/O base of SB16, Jazz16 and ES1688 Check from manual of the card' SB_MPU_BASE 330 +fi + + +if [ "$CONFIG_SB" = "y" ]; then +comment 'MPU401 IRQ is only required with Jazz16, SM Wave and ESS1688.' +fi + + +if [ "$CONFIG_SB" = "y" ]; then +comment 'Enter -1 to the following question if you have something else such as SB16/32.' +fi + +if [ "$CONFIG_SB" = "y" ]; then +int 'SB MPU401 IRQ (Jazz16, SM Wave and ES1688) Check from manual of the card' SB_MPU_IRQ -1 +fi + +if [ "$CONFIG_PAS" = "y" ]; then +int 'PAS16 IRQ 3, 4, 5, 7, 9, 10, 11, 12, 14 or 15' PAS_IRQ 10 +fi + +if [ "$CONFIG_PAS" = "y" ]; then +int 'PAS16 DMA 0, 1, 3, 5, 6 or 7' PAS_DMA 3 +fi + +if [ "$CONFIG_GUS" = "y" ]; then +hex 'I/O base for GUS 210, 220, 230, 240, 250 or 260' GUS_BASE 220 +fi + +if [ "$CONFIG_GUS" = "y" ]; then +int 'GUS IRQ 3, 5, 7, 9, 11, 12 or 15' GUS_IRQ 15 +fi + +if [ "$CONFIG_GUS" = "y" ]; then +int 'GUS DMA 1, 3, 5, 6 or 7' GUS_DMA 6 +fi + +if [ "$CONFIG_GUS" = "y" ]; then +int 'Second DMA channel for GUS 1, 3, 5, 6 or 7' GUS_DMA2 -1 +fi + +if [ "$CONFIG_GUS16" = "y" ]; then +hex 'I/O base for the 16 bit daughtercard of GUS 530, 604, E80 or F40' GUS16_BASE 530 +fi + +if [ "$CONFIG_GUS16" = "y" ]; then +int 'GUS 16 bit daughtercard IRQ 3, 4, 5, 7, or 9' GUS16_IRQ 7 +fi + +if [ "$CONFIG_GUS16" = "y" ]; then +int 'GUS DMA 0, 1 or 3' GUS16_DMA 3 +fi + +if [ "$CONFIG_MPU401" = "y" ]; then +hex 'I/O base for MPU401 Check from manual of the card' MPU_BASE 330 +fi + +if [ "$CONFIG_MPU401" = "y" ]; then +int 'MPU401 IRQ Check from manual of the card' MPU_IRQ 9 +fi + + +if [ "$CONFIG_MAUI" = "y" ]; then +comment 'ERROR! You have to use old sound configuration method with Maui.' +fi + +if [ "$CONFIG_MAUI" = "y" ]; then +hex 'I/O base for Maui 210, 230, 260, 290, 300, 320, 338 or 330' MAUI_BASE 330 +fi + +if [ "$CONFIG_MAUI" = "y" ]; then +int 'Maui IRQ 5, 9, 12 or 15' MAUI_IRQ 9 +fi + +if [ "$CONFIG_UART6850" = "y" ]; then +hex 'I/O base for UART 6850 MIDI port (Unknown)' U6850_BASE 0 +fi + +if [ "$CONFIG_UART6850" = "y" ]; then +int 'UART6850 IRQ (Unknown)' U6850_IRQ -1 +fi + + +if [ "$CONFIG_PSS" = "y" ]; then +comment 'ERROR! You have to use old sound configuration method with PSS cards.' +fi + +if [ "$CONFIG_PSS" = "y" ]; then +hex 'PSS I/O base 220 or 240' PSS_BASE 220 +fi + +if [ "$CONFIG_PSS" = "y" ]; then +hex 'PSS audio I/O base 530, 604, E80 or F40' PSS_MSS_BASE 530 +fi + +if [ "$CONFIG_PSS" = "y" ]; then +int 'PSS audio IRQ 7, 9, 10 or 11' PSS_MSS_IRQ 11 +fi + +if [ "$CONFIG_PSS" = "y" ]; then +int 'PSS audio DMA 0, 1 or 3' PSS_MSS_DMA 3 +fi +if [ "$CONFIG_PSS" = "y" ]; then +hex 'PSS MIDI I/O base ' PSS_MPU_BASE 330 +fi + +if [ "$CONFIG_PSS" = "y" ]; then +int 'PSS MIDI IRQ 3, 4, 5, 7 or 9' PSS_MPU_IRQ 9 +fi + +if [ "$CONFIG_MSS" = "y" ]; then +hex 'MSS/WSS I/O base 530, 604, E80 or F40' MSS_BASE 530 +fi + +if [ "$CONFIG_MSS" = "y" ]; then +int 'MSS/WSS IRQ 7, 9, 10 or 11' MSS_IRQ 11 +fi + +if [ "$CONFIG_MSS" = "y" ]; then +int 'MSS/WSS DMA 0, 1 or 3' MSS_DMA 3 +fi + +if [ "$CONFIG_MSS" = "y" ]; then +int 'MSS/WSS second DMA (if possible) 0, 1 or 3' MSS_DMA2 -1 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +hex 'SoundScape MIDI I/O base 320, 330, 340 or 350' SSCAPE_BASE 330 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +int 'SoundScape MIDI IRQ ' SSCAPE_IRQ 9 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +int 'SoundScape initialization DMA 0, 1 or 3' SSCAPE_DMA 3 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +hex 'SoundScape audio I/O base 534, 608, E84 or F44' SSCAPE_MSS_BASE 534 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +int 'SoundScape audio IRQ 7, 9, 10 or 11' SSCAPE_MSS_IRQ 11 +fi + + +if [ "$CONFIG_TRIX" = "y" ]; then +comment 'ERROR! You have to use old sound configuration method with AudioTrix.' +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +hex 'AudioTrix audio I/O base 530, 604, E80 or F40' TRIX_BASE 530 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'AudioTrix audio IRQ 7, 9, 10 or 11' TRIX_IRQ 11 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'AudioTrix audio DMA 0, 1 or 3' TRIX_DMA 0 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'AudioTrix second (duplex) DMA 0, 1 or 3' TRIX_DMA2 3 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +hex 'AudioTrix MIDI I/O base 330, 370, 3B0 or 3F0' TRIX_MPU_BASE 330 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'AudioTrix MIDI IRQ 3, 4, 5, 7 or 9' TRIX_MPU_IRQ 9 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +hex 'AudioTrix SB I/O base 220, 210, 230, 240, 250, 260 or 270' TRIX_SB_BASE 220 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'AudioTrix SB IRQ 3, 4, 5 or 7' TRIX_SB_IRQ 7 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'AudioTrix SB DMA 1 or 3' TRIX_SB_DMA 1 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +hex 'CS4232 audio I/O base 530, 604, E80 or F40' CS4232_BASE 530 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +int 'CS4232 audio IRQ 5, 7, 9, 11, 12 or 15' CS4232_IRQ 11 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +int 'CS4232 audio DMA 0, 1 or 3' CS4232_DMA 0 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +int 'CS4232 second (duplex) DMA 0, 1 or 3' CS4232_DMA2 3 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +hex 'CS4232 MIDI I/O base 330, 370, 3B0 or 3F0' CS4232_MPU_BASE 330 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +int 'CS4232 MIDI IRQ 5, 7, 9, 11, 12 or 15' CS4232_MPU_IRQ 9 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +hex 'MAD16 audio I/O base 530, 604, E80 or F40' MAD16_BASE 530 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +int 'MAD16 audio IRQ 7, 9, 10 or 11' MAD16_IRQ 11 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +int 'MAD16 audio DMA 0, 1 or 3' MAD16_DMA 3 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +int 'MAD16 second (duplex) DMA 0, 1 or 3' MAD16_DMA2 0 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +hex 'MAD16 MIDI I/O base 300, 310, 320 or 330 (0 disables)' MAD16_MPU_BASE 330 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +int 'MAD16 MIDI IRQ 5, 7, 9 or 10' MAD16_MPU_IRQ 9 +fi +# +$MAKE -C drivers/sound kernelconfig || exit 1 bool 'Additional low level drivers' CONFIG_LOWLEVEL_SOUND if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then diff --git a/drivers/sound/dev_table.h b/drivers/sound/dev_table.h index 7013c3c8d..a7e4026e8 100644 --- a/drivers/sound/dev_table.h +++ b/drivers/sound/dev_table.h @@ -15,6 +15,7 @@ #ifndef _DEV_TABLE_H_ #define _DEV_TABLE_H_ +#include <linux/config.h> /* * Sound card numbers 27 to 999. (1 to 26 are defined in soundcard.h) diff --git a/drivers/sound/soundcard.c b/drivers/sound/soundcard.c index 957150f7b..a9af5956b 100644 --- a/drivers/sound/soundcard.c +++ b/drivers/sound/soundcard.c @@ -246,7 +246,7 @@ sound_poll (struct file *file, poll_table * wait) struct inode *inode; int ret = 0; - inode = file->f_inode; + inode = file->f_dentry->d_inode; if (sound_select (inode, file, SEL_IN, wait)) ret |= POLLIN; @@ -326,8 +326,7 @@ sound_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma) vma->vm_page_prot)) return -EAGAIN; - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + vma->vm_dentry = dget(file->f_dentry); dmap->mapping_flags |= DMA_MAP_MAPPED; diff --git a/fs/Config.in b/fs/Config.in index a9f922d8a..cfc601630 100644 --- a/fs/Config.in +++ b/fs/Config.in @@ -5,26 +5,6 @@ mainmenu_option next_comment comment 'Filesystems' bool 'Quota support' CONFIG_QUOTA -bool 'Preload dcache entries in readdir() [ALPHA, currently dangerous!]' CONFIG_DCACHE_PRELOAD -bool 'Include support for omirr online mirror' CONFIG_OMIRR -bool 'Translate filename suffixes' CONFIG_TRANS_NAMES -if [ "$CONFIG_TRANS_NAMES" = "y" ]; then - bool ' Restrict translation to specific gid' CONFIG_TRANS_RESTRICT - if [ "$CONFIG_TRANS_RESTRICT" = "y" ]; then - int ' Enter gid to compile in' CONFIG_TRANS_GID 4 - fi - bool ' Translate nodename' CONFIG_TR_NODENAME - bool ' Translate compiled-in kernelname' CONFIG_TR_KERNNAME - if [ "$CONFIG_TR_KERNNAME" = "y" ]; then - string ' Enter kernelname string to compile in' CONFIG_KERNNAME banana - fi - bool ' Translate compiled-in kerneltype' CONFIG_TR_KERNTYPE - if [ "$CONFIG_TR_KERNTYPE" = "y" ]; then - string ' Enter kerneltype string to compile in' CONFIG_KERNTYPE default - fi - bool ' Translate machine type' CONFIG_TR_MACHINE - bool ' Translate sysname' CONFIG_TR_SYSNAME -fi tristate 'Minix fs support' CONFIG_MINIX_FS tristate 'Second extended fs support' CONFIG_EXT2_FS diff --git a/fs/affs/Changes b/fs/affs/Changes index a65dc326a..b19c24ae6 100644 --- a/fs/affs/Changes +++ b/fs/affs/Changes @@ -13,11 +13,25 @@ Known bugs: reads basically work (but all files are of size 0). Alas, I've got no alpha to debug. :-( - If an affs mounted filesystem is exported via - nfs, it cannot be written to. No networking to - test that, either. :-( + nfs, it cannot be written to. + As soon as I have my network up and running, I'll + try to fix this. +- The partition checker (drivers/block/genhd.c) + doesn't work with devices which have 256 byte + blocks (some very old SCSI drives). Please direct bug reports to: hjw@zvw.de +Version 3.5 +----------- + +- Extension block caches are now allocated on + demand instead of when a file is opened, as + files can be read and written without opening + them (e. g. the loopback device does this). + +- Removed an unused function. + Version 3.4 ----------- @@ -80,7 +94,7 @@ Version 3.0 interface in Linux 1.3. - Write support. - Support for hard and symbolic links. -- Lots of things I remeber even less ... +- Lots of things I remember even less ... Version 2.0 ----------- diff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c index 4afd66e49..7ddb54a62 100644 --- a/fs/affs/amigaffs.c +++ b/fs/affs/amigaffs.c @@ -26,27 +26,6 @@ static char ErrorBuffer[256]; * */ -/* Find the next used hash entry at or after *HASH_POS in a directory's hash - table. *HASH_POS is assigned that entry's number. DIR_DATA points to - the directory header block in memory. If there are no more entries, - 0 is returned. Otherwise, the key number in the next used hash slot - is returned. */ - -static int -affs_find_next_hash_entry(int hsize, void *dir_data, int *hash_pos) -{ - struct dir_front *dir_front = dir_data; - int i; - - for (i = *hash_pos; i < hsize; i++) - if (dir_front->hashtable[i] != 0) - break; - if (i >= hsize) - return 0; - *hash_pos = i; - return htonl(dir_front->hashtable[i]); -} - /* Set *NAME to point to the file name in a file header block in memory pointed to by FH_DATA. The length of the name is returned. */ diff --git a/fs/affs/file.c b/fs/affs/file.c index 46b10bcb1..7777e4763 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -43,8 +43,8 @@ static long affs_file_write(struct inode *inode, struct file *filp, const char * unsigned long count); static long affs_file_write_ofs(struct inode *inode, struct file *filp, const char *buf, unsigned long count); -static int affs_open_file(struct inode *inode, struct file *filp); static int affs_release_file(struct inode *inode, struct file *filp); +static int alloc_ext_cache(struct inode *inode); static struct file_operations affs_file_operations = { NULL, /* lseek - default */ @@ -54,7 +54,7 @@ static struct file_operations affs_file_operations = { NULL, /* poll - default */ NULL, /* ioctl - default */ generic_file_mmap, /* mmap */ - affs_open_file, /* special open is needed */ + NULL, /* no special open */ affs_release_file, /* release */ file_fsync /* brute force, but works */ }; @@ -87,7 +87,7 @@ static struct file_operations affs_file_operations_ofs = { NULL, /* poll - default */ NULL, /* ioctl - default */ NULL, /* mmap */ - affs_open_file, /* special open is needed */ + NULL, /* no special open */ affs_release_file, /* release */ file_fsync /* brute force, but works */ }; @@ -248,9 +248,9 @@ affs_bmap(struct inode *inode, int block) return 0; } if (!inode->u.affs_i.i_ec) { - affs_error(inode->i_sb,"bmap","No extension cache for open file (inode=%lu)", - inode->i_ino); - return 0; + if (alloc_ext_cache(inode)) { + return 0; + } } /* Try to find the requested key in the cache. @@ -582,6 +582,12 @@ affs_file_write(struct inode *inode, struct file *filp, const char *buf, unsigne iput(inode); return -EINVAL; } + if (!inode->u.affs_i.i_ec) { + if (alloc_ext_cache(inode)) { + iput(inode); + return -ENOMEM; + } + } if (filp->f_flags & O_APPEND) { pos = inode->i_size; } else @@ -861,40 +867,6 @@ affs_truncate(struct inode *inode) } static int -affs_open_file(struct inode *inode, struct file *filp) -{ - int error; - u32 key; - int i; - - pr_debug("AFFS: open_file(ino=%lu)\n",inode->i_ino); - - error = 0; - inode->u.affs_i.i_cache_users++; - lock_super(inode->i_sb); - if (!inode->u.affs_i.i_ec) { - inode->u.affs_i.i_ec = (struct ext_cache *)get_free_page(GFP_KERNEL); - if (!inode->u.affs_i.i_ec) { - affs_error(inode->i_sb,"open_file","Cache allocation failed"); - error = ENOMEM; - } else { - /* We only have to initialize non-zero values. - * get_free_page() zeroed the page already. - */ - key = inode->u.affs_i.i_original ? inode->u.affs_i.i_original : inode->i_ino; - inode->u.affs_i.i_ec->ec[0] = key; - for (i = 0; i < 4; i++) { - inode->u.affs_i.i_ec->kc[i].kc_this_key = key; - inode->u.affs_i.i_ec->kc[i].kc_last = -1; - } - } - } - unlock_super(inode->i_sb); - - return error; -} - -static int affs_release_file(struct inode *inode, struct file *filp) { struct affs_zone *zone; @@ -916,13 +888,35 @@ affs_release_file(struct inode *inode, struct file *filp) unlock_super(inode->i_sb); } } + return 0; +} + +static int +alloc_ext_cache(struct inode *inode) +{ + s32 key; + int i; + lock_super(inode->i_sb); - if (--inode->u.affs_i.i_cache_users == 0) { + if (!inode->u.affs_i.i_ec) { + inode->u.affs_i.i_ec = (struct ext_cache *)get_free_page(GFP_KERNEL); if (inode->u.affs_i.i_ec) { - free_page((unsigned long)inode->u.affs_i.i_ec); - inode->u.affs_i.i_ec = NULL; + /* We only have to initialize non-zero values. + * get_free_page() zeroed the page already. + */ + key = inode->u.affs_i.i_original ? inode->u.affs_i.i_original : inode->i_ino; + inode->u.affs_i.i_ec->ec[0] = key; + for (i = 0; i < 4; i++) { + inode->u.affs_i.i_ec->kc[i].kc_this_key = key; + inode->u.affs_i.i_ec->kc[i].kc_last = -1; + } } } unlock_super(inode->i_sb); + + if (!inode->u.affs_i.i_ec) { + affs_error(inode->i_sb,"alloc_ext_cache","Cache allocation failed"); + return -ENOMEM; + } return 0; } diff --git a/fs/affs/inode.c b/fs/affs/inode.c index 2805f1ccf..a1e380cce 100644 --- a/fs/affs/inode.c +++ b/fs/affs/inode.c @@ -691,7 +691,6 @@ affs_read_inode(struct inode *inode) inode->u.affs_i.i_pa_next = 0; inode->u.affs_i.i_pa_last = 0; inode->u.affs_i.i_ec = NULL; - inode->u.affs_i.i_cache_users = 0; inode->u.affs_i.i_lastblock = -1; inode->i_nlink = 1; inode->i_mode = 0; @@ -870,6 +869,12 @@ static void affs_put_inode(struct inode *inode) { pr_debug("AFFS: put_inode(ino=%lu, nlink=%u)\n",inode->i_ino,inode->i_nlink); + lock_super(inode->i_sb); + if (inode->u.affs_i.i_ec) { + free_page((unsigned long)inode->u.affs_i.i_ec); + inode->u.affs_i.i_ec = NULL; + } + unlock_super(inode->i_sb); if (inode->i_nlink) { return; } @@ -899,7 +904,7 @@ affs_new_inode(const struct inode *dir) return NULL; } - atomic_set(&inode->i_count, 1); + inode->i_count = 1; inode->i_nlink = 1; inode->i_dev = sb->s_dev; inode->i_uid = current->fsuid; @@ -921,7 +926,6 @@ affs_new_inode(const struct inode *dir) inode->u.affs_i.i_pa_next = 0; inode->u.affs_i.i_pa_last = 0; inode->u.affs_i.i_ec = NULL; - inode->u.affs_i.i_cache_users = 0; inode->u.affs_i.i_lastblock = -1; insert_inode_hash(inode); diff --git a/fs/affs/namei.c b/fs/affs/namei.c index 5ea649425..ce04df073 100644 --- a/fs/affs/namei.c +++ b/fs/affs/namei.c @@ -343,7 +343,7 @@ affs_rmdir(struct inode *dir, const char *name, int len) retval = -ENOTEMPTY; goto rmdir_done; } - if (atomic_read(&inode->i_count) > 1) { + if (inode->i_count > 1) { retval = -EBUSY; goto rmdir_done; } @@ -512,7 +512,7 @@ subdir(struct inode *new_inode, struct inode *old_inode) int ino; int result; - atomic_inc(&new_inode->i_count); + new_inode->i_count++; result = 0; for (;;) { if (new_inode == old_inode) { @@ -566,12 +566,12 @@ start_up: old_bh = affs_find_entry(old_dir,old_name,old_len,&old_ino); if (!old_bh) goto end_rename; - old_inode = __iget(old_dir->i_sb,old_ino,0); + old_inode = iget(old_dir->i_sb,old_ino); if (!old_inode) goto end_rename; new_bh = affs_find_entry(new_dir,new_name,new_len,&new_ino); if (new_bh) { - new_inode = __iget(new_dir->i_sb,new_ino,0); + new_inode = iget(new_dir->i_sb,new_ino); if (!new_inode) { /* What does this mean? */ affs_brelse(new_bh); new_bh = NULL; @@ -592,7 +592,7 @@ start_up: if (!empty_dir(new_bh,AFFS_I2HSIZE(new_inode))) goto end_rename; retval = -EBUSY; - if (atomic_read(&new_inode->i_count) > 1) + if (new_inode->i_count > 1) goto end_rename; } if (S_ISDIR(old_inode->i_mode)) { @@ -74,7 +74,7 @@ void inode_setattr(struct inode * inode, struct iattr * attr) if (!fsuser() && !in_group_p(inode->i_gid)) inode->i_mode &= ~S_ISGID; } - inode->i_dirt = 1; + mark_inode_dirty(inode); } } diff --git a/fs/autofs/autofs_i.h b/fs/autofs/autofs_i.h index cc38577aa..f2bb6c339 100644 --- a/fs/autofs/autofs_i.h +++ b/fs/autofs/autofs_i.h @@ -69,10 +69,8 @@ static inline int copy_from_user(void *dst, void *src, unsigned long len) #define AUTOFS_HASH_SIZE 67 -typedef u32 autofs_hash_t; /* Type returned by autofs_hash() */ - struct autofs_dir_ent { - autofs_hash_t hash; + int hash; struct autofs_dir_ent *next; struct autofs_dir_ent **back; char *name; @@ -94,7 +92,7 @@ struct autofs_wait_queue { struct wait_queue *queue; struct autofs_wait_queue *next; /* We use the following to see what we are waiting for */ - autofs_hash_t hash; + int hash; int len; char *name; /* This is for status reporting upon return */ @@ -151,9 +149,8 @@ void autofs_check_waitlist_integrity(struct autofs_sb_info *,char *); /* Hash operations */ -autofs_hash_t autofs_hash(const char *,int); void autofs_initialize_hash(struct autofs_dirhash *); -struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *,autofs_hash_t,const char *,int); +struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *,struct qstr *); void autofs_hash_insert(struct autofs_dirhash *,struct autofs_dir_ent *); void autofs_hash_delete(struct autofs_dir_ent *); struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *,off_t *); @@ -176,7 +173,7 @@ struct super_block *autofs_read_super(struct super_block *, void *,int); /* Queue management functions */ -int autofs_wait(struct autofs_sb_info *,autofs_hash_t,const char *,int); +int autofs_wait(struct autofs_sb_info *,struct qstr *); int autofs_wait_release(struct autofs_sb_info *,unsigned long,int); void autofs_catatonic_mode(struct autofs_sb_info *); diff --git a/fs/autofs/dir.c b/fs/autofs/dir.c index 0f529c900..61900c481 100644 --- a/fs/autofs/dir.c +++ b/fs/autofs/dir.c @@ -34,25 +34,13 @@ static int autofs_dir_readdir(struct inode *inode, struct file *filp, return 1; } -static int autofs_dir_lookup(struct inode *dir, const char *name, int len, - struct inode **result) +/* + * No entries except for "." and "..", both of which are handled by the VFS layer + */ +static int autofs_dir_lookup(struct inode *dir, struct dentry * dentry) { - *result = dir; - if (!len) - return 0; - if (name[0] == '.') { - if (len == 1) - return 0; - if (name[1] == '.' && len == 2) { - /* Return the root directory */ - *result = iget(dir->i_sb,AUTOFS_ROOT_INO); - iput(dir); - return 0; - } - } - *result = NULL; - iput(dir); - return -ENOENT; /* No other entries */ + d_add(dentry, NULL); + return 0; } static struct file_operations autofs_dir_operations = { diff --git a/fs/autofs/dirhash.c b/fs/autofs/dirhash.c index 90c18695a..60a3c6933 100644 --- a/fs/autofs/dirhash.c +++ b/fs/autofs/dirhash.c @@ -48,35 +48,23 @@ struct autofs_dir_ent *autofs_expire(struct autofs_dirhash *dh, return (jiffies - ent->last_usage >= timeout) ? ent : NULL; } -/* Adapted from the Dragon Book, page 436 */ -/* This particular hashing algorithm requires autofs_hash_t == u32 */ -autofs_hash_t autofs_hash(const char *name, int len) -{ - autofs_hash_t h = 0; - while ( len-- ) { - h = (h << 4) + (unsigned char) (*name++); - h ^= ((h & 0xf0000000) >> 24); - } - return h; -} - void autofs_initialize_hash(struct autofs_dirhash *dh) { memset(&dh->h, 0, AUTOFS_HASH_SIZE*sizeof(struct autofs_dir_ent *)); dh->expiry_head.exp_next = dh->expiry_head.exp_prev = &dh->expiry_head; } -struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *dh, autofs_hash_t hash, const char *name, int len) +struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *dh, struct qstr *name) { struct autofs_dir_ent *dhn; - DPRINTK(("autofs_hash_lookup: hash = 0x%08x, name = ", hash)); - autofs_say(name,len); + DPRINTK(("autofs_hash_lookup: hash = 0x%08x, name = ", name->hash)); + autofs_say(name->name,name->len); - for ( dhn = dh->h[hash % AUTOFS_HASH_SIZE] ; dhn ; dhn = dhn->next ) { - if ( hash == dhn->hash && - len == dhn->len && - !memcmp(name, dhn->name, len) ) + for ( dhn = dh->h[(unsigned) name->hash % AUTOFS_HASH_SIZE] ; dhn ; dhn = dhn->next ) { + if ( name->hash == dhn->hash && + name->len == dhn->len && + !memcmp(name->name, dhn->name, name->len) ) break; } @@ -92,7 +80,7 @@ void autofs_hash_insert(struct autofs_dirhash *dh, struct autofs_dir_ent *ent) autofs_init_usage(dh,ent); - dhnp = &dh->h[ent->hash % AUTOFS_HASH_SIZE]; + dhnp = &dh->h[(unsigned) ent->hash % AUTOFS_HASH_SIZE]; ent->next = *dhnp; ent->back = dhnp; *dhnp = ent; diff --git a/fs/autofs/init.c b/fs/autofs/init.c index 4dbb76c85..ed4401ce5 100644 --- a/fs/autofs/init.c +++ b/fs/autofs/init.c @@ -21,7 +21,7 @@ static struct file_system_type autofs_fs_type = { "autofs", - FS_NO_DCACHE, + 0 /* FS_NO_DCACHE doesn't work correctly */, autofs_read_super, NULL }; diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c index 20ca0907a..870ff120d 100644 --- a/fs/autofs/inode.c +++ b/fs/autofs/inode.c @@ -19,11 +19,17 @@ #define __NO_VERSION__ #include <linux/module.h> +/* + * Dummy functions - do we ever actually want to do + * something here? + */ static void autofs_put_inode(struct inode *inode) { - if (inode->i_nlink) - return; - inode->i_size = 0; +} + +static void autofs_delete_inode(struct inode *inode) +{ + inode->i_size = 0; } static void autofs_put_super(struct super_block *sb) @@ -35,16 +41,16 @@ static void autofs_put_super(struct super_block *sb) if ( !sbi->catatonic ) autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */ - lock_super(sb); + lock_super(sb); autofs_hash_nuke(&sbi->dirhash); for ( n = 0 ; n < AUTOFS_MAX_SYMLINKS ; n++ ) { if ( test_bit(n, sbi->symlink_bitmap) ) kfree(sbi->symlink[n].data); } - sb->s_dev = 0; + sb->s_dev = 0; kfree(sb->u.generic_sbp); - unlock_super(sb); + unlock_super(sb); DPRINTK(("autofs: shutting down\n")); @@ -53,95 +59,96 @@ static void autofs_put_super(struct super_block *sb) #endif } -static void autofs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz); +static int autofs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz); static void autofs_read_inode(struct inode *inode); static void autofs_write_inode(struct inode *inode); static struct super_operations autofs_sops = { - autofs_read_inode, - NULL, - autofs_write_inode, - autofs_put_inode, - autofs_put_super, - NULL, - autofs_statfs, - NULL + autofs_read_inode, + autofs_write_inode, + autofs_put_inode, + autofs_delete_inode, + NULL, /* notify_change */ + autofs_put_super, + NULL, /* write_super */ + autofs_statfs, + NULL }; static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, pid_t *pgrp, int *minproto, int *maxproto) { - char *this_char, *value; - - *uid = current->uid; - *gid = current->gid; + char *this_char, *value; + + *uid = current->uid; + *gid = current->gid; *pgrp = current->pgrp; *minproto = *maxproto = AUTOFS_PROTO_VERSION; - *pipefd = -1; + *pipefd = -1; - if ( !options ) return 1; - for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) { - if ((value = strchr(this_char,'=')) != NULL) - *value++ = 0; - if (!strcmp(this_char,"fd")) { - if (!value || !*value) - return 1; - *pipefd = simple_strtoul(value,&value,0); - if (*value) - return 1; - } - else if (!strcmp(this_char,"uid")) { - if (!value || !*value) - return 1; - *uid = simple_strtoul(value,&value,0); - if (*value) - return 1; - } - else if (!strcmp(this_char,"gid")) { - if (!value || !*value) - return 1; - *gid = simple_strtoul(value,&value,0); - if (*value) - return 1; - } - else if (!strcmp(this_char,"pgrp")) { - if (!value || !*value) - return 1; - *pgrp = simple_strtoul(value,&value,0); - if (*value) - return 1; - } - else if (!strcmp(this_char,"minproto")) { - if (!value || !*value) - return 1; - *minproto = simple_strtoul(value,&value,0); - if (*value) - return 1; - } - else if (!strcmp(this_char,"maxproto")) { - if (!value || !*value) - return 1; - *maxproto = simple_strtoul(value,&value,0); - if (*value) - return 1; - } - else break; - } - return (*pipefd < 0); + if ( !options ) return 1; + for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) { + if ((value = strchr(this_char,'=')) != NULL) + *value++ = 0; + if (!strcmp(this_char,"fd")) { + if (!value || !*value) + return 1; + *pipefd = simple_strtoul(value,&value,0); + if (*value) + return 1; + } + else if (!strcmp(this_char,"uid")) { + if (!value || !*value) + return 1; + *uid = simple_strtoul(value,&value,0); + if (*value) + return 1; + } + else if (!strcmp(this_char,"gid")) { + if (!value || !*value) + return 1; + *gid = simple_strtoul(value,&value,0); + if (*value) + return 1; + } + else if (!strcmp(this_char,"pgrp")) { + if (!value || !*value) + return 1; + *pgrp = simple_strtoul(value,&value,0); + if (*value) + return 1; + } + else if (!strcmp(this_char,"minproto")) { + if (!value || !*value) + return 1; + *minproto = simple_strtoul(value,&value,0); + if (*value) + return 1; + } + else if (!strcmp(this_char,"maxproto")) { + if (!value || !*value) + return 1; + *maxproto = simple_strtoul(value,&value,0); + if (*value) + return 1; + } + else break; + } + return (*pipefd < 0); } struct super_block *autofs_read_super(struct super_block *s, void *data, - int silent) + int silent) { - int pipefd; + int pipefd; struct autofs_sb_info *sbi; int minproto, maxproto; MOD_INC_USE_COUNT; - lock_super(s); - sbi = (struct autofs_sb_info *) kmalloc(sizeof(struct autofs_sb_info), GFP_KERNEL); + lock_super(s); + sbi = (struct autofs_sb_info *) kmalloc(sizeof(struct autofs_sb_info), GFP_KERNEL); if ( !sbi ) { s->s_dev = 0; MOD_DEC_USE_COUNT; @@ -158,30 +165,31 @@ struct super_block *autofs_read_super(struct super_block *s, void *data, sbi->queues = NULL; memset(sbi->symlink_bitmap, 0, sizeof(u32)*AUTOFS_SYMLINK_BITMAP_LEN); sbi->next_dir_ino = AUTOFS_FIRST_DIR_INO; - s->s_blocksize = 1024; - s->s_blocksize_bits = 10; - s->s_magic = AUTOFS_SUPER_MAGIC; - s->s_op = &autofs_sops; - unlock_super(s); - if (!(s->s_mounted = iget(s, AUTOFS_ROOT_INO))) { - s->s_dev = 0; + s->s_blocksize = 1024; + s->s_blocksize_bits = 10; + s->s_magic = AUTOFS_SUPER_MAGIC; + s->s_op = &autofs_sops; + unlock_super(s); + s->s_root = d_alloc_root(iget(s, AUTOFS_ROOT_INO), NULL); + if (!s->s_root) { + s->s_dev = 0; kfree(sbi); - printk("autofs: get root inode failed\n"); + printk("autofs: get root inode failed\n"); MOD_DEC_USE_COUNT; - return NULL; - } + return NULL; + } - if ( parse_options(data,&pipefd,&s->s_mounted->i_uid,&s->s_mounted->i_gid,&sbi->oz_pgrp,&minproto,&maxproto) ) { - iput(s->s_mounted); - s->s_dev = 0; + if ( parse_options(data,&pipefd,&s->s_root->d_inode->i_uid,&s->s_root->d_inode->i_gid,&sbi->oz_pgrp,&minproto,&maxproto) ) { + dput(s->s_root); + s->s_dev = 0; kfree(sbi); - printk("autofs: called with bogus options\n"); + printk("autofs: called with bogus options\n"); MOD_DEC_USE_COUNT; - return NULL; - } + return NULL; + } if ( minproto > AUTOFS_PROTO_VERSION || maxproto < AUTOFS_PROTO_VERSION ) { - iput(s->s_mounted); + dput(s->s_root); s->s_dev = 0; kfree(sbi); printk("autofs: kernel does not match daemon version\n"); @@ -193,60 +201,60 @@ struct super_block *autofs_read_super(struct super_block *s, void *data, sbi->pipe = fget(pipefd); if ( !sbi->pipe || !sbi->pipe->f_op || !sbi->pipe->f_op->write ) { if ( sbi->pipe ) { - fput(sbi->pipe, sbi->pipe->f_inode); + fput(sbi->pipe); printk("autofs: pipe file descriptor does not contain proper ops\n"); } else { printk("autofs: could not open pipe file descriptor\n"); } - iput(s->s_mounted); + dput(s->s_root); s->s_dev = 0; kfree(sbi); MOD_DEC_USE_COUNT; return NULL; } - return s; + return s; } -static void autofs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) +static int autofs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) { - struct statfs tmp; + struct statfs tmp; - tmp.f_type = AUTOFS_SUPER_MAGIC; - tmp.f_bsize = 1024; - tmp.f_blocks = 0; - tmp.f_bfree = 0; - tmp.f_bavail = 0; - tmp.f_files = 0; - tmp.f_ffree = 0; - tmp.f_namelen = NAME_MAX; - copy_to_user(buf, &tmp, bufsiz); + tmp.f_type = AUTOFS_SUPER_MAGIC; + tmp.f_bsize = 1024; + tmp.f_blocks = 0; + tmp.f_bfree = 0; + tmp.f_bavail = 0; + tmp.f_files = 0; + tmp.f_ffree = 0; + tmp.f_namelen = NAME_MAX; + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } static void autofs_read_inode(struct inode *inode) { - ino_t ino = inode->i_ino; + ino_t ino = inode->i_ino; unsigned int n; - struct autofs_sb_info *sbi = + struct autofs_sb_info *sbi = (struct autofs_sb_info *) inode->i_sb->u.generic_sbp; - inode->i_op = NULL; - inode->i_mode = 0; - inode->i_nlink = 2; - inode->i_size = 0; - inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; - inode->i_blocks = 0; - inode->i_blksize = 1024; + inode->i_op = NULL; + inode->i_mode = 0; + inode->i_nlink = 2; + inode->i_size = 0; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + inode->i_blocks = 0; + inode->i_blksize = 1024; - if ( ino == AUTOFS_ROOT_INO ) { - inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR; - inode->i_op = &autofs_root_inode_operations; + if ( ino == AUTOFS_ROOT_INO ) { + inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR; + inode->i_op = &autofs_root_inode_operations; inode->i_uid = inode->i_gid = 0; /* Changed in read_super */ - return; - } + return; + } + + inode->i_uid = inode->i_sb->s_root->d_inode->i_uid; + inode->i_gid = inode->i_sb->s_root->d_inode->i_gid; - inode->i_uid = inode->i_sb->s_mounted->i_uid; - inode->i_gid = inode->i_sb->s_mounted->i_gid; - if ( ino >= AUTOFS_FIRST_SYMLINK && ino < AUTOFS_FIRST_DIR_INO ) { /* Symlink inode - should be in symlink list */ struct autofs_symlink *sl; @@ -273,5 +281,4 @@ static void autofs_read_inode(struct inode *inode) static void autofs_write_inode(struct inode *inode) { - inode->i_dirt = 0; } diff --git a/fs/autofs/root.c b/fs/autofs/root.c index a615ede29..4ecc6520e 100644 --- a/fs/autofs/root.c +++ b/fs/autofs/root.c @@ -16,11 +16,11 @@ #include "autofs_i.h" static int autofs_root_readdir(struct inode *,struct file *,void *,filldir_t); -static int autofs_root_lookup(struct inode *,const char *,int,struct inode **); -static int autofs_root_symlink(struct inode *,const char *,int,const char *); -static int autofs_root_unlink(struct inode *,const char *,int); -static int autofs_root_rmdir(struct inode *,const char *,int); -static int autofs_root_mkdir(struct inode *,const char *,int,int); +static int autofs_root_lookup(struct inode *,struct dentry *); +static int autofs_root_symlink(struct inode *,struct dentry *,const char *); +static int autofs_root_unlink(struct inode *,struct dentry *); +static int autofs_root_rmdir(struct inode *,struct dentry *); +static int autofs_root_mkdir(struct inode *,struct dentry *,int); static int autofs_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); static struct file_operations autofs_root_operations = { @@ -48,6 +48,7 @@ struct inode_operations autofs_root_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -92,167 +93,189 @@ static int autofs_root_readdir(struct inode *inode, struct file *filp, return 0; } -static int autofs_root_lookup(struct inode *dir, const char *name, int len, - struct inode **result) +static int try_to_fill_dentry(struct dentry * dentry, struct super_block * sb, struct autofs_sb_info *sbi) { - struct autofs_sb_info *sbi; + struct inode * inode; struct autofs_dir_ent *ent; - struct inode *res; - autofs_hash_t hash; - int status, oz_mode; + + while (!(ent = autofs_hash_lookup(&sbi->dirhash, &dentry->d_name))) { + int status = autofs_wait(sbi, &dentry->d_name); - DPRINTK(("autofs_root_lookup: name = ")); - autofs_say(name,len); + /* Turn this into a real negative dentry? */ + if (status == -ENOENT) { + dentry->d_flags = 0; + return 0; + } + if (status) + return status; + } - *result = NULL; - if (!dir) - return -ENOENT; - if (!S_ISDIR(dir->i_mode)) { - iput(dir); - return -ENOTDIR; + if (!dentry->d_inode) { + inode = iget(sb, ent->ino); + if (!inode) + return -EACCES; + + dentry->d_inode = inode; } - /* Handle special cases: . and ..; since this is a root directory, - they both point to the inode itself */ - *result = dir; - if (!len) - return 0; - if (name[0] == '.') { - if (len == 1) - return 0; - if (name[1] == '.' && len == 2) - return 0; + if (S_ISDIR(dentry->d_inode->i_mode)) { + while (dentry == dentry->d_mounts) + schedule(); } + dentry->d_flags = 0; + return 0; +} + + +/* + * Revalidate is called on every cache lookup. Some of those + * cache lookups may actually happen while the dentry is not + * yet completely filled in, and revalidate has to delay such + * lookups.. + */ +static struct dentry * autofs_revalidate(struct dentry * dentry) +{ + struct autofs_sb_info *sbi; + struct inode * dir = dentry->d_parent->d_inode; - *result = res = NULL; sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp; - hash = autofs_hash(name,len); + /* Incomplete dentry? */ + if (dentry->d_flags) { + if (autofs_oz_mode(sbi)) + return dentry; - oz_mode = autofs_oz_mode(sbi); - DPRINTK(("autofs_lookup: pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d\n", current->pid, current->pgrp, sbi->catatonic, oz_mode)); + try_to_fill_dentry(dentry, dir->i_sb, sbi); + return dentry; + } - do { - while ( !(ent = autofs_hash_lookup(&sbi->dirhash,hash,name,len)) ) { - DPRINTK(("lookup failed, pid = %u, pgrp = %u\n", current->pid, current->pgrp)); - - if ( oz_mode ) { - iput(dir); - return -ENOENT; - } else { - status = autofs_wait(sbi,hash,name,len); - DPRINTK(("autofs_wait returned %d\n", status)); - if ( status ) { - iput(dir); - return status; - } - } - } + /* Negative dentry.. Should we time these out? */ + if (!dentry->d_inode) + return dentry; - DPRINTK(("lookup successful, inode = %08x\n", (unsigned int)ent->ino)); + /* We should update the usage stuff here.. */ + return dentry; +} - if (!(res = iget(dir->i_sb,ent->ino))) { - printk("autofs: iget returned null!\n"); - iput(dir); - return -EACCES; - } - - if ( !oz_mode && S_ISDIR(res->i_mode) && res->i_sb == dir->i_sb ) { - /* Not a mount point yet, call 1-800-DAEMON */ - DPRINTK(("autofs: waiting on non-mountpoint dir, inode = %lu, pid = %u, pgrp = %u\n", res->i_ino, current->pid, current->pgrp)); - iput(res); - res = NULL; - status = autofs_wait(sbi,hash,name,len); - if ( status ) { - iput(dir); - return status; - } - } - } while(!res); - autofs_update_usage(&sbi->dirhash,ent); - - *result = res; - iput(dir); +static int autofs_root_lookup(struct inode *dir, struct dentry * dentry) +{ + struct autofs_sb_info *sbi; + struct inode *res; + int oz_mode; + + DPRINTK(("autofs_root_lookup: name = ")); + autofs_say(dentry->d_name.name,dentry->d_name.len); + + if (!S_ISDIR(dir->i_mode)) + return -ENOTDIR; + + res = NULL; + sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp; + + oz_mode = autofs_oz_mode(sbi); + DPRINTK(("autofs_lookup: pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d\n", current->pid, current->pgrp, sbi->catatonic, oz_mode)); + + /* + * Mark the dentry incomplete, but add it. This is needed so + * that the VFS layer knows about the dentry, and we can count + * on catching any lookups through the revalidate. + * + * Let all the hard work be done by the revalidate function that + * needs to be able to do this anyway.. + * + * We need to do this before we release the directory semaphore. + */ + dentry->d_revalidate = autofs_revalidate; + dentry->d_flags = 1; + d_add(dentry, NULL); + + up(&dir->i_sem); + autofs_revalidate(dentry); + down(&dir->i_sem); return 0; } -static int autofs_root_symlink(struct inode *dir, const char *name, int len, const char *symname) +static int autofs_root_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp; struct autofs_dirhash *dh = &sbi->dirhash; - autofs_hash_t hash = autofs_hash(name,len); struct autofs_dir_ent *ent; unsigned int n; int slsize; struct autofs_symlink *sl; DPRINTK(("autofs_root_symlink: %s <- ", symname)); - autofs_say(name,len); + autofs_say(dentry->d_name.name,dentry->d_name.len); - if ( !autofs_oz_mode(sbi) ) { - iput(dir); + if ( !autofs_oz_mode(sbi) ) return -EPERM; - } - if ( autofs_hash_lookup(dh,hash,name,len) ) { - iput(dir); + + if ( autofs_hash_lookup(dh, &dentry->d_name) ) return -EEXIST; - } + n = find_first_zero_bit(sbi->symlink_bitmap,AUTOFS_MAX_SYMLINKS); - if ( n >= AUTOFS_MAX_SYMLINKS ) { - iput(dir); + if ( n >= AUTOFS_MAX_SYMLINKS ) return -ENOSPC; - } + set_bit(n,sbi->symlink_bitmap); sl = &sbi->symlink[n]; sl->len = strlen(symname); sl->data = kmalloc(slsize = sl->len+1, GFP_KERNEL); if ( !sl->data ) { clear_bit(n,sbi->symlink_bitmap); - iput(dir); return -ENOSPC; } + ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL); if ( !ent ) { kfree(sl->data); clear_bit(n,sbi->symlink_bitmap); - iput(dir); return -ENOSPC; } - ent->name = kmalloc(len, GFP_KERNEL); + + ent->name = kmalloc(dentry->d_name.len, GFP_KERNEL); if ( !ent->name ) { kfree(sl->data); kfree(ent); clear_bit(n,sbi->symlink_bitmap); - iput(dir); return -ENOSPC; } + memcpy(sl->data,symname,slsize); sl->mtime = CURRENT_TIME; ent->ino = AUTOFS_FIRST_SYMLINK + n; - ent->hash = hash; - memcpy(ent->name,name,ent->len = len); + ent->hash = dentry->d_name.hash; + memcpy(ent->name, dentry->d_name.name,ent->len = dentry->d_name.len); autofs_hash_insert(dh,ent); - iput(dir); + d_instantiate(dentry, iget(dir->i_sb,ent->ino)); return 0; } -static int autofs_root_unlink(struct inode *dir, const char *name, int len) +/* + * NOTE! + * + * Normal filesystems would do a "d_delete()" to tell the VFS dcache + * that the file no longer exists. However, doing that means that the + * VFS layer can turn the dentry into a negative dentry, which we + * obviously do not want (we're dropping the entry not because it + * doesn't exist, but because it has timed out). + * + * Also see autofs_root_rmdir().. + */ +static int autofs_root_unlink(struct inode *dir, struct dentry *dentry) { struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp; struct autofs_dirhash *dh = &sbi->dirhash; - autofs_hash_t hash = autofs_hash(name,len); struct autofs_dir_ent *ent; unsigned int n; - iput(dir); /* Nothing below can sleep */ - if ( !autofs_oz_mode(sbi) ) return -EPERM; - ent = autofs_hash_lookup(dh,hash,name,len); + ent = autofs_hash_lookup(dh, &dentry->d_name); if ( !ent ) return -ENOENT; @@ -263,75 +286,68 @@ static int autofs_root_unlink(struct inode *dir, const char *name, int len) autofs_hash_delete(ent); clear_bit(n,sbi->symlink_bitmap); kfree(sbi->symlink[n].data); + d_drop(dentry); return 0; } -static int autofs_root_rmdir(struct inode *dir, const char *name, int len) +static int autofs_root_rmdir(struct inode *dir, struct dentry *dentry) { struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp; struct autofs_dirhash *dh = &sbi->dirhash; - autofs_hash_t hash = autofs_hash(name,len); struct autofs_dir_ent *ent; - if ( !autofs_oz_mode(sbi) ) { - iput(dir); + if ( !autofs_oz_mode(sbi) ) return -EPERM; - } - ent = autofs_hash_lookup(dh,hash,name,len); - if ( !ent ) { - iput(dir); + + ent = autofs_hash_lookup(dh, &dentry->d_name); + if ( !ent ) return -ENOENT; - } - if ( (unsigned int)ent->ino < AUTOFS_FIRST_DIR_INO ) { - iput(dir); + + if ( (unsigned int)ent->ino < AUTOFS_FIRST_DIR_INO ) return -ENOTDIR; /* Not a directory */ - } + autofs_hash_delete(ent); dir->i_nlink--; - iput(dir); + d_drop(dentry); return 0; } -static int autofs_root_mkdir(struct inode *dir, const char *name, int len, int mode) +static int autofs_root_mkdir(struct inode *dir, struct dentry *dentry, int mode) { struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp; struct autofs_dirhash *dh = &sbi->dirhash; - autofs_hash_t hash = autofs_hash(name,len); struct autofs_dir_ent *ent; - if ( !autofs_oz_mode(sbi) ) { - iput(dir); + if ( !autofs_oz_mode(sbi) ) return -EPERM; - } - ent = autofs_hash_lookup(dh,hash,name,len); - if ( ent ) { - iput(dir); + + ent = autofs_hash_lookup(dh, &dentry->d_name); + if ( ent ) return -EEXIST; - } + if ( sbi->next_dir_ino < AUTOFS_FIRST_DIR_INO ) { printk("autofs: Out of inode numbers -- what the heck did you do??\n"); - iput(dir); return -ENOSPC; } + ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL); - if ( !ent ) { - iput(dir); + if ( !ent ) return -ENOSPC; - } - ent->name = kmalloc(len, GFP_KERNEL); + + ent->name = kmalloc(dentry->d_name.len, GFP_KERNEL); if ( !ent->name ) { kfree(ent); - iput(dir); return -ENOSPC; } - ent->hash = hash; - memcpy(ent->name, name, ent->len = len); + + ent->hash = dentry->d_name.hash; + memcpy(ent->name, dentry->d_name.name, ent->len = dentry->d_name.len); ent->ino = sbi->next_dir_ino++; autofs_hash_insert(dh,ent); dir->i_nlink++; - iput(dir); + d_instantiate(dentry, iget(dir->i_sb,ent->ino)); return 0; } diff --git a/fs/autofs/symlink.c b/fs/autofs/symlink.c index d6ac82ed4..e3ff3d31b 100644 --- a/fs/autofs/symlink.c +++ b/fs/autofs/symlink.c @@ -19,18 +19,21 @@ static int autofs_readlink(struct inode *inode, char *buffer, int buflen) struct autofs_symlink *sl; int len; - if (!S_ISLNK(inode->i_mode)) { - iput(inode); - return -EINVAL; - } sl = (struct autofs_symlink *)inode->u.generic_ip; len = sl->len; if (len > buflen) len = buflen; copy_to_user(buffer,sl->data,len); - iput(inode); return len; } +static struct dentry * autofs_follow_link(struct inode *inode, struct dentry *base) +{ + struct autofs_symlink *sl; + + sl = (struct autofs_symlink *)inode->u.generic_ip; + return lookup_dentry(sl->data, base, 1); +} + struct inode_operations autofs_symlink_inode_operations = { NULL, /* file operations */ NULL, /* create */ @@ -43,6 +46,7 @@ struct inode_operations autofs_symlink_inode_operations = { NULL, /* mknod */ NULL, /* rename */ autofs_readlink, /* readlink */ + autofs_follow_link, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/autofs/waitq.c b/fs/autofs/waitq.c index cbe270a53..39beed699 100644 --- a/fs/autofs/waitq.c +++ b/fs/autofs/waitq.c @@ -37,7 +37,7 @@ void autofs_catatonic_mode(struct autofs_sb_info *sbi) wake_up(&wq->queue); wq = nwq; } - fput(sbi->pipe, sbi->pipe->f_inode); /* Close the pipe */ + fput(sbi->pipe); /* Close the pipe */ } static int autofs_write(struct file *file, const void *addr, int bytes) @@ -55,7 +55,7 @@ static int autofs_write(struct file *file, const void *addr, int bytes) old_signal = current->signal; - while ( bytes && (written = file->f_op->write(file->f_inode,file,data,bytes)) > 0 ) { + while ( bytes && (written = file->f_op->write(file->f_dentry->d_inode,file,data,bytes)) > 0 ) { data += written; bytes -= written; } @@ -90,15 +90,15 @@ static void autofs_notify_daemon(struct autofs_sb_info *sbi, struct autofs_wait_ autofs_catatonic_mode(sbi); } -int autofs_wait(struct autofs_sb_info *sbi, autofs_hash_t hash, const char *name, int len) +int autofs_wait(struct autofs_sb_info *sbi, struct qstr * name) { struct autofs_wait_queue *wq; int status; for ( wq = sbi->queues ; wq ; wq = wq->next ) { - if ( wq->hash == hash && - wq->len == len && - wq->name && !memcmp(wq->name,name,len) ) + if ( wq->hash == name->hash && + wq->len == name->len && + wq->name && !memcmp(wq->name,name->name,name->len) ) break; } @@ -108,17 +108,17 @@ int autofs_wait(struct autofs_sb_info *sbi, autofs_hash_t hash, const char *name if ( !wq ) return -ENOMEM; - wq->name = kmalloc(len,GFP_KERNEL); + wq->name = kmalloc(name->len,GFP_KERNEL); if ( !wq->name ) { kfree(wq); return -ENOMEM; } wq->wait_queue_token = autofs_next_wait_queue++; init_waitqueue(&wq->queue); - wq->hash = hash; - wq->len = len; + wq->hash = name->hash; + wq->len = name->len; wq->status = -EINTR; /* Status return if interrupted */ - memcpy(wq->name, name, len); + memcpy(wq->name, name->name, name->len); wq->next = sbi->queues; sbi->queues = wq; diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index 394f41eb1..c5c7998cf 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -79,6 +79,7 @@ if (file.f_op->llseek) { \ static inline int do_aout_core_dump(long signr, struct pt_regs * regs) { + struct dentry * dentry = NULL; struct inode * inode = NULL; struct file file; unsigned short fs; @@ -114,26 +115,20 @@ do_aout_core_dump(long signr, struct pt_regs * regs) #else corefile[4] = '\0'; #endif - if (open_namei(corefile,O_CREAT | 2 | O_TRUNC,0600,&inode,NULL)) { - inode = NULL; + dentry = open_namei(corefile,O_CREAT | 2 | O_TRUNC, 0600); + if (IS_ERR(dentry)) { + dentry = NULL; goto end_coredump; } + inode = dentry->d_inode; if (!S_ISREG(inode->i_mode)) goto end_coredump; if (!inode->i_op || !inode->i_op->default_file_ops) goto end_coredump; if (get_write_access(inode)) goto end_coredump; - file.f_mode = 3; - file.f_flags = 0; - file.f_count = 1; - file.f_inode = inode; - file.f_pos = 0; - file.f_reada = 0; - file.f_op = inode->i_op->default_file_ops; - if (file.f_op->open) - if (file.f_op->open(inode,&file)) - goto done_coredump; + if (init_private_file(&file, dentry, 3)) + goto end_coredump; if (!file.f_op->write) goto close_coredump; has_dumped = 1; @@ -214,7 +209,6 @@ do_aout_core_dump(long signr, struct pt_regs * regs) /* Finally dump the task struct. Not be used by gdb, but could be useful */ set_fs(KERNEL_DS); DUMP_WRITE(current,sizeof(*current)); - inode->i_status |= ST_MODIFIED; close_coredump: if (file.f_op->release) file.f_op->release(inode,&file); @@ -222,7 +216,7 @@ done_coredump: put_write_access(inode); end_coredump: set_fs(fs); - iput(inode); + dput(dentry); return has_dumped; } @@ -318,7 +312,7 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC && N_MAGIC(ex) != QMAGIC && N_MAGIC(ex) != NMAGIC) || N_TRSIZE(ex) || N_DRSIZE(ex) || - bprm->inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) { + bprm->dentry->d_inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) { return -ENOEXEC; } @@ -332,7 +326,7 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs } if (N_MAGIC(ex) == ZMAGIC && ex.a_text && - (fd_offset < bprm->inode->i_sb->s_blocksize)) { + (fd_offset < bprm->dentry->d_inode->i_sb->s_blocksize)) { printk(KERN_NOTICE "N_TXTOFF < BLOCK_SIZE. Please convert binary.\n"); return -ENOEXEC; } @@ -372,12 +366,12 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs error = do_mmap(NULL, N_TXTADDR(ex), ex.a_text, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0); - read_exec(bprm->inode, fd_offset, (char *) N_TXTADDR(ex), + read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex), ex.a_text, 0); error = do_mmap(NULL, N_DATADDR(ex), ex.a_data, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0); - read_exec(bprm->inode, fd_offset + ex.a_text, (char *) N_DATADDR(ex), + read_exec(bprm->dentry, fd_offset + ex.a_text, (char *) N_DATADDR(ex), ex.a_data, 0); goto beyond_if; } @@ -389,20 +383,20 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs ex.a_text+ex.a_data + PAGE_SIZE - 1, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0); - read_exec(bprm->inode, fd_offset, (char *) N_TXTADDR(ex), + read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex), ex.a_text+ex.a_data, 0); #else do_mmap(NULL, 0, ex.a_text+ex.a_data, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0); - read_exec(bprm->inode, 32, (char *) 0, ex.a_text+ex.a_data, 0); + read_exec(bprm->dentry, 32, (char *) 0, ex.a_text+ex.a_data, 0); #endif } else { if ((ex.a_text & 0xfff || ex.a_data & 0xfff) && (N_MAGIC(ex) != NMAGIC)) printk(KERN_NOTICE "executable not page aligned\n"); - fd = open_inode(bprm->inode, O_RDONLY); + fd = open_dentry(bprm->dentry, O_RDONLY); if (fd < 0) return fd; @@ -412,7 +406,7 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs do_mmap(NULL, 0, ex.a_text+ex.a_data, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0); - read_exec(bprm->inode, fd_offset, + read_exec(bprm->dentry, fd_offset, (char *) N_TXTADDR(ex), ex.a_text+ex.a_data, 0); goto beyond_if; } @@ -482,18 +476,21 @@ do_load_aout_library(int fd) { struct file * file; struct exec ex; - struct inode * inode; + struct dentry * dentry; + struct inode * inode; unsigned int len; unsigned int bss; unsigned int start_addr; unsigned long error; file = current->files->fd[fd]; - inode = file->f_inode; if (!file || !file->f_op) return -EACCES; + dentry = file->f_dentry; + inode = dentry->d_inode; + /* Seek into the file */ if (file->f_op->llseek) { if ((error = file->f_op->llseek(inode, file, 0, 0)) != 0) diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index ff987e0e8..7629fd0d8 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -134,23 +134,20 @@ create_elf_tables(char *p, int argc, int envc, { elf_caddr_t *argv; elf_caddr_t *envp; - elf_addr_t *sp; + elf_addr_t *sp, *csp; /* - * Force 16 byte alignment here for generality. + * Force 16 byte _final_ alignment here for generality. */ sp = (elf_addr_t *) (~15UL & (unsigned long) p); -#if defined(__mips__) || defined(__sparc__) -{ - elf_addr_t *csp; csp = sp; csp -= exec ? DLINFO_ITEMS*2 : 2; csp -= envc+1; csp -= argc+1; - if (!(((unsigned long) csp) & 4)) - sp--; -} -#endif + csp -= (!ibcs ? 3 : 1); /* argc itself */ + if ((unsigned long)csp & 15UL) { + sp -= (16UL - ((unsigned long)csp & 15UL)) / sizeof(*sp); + } /* * Put the ELF interpreter info on the stack @@ -165,17 +162,17 @@ create_elf_tables(char *p, int argc, int envc, if (exec) { sp -= 11*2; - NEW_AUX_ENT (0, AT_PHDR, load_addr + exec->e_phoff); - NEW_AUX_ENT (1, AT_PHENT, sizeof (struct elf_phdr)); - NEW_AUX_ENT (2, AT_PHNUM, exec->e_phnum); - NEW_AUX_ENT (3, AT_PAGESZ, ELF_EXEC_PAGESIZE); - NEW_AUX_ENT (4, AT_BASE, interp_load_addr); - NEW_AUX_ENT (5, AT_FLAGS, 0); - NEW_AUX_ENT (6, AT_ENTRY, (elf_addr_t) exec->e_entry); - NEW_AUX_ENT (7, AT_UID, (elf_addr_t) current->uid); - NEW_AUX_ENT (8, AT_EUID, (elf_addr_t) current->euid); - NEW_AUX_ENT (9, AT_GID, (elf_addr_t) current->gid); - NEW_AUX_ENT (10, AT_EGID, (elf_addr_t) current->egid); + NEW_AUX_ENT (0, AT_PHDR, load_addr + exec->e_phoff); + NEW_AUX_ENT (1, AT_PHENT, sizeof (struct elf_phdr)); + NEW_AUX_ENT (2, AT_PHNUM, exec->e_phnum); + NEW_AUX_ENT (3, AT_PAGESZ, ELF_EXEC_PAGESIZE); + NEW_AUX_ENT (4, AT_BASE, interp_load_addr); + NEW_AUX_ENT (5, AT_FLAGS, 0); + NEW_AUX_ENT (6, AT_ENTRY, (elf_addr_t) exec->e_entry); + NEW_AUX_ENT (7, AT_UID, (elf_addr_t) current->uid); + NEW_AUX_ENT (8, AT_EUID, (elf_addr_t) current->euid); + NEW_AUX_ENT (9, AT_GID, (elf_addr_t) current->gid); + NEW_AUX_ENT (10, AT_EGID, (elf_addr_t) current->egid); } #undef NEW_AUX_ENT @@ -212,7 +209,7 @@ create_elf_tables(char *p, int argc, int envc, an ELF header */ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex, - struct inode * interpreter_inode, + struct dentry * interpreter_dentry, unsigned long *interp_load_addr) { struct file * file; @@ -234,8 +231,9 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex, if ((interp_elf_ex->e_type != ET_EXEC && interp_elf_ex->e_type != ET_DYN) || !elf_check_arch(interp_elf_ex->e_machine) || - (!interpreter_inode->i_op || - !interpreter_inode->i_op->default_file_ops->mmap)){ + (!interpreter_dentry->d_inode->i_op || + !interpreter_dentry->d_inode->i_op->default_file_ops->mmap)){ + #ifdef DEBUG_ELF printk("bad e_type %d ", interp_elf_ex->e_type); #endif @@ -265,7 +263,7 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex, return ~0UL; } - retval = read_exec(interpreter_inode, interp_elf_ex->e_phoff, + retval = read_exec(interpreter_dentry, interp_elf_ex->e_phoff, (char *) elf_phdata, sizeof(struct elf_phdr) * interp_elf_ex->e_phnum, 1); @@ -274,7 +272,7 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex, return retval; } - elf_exec_fileno = open_inode(interpreter_inode, O_RDONLY); + elf_exec_fileno = open_dentry(interpreter_dentry, O_RDONLY); if (elf_exec_fileno < 0) { kfree(elf_phdata); return ~0UL; @@ -366,7 +364,7 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex, } static unsigned long load_aout_interp(struct exec * interp_ex, - struct inode * interpreter_inode) + struct dentry * interpreter_dentry) { int retval; unsigned long elf_entry; @@ -381,13 +379,13 @@ static unsigned long load_aout_interp(struct exec * interp_ex, do_mmap(NULL, 0, interp_ex->a_text+interp_ex->a_data, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0); - retval = read_exec(interpreter_inode, 32, (char *) 0, + retval = read_exec(interpreter_dentry, 32, (char *) 0, interp_ex->a_text+interp_ex->a_data, 0); } else if (N_MAGIC(*interp_ex) == ZMAGIC || N_MAGIC(*interp_ex) == QMAGIC) { do_mmap(NULL, 0, interp_ex->a_text+interp_ex->a_data, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0); - retval = read_exec(interpreter_inode, + retval = read_exec(interpreter_dentry, N_TXTOFF(*interp_ex) , (char *) N_TXTADDR(*interp_ex), interp_ex->a_text+interp_ex->a_data, 0); @@ -422,7 +420,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) struct elfhdr interp_elf_ex; struct file * file; struct exec interp_ex; - struct inode *interpreter_inode; + struct dentry *interpreter_dentry; unsigned long load_addr; int load_addr_set = 0; unsigned int interpreter_type = INTERPRETER_NONE; @@ -456,8 +454,8 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) if ((elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN) || (! elf_check_arch(elf_ex.e_machine)) || - (!bprm->inode->i_op || !bprm->inode->i_op->default_file_ops || - !bprm->inode->i_op->default_file_ops->mmap)){ + (!bprm->dentry->d_inode->i_op || !bprm->dentry->d_inode->i_op->default_file_ops || + !bprm->dentry->d_inode->i_op->default_file_ops->mmap)){ return -ENOEXEC; } @@ -475,7 +473,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) return -ENOMEM; } - retval = read_exec(bprm->inode, elf_ex.e_phoff, (char *) elf_phdata, + retval = read_exec(bprm->dentry, elf_ex.e_phoff, (char *) elf_phdata, elf_ex.e_phentsize * elf_ex.e_phnum, 1); if (retval < 0) { kfree (elf_phdata); @@ -487,7 +485,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) elf_bss = 0; elf_brk = 0; - elf_exec_fileno = open_inode(bprm->inode, O_RDONLY); + elf_exec_fileno = open_dentry(bprm->dentry, O_RDONLY); if (elf_exec_fileno < 0) { kfree (elf_phdata); @@ -525,7 +523,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) return -ENOMEM; } - retval = read_exec(bprm->inode,elf_ppnt->p_offset, + retval = read_exec(bprm->dentry,elf_ppnt->p_offset, elf_interpreter, elf_ppnt->p_filesz, 1); /* If the program interpreter is one of these two, @@ -540,13 +538,14 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) if (retval >= 0) { old_fs = get_fs(); /* This could probably be optimized */ set_fs(get_ds()); - retval = open_namei(elf_interpreter, 0, 0, - &interpreter_inode, NULL); + interpreter_dentry = open_namei(elf_interpreter, 0, 0); set_fs(old_fs); + if (IS_ERR(interpreter_dentry)) + retval = PTR_ERR(interpreter_dentry); } if (retval >= 0) - retval = read_exec(interpreter_inode,0,bprm->buf,128, 1); + retval = read_exec(interpreter_dentry,0,bprm->buf,128, 1); if (retval >= 0) { interp_ex = *((struct exec *) bprm->buf); /* exec-header */ @@ -682,13 +681,13 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) if (elf_interpreter) { if (interpreter_type & 1) elf_entry = load_aout_interp(&interp_ex, - interpreter_inode); + interpreter_dentry); else if (interpreter_type & 2) elf_entry = load_elf_interp(&interp_elf_ex, - interpreter_inode, + interpreter_dentry, &interp_load_addr); - iput(interpreter_inode); + dput(interpreter_dentry); kfree(elf_interpreter); if (elf_entry == ~0UL) { @@ -716,8 +715,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) __MOD_INC_USE_COUNT(current->binfmt->module); #ifndef VM_STACK_FLAGS - current->executable = bprm->inode; - atomic_inc(&bprm->inode->i_count); + current->executable = dget(bprm->dentry); #endif #ifdef LOW_ELF_STACK current->start_stack = bprm->p = elf_stack - 4; @@ -802,7 +800,8 @@ do_load_elf_library(int fd){ struct file * file; struct elfhdr elf_ex; struct elf_phdr *elf_phdata = NULL; - struct inode * inode; + struct dentry * dentry; + struct inode * inode; unsigned long len; int elf_bss; int retval; @@ -812,7 +811,8 @@ do_load_elf_library(int fd){ len = 0; file = current->files->fd[fd]; - inode = file->f_inode; + dentry = file->f_dentry; + inode = dentry->d_inode; elf_bss = 0; if (!file || !file->f_op) @@ -851,7 +851,7 @@ do_load_elf_library(int fd){ if (elf_phdata == NULL) return -ENOMEM; - retval = read_exec(inode, elf_ex.e_phoff, (char *) elf_phdata, + retval = read_exec(dentry, elf_ex.e_phoff, (char *) elf_phdata, sizeof(struct elf_phdr) * elf_ex.e_phnum, 1); j = 0; @@ -923,14 +923,13 @@ static int load_elf_library(int fd) */ static int dump_write(struct file *file, const void *addr, int nr) { - file->f_inode->i_status |= ST_MODIFIED; - return file->f_op->write(file->f_inode, file, addr, nr) == nr; + return file->f_op->write(file->f_dentry->d_inode, file, addr, nr) == nr; } static int dump_seek(struct file *file, off_t off) { if (file->f_op->llseek) { - if (file->f_op->llseek(file->f_inode, file, off, 0) != off) + if (file->f_op->llseek(file->f_dentry->d_inode, file, off, 0) != off) return 0; } else file->f_pos = off; @@ -1045,6 +1044,7 @@ static int elf_core_dump(long signr, struct pt_regs * regs) { int has_dumped = 0; struct file file; + struct dentry *dentry; struct inode *inode; unsigned short fs; char corefile[6+sizeof(current->comm)]; @@ -1118,24 +1118,18 @@ static int elf_core_dump(long signr, struct pt_regs * regs) #else corefile[4] = '\0'; #endif - if (open_namei(corefile,O_CREAT | 2 | O_TRUNC,0600,&inode,NULL)) { - inode = NULL; + dentry = open_namei(corefile, O_CREAT | 2 | O_TRUNC, 0600); + if (IS_ERR(dentry)) { + dentry = NULL; goto end_coredump; } + inode = dentry->d_inode; if (!S_ISREG(inode->i_mode)) goto end_coredump; if (!inode->i_op || !inode->i_op->default_file_ops) goto end_coredump; - file.f_mode = 3; - file.f_flags = 0; - file.f_count = 1; - file.f_inode = inode; - file.f_pos = 0; - file.f_reada = 0; - file.f_op = inode->i_op->default_file_ops; - if (file.f_op->open) - if (file.f_op->open(inode,&file)) - goto end_coredump; + if (init_private_file(&file, dentry, 3)) + goto end_coredump; if (!file.f_op->write) goto close_coredump; has_dumped = 1; @@ -1326,7 +1320,7 @@ static int elf_core_dump(long signr, struct pt_regs * regs) end_coredump: set_fs(fs); - iput(inode); + dput(dentry); #ifndef CONFIG_BINFMT_ELF MOD_DEC_USE_COUNT; #endif diff --git a/fs/binfmt_em86.c b/fs/binfmt_em86.c index 7bd71f212..133586e69 100644 --- a/fs/binfmt_em86.c +++ b/fs/binfmt_em86.c @@ -23,6 +23,7 @@ static int do_load_em86(struct linux_binprm *bprm,struct pt_regs *regs) { char *interp, *i_name, *i_arg; + struct dentry * dentry; int retval; struct elfhdr elf_ex; @@ -39,14 +40,14 @@ static int do_load_em86(struct linux_binprm *bprm,struct pt_regs *regs) if ((elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN) || (!((elf_ex.e_machine == EM_386) || (elf_ex.e_machine == EM_486))) || - (!bprm->inode->i_op || !bprm->inode->i_op->default_file_ops || - !bprm->inode->i_op->default_file_ops->mmap)){ + (!bprm->dentry->d_inode->i_op || !bprm->dentry->d_inode->i_op->default_file_ops || + !bprm->dentry->d_inode->i_op->default_file_ops->mmap)){ return -ENOEXEC; } bprm->sh_bang++; /* Well, the bang-shell is implicit... */ - iput(bprm->inode); - bprm->dont_iput = 1; + dput(bprm->dentry); + bprm->dentry = NULL; /* Unlike in the script case, we don't have to do any hairy * parsing to find our interpreter... it's hardcoded! @@ -79,14 +80,17 @@ static int do_load_em86(struct linux_binprm *bprm,struct pt_regs *regs) * Note that we use open_namei() as the name is now in kernel * space, and we don't need to copy it. */ - retval = open_namei(interp, 0, 0, &bprm->inode, NULL); - if (retval) - return retval; - bprm->dont_iput=0; - retval=prepare_binprm(bprm); - if(retval<0) + dentry = open_namei(interp, 0, 0); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + bprm->dentry = dentry; + + retval = prepare_binprm(bprm); + if (retval < 0) return retval; - return search_binary_handler(bprm,regs); + + return search_binary_handler(bprm, regs); } static int load_em86(struct linux_binprm *bprm,struct pt_regs *regs) diff --git a/fs/binfmt_java.c b/fs/binfmt_java.c index fcf664c5d..41e66a21a 100644 --- a/fs/binfmt_java.c +++ b/fs/binfmt_java.c @@ -28,7 +28,9 @@ static int do_load_java(struct linux_binprm *bprm,struct pt_regs *regs) char *i_name; int len; int retval; + struct dentry * dentry; unsigned char *ucp = (unsigned char *) bprm->buf; + if ((ucp[0] != 0xca) || (ucp[1] != 0xfe) || (ucp[2] != 0xba) || (ucp[3] != 0xbe)) return -ENOEXEC; @@ -42,8 +44,8 @@ static int do_load_java(struct linux_binprm *bprm,struct pt_regs *regs) bprm->java = 1; - iput(bprm->inode); - bprm->dont_iput=1; + dput(bprm->dentry); + bprm->dentry = NULL; /* * Set args: [0] the name of the java interpreter @@ -75,15 +77,17 @@ static int do_load_java(struct linux_binprm *bprm,struct pt_regs *regs) if (!bprm->p) return -E2BIG; /* - * OK, now restart the process with the interpreter's inode. + * OK, now restart the process with the interpreter's dentry. */ bprm->filename = binfmt_java_interpreter; - retval = open_namei(binfmt_java_interpreter, 0, 0, &bprm->inode, NULL); - if (retval) + dentry = open_namei(binfmt_java_interpreter, 0, 0); + retval = PTR_ERR(dentry); + if (IS_ERR(dentry)) return retval; - bprm->dont_iput=0; - retval=prepare_binprm(bprm); - if(retval<0) + + bprm->dentry = dentry; + retval = prepare_binprm(bprm); + if (retval < 0) return retval; return search_binary_handler(bprm,regs); @@ -92,12 +96,14 @@ static int do_load_java(struct linux_binprm *bprm,struct pt_regs *regs) static int do_load_applet(struct linux_binprm *bprm,struct pt_regs *regs) { char *i_name; + struct dentry * dentry; int retval; + if (strncmp (bprm->buf, "<!--applet", 10)) return -ENOEXEC; - iput(bprm->inode); - bprm->dont_iput=1; + dput(bprm->dentry); + bprm->dentry = NULL; /* * Set args: [0] the name of the appletviewer @@ -118,15 +124,17 @@ static int do_load_applet(struct linux_binprm *bprm,struct pt_regs *regs) if (!bprm->p) return -E2BIG; /* - * OK, now restart the process with the interpreter's inode. + * OK, now restart the process with the interpreter's dentry. */ bprm->filename = binfmt_java_appletviewer; - retval = open_namei(binfmt_java_appletviewer, 0, 0, &bprm->inode, NULL); - if (retval) + dentry = open_namei(binfmt_java_appletviewer, 0, 0); + retval = PTR_ERR(dentry); + if (IS_ERR(dentry)) return retval; - bprm->dont_iput=0; - retval=prepare_binprm(bprm); - if(retval<0) + + bprm->dentry = dentry; + retval = prepare_binprm(bprm); + if (retval < 0) return retval; return search_binary_handler(bprm,regs); diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 28dced394..ffca300d9 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -7,9 +7,11 @@ * a specified wrapper. This should obsolete binfmt_java, binfmt_em86 and * binfmt_mz. * - * 25.4.97 first version - * [...] - * 19.5.97 cleanup + * 1997-04-25 first version + * [...] + * 1997-05-19 cleanup + * 1997-06-26 hpa: pass the real filename rather than argv[0] + * 1997-06-30 minor cleanup */ #include <linux/module.h> @@ -48,7 +50,7 @@ struct binfmt_entry { #define ENTRY_ENABLED 1 /* the old binfmt_entry.enabled */ #define ENTRY_MAGIC 8 /* not filename detection */ -#define ENTRY_STRIP_EXT 32 /* strip of last filename extension */ +#define ENTRY_STRIP_EXT 32 /* strip off last filename extension */ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs); static void entry_proc_cleanup(struct binfmt_entry *e); @@ -85,7 +87,6 @@ static void clear_entry(int id) *ep = e->next; entry_proc_cleanup(e); kfree(e); - MOD_DEC_USE_COUNT; } write_unlock(&entries_lock); } @@ -102,7 +103,6 @@ static void clear_entries(void) entries = entries->next; entry_proc_cleanup(e); kfree(e); - MOD_DEC_USE_COUNT; } write_unlock(&entries_lock); } @@ -157,6 +157,7 @@ static struct binfmt_entry *check_file(struct linux_binprm *bprm) static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs) { struct binfmt_entry *fmt; + struct dentry * dentry; char iname[128]; char *iname_addr = iname, *p; int retval, fmt_flags = 0; @@ -180,17 +181,16 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs) goto _ret; } - iput(bprm->inode); - bprm->dont_iput = 1; + dput(bprm->dentry); + bprm->dentry = NULL; /* Build args for interpreter */ if ((fmt_flags & ENTRY_STRIP_EXT) && - (p = strrchr(bprm->filename, '.'))) { + (p = strrchr(bprm->filename, '.'))) *p = '\0'; - remove_arg_zero(bprm); - bprm->p = copy_strings(1, &bprm->filename, bprm->page, bprm->p, 2); - bprm->argc++; - } + remove_arg_zero(bprm); + bprm->p = copy_strings(1, &bprm->filename, bprm->page, bprm->p, 2); + bprm->argc++; bprm->p = copy_strings(1, &iname_addr, bprm->page, bprm->p, 2); bprm->argc++; if (!bprm->p) { @@ -199,11 +199,14 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs) } bprm->filename = iname; /* for binfmt_script */ - if ((retval = open_namei(iname, 0, 0, &bprm->inode, NULL))) + dentry = open_namei(iname, 0, 0); + retval = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto _ret; - bprm->dont_iput = 0; + bprm->dentry = dentry; - if ((retval = prepare_binprm(bprm)) >= 0) + retval = prepare_binprm(bprm); + if (retval >= 0) retval = search_binary_handler(bprm, regs); _ret: MOD_DEC_USE_COUNT; @@ -322,7 +325,7 @@ static int proc_write_register(struct file *file, const char *buffer, entries = e; write_unlock(&entries_lock); - return count; + err = count; _err: MOD_DEC_USE_COUNT; return err; @@ -499,6 +502,7 @@ void cleanup_module(void) unregister_binfmt(&misc_format); remove_proc_entry("register", bm_dir); remove_proc_entry("status", bm_dir); + clear_entries(); remove_proc_entry("sys/fs/binfmt_misc", NULL); } #endif diff --git a/fs/binfmt_script.c b/fs/binfmt_script.c index 1bd2f0d10..5a38cf545 100644 --- a/fs/binfmt_script.c +++ b/fs/binfmt_script.c @@ -15,8 +15,10 @@ static int do_load_script(struct linux_binprm *bprm,struct pt_regs *regs) { char *cp, *i_name, *i_name_start, *i_arg; + struct dentry * dentry; char interp[128]; int retval; + if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!') || (bprm->sh_bang)) return -ENOEXEC; /* @@ -25,8 +27,8 @@ static int do_load_script(struct linux_binprm *bprm,struct pt_regs *regs) */ bprm->sh_bang++; - iput(bprm->inode); - bprm->dont_iput=1; + dput(bprm->dentry); + bprm->dentry = NULL; bprm->buf[127] = '\0'; if ((cp = strchr(bprm->buf, '\n')) == NULL) @@ -75,14 +77,15 @@ static int do_load_script(struct linux_binprm *bprm,struct pt_regs *regs) if (!bprm->p) return -E2BIG; /* - * OK, now restart the process with the interpreter's inode. + * OK, now restart the process with the interpreter's dentry. */ - retval = open_namei(interp, 0, 0, &bprm->inode, NULL); - if (retval) - return retval; - bprm->dont_iput=0; - retval=prepare_binprm(bprm); - if(retval<0) + dentry = open_namei(interp, 0, 0); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + bprm->dentry = dentry; + retval = prepare_binprm(bprm); + if (retval < 0) return retval; return search_binary_handler(bprm,regs); } diff --git a/fs/buffer.c b/fs/buffer.c index bd06972f3..3e1b5cc35 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -289,16 +289,35 @@ int file_fsync (struct inode *inode, struct file *filp) asmlinkage int sys_fsync(unsigned int fd) { struct file * file; + struct dentry * dentry; struct inode * inode; - int err = 0; + int err; lock_kernel(); - if (fd>=NR_OPEN || !(file=current->files->fd[fd]) || !(inode=file->f_inode)) - err = -EBADF; - else if (!file->f_op || !file->f_op->fsync) - err = -EINVAL; - else if (file->f_op->fsync(inode,file)) - err = -EIO; + err = -EBADF; + + if (fd >= NR_OPEN) + goto out; + + file = current->files->fd[fd]; + if (!file) + goto out; + + dentry = file->f_dentry; + if (!dentry) + goto out; + + inode = dentry->d_inode; + if (!inode) + goto out; + + err = -EINVAL; + if (!file->f_op || !file->f_op->fsync) + goto out; + + err = file->f_op->fsync(inode,file); + +out: unlock_kernel(); return err; } @@ -306,20 +325,35 @@ asmlinkage int sys_fsync(unsigned int fd) asmlinkage int sys_fdatasync(unsigned int fd) { struct file * file; + struct dentry * dentry; struct inode * inode; - int err = -EBADF; + int err; lock_kernel(); - if (fd>=NR_OPEN || !(file=current->files->fd[fd]) || !(inode=file->f_inode)) + err = -EBADF; + + if (fd >= NR_OPEN) + goto out; + + file = current->files->fd[fd]; + if (!file) + goto out; + + dentry = file->f_dentry; + if (!dentry) goto out; + + inode = dentry->d_inode; + if (!inode) + goto out; + err = -EINVAL; if (!file->f_op || !file->f_op->fsync) goto out; + /* this needs further work, at the moment it is identical to fsync() */ - if (file->f_op->fsync(inode,file)) - err = -EIO; - else - err = 0; + err = file->f_op->fsync(inode,file); + out: unlock_kernel(); return err; @@ -495,17 +529,18 @@ static inline void insert_into_queues(struct buffer_head * bh) static inline struct buffer_head * find_buffer(kdev_t dev, int block, int size) { - struct buffer_head * tmp; - - for (tmp = hash(dev,block) ; tmp != NULL ; tmp = tmp->b_next) - if (tmp->b_blocknr == block && tmp->b_dev == dev) { - if (tmp->b_size == size) - return tmp; + struct buffer_head * next; - printk("VFS: Wrong blocksize on device %s\n", - kdevname(dev)); - return NULL; - } + next = hash(dev,block); + for (;;) { + struct buffer_head *tmp = next; + if (!next) + break; + next = tmp->b_next; + if (tmp->b_blocknr != block || tmp->b_size != size || tmp->b_dev != dev) + continue; + return tmp; + } return NULL; } @@ -518,10 +553,11 @@ static inline struct buffer_head * find_buffer(kdev_t dev, int block, int size) */ struct buffer_head * get_hash_table(kdev_t dev, int block, int size) { - struct buffer_head * bh; - for (;;) { - if (!(bh=find_buffer(dev,block,size))) + struct buffer_head * bh; + + bh=find_buffer(dev,block,size); + if (!bh) return NULL; bh->b_count++; wait_on_buffer(bh); @@ -1610,6 +1646,7 @@ asmlinkage int sync_old_buffers(void) next->b_count--; } } + run_task_queue(&tq_disk); #ifdef DEBUG if (ncount) printk("sync_old_buffers: %d dirty buffers not on dirty list\n", ncount); printk("Wrote %d/%d buffers\n", nwritten, ndirty); diff --git a/fs/dcache.c b/fs/dcache.c index 0472487e0..395aed829 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -5,1035 +5,341 @@ * (C) 1997 Thomas Schoebel-Theuer */ -/* The new dcache is exclusively called from the VFS, not from - * the specific fs'es any more. Despite having the same name as in the - * old code, it has less to do with it. - * - * It serves many purposes: - * - * 1) Any inode that has been retrieved with lookup() and is in use - * (i_count>0), has access to its full absolute path name, by going - * to inode->i_dentry and then recursively following the entry->d_parent - * chain. Use d_path() as predefined method for that. - * You may find out the corresponding inode belonging to - * a dentry by calling d_inode(). This can be used as an easy way for - * determining .. and its absolute pathname, an old UNIX problem that - * deserved a solution for a long time. - * Note that hardlinked inodes may have multiple dentries assigned to - * (via the d_next chain), reflecting multiple alias pathnames. - * - * 2) If not disabled by filesystem types specifying FS_NO_DCACHE, - * the dentries of unused (aged) inodes are retained for speeding up - * lookup()s, by allowing hashed inquiry starting from the dentry of - * the parent directory. - * - * 3) It can remeber so-called "negative entries", that is dentries for - * pathnames that are known to *not* exist, so unneccessary repeated - * lookup()s for non-existant names can be saved. - * - * 4) It provides a means for keeping deleted files (inode->i_nlink==0) - * accessible in the so-called *basket*. Inodes in the basket have been - * removed with unlink() while being in use (i_count>0), so they would - * normally use up space on the disk and be accessile through their - * filedescriptor, but would not be accessible for lookup() any more. - * The basket simply keeps such files in the dcache (for potential - * dcache lookup) until they are either eventually removed completely, - * or transferred to the second-level basket, the so-called *ibasket*. - * The ibasket is implemented in the new inode code, on request of - * filesystem types that have the flag FS_IBASKET set, and proliferates - * the unlinked files when i_count has gone to zero, at least as long - * as there is space on the disk and enough inodes remain available - * and no umount() has started. - * - * 5) Preliminary dentries can be added by readdir(). While normal dentries - * directly point to the inode via u.d_inode only the inode number is - * known from readdir(), but not more. They can be converted to - * normal dentries by using d_inode(). - */ - /* * Notes on the allocation strategy: * - * The dcache is a full slave cache of the inodes. Whenever an inode - * is cleared, all the dentries associated with it will recursively - * disappear. dentries have no own reference counting; this has to - * be obeyed for SMP. - * If directories could go out of inode cache while - * successors are alive, this would interrupt the d_parent chain of - * the live successors. To prevent this without using zombies, all - * directories are thus prevented from __iput() as long as successors - * are alive. + * The dcache is a master of the icache - whenever a dcache entry + * exists, the inode will always exist. "iput()" is done either when + * the dcache entry is deleted or garbage collected. */ -#include <linux/config.h> #include <linux/string.h> #include <linux/mm.h> #include <linux/fs.h> -#include <linux/dalloc.h> -#include <linux/dlists.h> - -/* this should be removed after the beta phase */ -/* #define DEBUG */ -/*#undef DEBUG*/ -/* #define DEBUG_DDIR_COUNT */ - -#define D_HASHSIZE 64 - -/* local flags for d_flag */ -#define D_DIR 32 -#define D_HASHED 64 -#define D_ZOMBIE 128 -#define D_PRELIMINARY 256 -#define D_INC_DDIR 512 - -/* local flags for d_del() */ -#define D_RECURSIVE 4 -#define D_NO_FREE 8 - -/* adjust these constants if you know a probability distribution ... */ -#define D_SMALL 16 -#define D_MEDIUM 64 -#define D_LARGE 256 -#define D_HUGE D_MAXLEN +#include <linux/malloc.h> -#define BASE_DHEADER(x) (struct dheader*)((unsigned long)(x) & ~(PAGE_SIZE-1)) -#define BYTE_ADD(x,n) (void*)((char*)(x) + (n)) -#define BYTE_SUB(x,n) (void*)((char*)(x) - (n)) - -/* This is for global allocation of dentries. Remove this when - * converting to SLAB. - */ -struct dheader { - struct dentry * emptylist; - short free, maxfree; - struct dheader * next; - struct dheader * prev; -}; - -struct anchors { - struct dheader * free; /* each contains at least 1 empty dentry */ - struct dheader * full; /* all the used up ones */ - struct dheader * dir_free; - struct dheader * dir_full; -}; - -/* This is only used for directory dentries. Think of it as an extension - * of the dentry. - * It is defined as separate struct, so it uses up space only - * where necessary. +/* + * This is the single most critical data structure when it comes + * to the dcache: the hashtable for lookups. Somebody should try + * to make this good - I've just made it work. + * + * This hash-function tries to avoid losing too many bits of hash + * information, yet avoid using a prime hash-size or similar. */ -struct ddir { - struct dentry * dd_hashtable[D_HASHSIZE]; - struct dentry * dd_neglist; - struct dentry * dd_basketlist; - struct dentry * dd_zombielist; - unsigned short dd_alloced; /* # d_alloc()ed, but not yet d_add()ed */ - unsigned short dd_hashed; /* # of entries in hashtable */ - unsigned short dd_true_hashed; /* # non-preliminaries in hashtable */ - unsigned short dd_negs; /* # of negative entries */ -}; - -DEF_INSERT(header,struct dheader,next,prev) -DEF_REMOVE(header,struct dheader,next,prev) +#define D_HASHBITS 10 +#define D_HASHSIZE (1UL << D_HASHBITS) +#define D_HASHMASK (D_HASHSIZE-1) -DEF_INSERT(alias,struct dentry,d_next,d_prev) -DEF_REMOVE(alias,struct dentry,d_next,d_prev) +static struct list_head dentry_hashtable[D_HASHSIZE]; +static LIST_HEAD(dentry_unused); -DEF_INSERT(hash,struct dentry,d_hash_next,d_hash_prev) -DEF_REMOVE(hash,struct dentry,d_hash_next,d_hash_prev) - -DEF_INSERT(basket,struct dentry,d_basket_next,d_basket_prev) -DEF_REMOVE(basket,struct dentry,d_basket_next,d_basket_prev) - -static struct anchors anchors[4]; - -struct dentry * the_root = NULL; - -unsigned long name_cache_init(unsigned long mem_start, unsigned long mem_end) +void d_free(struct dentry *dentry) { - memset(anchors, 0, sizeof(anchors)); - return mem_start; + kfree(dentry->d_name.name); + kfree(dentry); } -#ifdef DEBUG -/* throw this away after the beta phase */ -/*************************************************************************/ -extern void xcheck(char * txt, struct inode * p); - -static int x_alloc = 0; -static int x_freed = 0; -static int x_free = 0; - -static void * tst[20000]; -static int cnt = 0; - -static void ins(void* ptr) -{ - extern int inodes_stat; - tst[cnt++] = ptr; - if(cnt % 1000 == 0) - printk("------%d allocated: %d: %d %d %d\n", inodes_stat, cnt, - x_alloc, x_freed, x_free); - if(cnt>=20000) panic("stop"); -} - -#if 0 -static inline int search(void* ptr) -{ - int i; - for(i = cnt-1; i>=0; i--) - if(tst[i] == ptr) - return i; - return -1; -} - -#define TST(n,x) if(search(x)<0) printk("%s bad ptr %p line %d\n", n, x, __LINE__) -#else -#define TST(n,x) /*nothing*/ -#endif - -void LOG(char * txt, struct dentry * entry) -{ - static int count = 0; - if(entry) { - TST(txt,entry); - } - if(count) { - count--; - printk("%s: entry=%p\n", txt, entry); - } -} - -#ifdef DEBUG_DDIR_COUNT -static struct ddir * d_dir(struct dentry * entry); -void recursive_test(struct dentry * entry) -{ - int i; - struct ddir * ddir = d_dir(entry); - int sons = 0; - - if(ddir->dd_zombielist) - sons++; - for(i=0; i < D_HASHSIZE; i++) { - struct dentry ** base = &ddir->dd_hashtable[i]; - struct dentry * tmp = *base; - if(tmp) do { - TST("__clear",tmp); - if(!(tmp->d_flag & D_HASHED)) { - printk("VFS: dcache entry not hashed!\n"); - printpath(*base); printk("\n"); - printpath(tmp); - } - if(!(tmp->d_flag & D_PRELIMINARY)) - sons++; - if(tmp->d_flag & D_DIR) - recursive_test(tmp); - tmp = tmp->d_hash_next; - } while(tmp && tmp != *base); - } - if(!sons && !(entry->d_flag & D_PRELIMINARY) && entry->u.d_inode) { - struct inode * inode = entry->u.d_inode; - if(!atomic_read(&inode->i_count)) { - if(!(inode->i_status & 1/*ST_AGED*/)) { - printpath(entry); - printk(" is not aged!\n"); - } - if(inode->i_ddir_count) { - printpath(entry); - printk(" has ddir_count blockage!\n"); +/* + * dput() + * + * This is complicated by the fact that we do not want to put + * dentries that are no longer on any hash chain on the unused + * list: we'd much rather just get rid of them immediately. + * + * However, that implies that we have to traverse the dentry + * tree upwards to the parents which might _also_ now be + * scheduled for deletion (it may have been only waiting for + * its last child to go away). + * + * This tail recursion is done by hand as we don't want to depend + * on the compiler to always get this right (gcc generally doesn't). + * Real recursion would eat up our stack space. + */ +void dput(struct dentry *dentry) +{ + if (dentry) { + int count; +repeat: + count = dentry->d_count-1; + if (count < 0) { + printk("Negative d_count (%d) for %s/%s\n", + count, + dentry->d_parent->d_name.name, + dentry->d_name.name); + *(int *)0 = 0; + } + dentry->d_count = count; + if (!count) { + list_del(&dentry->d_lru); + if (list_empty(&dentry->d_hash)) { + struct inode *inode = dentry->d_inode; + struct dentry * parent; + if (inode) { + list_del(&dentry->d_alias); + iput(inode); + } + parent = dentry->d_parent; + d_free(dentry); + if (dentry == parent) + return; + dentry = parent; + goto repeat; } + list_add(&dentry->d_lru, &dentry_unused); } } } -#else -#define recursive_test(e) /*nothing*/ -#endif -#else -#define TST(n,x) /*nothing*/ -#define LOG(n,x) /*nothing*/ -#define xcheck(t,i) /*nothing*/ -#define recursive_test(e) /*nothing*/ -/*****************************************************************************/ -#endif -void printpath(struct dentry * entry) -{ - if(!IS_ROOT(entry)) - printpath(entry->d_parent); - printk("/%s", entry->d_name); -} - -static inline long has_sons(struct ddir * ddir) -{ - return ((ddir->dd_alloced | ddir->dd_hashed) || - ddir->dd_neglist || - ddir->dd_basketlist || - ddir->dd_zombielist); -} - -static inline int has_true_sons(struct ddir * ddir) -{ - return (ddir->dd_alloced | ddir->dd_true_hashed); -} - -/* Only hold the i_ddir_count pseudo refcount when neccessary (i.e. when - * they have true_sons), to prevent keeping too much dir inodes in use. +/* + * Shrink the dcache. This is done when we need + * more memory, or simply when we need to unmount + * something (at which point we need to unuse + * all dentries). */ -static inline void inc_ddir(struct dentry * entry, struct inode * inode) -{ - if(!(entry->d_flag & D_INC_DDIR)) { - entry->d_flag |= D_INC_DDIR; -#ifdef DEBUG - if(inode->i_ddir_count) { - printpath(entry); - printk(" ddir_count=%d\n", inode->i_ddir_count); +void shrink_dcache(void) +{ + for (;;) { + struct dentry *dentry; + struct list_head *tmp = dentry_unused.prev; + + if (tmp == &dentry_unused) + break; + list_del(tmp); + INIT_LIST_HEAD(tmp); + dentry = list_entry(tmp, struct dentry, d_lru); + if (!dentry->d_count) { + struct dentry * parent; + + list_del(&dentry->d_hash); + if (dentry->d_inode) { + struct inode * inode = dentry->d_inode; + + list_del(&dentry->d_alias); + dentry->d_inode = NULL; + iput(inode); + } + parent = dentry->d_parent; + d_free(dentry); + dput(parent); } -#endif - inode->i_ddir_count++; - _get_inode(inode); - } -} - -static inline blocking void dec_ddir(struct dentry * entry, struct inode * inode) -{ - if(entry->d_flag & D_INC_DDIR) { - entry->d_flag &= ~D_INC_DDIR; - inode->i_ddir_count--; - if(!inode->i_ddir_count) - __iput(inode); } } -/* Do not inline this many times. */ -static void d_panic(void) -{ - panic("VFS: dcache directory corruption"); -} +#define NAME_ALLOC_LEN(len) ((len+16) & ~15) -static inline struct ddir * d_dir(struct dentry * entry) +struct dentry * d_alloc(struct dentry * parent, const struct qstr *name) { - struct ddir * res = BYTE_SUB(entry, sizeof(struct ddir)); - - if(!(entry->d_flag & D_DIR)) - d_panic(); -#ifdef DEBUG - if(!entry) - panic("entry NULL!"); - if(BASE_DHEADER(res) != BASE_DHEADER(entry)) - printk("Scheisse!!!\n"); -#endif - return res; -} + char * str; + struct dentry *dentry; -static /*inline*/ struct dheader * dinit(int isdir, int size) -{ - struct dheader * res = (struct dheader*)__get_free_page(GFP_KERNEL); - int restlen = PAGE_SIZE - sizeof(struct dheader); - struct dentry * ptr = BYTE_ADD(res, sizeof(struct dheader)); + dentry = kmalloc(sizeof(struct dentry), GFP_KERNEL); + if (!dentry) + return NULL; - if(!res) + str = kmalloc(NAME_ALLOC_LEN(name->len), GFP_KERNEL); + if (!str) { + kfree(dentry); return NULL; - memset(res, 0, sizeof(struct dheader)); - if(isdir) { - ptr = BYTE_ADD(ptr, sizeof(struct ddir)); - size += sizeof(struct ddir); } - if(BASE_DHEADER(ptr) != res) - panic("Bad kernel page alignment"); - size += sizeof(struct dentry) - D_MAXLEN; - res->emptylist = NULL; - res->free = 0; - while(restlen >= size) { -#ifdef DEBUG - ins(ptr); - if(BASE_DHEADER(ptr) != res) - panic("Wrong dinit!"); -#endif - ptr->d_next = res->emptylist; - res->emptylist = ptr; - ptr = BYTE_ADD(ptr, size); - res->free++; - restlen -= size; - } - res->maxfree = res->free; - return res; -} -static /*inline*/ struct dentry * __dalloc(struct anchors * anchor, - struct dentry * parent, int isdir, - int len, int size) -{ - struct dheader ** free = isdir ? &anchor->dir_free : &anchor->free; - struct dheader ** full = isdir ? &anchor->dir_full : &anchor->full; - struct dheader * base = *free; - struct dentry * res; + memcpy(str, name->name, name->len); + str[name->len] = 0; - if(!base) { - base = dinit(isdir, size); - if(!base) - return NULL; - insert_header(free, base); - } - base->free--; - res = base->emptylist; - if(!(base->emptylist = res->d_next)) { - remove_header(free, base); - insert_header(full, base); - } - memset(res, 0, sizeof(struct dentry) - D_MAXLEN); - if(isdir) { - res->d_flag = D_DIR; - memset(d_dir(res), 0, sizeof(struct ddir)); - } - res->d_len = len; - res->d_parent = parent; - if(parent) { - struct ddir * pdir = d_dir(parent); -#ifdef DEBUG - if(pdir->dd_alloced > 1 && !IS_ROOT(parent)) { - printpath(parent); - printk(" dd_alloced=%d\n", pdir->dd_alloced); - } -#endif - pdir->dd_alloced++; - } -#ifdef DEBUG - x_alloc++; -#endif - return res; + dentry->d_count = 0; + dentry->d_flags = 0; + dentry->d_inode = NULL; + dentry->d_parent = parent; + dentry->d_mounts = dentry; + dentry->d_covers = dentry; + INIT_LIST_HEAD(&dentry->d_hash); + INIT_LIST_HEAD(&dentry->d_alias); + INIT_LIST_HEAD(&dentry->d_lru); + + dentry->d_name.name = str; + dentry->d_name.len = name->len; + dentry->d_name.hash = name->hash; + dentry->d_revalidate = NULL; + return dentry; } -struct dentry * d_alloc(struct dentry * parent, int len, int isdir) +/* + * Fill in inode information in the entry. + * + * This turns negative dentries into productive full members + * of society. + * + * NOTE! This assumes that the inode count has been incremented + * (or otherwise set) by the caller to indicate that it is now + * in use by the dcache.. + */ +void d_instantiate(struct dentry *entry, struct inode * inode) { - int i, size; + if (inode) + list_add(&entry->d_alias, &inode->i_dentry); -#ifdef DEBUG - if(the_root) - recursive_test(the_root); - LOG("d_alloc", parent); -#endif - if(len >= D_MEDIUM) { - if(len >= D_LARGE) { - i = 3; - size = D_HUGE; - } else { - i = 2; - size = D_LARGE; - } - } else if(len >= D_SMALL) { - i = 1; - size = D_MEDIUM; - } else { - i = 0; - size = D_SMALL; - } - return __dalloc(&anchors[i], parent, isdir, len, size); + entry->d_inode = inode; } -extern blocking struct dentry * d_alloc_root(struct inode * root_inode) +struct dentry * d_alloc_root(struct inode * root_inode, struct dentry *old_root) { - struct dentry * res = the_root; - - if(res) { - d_del(res, D_NO_CLEAR_INODE); /* invalidate everything beyond */ - } else { - struct ddir * ddir; + struct dentry *res = NULL; - the_root = res = d_alloc(NULL, 0, 1); - LOG("d_alloc_root", res); + if (root_inode) { + res = d_alloc(NULL, &(const struct qstr) { "/", 1, 0 }); res->d_parent = res; - res->d_name[0]='\0'; - ddir = d_dir(res); - ddir->dd_alloced = 999; /* protect from deletion */ + res->d_count = 1; + d_instantiate(res, root_inode); } - insert_alias(&root_inode->i_dentry, res); - root_inode->i_dent_count++; - root_inode->i_ddir_count++; - res->u.d_inode = root_inode; return res; } -static inline unsigned long d_hash(char first, char last) +static inline struct list_head * d_hash(struct dentry * parent, unsigned long hash) { - return ((unsigned long)first ^ ((unsigned long)last << 4)) & (D_HASHSIZE-1); + hash += (unsigned long) parent; + hash = hash ^ (hash >> D_HASHBITS) ^ (hash >> D_HASHBITS*2); + return dentry_hashtable + (hash & D_HASHMASK); } -static inline struct dentry ** d_base_entry(struct ddir * pdir, struct dentry * entry) +static inline struct dentry * __dlookup(struct list_head *head, struct dentry * parent, struct qstr * name) { - return &pdir->dd_hashtable[d_hash(entry->d_name[0], - entry->d_name[entry->d_len-1])]; -} + struct list_head *tmp = head->next; + int len = name->len; + int hash = name->hash; + const unsigned char *str = name->name; -static inline struct dentry ** d_base_qstr(struct ddir * pdir, - struct qstr * s1, - struct qstr * s2) -{ - unsigned long hash; + while (tmp != head) { + struct dentry * dentry = list_entry(tmp, struct dentry, d_hash); - if(s2 && s2->len) { - hash = d_hash(s1->name[0], s2->name[s2->len-1]); - } else { - hash = d_hash(s1->name[0], s1->name[s1->len-1]); + tmp = tmp->next; + if (dentry->d_name.hash != hash) + continue; + if (dentry->d_name.len != len) + continue; + if (dentry->d_parent != parent) + continue; + if (memcmp(dentry->d_name.name, str, len)) + continue; + return dentry; } - return &pdir->dd_hashtable[hash]; -} - - -static /*inline*/ blocking void _d_remove_from_parent(struct dentry * entry, - struct ddir * pdir, - struct inode * inode, - int flags) -{ - if(entry->d_flag & D_HASHED) { - struct dentry ** base = d_base_entry(pdir, entry); - - remove_hash(base, entry); - entry->d_flag &= ~D_HASHED; - pdir->dd_hashed--; - if(!(entry->d_flag & D_PRELIMINARY)) { - pdir->dd_true_hashed--; - if(!inode) { -#ifdef DEBUG - if(!entry->d_next || !entry->d_prev) { - printpath(entry); - printk(" flags=%x d_flag=%x negs=%d " - "hashed=%d\n", flags, entry->d_flag, - pdir->dd_negs, pdir->dd_hashed); - } -#endif - remove_alias(&pdir->dd_neglist, entry); - pdir->dd_negs--; - } - } - } else if(!(entry->d_flag & D_ZOMBIE)) { -#ifdef DEBUG - if(!pdir->dd_alloced) printk("dd_alloced is 0!\n"); -#endif - pdir->dd_alloced--; - } - if(entry->d_flag & D_BASKET) { - remove_basket(&pdir->dd_basketlist, entry); - entry->d_flag &= ~D_BASKET; - } -} - -/* Theoretically, zombies should never or extremely seldom appear, - * so this code is nearly superfluous. - * A way to get zombies is while using inodes (i_count>0), unlink() - * them as well as rmdir() the parent dir => the parent dir becomes a zombie. - * Zombies are *not* in the hashtable, because somebody could re-creat() - * that filename in it's parent dir again. - * Besides coding errors during beta phase, when forcing an umount() - * (e.g. at shutdown time), inodes could be in use such that the parent - * dir is cleared, resulting also in zombies. - */ -static /*inline*/ void _d_handle_zombie(struct dentry * entry, - struct ddir * ddir, - struct ddir * pdir) -{ - if(entry->d_flag & D_DIR) { - if(entry->d_flag & D_ZOMBIE) { - if(!has_sons(ddir)) { - entry->d_flag &= ~D_ZOMBIE; - remove_hash(&pdir->dd_zombielist, entry); - if(!pdir->dd_zombielist && - (entry->d_parent->d_flag & D_ZOMBIE)) { - d_del(entry->d_parent, D_NORMAL); - } - } - } else if(has_sons(ddir)) { - entry->d_flag |= D_ZOMBIE; - insert_hash(&pdir->dd_zombielist, entry); - - /* This condition is no longer a bug, with the removal - * of recursive_clear() this happens naturally during - * an unmount attempt of a filesystem which is busy. - */ -#if 0 - /* Not sure when this message should show up... */ - if(!IS_ROOT(entry)) { - printk("VFS: clearing dcache directory " - "with successors\n"); -#ifdef DEBUG - printpath(entry); - printk(" d_flag=%x alloced=%d negs=%d hashed=%d " - "basket=%p zombies=%p\n", - entry->d_flag, ddir->dd_alloced, - ddir->dd_negs, ddir->dd_hashed, - ddir->dd_basketlist, ddir->dd_zombielist); -#endif - } -#endif - } - } -} - -static /*inline*/ blocking void _d_del(struct dentry * entry, - struct anchors * anchor, - int flags) -{ - struct dheader ** free; - struct dheader ** full; - struct dheader * base = BASE_DHEADER(entry); - struct ddir * ddir = NULL; - struct ddir * pdir; - struct inode * inode = entry->d_flag & D_PRELIMINARY ? NULL : entry->u.d_inode; - -#ifdef DEBUG - if(inode) - xcheck("_d_del", inode); -#endif - if(!entry->d_parent) { - printk("VFS: dcache parent is NULL\n"); - return; - } - if(entry->d_flag & D_DIR) { - free = &anchor->dir_free; - full = &anchor->dir_full; - } else { - free = &anchor->free; - full = &anchor->full; - } - pdir = d_dir(entry->d_parent); - if(!IS_ROOT(entry)) - _d_remove_from_parent(entry, pdir, inode, flags); - - /* This may block, be careful! _d_remove_from_parent() is - * thus called before. - */ - if(entry->d_flag & D_DIR) - ddir = d_dir(entry); - if(IS_ROOT(entry)) - return; - - if(flags & D_NO_FREE) { - /* Make it re-d_add()able */ - pdir->dd_alloced++; - entry->d_flag &= D_DIR; - } else - _d_handle_zombie(entry, ddir, pdir); - - /* This dec_ddir() must occur after zombie handling. */ - if(!has_true_sons(pdir)) - dec_ddir(entry->d_parent, entry->d_parent->u.d_inode); - - entry->u.d_inode = NULL; - if(inode) { - remove_alias(&inode->i_dentry, entry); - inode->i_dent_count--; - if (entry->d_flag & D_DIR) - dec_ddir(entry, inode); - - if(!(flags & D_NO_CLEAR_INODE) && - !(atomic_read(&inode->i_count) + - inode->i_ddir_count + - inode->i_dent_count)) { -#ifdef DEBUG - printk("#"); -#endif - /* This may block also. */ - _clear_inode(inode, 0, 0); - } - } - if(!(flags & D_NO_FREE) && !(entry->d_flag & D_ZOMBIE)) { - base->free++; - if(base->free == base->maxfree) { -#ifndef DEBUG - remove_header(free, base); - free_page((unsigned long)base); - goto done; -#endif - } - entry->d_next = base->emptylist; - base->emptylist = entry; - if(!entry->d_next) { - remove_header(full, base); - insert_header(free, base); - } -#ifdef DEBUG - x_freed++; -#endif - } -#ifndef DEBUG -done: -#else - x_free++; -#endif + return NULL; } -blocking void d_del(struct dentry * entry, int flags) +struct dentry * d_lookup(struct dentry * dir, struct qstr * name) { - int i; - - if(!entry) - return; - LOG("d_clear", entry); - if(entry->d_len >= D_MEDIUM) { - if(entry->d_len >= D_LARGE) { - i = 3; - } else { - i = 2; - } - } else if(entry->d_len >= D_SMALL) { - i = 1; - } else { - i = 0; - } - _d_del(entry, &anchors[i], flags); + return __dlookup(d_hash(dir, name->hash), dir, name); } -static inline struct dentry * __dlookup(struct dentry ** base, - struct qstr * name, - struct qstr * appendix) +static inline void d_insert_to_parent(struct dentry * entry, struct dentry * parent) { - struct dentry * tmp = *base; - - if(tmp && name->len) { - int totallen = name->len; - - if(appendix) - totallen += appendix->len; - do { - if(tmp->d_len == totallen && - !(tmp->d_flag & D_DUPLICATE) && - !strncmp(tmp->d_name, name->name, name->len) && - (!appendix || !strncmp(tmp->d_name+name->len, - appendix->name, appendix->len))) - return tmp; - tmp = tmp->d_hash_next; - } while(tmp != *base); - } - return NULL; + list_add(&entry->d_hash, d_hash(dget(parent), entry->d_name.hash)); } -struct dentry * d_lookup(struct inode * dir, - struct qstr * name, - struct qstr * appendix) +static inline void d_remove_from_parent(struct dentry * dentry, struct dentry * parent) { - if(dir->i_dentry) { - struct ddir * ddir = d_dir(dir->i_dentry); - struct dentry ** base = d_base_qstr(ddir, name, appendix); - - return __dlookup(base, name, appendix); - } - return NULL; + list_del(&dentry->d_hash); + dput(parent); } -static /*inline*/ blocking void _d_insert_to_parent(struct dentry * entry, - struct ddir * pdir, - struct inode * inode, - struct qstr * ininame, - int flags) -{ - struct dentry ** base; - struct dentry * parent = entry->d_parent; -#ifdef DEBUG - if(!pdir->dd_alloced) - printk("dd_alloced is 0!\n"); -#endif - base = d_base_qstr(pdir, ininame, NULL); - if(!(flags & (D_NOCHECKDUP|D_DUPLICATE)) && - __dlookup(base, ininame, NULL)) { - d_del(entry, D_NO_CLEAR_INODE); - return; - } - if(entry->d_flag & D_HASHED) { - printk("VFS: dcache entry is already hashed\n"); - return; - } - if(!(flags & D_PRELIMINARY)) - pdir->dd_true_hashed++; - pdir->dd_hashed++; - insert_hash(base, entry); - entry->d_flag |= D_HASHED; - pdir->dd_alloced--; - if(flags & D_BASKET) - insert_basket(&pdir->dd_basketlist, entry); - -#ifdef DEBUG - if(inode && inode->i_dentry && (entry->d_flag & D_DIR)) { - struct dentry * tmp = inode->i_dentry; - printk("Auweia inode=%p entry=%p (%p %p %s)\n", - inode, entry, parent->u.d_inode, parent, parent->d_name); - printk("entry path="); printpath(entry); printk("\n"); - do { - TST("auweia",tmp); - printk("alias path="); printpath(tmp); printk("\n"); - tmp = tmp->d_next; - } while(tmp != inode->i_dentry); - printk("\n"); - } -#endif - if(has_true_sons(pdir)) - inc_ddir(parent, parent->u.d_inode); - if(!inode && !(flags & D_PRELIMINARY)) { - insert_alias(&pdir->dd_neglist, entry); - pdir->dd_negs++; - - /* Don't allow the negative list to grow too much ... */ - while(pdir->dd_negs > (pdir->dd_true_hashed >> 1) + 5) - d_del(pdir->dd_neglist->d_prev, D_REMOVE); - } -} - -blocking void d_add(struct dentry * entry, struct inode * inode, - struct qstr * ininame, int flags) +/* + * When a file is deleted, we have two options: + * - turn this dentry into a negative dentry + * - unhash this dentry and free it. + * + * Usually, we want to just turn this into + * a negative dentry, but if anybody else is + * currently using the dentry or the inode + * we can't do that and we fall back on removing + * it from the hash queues and waiting for + * it to be deleted later when it has no users + */ +void d_delete(struct dentry * dentry) { - struct dentry * parent = entry->d_parent; - struct qstr dummy; - struct ddir * pdir; + /* + * Are we the only user? + */ + if (dentry->d_count == 1) { + struct inode * inode = dentry->d_inode; -#ifdef DEBUG - if(inode) - xcheck("d_add", inode); - if(IS_ROOT(entry)) { - printk("VFS: d_add for root dentry "); - printpath(entry); - printk(" -> "); - if(ininame) - printk("%s", ininame->name); - printk("\n"); + dentry->d_inode = NULL; + list_del(&dentry->d_alias); + iput(inode); return; } - if(!parent) - panic("d_add with parent==NULL"); - LOG("d_add", entry); -#endif - if(ininame) { - if(ininame->len != entry->d_len) { - printk("VFS: d_add with wrong string length"); - entry->d_len = ininame->len; /* kludge */ - } - memcpy(entry->d_name, ininame->name, ininame->len); - entry->d_name[ininame->len] = '\0'; - } else { - dummy.name = entry->d_name; - dummy.len = entry->d_len; - ininame = &dummy; - } - if(entry->d_flag & D_HASHED) - printk("VFS: d_add of already added dcache entry\n"); - pdir = d_dir(parent); - _d_insert_to_parent(entry, pdir, inode, ininame, flags); - entry->d_flag |= flags; - if(inode && !(flags & D_PRELIMINARY)) { - if(entry->d_flag & D_DIR) { - if(inode->i_dentry) { - printk("VFS: creating dcache directory alias\n"); - return; - } - } - insert_alias(&inode->i_dentry, entry); - inode->i_dent_count++; - } - entry->u.d_inode = inode; + /* + * If not, just drop the dentry and let dput + * pick up the tab.. + */ + d_drop(dentry); } -blocking struct dentry * d_entry(struct dentry * parent, - struct qstr * name, - struct inode * inode) +void d_add(struct dentry * entry, struct inode * inode) { - struct ddir * pdir = d_dir(parent); - struct dentry ** base = d_base_qstr(pdir, name, NULL); - struct dentry * found = __dlookup(base, name, NULL); - - if(!found) { - int isdir = (inode && S_ISDIR(inode->i_mode)); - - found = d_alloc(parent, name->len, isdir); - if(found) { - d_add(found, inode, name, - isdir ? (D_DIR|D_NOCHECKDUP) : D_NOCHECKDUP); - } else - printk("VFS: problem with d_alloc\n"); - } - return found; + d_insert_to_parent(entry, entry->d_parent); + d_instantiate(entry, inode); } -blocking void d_entry_preliminary(struct dentry * parent, - struct qstr * name, - unsigned long ino) +static inline void alloc_new_name(struct dentry * entry, struct qstr *newname) { - struct ddir * pdir = d_dir(parent); - struct dentry ** base = d_base_qstr(pdir, name, NULL); - struct dentry * found = __dlookup(base, name, NULL); + int len = newname->len; + int hash = newname->hash; + char *name = (char *) entry->d_name.name; - if(!found && ino) { - struct dentry * new = d_alloc(parent, name->len, 0); - - if(new) { - d_add(new, NULL, name, D_PRELIMINARY|D_NOCHECKDUP); - new->u.d_ino = ino; - } else - printk("VFS: problem with d_alloc\n"); + if (NAME_ALLOC_LEN(len) != NAME_ALLOC_LEN(entry->d_name.len)) { + name = kmalloc(NAME_ALLOC_LEN(len), GFP_KERNEL); + if (!name) + printk("out of memory for dcache\n"); + kfree(entry->d_name.name); + entry->d_name.name = name; } + memcpy(name, newname->name, len); + name[len] = 0; + entry->d_name.len = len; + entry->d_name.hash = hash; } -blocking void d_move(struct dentry * entry, struct inode * newdir, - struct qstr * newname, struct qstr * newapp) +void d_move(struct dentry * dentry, struct dentry * newdir, struct qstr * newname) { - struct ddir tmp; - struct dentry * new; - struct inode * inode; - int len; - int flags; - - if(!entry) + if (!dentry) return; - inode = entry->u.d_inode; - flags = entry->d_flag; - if((flags & D_PRELIMINARY) || !inode) { - if(!(flags & D_PRELIMINARY)) - printk("VFS: trying to move negative dcache entry\n"); - d_del(entry, D_NO_CLEAR_INODE); - return; - } -#if 0 -printk("d_move %p '%s' -> '%s%s' dent_count=%d\n", inode, entry->d_name, - newname->name, newapp ? newapp->name : "", inode->i_dent_count); -#endif - if(flags & D_ZOMBIE) { - printk("VFS: moving zombie entry\n"); - } - if(flags & D_DIR) { - struct ddir * ddir = d_dir(entry); - - memcpy(&tmp, ddir, sizeof(struct ddir)); - /* Simulate empty dir for d_del(). */ - memset(ddir, 0, sizeof(struct ddir)); - } - len = newname->len; - if(newapp) { - len += newapp->len; - flags |= D_BASKET; - } else - flags &= ~D_BASKET; - new = d_alloc(newdir->i_dentry, len, flags & D_DIR); - memcpy(new->d_name, newname->name, newname->len); - if(newapp) - memcpy(new->d_name+newname->len, newapp->name, newapp->len); - new->d_name[len] = '\0'; - d_del(entry, D_NO_CLEAR_INODE); - d_add(new, inode, NULL, flags & (D_DIR|D_BASKET)); - if(flags & D_DIR) { - struct ddir * ddir = d_dir(new); + if (!dentry->d_inode) + printk("VFS: moving negative dcache entry\n"); - memcpy(ddir, &tmp, sizeof(struct ddir)); - } + d_remove_from_parent(dentry, dentry->d_parent); + alloc_new_name(dentry, newname); + dentry->d_parent = newdir; + d_insert_to_parent(dentry, newdir); } -int d_path(struct dentry * entry, struct inode * chroot, char * buf) +int d_path(struct dentry * entry, struct dentry * chroot, char * buf) { - if(IS_ROOT(entry) || (chroot && entry->u.d_inode == chroot && - !(entry->d_flag & D_PRELIMINARY))) { + if (IS_ROOT(entry) || (chroot && entry == chroot)) { *buf = '/'; return 1; } else { int len = d_path(entry->d_parent, chroot, buf); buf += len; - if(len > 1) { + if (len > 1) { *buf++ = '/'; len++; } - memcpy(buf, entry->d_name, entry->d_len); - return len + entry->d_len; + memcpy(buf, entry->d_name.name, entry->d_name.len); + return len + entry->d_name.len; } } -struct dentry * d_basket(struct dentry * dir_entry) +void dcache_init(void) { - if(dir_entry && (dir_entry->d_flag & D_DIR)) { - struct ddir * ddir = d_dir(dir_entry); - - return ddir->dd_basketlist; - } else - return NULL; -} - -int d_isbasket(struct dentry * entry) -{ - return entry->d_flag & D_BASKET; -} - -blocking struct inode * d_inode(struct dentry ** changing_entry) -{ - struct dentry * entry = *changing_entry; - struct inode * inode; - -#ifdef CONFIG_DCACHE_PRELOAD - if(entry->d_flag & D_PRELIMINARY) { - struct qstr name = { entry->d_name, entry->d_len }; - struct ddir * pdir = d_dir(entry->d_parent); - struct dentry ** base = d_base_qstr(pdir, &name, NULL); - struct dentry * found; - unsigned long ino; - struct inode * dir = entry->d_parent->u.d_inode; - TST("d_inode",entry); - ino = entry->u.d_ino; - if(!dir) - d_panic(); - - /* Prevent concurrent d_lookup()s or d_inode()s before - * giving up vfs_lock. This just removes from the parent, - * but does not deallocate it. - */ - - /* !!!!!!! Aiee, here is an unresolved race if somebody - * unlink()s the inode during the iget(). The problem is - * that we need to synchronize externally. Proposed solution: - * put a rw_lock (read-mode) on the parent dir for each - * iget(), lookup() and so on, and a write-mode lock for - * everything that changes the dir (e.g. unlink()), and do - * this consistently everywhere in the generic VFS (not in - * the concrete filesystems). This should kill similar - * races everywhere, with a single clean concept. - * Later, the synchronization stuff can be cleaned out - * of the concrete fs'es. - */ - d_del(entry, D_NO_CLEAR_INODE|D_NO_FREE); - vfs_unlock(); - - /* This circumvents the normal lookup() of pathnames. - * Therefore, preliminary entries must not be used - * (see FS_NO_DCACHE and FS_NO_PRELIM) if the fs does not - * permit fetching *valid* inodes with plain iget(). - */ - inode = __iget(dir->i_sb, ino, 0); - vfs_lock(); - if(!inode) { - printk("VFS: preliminary dcache entry was invalid\n"); - *changing_entry = NULL; - return NULL; - } - xcheck("d_inode iget()", inode); - if((found = __dlookup(base, &name, NULL))) { - d_del(entry, D_NO_CLEAR_INODE); - *changing_entry = found; - } else if(S_ISDIR(inode->i_mode)) { - struct dentry * new = d_alloc(entry->d_parent, entry->d_len, 1); - if(new) - d_add(new, inode, &name, D_DIR); - *changing_entry = new; - - /* Finally deallocate old entry. */ - d_del(entry, D_NO_CLEAR_INODE); - } else { - /* Re-insert to the parent, but now as normal dentry. */ - d_add(entry, inode, NULL, 0); - } - return inode; - } -#endif - inode = entry->u.d_inode; - if(inode) { -#ifdef DEBUG - xcheck("d_inode", inode); -#endif - iinc_zero(inode); - } - return inode; + int i; + struct list_head *d = dentry_hashtable; + + i = D_HASHSIZE; + do { + INIT_LIST_HEAD(d); + d++; + i--; + } while (i); } diff --git a/fs/devices.c b/fs/devices.c index d3b1d6846..26f668e7f 100644 --- a/fs/devices.c +++ b/fs/devices.c @@ -209,9 +209,7 @@ int check_disk_change(kdev_t dev) printk(KERN_DEBUG "VFS: Disk change detected on device %s\n", kdevname(dev)); - for (i=0 ; i<NR_SUPER ; i++) - if (super_blocks[i].s_dev == dev) - put_super(super_blocks[i].s_dev); + invalidate_inodes(dev); invalidate_buffers(dev); diff --git a/fs/dquot.c b/fs/dquot.c index 59d2112d9..b19c939be 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -13,7 +13,7 @@ * diskquota system. This implementation is not based on any BSD * kernel sourcecode. * - * Version: $Id: dquot.c,v 1.11 1997/01/06 06:53:02 davem Exp $ + * Version: $Id: dquot.c,v 1.2 1997/06/17 13:25:58 ralf Exp $ * * Author: Marco van Wieringen <mvw@mcs.ow.nl> <mvw@tnix.net> * @@ -227,7 +227,7 @@ static void write_dquot(struct dquot *dquot) lock_dquot(dquot); down(&dquot->dq_mnt->mnt_sem); if (filp->f_op->llseek) { - if (filp->f_op->llseek(filp->f_inode, filp, + if (filp->f_op->llseek(filp->f_dentry->d_inode, filp, dqoff(dquot->dq_id), 0) != dqoff(dquot->dq_id)) { up(&dquot->dq_mnt->mnt_sem); unlock_dquot(dquot); @@ -238,10 +238,9 @@ static void write_dquot(struct dquot *dquot) fs = get_fs(); set_fs(KERNEL_DS); - if (filp->f_op->write(filp->f_inode, filp, + if (filp->f_op->write(filp->f_dentry->d_inode, filp, (char *)&dquot->dq_dqb, sizeof(struct dqblk)) == sizeof(struct dqblk)) dquot->dq_flags &= ~DQ_MOD; - /* inode->i_status |= ST_MODIFIED is willingly *not* done here */ up(&dquot->dq_mnt->mnt_sem); set_fs(fs); @@ -260,7 +259,7 @@ static void read_dquot(struct dquot *dquot) lock_dquot(dquot); down(&dquot->dq_mnt->mnt_sem); if (filp->f_op->llseek) { - if (filp->f_op->llseek(filp->f_inode, filp, + if (filp->f_op->llseek(filp->f_dentry->d_inode, filp, dqoff(dquot->dq_id), 0) != dqoff(dquot->dq_id)) { up(&dquot->dq_mnt->mnt_sem); unlock_dquot(dquot); @@ -270,7 +269,7 @@ static void read_dquot(struct dquot *dquot) filp->f_pos = dqoff(dquot->dq_id); fs = get_fs(); set_fs(KERNEL_DS); - filp->f_op->read(filp->f_inode, filp, (char *)&dquot->dq_dqb, sizeof(struct dqblk)); + filp->f_op->read(filp->f_dentry->d_inode, filp, (char *)&dquot->dq_dqb, sizeof(struct dqblk)); up(&dquot->dq_mnt->mnt_sem); set_fs(fs); if (dquot->dq_bhardlimit == 0 && dquot->dq_bsoftlimit == 0 && @@ -947,39 +946,53 @@ int quota_off(kdev_t dev, short type) int quota_on(kdev_t dev, short type, char *path) { - struct file *filp = (struct file *)NULL; + struct file *filp = NULL; + struct dentry *dentry; struct vfsmount *vfsmnt; struct inode *inode; struct dquot *dquot; char *tmp; int error; - if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL) - return(-ENODEV); - if (vfsmnt->mnt_quotas[type] != (struct file *)NULL) - return(-EBUSY); - if ((error = getname(path, &tmp)) != 0) - return(error); - error = open_namei(tmp, O_RDWR, 0600, &inode, 0); + vfsmnt = lookup_vfsmnt(dev); + if (vfsmnt == NULL) + return -ENODEV; + + if (vfsmnt->mnt_quotas[type] != NULL) + return -EBUSY; + + tmp = getname(path); + error = PTR_ERR(tmp); + if (IS_ERR(tmp)) + return error; + + dentry = open_namei(tmp, O_RDWR, 0600); putname(tmp); - if (error) - return(error); + + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) + return error; + inode = dentry->d_inode; + if (!S_ISREG(inode->i_mode)) { - iput(inode); - return(-EACCES); + dput(dentry); + return -EACCES; } - if ((filp = get_empty_filp()) != (struct file *)NULL) { + + filp = get_empty_filp(); + if (filp != NULL) { filp->f_mode = (O_RDWR + 1) & O_ACCMODE; filp->f_flags = O_RDWR; - filp->f_inode = inode; + filp->f_dentry = dentry; filp->f_pos = 0; filp->f_reada = 0; filp->f_op = inode->i_op->default_file_ops; if (filp->f_op->read || filp->f_op->write) { - if ((error = get_write_access(inode)) == 0) { + error = get_write_access(inode); + if (!error) { if (filp->f_op && filp->f_op->open) error = filp->f_op->open(inode, filp); - if (error == 0) { + if (!error) { vfsmnt->mnt_quotas[type] = filp; dquot = dqget(dev, 0, type); vfsmnt->mnt_iexp[type] = (dquot) ? dquot->dq_itime : MAX_IQ_TIME; @@ -987,7 +1000,7 @@ int quota_on(kdev_t dev, short type, char *path) dqput(dquot); vfsmnt->mnt_sb->dq_op = &dquot_operations; add_dquot_ref(dev, type); - return(0); + return 0; } put_write_access(inode); } @@ -996,8 +1009,8 @@ int quota_on(kdev_t dev, short type, char *path) put_filp(filp); } else error = -EMFILE; - iput(inode); - return(error); + dput(dentry); + return error; } /* @@ -1009,7 +1022,6 @@ int quota_on(kdev_t dev, short type, char *path) asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr) { int cmds = 0, type = 0, flags = 0; - struct inode *ino; kdev_t dev; int ret = -EINVAL; @@ -1035,19 +1047,22 @@ asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr) } ret = -EINVAL; - if (special == (char *)NULL && (cmds == Q_SYNC || cmds == Q_GETSTATS)) - dev = 0; - else { - int error = namei(NAM_FOLLOW_LINK, special, &ino); - if(error) + dev = 0; + if (special != NULL || (cmds != Q_SYNC && cmds != Q_GETSTATS)) { + mode_t mode; + struct dentry * dentry; + + dentry = namei(special); + if (IS_ERR(dentry)) goto out; - dev = ino->i_rdev; + + dev = dentry->d_inode->i_rdev; + mode = dentry->d_inode->i_mode; + dput(dentry); + ret = -ENOTBLK; - if (!S_ISBLK(ino->i_mode)) { - iput(ino); + if (!S_ISBLK(mode)) goto out; - } - iput(ino); } ret = -EINVAL; @@ -88,6 +88,10 @@ __initfunc(void binfmt_setup(void)) init_aout_binfmt(); #endif +#ifdef CONFIG_BINFMT_AOUT32 + init_aout32_binfmt(); +#endif + #ifdef CONFIG_BINFMT_JAVA init_java_binfmt(); #endif @@ -134,22 +138,24 @@ int unregister_binfmt(struct linux_binfmt * fmt) } #endif /* CONFIG_MODULES */ -int open_inode(struct inode * inode, int mode) +int open_dentry(struct dentry * dentry, int mode) { int fd; + struct inode * inode = dentry->d_inode; if (!inode->i_op || !inode->i_op->default_file_ops) return -EINVAL; fd = get_unused_fd(); if (fd >= 0) { struct file * f = get_empty_filp(); + if (!f) { put_unused_fd(fd); return -ENFILE; } f->f_flags = mode; f->f_mode = (mode+1) & O_ACCMODE; - f->f_inode = inode; + f->f_dentry = dentry; f->f_pos = 0; f->f_reada = 0; f->f_op = inode->i_op->default_file_ops; @@ -162,7 +168,7 @@ int open_inode(struct inode * inode, int mode) } } current->files->fd[fd] = f; - atomic_inc(&inode->i_count); + dget(dentry); } return fd; } @@ -186,7 +192,7 @@ asmlinkage int sys_uselib(const char * library) goto out; file = current->files->fd[fd]; retval = -ENOEXEC; - if (file && file->f_inode && file->f_op && file->f_op->read) { + if (file && file->f_dentry && file->f_op && file->f_op->read) { for (fmt = formats ; fmt ; fmt = fmt->next) { int (*fn)(int) = fmt->load_shlib; if (!fn) @@ -320,7 +326,7 @@ unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm) mpnt->vm_flags = VM_STACK_FLAGS; mpnt->vm_ops = NULL; mpnt->vm_offset = 0; - mpnt->vm_inode = NULL; + mpnt->vm_dentry = NULL; mpnt->vm_pte = 0; insert_vm_struct(current->mm, mpnt); current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT; @@ -341,25 +347,18 @@ unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm) * that aren't on a block boundary, and for files on filesystems * without bmap support. */ -int read_exec(struct inode *inode, unsigned long offset, +int read_exec(struct dentry *dentry, unsigned long offset, char * addr, unsigned long count, int to_kmem) { struct file file; + struct inode * inode = dentry->d_inode; int result = -ENOEXEC; if (!inode->i_op || !inode->i_op->default_file_ops) goto end_readexec; - file.f_mode = 1; - file.f_flags = 0; - file.f_count = 1; - file.f_inode = inode; - file.f_pos = 0; - file.f_reada = 0; - file.f_op = inode->i_op->default_file_ops; - if (file.f_op->open) - if (file.f_op->open(inode,&file)) - goto end_readexec; - if (!file.f_op || !file.f_op->read) + if (init_private_file(&file, dentry, 1)) + goto end_readexec; + if (!file.f_op->read) goto close_readexec; if (file.f_op->llseek) { if (file.f_op->llseek(inode,&file,offset,0) != offset) @@ -481,7 +480,7 @@ void flush_old_exec(struct linux_binprm * bprm) flush_thread(); if (bprm->e_uid != current->euid || bprm->e_gid != current->egid || - permission(bprm->inode,MAY_READ)) + permission(bprm->dentry->d_inode,MAY_READ)) current->dumpable = 0; flush_old_signals(current->sig); @@ -496,20 +495,21 @@ int prepare_binprm(struct linux_binprm *bprm) { int mode; int retval,id_change; + struct inode * inode = bprm->dentry->d_inode; - mode = bprm->inode->i_mode; + mode = inode->i_mode; if (!S_ISREG(mode)) /* must be regular file */ return -EACCES; if (!(mode & 0111)) /* with at least _one_ execute bit set */ return -EACCES; - if (IS_NOEXEC(bprm->inode)) /* FS mustn't be mounted noexec */ + if (IS_NOEXEC(inode)) /* FS mustn't be mounted noexec */ return -EACCES; - if (!bprm->inode->i_sb) + if (!inode->i_sb) return -EACCES; - if ((retval = permission(bprm->inode, MAY_EXEC)) != 0) + if ((retval = permission(inode, MAY_EXEC)) != 0) return retval; /* better not execute files which are being written to */ - if (bprm->inode->i_writecount > 0) + if (inode->i_writecount > 0) return -ETXTBSY; bprm->e_uid = current->euid; @@ -518,7 +518,7 @@ int prepare_binprm(struct linux_binprm *bprm) /* Set-uid? */ if (mode & S_ISUID) { - bprm->e_uid = bprm->inode->i_uid; + bprm->e_uid = inode->i_uid; if (bprm->e_uid != current->euid) id_change = 1; } @@ -530,7 +530,7 @@ int prepare_binprm(struct linux_binprm *bprm) * executable. */ if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { - bprm->e_gid = bprm->inode->i_gid; + bprm->e_gid = inode->i_gid; if (!in_group_p(bprm->e_gid)) id_change = 1; } @@ -539,7 +539,7 @@ int prepare_binprm(struct linux_binprm *bprm) /* We can't suid-execute if we're sharing parts of the executable */ /* or if we're being traced (or if suid execs are not allowed) */ /* (current->mm->count > 1 is ok, as we'll get a new mm anyway) */ - if (IS_NOSUID(bprm->inode) + if (IS_NOSUID(inode) || (current->flags & PF_PTRACED) || (current->fs->count > 1) || (atomic_read(¤t->sig->count) > 1) @@ -550,7 +550,7 @@ int prepare_binprm(struct linux_binprm *bprm) } memset(bprm->buf,0,sizeof(bprm->buf)); - return read_exec(bprm->inode,0,bprm->buf,128,1); + return read_exec(bprm->dentry,0,bprm->buf,128,1); } void remove_arg_zero(struct linux_binprm *bprm) @@ -585,16 +585,19 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) (eh->fh.f_flags & 0x3000) == 0x3000) { char * dynloader[] = { "/sbin/loader" }; - iput(bprm->inode); - bprm->dont_iput = 1; + struct dentry * dentry; + + dput(bprm->dentry); + bprm->dentry = NULL; remove_arg_zero(bprm); bprm->p = copy_strings(1, dynloader, bprm->page, bprm->p, 2); bprm->argc++; bprm->loader = bprm->p; - retval = open_namei(dynloader[0], 0, 0, &bprm->inode, NULL); - if (retval) + dentry = open_namei(dynloader[0], 0, 0); + retval = PTR_ERR(dentry); + if (IS_ERR(dentry)) return retval; - bprm->dont_iput = 0; + bprm->dentry = dentry; retval = prepare_binprm(bprm); if (retval<0) return retval; @@ -610,15 +613,15 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) continue; retval = fn(bprm, regs); if (retval >= 0) { - if(!bprm->dont_iput) - iput(bprm->inode); - bprm->dont_iput=1; + if (bprm->dentry) + dput(bprm->dentry); + bprm->dentry = NULL; current->did_exec = 1; return retval; } if (retval != -ENOEXEC) break; - if (bprm->dont_iput) /* We don't have the inode anymore*/ + if (!bprm->dentry) /* We don't have the dentry anymore */ return retval; } if (retval != -ENOEXEC) { @@ -647,29 +650,38 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs) { struct linux_binprm bprm; + struct dentry * dentry; int retval; int i; bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */ bprm.page[i] = 0; - retval = open_namei(filename, 0, 0, &bprm.inode, NULL); - if (retval) + + dentry = open_namei(filename, 0, 0); + retval = PTR_ERR(dentry); + if (IS_ERR(dentry)) return retval; + + bprm.dentry = dentry; bprm.filename = filename; bprm.sh_bang = 0; bprm.java = 0; bprm.loader = 0; bprm.exec = 0; - bprm.dont_iput = 0; - if ((bprm.argc = count(argv)) < 0) + if ((bprm.argc = count(argv)) < 0) { + dput(dentry); return bprm.argc; - if ((bprm.envc = count(envp)) < 0) + } + + if ((bprm.envc = count(envp)) < 0) { + dput(dentry); return bprm.envc; + } retval = prepare_binprm(&bprm); - if(retval>=0) { + if (retval >= 0) { bprm.p = copy_strings(1, &bprm.filename, bprm.page, bprm.p, 2); bprm.exec = bprm.p; bprm.p = copy_strings(bprm.envc,envp,bprm.page,bprm.p,0); @@ -678,16 +690,18 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs retval = -E2BIG; } - if(retval>=0) + if (retval >= 0) retval = search_binary_handler(&bprm,regs); - if(retval>=0) + if (retval >= 0) /* execve success */ return retval; /* Something went wrong, return the inode and free the argument pages*/ - if(!bprm.dont_iput) - iput(bprm.inode); + if (bprm.dentry) + dput(bprm.dentry); + for (i=0 ; i<MAX_ARG_PAGES ; i++) free_page(bprm.page[i]); - return(retval); + + return retval; } diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 4d2b561ee..7e159e7d2 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -291,7 +291,7 @@ int ext2_new_block (const struct inode * inode, unsigned long goal, printk ("ext2_new_block: nonexistent device"); return 0; } -retry: + lock_super (sb); es = sb->u.ext2_sb.s_es; if (le32_to_cpu(es->s_free_blocks_count) <= le32_to_cpu(es->s_r_blocks_count) && @@ -299,8 +299,6 @@ retry: (sb->u.ext2_sb.s_resgid == 0 || !in_group_p (sb->u.ext2_sb.s_resgid)))) { unlock_super (sb); - if(sb->s_ibasket && free_ibasket(sb)) - goto retry; return 0; } @@ -392,8 +390,6 @@ repeat: } if (k >= sb->u.ext2_sb.s_groups_count) { unlock_super (sb); - if(sb->s_ibasket && free_ibasket(sb)) - goto retry; return 0; } bitmap_nr = load_block_bitmap (sb, i); diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index d9b1957e3..b75acdef5 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -65,6 +65,7 @@ struct inode_operations ext2_dir_inode_operations = { ext2_mknod, /* mknod */ ext2_rename, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -211,9 +212,6 @@ revalidate: offset = 0; brelse (bh); } - if (DO_UPDATE_ATIME(inode)) { - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; - } + UPDATE_ATIME(inode); return 0; } diff --git a/fs/ext2/file.c b/fs/ext2/file.c index 1627f5cee..d632133f1 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -72,6 +72,7 @@ struct inode_operations ext2_file_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ generic_readpage, /* readpage */ NULL, /* writepage */ ext2_bmap, /* bmap */ @@ -121,7 +122,7 @@ static inline void remove_suid(struct inode *inode) mode &= inode->i_mode; if (mode && !suser()) { inode->i_mode &= ~mode; - inode->i_dirt = 1; + mark_inode_dirty(inode); } } @@ -250,7 +251,7 @@ static long ext2_file_write (struct inode * inode, struct file * filp, inode->u.ext2_i.i_osync--; inode->i_ctime = inode->i_mtime = CURRENT_TIME; filp->f_pos = pos; - inode->i_dirt = 1; + mark_inode_dirty(inode); return written; } diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index a486679f9..bc16722e4 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -154,8 +154,26 @@ static int load_inode_bitmap (struct super_block * sb, return 0; } +/* + * NOTE! When we get the inode, we're the only people + * that have access to it, and as such there are no + * race conditions we have to worry about. The inode + * is not on the hash-lists, and it cannot be reached + * through the filesystem because the directory entry + * has been deleted earlier. + * + * HOWEVER: we must make sure that we get no aliases, + * which means that we have to call "clear_inode()" + * _before_ we mark the inode not in use in the inode + * bitmaps. Otherwise a newly created file might use + * the same inode number (not actually the same pointer + * though), and then we'd have two inodes sharing the + * same inode number and space on the harddisk. + */ void ext2_free_inode (struct inode * inode) { + int is_directory; + unsigned long ino; struct super_block * sb; struct buffer_head * bh; struct buffer_head * bh2; @@ -171,9 +189,8 @@ void ext2_free_inode (struct inode * inode) printk ("ext2_free_inode: inode has no device\n"); return; } - if (atomic_read(&inode->i_count) > 1) { - printk ("ext2_free_inode: inode has count=%d\n", - atomic_read(&inode->i_count)); + if (inode->i_count > 1) { + printk ("ext2_free_inode: inode has count=%d\n", inode->i_count); return; } if (inode->i_nlink) { @@ -186,47 +203,53 @@ void ext2_free_inode (struct inode * inode) return; } - ext2_debug ("freeing inode %lu\n", inode->i_ino); + ino = inode->i_ino; + ext2_debug ("freeing inode %lu\n", ino); sb = inode->i_sb; lock_super (sb); - if (inode->i_ino < EXT2_FIRST_INO(sb) || - inode->i_ino > le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count)) { + if (ino < EXT2_FIRST_INO(sb) || + ino > le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count)) { ext2_error (sb, "free_inode", "reserved inode or nonexistent inode"); unlock_super (sb); return; } es = sb->u.ext2_sb.s_es; - block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(sb); - bit = (inode->i_ino - 1) % EXT2_INODES_PER_GROUP(sb); + block_group = (ino - 1) / EXT2_INODES_PER_GROUP(sb); + bit = (ino - 1) % EXT2_INODES_PER_GROUP(sb); bitmap_nr = load_inode_bitmap (sb, block_group); bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr]; + + is_directory = S_ISDIR(inode->i_mode); + + /* Do this BEFORE marking the inode not in use */ + if (sb->dq_op) + sb->dq_op->free_inode (inode, 1); + clear_inode (inode); + + /* Ok, now we can actually update the inode bitmaps.. */ if (!ext2_clear_bit (bit, bh->b_data)) ext2_warning (sb, "ext2_free_inode", - "bit already cleared for inode %lu", inode->i_ino); + "bit already cleared for inode %lu", ino); else { gdp = get_group_desc (sb, block_group, &bh2); gdp->bg_free_inodes_count = cpu_to_le16(le16_to_cpu(gdp->bg_free_inodes_count) + 1); - if (S_ISDIR(inode->i_mode)) + if (is_directory) gdp->bg_used_dirs_count = cpu_to_le16(le16_to_cpu(gdp->bg_used_dirs_count) - 1); mark_buffer_dirty(bh2, 1); es->s_free_inodes_count = cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) + 1); mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); - inode->i_dirt = 0; } mark_buffer_dirty(bh, 1); if (sb->s_flags & MS_SYNCHRONOUS) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); } - if (sb->dq_op) - sb->dq_op->free_inode (inode, 1); sb->s_dirt = 1; - clear_inode (inode); unlock_super (sb); } @@ -240,7 +263,7 @@ static void inc_inode_version (struct inode * inode, int mode) { inode->u.ext2_i.i_version++; - inode->i_dirt = 1; + mark_inode_dirty(inode); return; } @@ -404,7 +427,6 @@ repeat: sb->s_dirt = 1; inode->i_mode = mode; inode->i_sb = sb; - atomic_set(&inode->i_count, 1); inode->i_nlink = 1; inode->i_dev = sb->s_dev; inode->i_uid = current->fsuid; @@ -416,7 +438,7 @@ repeat: mode |= S_ISGID; } else inode->i_gid = current->fsgid; - inode->i_dirt = 1; + mark_inode_dirty(inode); inode->i_ino = j; inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */ inode->i_blocks = 0; diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index f2dbff2d1..0fa14cfe1 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -31,14 +31,24 @@ static int ext2_update_inode(struct inode * inode, int do_sync); +/* + * Called at each iput() + */ void ext2_put_inode (struct inode * inode) { ext2_discard_prealloc (inode); - if (inode->i_nlink || inode->i_ino == EXT2_ACL_IDX_INO || +} + +/* + * Called at the last iput() if i_nlink is zero. + */ +void ext2_delete_inode (struct inode * inode) +{ + if (inode->i_ino == EXT2_ACL_IDX_INO || inode->i_ino == EXT2_ACL_DATA_INO) return; inode->u.ext2_i.i_dtime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); ext2_update_inode(inode, IS_SYNC(inode)); inode->i_size = 0; if (inode->i_blocks) @@ -248,7 +258,7 @@ repeat: if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) ext2_sync_inode (inode); else - inode->i_dirt = 1; + mark_inode_dirty(inode); return result; } @@ -322,7 +332,7 @@ repeat: } inode->i_ctime = CURRENT_TIME; inode->i_blocks += blocks; - inode->i_dirt = 1; + mark_inode_dirty(inode); inode->u.ext2_i.i_next_alloc_block = new_block; inode->u.ext2_i.i_next_alloc_goal = tmp; brelse (bh); @@ -591,7 +601,6 @@ static int ext2_update_inode(struct inode * inode, int do_sync) else for (block = 0; block < EXT2_N_BLOCKS; block++) raw_inode->i_block[block] = cpu_to_le32(inode->u.ext2_i.i_data[block]); mark_buffer_dirty(bh, 1); - inode->i_dirt = 0; if (do_sync) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); @@ -671,7 +680,7 @@ int ext2_notify_change(struct inode *inode, struct iattr *iattr) inode->i_flags &= ~S_IMMUTABLE; inode->u.ext2_i.i_flags &= ~EXT2_IMMUTABLE_FL; } - inode->i_dirt = 1; + mark_inode_dirty(inode); return 0; } diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c index 387600bbf..c0514c01e 100644 --- a/fs/ext2/ioctl.c +++ b/fs/ext2/ioctl.c @@ -62,7 +62,7 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, else inode->i_flags &= ~MS_NOATIME; inode->i_ctime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); return 0; case EXT2_IOC_GETVERSION: return put_user(inode->u.ext2_i.i_version, (int *) arg); @@ -74,7 +74,7 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, if (get_user(inode->u.ext2_i.i_version, (int *) arg)) return -EFAULT; inode->i_ctime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); return 0; default: return -ENOTTY; diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 421393581..2a7c42bff 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -27,6 +27,7 @@ #include <linux/string.h> #include <linux/locks.h> + /* * define how far ahead to read directories while searching them. */ @@ -154,36 +155,26 @@ failure: return NULL; } -int ext2_lookup (struct inode * dir, const char * name, int len, - struct inode ** result) +int ext2_lookup(struct inode * dir, struct dentry *dentry) { - unsigned long ino; + struct inode * inode; struct ext2_dir_entry * de; struct buffer_head * bh; - *result = NULL; - if (!dir) - return -ENOENT; - if (!S_ISDIR(dir->i_mode)) { - iput (dir); - return -ENOTDIR; - } - if (len > EXT2_NAME_LEN) { - iput (dir); + if (dentry->d_name.len > EXT2_NAME_LEN) return -ENAMETOOLONG; + + bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); + inode = NULL; + if (bh) { + unsigned long ino = le32_to_cpu(de->inode); + brelse (bh); + inode = iget(dir->i_sb, ino); + + if (!inode) + return -EACCES; } - ino = dir->i_version; - if (!(bh = ext2_find_entry (dir, name, len, &de))) { - iput (dir); - return -ENOENT; - } - ino = le32_to_cpu(de->inode); - brelse (bh); - if (!(*result = iget (dir->i_sb, ino))) { - iput (dir); - return -EACCES; - } - iput (dir); + d_add(dentry, inode); return 0; } @@ -256,7 +247,7 @@ static struct buffer_head * ext2_add_entry (struct inode * dir, de->inode = le32_to_cpu(0); de->rec_len = le16_to_cpu(sb->s_blocksize); dir->i_size = offset + sb->s_blocksize; - dir->i_dirt = 1; + mark_inode_dirty(dir); } else { ext2_debug ("skipping to next block\n"); @@ -301,7 +292,7 @@ static struct buffer_head * ext2_add_entry (struct inode * dir, * and/or different from the directory change time. */ dir->i_mtime = dir->i_ctime = CURRENT_TIME; - dir->i_dirt = 1; + mark_inode_dirty(dir); dir->i_version = ++event; mark_buffer_dirty(bh, 1); *res_dir = de; @@ -347,31 +338,35 @@ static int ext2_delete_entry (struct ext2_dir_entry * dir, return -ENOENT; } -int ext2_create (struct inode * dir,const char * name, int len, int mode, - struct inode ** result) +/* + * By the time this is called, we already have created + * the directory cache entry for the new file, but it + * is so far negative - it has no inode. + * + * If the create succeeds, we fill in the inode information + * with d_instantiate(). + */ +int ext2_create (struct inode * dir, struct dentry * dentry, int mode) { struct inode * inode; struct buffer_head * bh; struct ext2_dir_entry * de; int err; - *result = NULL; if (!dir) return -ENOENT; inode = ext2_new_inode (dir, mode, &err); - if (!inode) { - iput (dir); + if (!inode) return err; - } + inode->i_op = &ext2_file_inode_operations; inode->i_mode = mode; - inode->i_dirt = 1; - bh = ext2_add_entry (dir, name, len, &de, &err); + mark_inode_dirty(inode); + bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); if (!bh) { inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput (inode); - iput (dir); return err; } de->inode = cpu_to_le32(inode->i_ino); @@ -382,13 +377,11 @@ int ext2_create (struct inode * dir,const char * name, int len, int mode, wait_on_buffer (bh); } brelse (bh); - iput (dir); - *result = inode; + d_instantiate(dentry, inode); return 0; } -int ext2_mknod (struct inode * dir, const char * name, int len, int mode, - int rdev) +int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev) { struct inode * inode; struct buffer_head * bh; @@ -398,21 +391,13 @@ int ext2_mknod (struct inode * dir, const char * name, int len, int mode, if (!dir) return -ENOENT; - if (len > EXT2_NAME_LEN) { - iput (dir); + if (dentry->d_name.len > EXT2_NAME_LEN) return -ENAMETOOLONG; - } - bh = ext2_find_entry (dir, name, len, &de); - if (bh) { - brelse (bh); - iput (dir); - return -EEXIST; - } + inode = ext2_new_inode (dir, mode, &err); - if (!inode) { - iput (dir); + if (!inode) return err; - } + inode->i_uid = current->fsuid; inode->i_mode = mode; inode->i_op = NULL; @@ -433,13 +418,12 @@ int ext2_mknod (struct inode * dir, const char * name, int len, int mode, init_fifo(inode); if (S_ISBLK(mode) || S_ISCHR(mode)) inode->i_rdev = to_kdev_t(rdev); - inode->i_dirt = 1; - bh = ext2_add_entry (dir, name, len, &de, &err); + mark_inode_dirty(inode); + bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); if (!bh) { inode->i_nlink--; - inode->i_dirt = 1; - iput (inode); - iput (dir); + mark_inode_dirty(inode); + iput(inode); return err; } de->inode = cpu_to_le32(inode->i_ino); @@ -449,47 +433,34 @@ int ext2_mknod (struct inode * dir, const char * name, int len, int mode, ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); } - brelse (bh); - iput (dir); - iput (inode); + brelse(bh); + d_instantiate(dentry, inode); return 0; } -int ext2_mkdir (struct inode * dir, const char * name, int len, int mode) +int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode) { struct inode * inode; struct buffer_head * bh, * dir_block; struct ext2_dir_entry * de; int err; - if (!dir) - return -ENOENT; - if (len > EXT2_NAME_LEN) { - iput (dir); + if (dentry->d_name.len > EXT2_NAME_LEN) return -ENAMETOOLONG; - } - bh = ext2_find_entry (dir, name, len, &de); - if (bh) { - brelse (bh); - iput (dir); - return -EEXIST; - } - if (dir->i_nlink >= EXT2_LINK_MAX) { - iput (dir); + + if (dir->i_nlink >= EXT2_LINK_MAX) return -EMLINK; - } + inode = ext2_new_inode (dir, S_IFDIR, &err); - if (!inode) { - iput (dir); + if (!inode) return err; - } + inode->i_op = &ext2_dir_inode_operations; inode->i_size = inode->i_sb->s_blocksize; dir_block = ext2_bread (inode, 0, 1, &err); if (!dir_block) { - iput (dir); inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput (inode); return err; } @@ -510,12 +481,11 @@ int ext2_mkdir (struct inode * dir, const char * name, int len, int mode) inode->i_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask); if (dir->i_mode & S_ISGID) inode->i_mode |= S_ISGID; - inode->i_dirt = 1; - bh = ext2_add_entry (dir, name, len, &de, &err); + mark_inode_dirty(inode); + bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); if (!bh) { - iput (dir); inode->i_nlink = 0; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput (inode); return err; } @@ -527,9 +497,8 @@ int ext2_mkdir (struct inode * dir, const char * name, int len, int mode) wait_on_buffer (bh); } dir->i_nlink++; - dir->i_dirt = 1; - iput (dir); - iput (inode); + mark_inode_dirty(dir); + d_instantiate(dentry, inode); brelse (bh); return 0; } @@ -593,58 +562,53 @@ static int empty_dir (struct inode * inode) return 1; } -int ext2_rmdir (struct inode * dir, const char * name, int len) +int ext2_rmdir (struct inode * dir, struct dentry *dentry) { int retval; struct inode * inode; struct buffer_head * bh; struct ext2_dir_entry * de; -repeat: if (!dir) return -ENOENT; inode = NULL; - if (len > EXT2_NAME_LEN) { - iput (dir); + if (dentry->d_name.len > EXT2_NAME_LEN) return -ENAMETOOLONG; - } - bh = ext2_find_entry (dir, name, len, &de); + + bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); retval = -ENOENT; if (!bh) goto end_rmdir; retval = -EPERM; - if (!(inode = iget (dir->i_sb, le32_to_cpu(de->inode)))) - goto end_rmdir; + inode = dentry->d_inode; + if (inode->i_sb->dq_op) inode->i_sb->dq_op->initialize (inode, -1); - if (inode->i_dev != dir->i_dev) { - retval = -EBUSY; - goto end_rmdir; - } - if (le32_to_cpu(de->inode) != inode->i_ino) { - iput(inode); - brelse(bh); - current->counter = 0; - schedule(); - goto repeat; - } + if ((dir->i_mode & S_ISVTX) && !fsuser() && current->fsuid != inode->i_uid && current->fsuid != dir->i_uid) goto end_rmdir; if (inode == dir) /* we may not delete ".", but "../dir" is ok */ goto end_rmdir; - if (!S_ISDIR(inode->i_mode)) { - retval = -ENOTDIR; + + retval = -ENOTDIR; + if (!S_ISDIR(inode->i_mode)) goto end_rmdir; - } + + retval = -EIO; + if (inode->i_dev != dir->i_dev) + goto end_rmdir; + if (le32_to_cpu(de->inode) != inode->i_ino) + goto end_rmdir; + down(&inode->i_sem); if (!empty_dir (inode)) retval = -ENOTEMPTY; else if (le32_to_cpu(de->inode) != inode->i_ino) retval = -ENOENT; else { - if (atomic_read(&inode->i_count) > 1) { + if (inode->i_count > 1) { /* * Are we deleting the last instance of a busy directory? * Better clean up if so. @@ -671,56 +635,51 @@ repeat: (int) inode->i_nlink); inode->i_version = ++event; inode->i_nlink = 0; - inode->i_dirt = 1; + mark_inode_dirty(inode); dir->i_nlink--; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; - dir->i_dirt = 1; + mark_inode_dirty(dir); + d_delete(dentry); + end_rmdir: - iput (dir); - iput (inode); brelse (bh); return retval; } -int ext2_unlink (struct inode * dir, const char * name, int len) +int ext2_unlink(struct inode * dir, struct dentry *dentry) { int retval; struct inode * inode; struct buffer_head * bh; struct ext2_dir_entry * de; -repeat: - if (!dir) - return -ENOENT; retval = -ENOENT; inode = NULL; - if (len > EXT2_NAME_LEN) { - iput (dir); + if (dentry->d_name.len > EXT2_NAME_LEN) return -ENAMETOOLONG; - } - bh = ext2_find_entry (dir, name, len, &de); + + bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); if (!bh) goto end_unlink; - if (!(inode = iget (dir->i_sb, le32_to_cpu(de->inode)))) - goto end_unlink; + + inode = dentry->d_inode; if (inode->i_sb->dq_op) inode->i_sb->dq_op->initialize (inode, -1); + retval = -EPERM; if (S_ISDIR(inode->i_mode)) goto end_unlink; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) goto end_unlink; - if (le32_to_cpu(de->inode) != inode->i_ino) { - iput(inode); - brelse(bh); - current->counter = 0; - schedule(); - goto repeat; - } if ((dir->i_mode & S_ISVTX) && !fsuser() && current->fsuid != inode->i_uid && current->fsuid != dir->i_uid) goto end_unlink; + + retval = -EIO; + if (le32_to_cpu(de->inode) != inode->i_ino) + goto end_unlink; + if (!inode->i_nlink) { ext2_warning (inode->i_sb, "ext2_unlink", "Deleting nonexistent file (%lu), %d", @@ -737,20 +696,19 @@ repeat: wait_on_buffer (bh); } dir->i_ctime = dir->i_mtime = CURRENT_TIME; - dir->i_dirt = 1; + mark_inode_dirty(dir); inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); inode->i_ctime = dir->i_ctime; retval = 0; + d_delete(dentry); /* This also frees the inode */ + end_unlink: brelse (bh); - iput (inode); - iput (dir); return retval; } -int ext2_symlink (struct inode * dir, const char * name, int len, - const char * symname) +int ext2_symlink (struct inode * dir, struct dentry *dentry, const char * symname) { struct ext2_dir_entry * de; struct inode * inode = NULL; @@ -761,7 +719,6 @@ int ext2_symlink (struct inode * dir, const char * name, int len, char c; if (!(inode = ext2_new_inode (dir, S_IFLNK, &err))) { - iput (dir); return err; } inode->i_mode = S_IFLNK | S_IRWXUGO; @@ -775,9 +732,8 @@ int ext2_symlink (struct inode * dir, const char * name, int len, name_block = ext2_bread (inode, 0, 1, &err); if (!name_block) { - iput (dir); inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput (inode); return err; } @@ -797,23 +753,13 @@ int ext2_symlink (struct inode * dir, const char * name, int len, brelse (name_block); } inode->i_size = i; - inode->i_dirt = 1; + mark_inode_dirty(inode); - bh = ext2_find_entry (dir, name, len, &de); - if (bh) { - inode->i_nlink--; - inode->i_dirt = 1; - iput (inode); - brelse (bh); - iput (dir); - return -EEXIST; - } - bh = ext2_add_entry (dir, name, len, &de, &err); + bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); if (!bh) { inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput (inode); - iput (dir); return err; } de->inode = cpu_to_le32(inode->i_ino); @@ -824,47 +770,30 @@ int ext2_symlink (struct inode * dir, const char * name, int len, wait_on_buffer (bh); } brelse (bh); - iput (dir); - iput (inode); + d_instantiate(dentry, inode); return 0; } -int ext2_link (struct inode * oldinode, struct inode * dir, - const char * name, int len) +int ext2_link (struct inode * inode, struct inode * dir, struct dentry *dentry) { struct ext2_dir_entry * de; struct buffer_head * bh; int err; - if (S_ISDIR(oldinode->i_mode)) { - iput (oldinode); - iput (dir); + if (S_ISDIR(inode->i_mode)) return -EPERM; - } - if (IS_APPEND(oldinode) || IS_IMMUTABLE(oldinode)) { - iput (oldinode); - iput (dir); + + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) return -EPERM; - } - if (oldinode->i_nlink >= EXT2_LINK_MAX) { - iput (oldinode); - iput (dir); + + if (inode->i_nlink >= EXT2_LINK_MAX) return -EMLINK; - } - bh = ext2_find_entry (dir, name, len, &de); - if (bh) { - brelse (bh); - iput (dir); - iput (oldinode); - return -EEXIST; - } - bh = ext2_add_entry (dir, name, len, &de, &err); - if (!bh) { - iput (dir); - iput (oldinode); + + bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); + if (!bh) return err; - } - de->inode = cpu_to_le32(oldinode->i_ino); + + de->inode = cpu_to_le32(inode->i_ino); dir->i_version = ++event; mark_buffer_dirty(bh, 1); if (IS_SYNC(dir)) { @@ -872,35 +801,33 @@ int ext2_link (struct inode * oldinode, struct inode * dir, wait_on_buffer (bh); } brelse (bh); - iput (dir); - oldinode->i_nlink++; - oldinode->i_ctime = CURRENT_TIME; - oldinode->i_dirt = 1; - iput (oldinode); + inode->i_nlink++; + inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + inode->i_count++; + d_instantiate(dentry, inode); return 0; } -static int subdir (struct inode * new_inode, struct inode * old_inode) +/* + * Trivially implemented using the dcache structure + */ +static int subdir (struct dentry * new_dentry, struct dentry * old_dentry) { - int ino; int result; - atomic_inc(&new_inode->i_count); result = 0; for (;;) { - if (new_inode == old_inode) { - result = 1; - break; + if (new_dentry != old_dentry) { + struct dentry * parent = new_dentry->d_parent; + if (parent == new_dentry) + break; + new_dentry = parent; + continue; } - if (new_inode->i_dev != old_inode->i_dev) - break; - ino = new_inode->i_ino; - if (ext2_lookup (new_inode, "..", 2, &new_inode)) - break; - if (new_inode->i_ino == ino) - break; + result = 1; + break; } - iput (new_inode); return result; } @@ -908,10 +835,6 @@ static int subdir (struct inode * new_inode, struct inode * old_inode) ((struct ext2_dir_entry *) ((char *) buffer + \ le16_to_cpu(((struct ext2_dir_entry *) buffer)->rec_len)))->inode -#define PARENT_NAME(buffer) \ - ((struct ext2_dir_entry *) ((char *) buffer + \ - le16_to_cpu(((struct ext2_dir_entry *) buffer)->rec_len)))->name - /* * rename uses retrying to avoid race-conditions: at least they should be * minimal. @@ -923,43 +846,27 @@ static int subdir (struct inode * new_inode, struct inode * old_inode) * Anybody can rename anything with this: the permission checks are left to the * higher-level routines. */ -static int do_ext2_rename (struct inode * old_dir, const char * old_name, - int old_len, struct inode * new_dir, - const char * new_name, int new_len) +static int do_ext2_rename (struct inode * old_dir, struct dentry *old_dentry, + struct inode * new_dir,struct dentry *new_dentry) { struct inode * old_inode, * new_inode; struct buffer_head * old_bh, * new_bh, * dir_bh; struct ext2_dir_entry * old_de, * new_de; int retval; - goto start_up; -try_again: - if (new_bh && new_de) { - ext2_delete_entry(new_de, new_bh); - new_dir->i_version = ++event; - } - brelse (old_bh); - brelse (new_bh); - brelse (dir_bh); - iput (old_inode); - iput (new_inode); - current->counter = 0; - schedule (); -start_up: old_inode = new_inode = NULL; old_bh = new_bh = dir_bh = NULL; new_de = NULL; retval = -ENAMETOOLONG; - if (old_len > EXT2_NAME_LEN) + if (old_dentry->d_name.len > EXT2_NAME_LEN) goto end_rename; - old_bh = ext2_find_entry (old_dir, old_name, old_len, &old_de); + old_bh = ext2_find_entry (old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de); retval = -ENOENT; if (!old_bh) goto end_rename; - old_inode = __iget (old_dir->i_sb, le32_to_cpu(old_de->inode), 0); /* don't cross mnt-points */ - if (!old_inode) - goto end_rename; + old_inode = old_dentry->d_inode; + retval = -EPERM; if ((old_dir->i_mode & S_ISVTX) && current->fsuid != old_inode->i_uid && @@ -967,9 +874,10 @@ start_up: goto end_rename; if (IS_APPEND(old_inode) || IS_IMMUTABLE(old_inode)) goto end_rename; - new_bh = ext2_find_entry (new_dir, new_name, new_len, &new_de); + + new_inode = new_dentry->d_inode; + new_bh = ext2_find_entry (new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_de); if (new_bh) { - new_inode = __iget (new_dir->i_sb, le32_to_cpu(new_de->inode), 0); /* no mntp cross */ if (!new_inode) { brelse (new_bh); new_bh = NULL; @@ -987,13 +895,13 @@ start_up: if (!S_ISDIR(old_inode->i_mode)) goto end_rename; retval = -EINVAL; - if (subdir (new_dir, old_inode)) + if (subdir(new_dentry, old_dentry)) goto end_rename; retval = -ENOTEMPTY; if (!empty_dir (new_inode)) goto end_rename; retval = -EBUSY; - if (atomic_read(&new_inode->i_count) > 1) + if (new_inode->i_count > 1) goto end_rename; } retval = -EPERM; @@ -1006,7 +914,7 @@ start_up: if (new_inode && !S_ISDIR(new_inode->i_mode)) goto end_rename; retval = -EINVAL; - if (subdir (new_dir, old_inode)) + if (subdir(new_dentry, old_dentry)) goto end_rename; dir_bh = ext2_bread (old_inode, 0, 0, &retval); if (!dir_bh) @@ -1018,48 +926,37 @@ start_up: goto end_rename; } if (!new_bh) - new_bh = ext2_add_entry (new_dir, new_name, new_len, &new_de, + new_bh = ext2_add_entry (new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_de, &retval); if (!new_bh) goto end_rename; new_dir->i_version = ++event; - /* - * sanity checking before doing the rename - avoid races - */ - if (new_inode && (le32_to_cpu(new_de->inode) != new_inode->i_ino)) - goto try_again; - if (le32_to_cpu(new_de->inode) && !new_inode) - goto try_again; - if (le32_to_cpu(old_de->inode) != old_inode->i_ino) - goto try_again; + /* * ok, that's it */ new_de->inode = le32_to_cpu(old_inode->i_ino); - retval = ext2_delete_entry (old_de, old_bh); - if (retval == -ENOENT) - goto try_again; - if (retval) - goto end_rename; + ext2_delete_entry (old_de, old_bh); + old_dir->i_version = ++event; if (new_inode) { new_inode->i_nlink--; new_inode->i_ctime = CURRENT_TIME; - new_inode->i_dirt = 1; + mark_inode_dirty(new_inode); } old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; - old_dir->i_dirt = 1; + mark_inode_dirty(old_dir); if (dir_bh) { PARENT_INO(dir_bh->b_data) = le32_to_cpu(new_dir->i_ino); mark_buffer_dirty(dir_bh, 1); old_dir->i_nlink--; - old_dir->i_dirt = 1; + mark_inode_dirty(old_dir); if (new_inode) { new_inode->i_nlink--; - new_inode->i_dirt = 1; + mark_inode_dirty(new_inode); } else { new_dir->i_nlink++; - new_dir->i_dirt = 1; + mark_inode_dirty(new_dir); } } mark_buffer_dirty(old_bh, 1); @@ -1072,15 +969,15 @@ start_up: ll_rw_block (WRITE, 1, &new_bh); wait_on_buffer (new_bh); } + + /* Update the dcache */ + d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name); + d_delete(new_dentry); retval = 0; end_rename: brelse (dir_bh); brelse (old_bh); brelse (new_bh); - iput (old_inode); - iput (new_inode); - iput (old_dir); - iput (new_dir); return retval; } @@ -1097,16 +994,15 @@ end_rename: * super-block. This way, we really lock other renames only if they occur * on the same file system */ -int ext2_rename (struct inode * old_dir, const char * old_name, int old_len, - struct inode * new_dir, const char * new_name, int new_len) +int ext2_rename (struct inode * old_dir, struct dentry *old_dentry, + struct inode * new_dir, struct dentry *new_dentry) { int result; while (old_dir->i_sb->u.ext2_sb.s_rename_lock) sleep_on (&old_dir->i_sb->u.ext2_sb.s_rename_wait); old_dir->i_sb->u.ext2_sb.s_rename_lock = 1; - result = do_ext2_rename (old_dir, old_name, old_len, new_dir, - new_name, new_len); + result = do_ext2_rename (old_dir, old_dentry, new_dir, new_dentry); old_dir->i_sb->u.ext2_sb.s_rename_lock = 0; wake_up (&old_dir->i_sb->u.ext2_sb.s_rename_wait); return result; diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 635a45692..1adc82185 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -133,9 +133,10 @@ void ext2_put_super (struct super_block * sb) static struct super_operations ext2_sops = { ext2_read_inode, - NULL, ext2_write_inode, ext2_put_inode, + ext2_delete_inode, + NULL, ext2_put_super, ext2_write_super, ext2_statfs, @@ -632,7 +633,8 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data, */ sb->s_dev = dev; sb->s_op = &ext2_sops; - if (!(sb->s_mounted = iget (sb, EXT2_ROOT_INO))) { + sb->s_root = d_alloc_root(iget(sb, EXT2_ROOT_INO), NULL); + if (!sb->s_root) { sb->s_dev = 0; for (i = 0; i < db_count; i++) if (sb->u.ext2_sb.s_group_desc[i]) @@ -761,7 +763,7 @@ void cleanup_module(void) #endif -void ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz) +int ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz) { unsigned long overhead; unsigned long overhead_per_group; @@ -792,5 +794,5 @@ void ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz) tmp.f_files = le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count); tmp.f_ffree = ext2_count_free_inodes (sb); tmp.f_namelen = EXT2_NAME_LEN; - copy_to_user(buf, &tmp, bufsiz); + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c index 4d5a5cada..781f9165d 100644 --- a/fs/ext2/symlink.c +++ b/fs/ext2/symlink.c @@ -25,6 +25,7 @@ #include <linux/stat.h> static int ext2_readlink (struct inode *, char *, int); +static struct dentry *ext2_follow_link(struct inode *, struct dentry *); /* * symlinks can't do much... @@ -41,6 +42,7 @@ struct inode_operations ext2_symlink_inode_operations = { NULL, /* mknod */ NULL, /* rename */ ext2_readlink, /* readlink */ + ext2_follow_link, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -49,37 +51,54 @@ struct inode_operations ext2_symlink_inode_operations = { NULL /* smap */ }; +static struct dentry * ext2_follow_link(struct inode * inode, struct dentry *base) +{ + int error; + struct buffer_head * bh = NULL; + char * link; + + link = (char *) inode->u.ext2_i.i_data; + if (inode->i_blocks) { + if (!(bh = ext2_bread (inode, 0, 0, &error))) { + dput(base); + return ERR_PTR(-EIO); + } + link = bh->b_data; + } + UPDATE_ATIME(inode); + base = lookup_dentry(link, base, 1); + if (bh) + brelse(bh); + return base; +} + static int ext2_readlink (struct inode * inode, char * buffer, int buflen) { struct buffer_head * bh = NULL; char * link; - int i, err; + int i; if (buflen > inode->i_sb->s_blocksize - 1) buflen = inode->i_sb->s_blocksize - 1; + + link = (char *) inode->u.ext2_i.i_data; if (inode->i_blocks) { + int err; bh = ext2_bread (inode, 0, 0, &err); if (!bh) { - iput (inode); if(err < 0) /* indicate type of error */ return err; return 0; } link = bh->b_data; } - else - link = (char *) inode->u.ext2_i.i_data; i = 0; while (i < buflen && link[i]) i++; if (copy_to_user(buffer, link, i)) i = -EFAULT; - if (DO_UPDATE_ATIME(inode)) { - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; - } - iput (inode); + UPDATE_ATIME(inode); if (bh) brelse (bh); return i; diff --git a/fs/ext2/truncate.c b/fs/ext2/truncate.c index a9e59ca00..5933ff77c 100644 --- a/fs/ext2/truncate.c +++ b/fs/ext2/truncate.c @@ -91,7 +91,7 @@ repeat: } *p = 0; inode->i_blocks -= blocks; - inode->i_dirt = 1; + mark_inode_dirty(inode); bforget(bh); if (free_count == 0) { block_to_free = tmp; @@ -110,7 +110,8 @@ repeat: return retry; } -static int trunc_indirect (struct inode * inode, int offset, u32 * p) +static int trunc_indirect (struct inode * inode, int offset, u32 * p, + int in_inode) { int i, tmp; struct buffer_head * bh; @@ -124,103 +125,16 @@ static int trunc_indirect (struct inode * inode, int offset, u32 * p) #define INDIRECT_BLOCK ((int)DIRECT_BLOCK - offset) int indirect_block = INDIRECT_BLOCK; - tmp = *p; + tmp = in_inode ? *p : le32_to_cpu(*p); if (!tmp) return 0; ind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (tmp != *p) { - brelse (ind_bh); - return 1; - } - if (!ind_bh) { - *p = 0; - return 0; - } -repeat: - for (i = indirect_block ; i < addr_per_block ; i++) { - if (i < 0) - i = 0; - if (i < indirect_block) - goto repeat; - ind = i + (u32 *) ind_bh->b_data; - tmp = le32_to_cpu(*ind); - if (!tmp) - continue; - bh = get_hash_table (inode->i_dev, tmp, - inode->i_sb->s_blocksize); - if (i < indirect_block) { - brelse (bh); - goto repeat; - } - if ((bh && bh->b_count != 1) || tmp != le32_to_cpu(*ind)) { - retry = 1; - brelse (bh); - continue; - } - *ind = cpu_to_le32(0); - mark_buffer_dirty(ind_bh, 1); - bforget(bh); - if (free_count == 0) { - block_to_free = tmp; - free_count++; - } else if (free_count > 0 && block_to_free == tmp - free_count) - free_count++; - else { - ext2_free_blocks (inode, block_to_free, free_count); - block_to_free = tmp; - free_count = 1; - } -/* ext2_free_blocks (inode, tmp, 1); */ - inode->i_blocks -= blocks; - inode->i_dirt = 1; - } - if (free_count > 0) - ext2_free_blocks (inode, block_to_free, free_count); - ind = (u32 *) ind_bh->b_data; - for (i = 0; i < addr_per_block; i++) - if (le32_to_cpu(*(ind++))) - break; - if (i >= addr_per_block) - if (ind_bh->b_count != 1) - retry = 1; - else { - tmp = *p; - *p = 0; - inode->i_blocks -= blocks; - inode->i_dirt = 1; - ext2_free_blocks (inode, tmp, 1); - } - if (IS_SYNC(inode) && buffer_dirty(ind_bh)) { - ll_rw_block (WRITE, 1, &ind_bh); - wait_on_buffer (ind_bh); - } - brelse (ind_bh); - return retry; -} - -static int trunc_indirect_swab32 (struct inode * inode, int offset, u32 * p) -{ - int i, tmp; - struct buffer_head * bh; - struct buffer_head * ind_bh; - u32 * ind; - unsigned long block_to_free = 0; - unsigned long free_count = 0; - int retry = 0; - int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); - int blocks = inode->i_sb->s_blocksize / 512; - int indirect_block = INDIRECT_BLOCK; - - tmp = le32_to_cpu(*p); - if (!tmp) - return 0; - ind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (tmp != le32_to_cpu(*p)) { + if (tmp != (in_inode ? *p : le32_to_cpu(*p))) { brelse (ind_bh); return 1; } if (!ind_bh) { - *p = cpu_to_le32(0); + *p = in_inode ? 0 : cpu_to_le32(0); return 0; } repeat: @@ -259,7 +173,7 @@ repeat: } /* ext2_free_blocks (inode, tmp, 1); */ inode->i_blocks -= blocks; - inode->i_dirt = 1; + mark_inode_dirty(inode); } if (free_count > 0) ext2_free_blocks (inode, block_to_free, free_count); @@ -271,13 +185,15 @@ repeat: if (ind_bh->b_count != 1) retry = 1; else { - tmp = le32_to_cpu(*p); - *p = cpu_to_le32(0); + tmp = in_inode ? *p : le32_to_cpu(*p); + *p = in_inode ? 0 : cpu_to_le32(0); inode->i_blocks -= blocks; - inode->i_dirt = 1; + mark_inode_dirty(inode); ext2_free_blocks (inode, tmp, 1); + bforget(ind_bh); + ind_bh = NULL; } - if (IS_SYNC(inode) && buffer_dirty(ind_bh)) { + if (IS_SYNC(inode) && ind_bh && buffer_dirty(ind_bh)) { ll_rw_block (WRITE, 1, &ind_bh); wait_on_buffer (ind_bh); } @@ -286,7 +202,7 @@ repeat: } static int trunc_dindirect (struct inode * inode, int offset, - u32 * p) + u32 * p, int in_inode) { int i, tmp; struct buffer_head * dind_bh; @@ -297,75 +213,16 @@ static int trunc_dindirect (struct inode * inode, int offset, #define DINDIRECT_BLOCK (((int)DIRECT_BLOCK - offset) / addr_per_block) int dindirect_block = DINDIRECT_BLOCK; - tmp = *p; - if (!tmp) - return 0; - dind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (tmp != *p) { - brelse (dind_bh); - return 1; - } - if (!dind_bh) { - *p = 0; - return 0; - } -repeat: - for (i = dindirect_block ; i < addr_per_block ; i++) { - if (i < 0) - i = 0; - if (i < dindirect_block) - goto repeat; - dind = i + (u32 *) dind_bh->b_data; - tmp = le32_to_cpu(*dind); - if (!tmp) - continue; - retry |= trunc_indirect_swab32 (inode, offset + (i * addr_per_block), - dind); - mark_buffer_dirty(dind_bh, 1); - } - dind = (u32 *) dind_bh->b_data; - for (i = 0; i < addr_per_block; i++) - if (le32_to_cpu(*(dind++))) - break; - if (i >= addr_per_block) - if (dind_bh->b_count != 1) - retry = 1; - else { - tmp = *p; - *p = 0; - inode->i_blocks -= blocks; - inode->i_dirt = 1; - ext2_free_blocks (inode, tmp, 1); - } - if (IS_SYNC(inode) && buffer_dirty(dind_bh)) { - ll_rw_block (WRITE, 1, &dind_bh); - wait_on_buffer (dind_bh); - } - brelse (dind_bh); - return retry; -} - -static int trunc_dindirect_swab32 (struct inode * inode, int offset, - u32 * p) -{ - int i, tmp; - struct buffer_head * dind_bh; - u32 * dind; - int retry = 0; - int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); - int blocks = inode->i_sb->s_blocksize / 512; - int dindirect_block = DINDIRECT_BLOCK; - - tmp = le32_to_cpu(*p); + tmp = in_inode ? *p : le32_to_cpu(*p); if (!tmp) return 0; dind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (tmp != le32_to_cpu(*p)) { + if (tmp != (in_inode ? *p : le32_to_cpu(*p))) { brelse (dind_bh); return 1; } if (!dind_bh) { - *p = cpu_to_le32(0); + *p = in_inode ? 0 : cpu_to_le32(0); return 0; } repeat: @@ -378,8 +235,8 @@ repeat: tmp = le32_to_cpu(*dind); if (!tmp) continue; - retry |= trunc_indirect_swab32 (inode, offset + (i * addr_per_block), - dind); + retry |= trunc_indirect(inode, offset + (i * addr_per_block), + dind, 0); mark_buffer_dirty(dind_bh, 1); } dind = (u32 *) dind_bh->b_data; @@ -390,13 +247,15 @@ repeat: if (dind_bh->b_count != 1) retry = 1; else { - tmp = le32_to_cpu(*p); - *p = cpu_to_le32(0); + tmp = in_inode ? *p : le32_to_cpu(*p); + *p = in_inode ? 0 : cpu_to_le32(0); inode->i_blocks -= blocks; - inode->i_dirt = 1; + mark_inode_dirty(inode); ext2_free_blocks (inode, tmp, 1); + bforget(dind_bh); + dind_bh = 0; } - if (IS_SYNC(inode) && buffer_dirty(dind_bh)) { + if (IS_SYNC(inode) && dind_bh && buffer_dirty(dind_bh)) { ll_rw_block (WRITE, 1, &dind_bh); wait_on_buffer (dind_bh); } @@ -436,9 +295,9 @@ repeat: if (i < tindirect_block) goto repeat; tind = i + (u32 *) tind_bh->b_data; - retry |= trunc_dindirect_swab32(inode, EXT2_NDIR_BLOCKS + + retry |= trunc_dindirect(inode, EXT2_NDIR_BLOCKS + addr_per_block + (i + 1) * addr_per_block * addr_per_block, - tind); + tind, 0); mark_buffer_dirty(tind_bh, 1); } tind = (u32 *) tind_bh->b_data; @@ -452,10 +311,12 @@ repeat: tmp = *p; *p = 0; inode->i_blocks -= blocks; - inode->i_dirt = 1; + mark_inode_dirty(inode); ext2_free_blocks (inode, tmp, 1); + bforget(tind_bh); + tind_bh = 0; } - if (IS_SYNC(inode) && buffer_dirty(tind_bh)) { + if (IS_SYNC(inode) && tind_bh && buffer_dirty(tind_bh)) { ll_rw_block (WRITE, 1, &tind_bh); wait_on_buffer (tind_bh); } @@ -479,14 +340,14 @@ void ext2_truncate (struct inode * inode) while (1) { retry = trunc_direct(inode); retry |= trunc_indirect (inode, EXT2_IND_BLOCK, - (u32 *) &inode->u.ext2_i.i_data[EXT2_IND_BLOCK]); + (u32 *) &inode->u.ext2_i.i_data[EXT2_IND_BLOCK], 1); retry |= trunc_dindirect (inode, EXT2_IND_BLOCK + EXT2_ADDR_PER_BLOCK(inode->i_sb), - (u32 *) &inode->u.ext2_i.i_data[EXT2_DIND_BLOCK]); + (u32 *) &inode->u.ext2_i.i_data[EXT2_DIND_BLOCK], 1); retry |= trunc_tindirect (inode); if (!retry) break; - if (IS_SYNC(inode) && inode->i_dirt) + if (IS_SYNC(inode) && test_bit(I_DIRTY, &inode->i_state)) ext2_sync_inode (inode); current->counter = 0; schedule (); @@ -510,5 +371,5 @@ void ext2_truncate (struct inode * inode) } } inode->i_mtime = inode->i_ctime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); } diff --git a/fs/fat/cache.c b/fs/fat/cache.c index 62ff8af1e..6223c1c48 100644 --- a/fs/fat/cache.c +++ b/fs/fat/cache.c @@ -280,7 +280,7 @@ int fat_free(struct inode *inode,int skip) 12 ? EOF_FAT12 : EOF_FAT16); else { MSDOS_I(inode)->i_start = 0; - inode->i_dirt = 1; + mark_inode_dirty(inode); } lock_fat(inode->i_sb); while (nr != -1) { diff --git a/fs/fat/file.c b/fs/fat/file.c index 82787075a..ca35cc28f 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -52,6 +52,7 @@ struct inode_operations fat_file_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ generic_readpage, /* readpage */ NULL, /* writepage */ fat_bmap, /* bmap */ @@ -99,6 +100,7 @@ struct inode_operations fat_file_inode_operations_1024 = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -355,7 +357,7 @@ long fat_file_write( filp->f_pos += written; if (filp->f_pos > inode->i_size) { inode->i_size = filp->f_pos; - inode->i_dirt = 1; + mark_inode_dirty(inode); } fat_set_uptodate(sb, bh, 1); fat_mark_buffer_dirty(sb, bh, 0); @@ -365,7 +367,7 @@ long fat_file_write( return error; inode->i_mtime = inode->i_ctime = CURRENT_TIME; MSDOS_I(inode)->i_attrs |= ATTR_ARCH; - inode->i_dirt = 1; + mark_inode_dirty(inode); return buf-start; } @@ -379,5 +381,5 @@ void fat_truncate(struct inode *inode) cluster = SECTOR_SIZE*MSDOS_SB(inode->i_sb)->cluster_size; (void) fat_free(inode,(inode->i_size+(cluster-1))/cluster); MSDOS_I(inode)->i_attrs |= ATTR_ARCH; - inode->i_dirt = 1; + mark_inode_dirty(inode); } diff --git a/fs/fat/inode.c b/fs/fat/inode.c index cf14856d1..e35722aff 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -196,6 +196,7 @@ struct super_block *fat_read_super(struct super_block *sb,void *data, int silent int debug,error,fat; int blksize = 512; struct fat_mount_options opts; + struct inode *root_inode; MOD_INC_USE_COUNT; if (hardsect_size[MAJOR(sb->s_dev)] != NULL){ @@ -329,7 +330,10 @@ struct super_block *fat_read_super(struct super_block *sb,void *data, int silent MSDOS_SB(sb)->fat_lock = 0; MSDOS_SB(sb)->prev_free = 0; memcpy(&(MSDOS_SB(sb)->options), &opts, sizeof(struct fat_mount_options)); - if (!(sb->s_mounted = iget(sb,MSDOS_ROOT_INO))) { + + root_inode = iget(sb,MSDOS_ROOT_INO); + sb->s_root = d_alloc_root(root_inode, NULL); + if (!sb->s_root) { sb->s_dev = 0; printk("get root inode failed\n"); MOD_DEC_USE_COUNT; @@ -339,7 +343,7 @@ struct super_block *fat_read_super(struct super_block *sb,void *data, int silent } -void fat_statfs(struct super_block *sb,struct statfs *buf, int bufsiz) +int fat_statfs(struct super_block *sb,struct statfs *buf, int bufsiz) { int free,nr; struct statfs tmp; @@ -362,7 +366,7 @@ void fat_statfs(struct super_block *sb,struct statfs *buf, int bufsiz) tmp.f_files = 0; tmp.f_ffree = 0; tmp.f_namelen = 12; - copy_to_user(buf, &tmp, bufsiz); + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } @@ -514,10 +518,9 @@ void fat_write_inode(struct inode *inode) linked->i_blocks = inode->i_blocks; linked->i_atime = inode->i_atime; MSDOS_I(linked)->i_attrs = MSDOS_I(inode)->i_attrs; - linked->i_dirt = 1; + mark_inode_dirty(linked); } - inode->i_dirt = 0; if (inode->i_ino == MSDOS_ROOT_INO || !inode->i_nlink) return; if (!(bh = fat_bread(sb, inode->i_ino >> MSDOS_DPB_BITS))) { printk("dev = %s, ino = %ld\n", diff --git a/fs/fat/misc.c b/fs/fat/misc.c index d1e9bc6ca..034f62c1f 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -19,13 +19,14 @@ /* Well-known binary file extensions - of course there are many more */ -static char bin_extensions[] = - "EXE" "COM" "BIN" "APP" "SYS" "DRV" "OVL" "OVR" "OBJ" "LIB" "DLL" "PIF" /* program code */ - "ARC" "ZIP" "LHA" "LZH" "ZOO" "TAR" "Z " "ARJ" /* common archivers */ - "TZ " "TAZ" "TZP" "TPZ" /* abbreviations of tar.Z and tar.zip */ - "GZ " "TGZ" "DEB" /* .gz, .tar.gz and Debian packages */ - "GIF" "BMP" "TIF" "GL " "JPG" "PCX" /* graphics */ - "TFM" "VF " "GF " "PK " "PXL" "DVI"; /* TeX */ +static char ascii_extensions[] = + "TXT" "ME " "HTM" "1ST" "LOG" " " /* text files */ + "C " "H " "CPP" "LIS" "PAS" "FOR" /* programming languages */ + "F " "MAK" "INC" "BAS" /* programming languages */ + "BAT" "SH " /* program code :) */ + "INI" /* config files */ + "PBM" "PGM" "DXF" /* graphics */ + "TEX"; /* TeX */ /* @@ -39,9 +40,7 @@ void fat_fs_panic(struct super_block *s,const char *msg) not_ro = !(s->s_flags & MS_RDONLY); if (not_ro) s->s_flags |= MS_RDONLY; - printk("Filesystem panic (dev %s, ", kdevname(s->s_dev)); - printk("mounted on %s:%ld)\n %s\n", /* note: kdevname returns & static char[] */ - kdevname(s->s_covered->i_dev), s->s_covered->i_ino, msg); + printk("Filesystem panic (dev %s).", kdevname(s->s_dev)); if (not_ro) printk(" File system has been set read-only\n"); } @@ -62,9 +61,9 @@ int is_binary(char conversion,char *extension) case 't': return 0; case 'a': - for (walk = bin_extensions; *walk; walk += 3) - if (!strncmp(extension,walk,3)) return 1; - return 0; + for (walk = ascii_extensions; *walk; walk += 3) + if (!strncmp(extension,walk,3)) return 0; + return 1; /* default binary conversion */ default: printk("Invalid conversion mode - defaulting to " "binary.\n"); @@ -179,7 +178,7 @@ printk("last = %d\n",last); if (last) fat_access(sb,last,nr); else { MSDOS_I(inode)->i_start = nr; - inode->i_dirt = 1; + mark_inode_dirty(inode); } #ifdef DEBUG if (last) printk("next set to %d\n",fat_access(sb,last,-1)); @@ -216,7 +215,7 @@ if (last) printk("next set to %d\n",fat_access(sb,last,-1)); #ifdef DEBUG printk("size is %d now (%x)\n",inode->i_size,inode); #endif - inode->i_dirt = 1; + mark_inode_dirty(inode); } return 0; } diff --git a/fs/fat/mmap.c b/fs/fat/mmap.c index 6a3515eef..a858cd2cc 100644 --- a/fs/fat/mmap.c +++ b/fs/fat/mmap.c @@ -29,7 +29,7 @@ static unsigned long fat_file_mmap_nopage( unsigned long address, int error_code) { - struct inode * inode = area->vm_inode; + struct inode * inode = area->vm_dentry->d_inode; unsigned long page; unsigned int clear; int pos; @@ -101,11 +101,10 @@ int fat_mmap(struct inode * inode, struct file * file, struct vm_area_struct * v return -EACCES; if (!IS_RDONLY(inode)) { inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); } - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + vma->vm_dentry = dget(file->f_dentry); vma->vm_ops = &fat_file_mmap; return 0; } diff --git a/fs/fcntl.c b/fs/fcntl.c index 6418a5d83..57eef2530 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -67,6 +67,34 @@ asmlinkage int sys_dup(unsigned int fildes) return ret; } +#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | FASYNC) + +static int setfl(struct file * filp, unsigned long arg) +{ + struct inode * inode = filp->f_dentry->d_inode; + + /* + * In the case of an append-only file, O_APPEND + * cannot be cleared + */ + if (!(arg & O_APPEND) && IS_APPEND(inode)) + return -EPERM; + + /* Did FASYNC state change? */ + if ((arg ^ filp->f_flags) & FASYNC) { + if (filp->f_op->fasync) + filp->f_op->fasync(inode, filp, (arg & FASYNC) != 0); + } + + /* required for strict SunOS emulation */ + if (O_NONBLOCK != O_NDELAY) + if (arg & O_NDELAY) + arg |= O_NONBLOCK; + + filp->f_flags = (arg & SETFL_MASK) | (filp->f_flags & ~SETFL_MASK); + return 0; +} + asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) { struct file * filp; @@ -95,28 +123,7 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) err = filp->f_flags; break; case F_SETFL: - /* - * In the case of an append-only file, O_APPEND - * cannot be cleared - */ - err = -EPERM; - if (IS_APPEND(filp->f_inode) && !(arg & O_APPEND)) - break; - err = 0; - if ((arg & FASYNC) && !(filp->f_flags & FASYNC) && - filp->f_op->fasync) - filp->f_op->fasync(filp->f_inode, filp, 1); - if (!(arg & FASYNC) && (filp->f_flags & FASYNC) && - filp->f_op->fasync) - filp->f_op->fasync(filp->f_inode, filp, 0); - /* required for strict SunOS emulation */ - if (O_NONBLOCK != O_NDELAY) - if (arg & O_NDELAY) - arg |= O_NONBLOCK; - filp->f_flags &= ~(O_APPEND | O_NONBLOCK | - O_NDELAY | FASYNC); - filp->f_flags |= arg & (O_APPEND | O_NONBLOCK | - O_NDELAY | FASYNC); + err = setfl(filp, arg); break; case F_GETLK: err = fcntl_getlk(fd, (struct flock *) arg); @@ -186,12 +193,12 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) fasync_ok: err = 0; filp->f_owner = arg; - if (S_ISSOCK (filp->f_inode->i_mode)) + if (S_ISSOCK (filp->f_dentry->d_inode->i_mode)) err = sock_fcntl (filp, F_SETOWN, arg); break; default: /* sockets need a few special fcntls. */ - if (S_ISSOCK (filp->f_inode->i_mode)) + if (S_ISSOCK (filp->f_dentry->d_inode->i_mode)) err = sock_fcntl (filp, cmd, arg); else err = -EINVAL; @@ -153,7 +153,6 @@ struct inode_operations fifo_inode_operations = { void init_fifo(struct inode * inode) { inode->i_op = &fifo_inode_operations; - inode->i_pipe = 1; PIPE_LOCK(*inode) = 0; PIPE_BASE(*inode) = NULL; PIPE_START(*inode) = PIPE_LEN(*inode) = 0; diff --git a/fs/file_table.c b/fs/file_table.c index b8c8d4155..6413218ff 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -102,6 +102,24 @@ again: return f; } +/* + * Clear and initialize a (private) struct file for the given dentry, + * and call the open function (if any). The caller must verify that + * inode->i_op and inode->i_op->default_file_ops are not NULL. + */ +int init_private_file(struct file *filp, struct dentry *dentry, int mode) +{ + memset(filp, 0, sizeof(*filp)); + filp->f_mode = mode; + filp->f_count = 1; + filp->f_dentry = dentry; + filp->f_op = dentry->d_inode->i_op->default_file_ops; + if (filp->f_op->open) + return filp->f_op->open(dentry->d_inode, filp); + else + return 0; +} + #ifdef CONFIG_QUOTA void add_dquot_ref(kdev_t dev, short type) @@ -109,11 +127,15 @@ void add_dquot_ref(kdev_t dev, short type) struct file *filp; for (filp = inuse_filps; filp; filp = filp->f_next) { - if (!filp->f_inode || filp->f_inode->i_dev != dev) + struct inode * inode; + if (!filp->f_dentry) + continue; + inode = filp->f_dentry->d_inode; + if (!inode || inode->i_dev != dev) continue; - if (filp->f_mode & FMODE_WRITE && filp->f_inode->i_sb->dq_op) { - filp->f_inode->i_sb->dq_op->initialize(filp->f_inode, type); - filp->f_inode->i_flags |= S_WRITE; + if (filp->f_mode & FMODE_WRITE && inode->i_sb->dq_op) { + inode->i_sb->dq_op->initialize(inode, type); + inode->i_flags |= S_WRITE; } } } @@ -123,11 +145,15 @@ void reset_dquot_ptrs(kdev_t dev, short type) struct file *filp; for (filp = inuse_filps; filp; filp = filp->f_next) { - if (!filp->f_inode || filp->f_inode->i_dev != dev) + struct inode * inode; + if (!filp->f_dentry) + continue; + inode = filp->f_dentry->d_inode; + if (!inode || inode->i_dev != dev) continue; - if (IS_WRITABLE(filp->f_inode)) { - filp->f_inode->i_dquot[type] = NODQUOT; - filp->f_inode->i_flags &= ~S_WRITE; + if (IS_WRITABLE(inode)) { + inode->i_dquot[type] = NODQUOT; + inode->i_flags &= ~S_WRITE; } } } diff --git a/fs/filesystems.c b/fs/filesystems.c index 004ee0aff..74016aa67 100644 --- a/fs/filesystems.c +++ b/fs/filesystems.c @@ -8,7 +8,6 @@ #include <linux/config.h> #include <linux/fs.h> -#include <linux/nametrans.h> #include <linux/minix_fs.h> #include <linux/ext2_fs.h> @@ -45,10 +44,6 @@ __initfunc(static void do_sys_setup(void)) binfmt_setup(); -#ifdef CONFIG_TRANS_NAMES - init_nametrans(); -#endif - #ifdef CONFIG_EXT2_FS init_ext2_fs(); #endif diff --git a/fs/inode.c b/fs/inode.c index 7215e1204..8813bbd45 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1,708 +1,500 @@ /* - * fs/inode.c + * linux/fs/inode.c * - * Complete reimplementation - * (C) 1997 Thomas Schoebel-Theuer + * (C) 1997 Linus Torvalds */ -/* Everything here is intended to be MP-safe. However, other parts - * of the kernel are not yet MP-safe, in particular the inode->i_count++ - * that are spread over everywhere. These should be replaced by - * iinc() as soon as possible. Since I have no MP machine, I could - * not test it. - */ -#include <linux/config.h> -#include <linux/errno.h> #include <linux/fs.h> #include <linux/string.h> #include <linux/mm.h> -#include <linux/kernel.h> -#include <linux/dlists.h> -#include <linux/dalloc.h> -#include <linux/omirr.h> - -/* #define DEBUG */ - -#define HASH_SIZE 1024 /* must be a power of 2 */ -#define NR_LEVELS 4 - -#define ST_AGED 1 -#define ST_HASHED 2 -#define ST_EMPTY 4 -#define ST_TO_READ 8 -#define ST_TO_WRITE 16 -#define ST_TO_PUT 32 -#define ST_TO_DROP 64 -#define ST_IO (ST_TO_READ|ST_TO_WRITE|ST_TO_PUT|ST_TO_DROP) -#define ST_WAITING 128 -#define ST_FREEING 256 -#define ST_IBASKET 512 - -/* The idea is to keep empty inodes in a separate list, so no search - * is required as long as empty inodes exit. - * All reusable inodes occurring in the hash table with i_count==0 - * are also registered in the ringlist aged_i[level], but in LRU order. - * Used inodes with i_count>0 are kept solely in the hashtable and in - * all_i, but in no other list. - * The level is used for multilevel aging to avoid thrashing; each - * time i_count decreases to 0, the inode is inserted into the next level - * ringlist. Cache reusage is simply by taking the _last_ element from the - * lowest-level ringlist that contains inodes. - * In contrast to the old code, there isn't any O(n) search overhead now - * in iget/iput (if you make HASH_SIZE large enough). + +/* + * New inode.c implementation. + * + * This implementation has the basic premise of trying + * to be extremely low-overhead and SMP-safe, yet be + * simple enough to be "obviously correct". + * + * Famous last words. + */ + +/* + * Inode lookup is no longer as critical as it used to be: + * most of the lookups are going to be through the dcache. + */ +#define HASH_BITS 8 +#define HASH_SIZE (1UL << HASH_BITS) +#define HASH_MASK (HASH_SIZE-1) + +/* + * Each inode can be on two separate lists. One is + * the hash list of the inode, used for lookups. The + * other linked list is the "type" list: + * "in_use" - valid inode, hashed + * "dirty" - valid inode, hashed, dirty. + * "unused" - ready to be re-used. Not hashed. + * + * The two first versions also have a dirty list, allowing + * for low-overhead inode sync() operations. + */ + +static LIST_HEAD(inode_in_use); +static LIST_HEAD(inode_dirty); +static LIST_HEAD(inode_unused); +static struct list_head inode_hashtable[HASH_SIZE]; + +/* + * A simple spinlock to protect the list manipulations + */ +spinlock_t inode_lock = SPIN_LOCK_UNLOCKED; + +/* + * Statistics gathering.. Not actually done yet. */ -static struct inode * hashtable[HASH_SIZE];/* linked with i_hash_{next,prev} */ -static struct inode * all_i = NULL; /* linked with i_{next,prev} */ -static struct inode * empty_i = NULL; /* linked with i_{next,prev} */ -static struct inode * aged_i[NR_LEVELS+1]; /* linked with i_lru_{next,prev} */ -static int aged_reused[NR_LEVELS+1]; /* # removals from aged_i[level] */ -static int age_table[NR_LEVELS+1] = { /* You may tune this. */ - 1, 4, 10, 100, 1000 -}; /* after which # of uses to increase to the next level */ - -/* This is for kernel/sysctl.c */ - -/* Just aligning plain ints and arrays thereof doesn't work reliably.. */ struct { int nr_inodes; int nr_free_inodes; - int aged_count[NR_LEVELS+1]; /* # in each level */ + int dummy[10]; } inodes_stat; int max_inodes = NR_INODE; -unsigned long last_inode = 0; -void inode_init(void) +void __mark_inode_dirty(struct inode *inode) { - memset(hashtable, 0, sizeof(hashtable)); - memset(aged_i, 0, sizeof(aged_i)); - memset(aged_reused, 0, sizeof(aged_reused)); - memset(&inodes_stat, 0, sizeof(inodes_stat)); + spin_lock(&inode_lock); + list_del(&inode->i_list); + list_add(&inode->i_list, &inode_dirty); + spin_unlock(&inode_lock); } -/* Intended for short locks of the above global data structures. - * Could be replaced with spinlocks completely, since there is - * no blocking during manipulation of the static data; however the - * lock in invalidate_inodes() may last relatively long. - */ -#ifdef __SMP__ -struct semaphore vfs_sem = MUTEX; -#endif - -DEF_INSERT(all,struct inode,i_next,i_prev) -DEF_REMOVE(all,struct inode,i_next,i_prev) - -DEF_INSERT(lru,struct inode,i_lru_next,i_lru_prev) -DEF_REMOVE(lru,struct inode,i_lru_next,i_lru_prev) - -DEF_INSERT(hash,struct inode,i_hash_next,i_hash_prev) -DEF_REMOVE(hash,struct inode,i_hash_next,i_hash_prev) - -DEF_INSERT(ibasket,struct inode,i_basket_next,i_basket_prev) -DEF_REMOVE(ibasket,struct inode,i_basket_next,i_basket_prev) - -#ifdef DEBUG -extern void printpath(struct dentry * entry); -struct inode * xtst[15000]; -int xcnt = 0; - -void xcheck(char * txt, struct inode * p) +static inline void unlock_inode(struct inode *inode) { - int i; - for(i=xcnt-1; i>=0; i--) - if(xtst[i] == p) - return; - printk("Bogus inode %p in %s\n", p, txt); + clear_bit(I_LOCK, &inode->i_state); + wake_up(&inode->i_wait); } -#else -#define xcheck(t,p) /*nothing*/ -#endif -static inline struct inode * grow_inodes(void) +static void __wait_on_inode(struct inode * inode) { - struct inode * res; - struct inode * inode = res = (struct inode*)__get_free_page(GFP_KERNEL); - int size = PAGE_SIZE; - if(!inode) - return NULL; - - size -= sizeof(struct inode); - inode++; - inodes_stat.nr_inodes++; -#ifdef DEBUG -xtst[xcnt++]=res; -#endif - while(size >= sizeof(struct inode)) { -#ifdef DEBUG -xtst[xcnt++]=inode; -#endif - inodes_stat.nr_inodes++; - inodes_stat.nr_free_inodes++; - insert_all(&empty_i, inode); - inode->i_status = ST_EMPTY; - inode++; - size -= sizeof(struct inode); + struct wait_queue wait = { current, NULL }; + + add_wait_queue(&inode->i_wait, &wait); +repeat: + current->state = TASK_UNINTERRUPTIBLE; + if (test_bit(I_LOCK, &inode->i_state)) { + schedule(); + goto repeat; } - return res; + remove_wait_queue(&inode->i_wait, &wait); + current->state = TASK_RUNNING; } -static inline int hash(dev_t i_dev, unsigned long i_ino) +static inline void wait_on_inode(struct inode *inode) { - return ((int)i_ino ^ ((int)i_dev << 6)) & (HASH_SIZE-1); + if (test_bit(I_LOCK, &inode->i_state)) + __wait_on_inode(inode); } -static inline blocking void wait_io(struct inode * inode, unsigned short flags) +/* + * These are initializations that only need to be done + * once, because the fields are idempotent across use + * of the inode.. + */ +static inline void init_once(struct inode * inode) { - while(inode->i_status & flags) { - struct wait_queue wait = {current, NULL}; - inode->i_status |= ST_WAITING; - vfs_unlock(); - add_wait_queue(&inode->i_wait, &wait); - sleep_on(&inode->i_wait); - remove_wait_queue(&inode->i_wait, &wait); - vfs_lock(); - } + memset(inode, 0, sizeof(*inode)); + init_waitqueue(&inode->i_wait); + INIT_LIST_HEAD(&inode->i_dentry); + INIT_LIST_HEAD(&inode->i_hash); + sema_init(&inode->i_sem, 1); } -static inline blocking void set_io(struct inode * inode, - unsigned short waitflags, - unsigned short setflags) + +/* + * Look out! This returns with the inode lock held if + * it got an inode.. + */ +static struct inode * grow_inodes(void) { - wait_io(inode, waitflags); - inode->i_status |= setflags; - vfs_unlock(); + struct inode * inode = (struct inode *)__get_free_page(GFP_KERNEL); + + if (inode) { + int size; + struct inode * tmp; + + spin_lock(&inode_lock); + size = PAGE_SIZE - 2*sizeof(struct inode); + tmp = inode; + do { + tmp++; + init_once(tmp); + list_add(&tmp->i_list, &inode_unused); + size -= sizeof(struct inode); + } while (size >= 0); + init_once(inode); + } + return inode; } -static inline blocking int release_io(struct inode * inode, unsigned short flags) +static inline void write_inode(struct inode *inode) { - int res = 0; - vfs_lock(); - inode->i_status &= ~flags; - if(inode->i_status & ST_WAITING) { - inode->i_status &= ~ST_WAITING; - vfs_unlock(); - wake_up(&inode->i_wait); - res = 1; - } - return res; + if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->write_inode) + inode->i_sb->s_op->write_inode(inode); } -static inline blocking void _io(void (*op)(struct inode*), struct inode * inode, - unsigned short waitflags, unsigned short setflags) +static inline void sync_list(struct list_head *head, struct list_head *clean) { - /* Do nothing if the same op is already in progress. */ - if(op && !(inode->i_status & setflags)) { - set_io(inode, waitflags, setflags); - op(inode); - if(release_io(inode, setflags)) { - /* Somebody grabbed my inode from under me. */ -#ifdef DEBUG - printk("_io grab!\n"); -#endif - vfs_lock(); + struct list_head * tmp; + + while ((tmp = head->prev) != head) { + struct inode *inode = list_entry(tmp, struct inode, i_list); + list_del(tmp); + + /* + * If the inode is locked, it's already being written out. + * We have to wait for it, though. + */ + if (test_bit(I_LOCK, &inode->i_state)) { + list_add(tmp, head); + spin_unlock(&inode_lock); + __wait_on_inode(inode); + } else { + list_add(tmp, clean); + clear_bit(I_DIRTY, &inode->i_state); + set_bit(I_LOCK, &inode->i_state); + spin_unlock(&inode_lock); + write_inode(inode); + unlock_inode(inode); } - } + spin_lock(&inode_lock); + } } -blocking int _free_ibasket(struct super_block * sb) +/* + * "sync_inodes()" goes through the dirty list + * and writes them out and puts them back on + * the normal list. + */ +void sync_inodes(kdev_t dev) { - if(sb->s_ibasket) { - struct inode * delinquish = sb->s_ibasket->i_basket_prev; -#if 0 -printpath(delinquish->i_dentry); -printk(" delinquish\n"); -#endif - _clear_inode(delinquish, 0, 1); - return 1; - } - return 0; + spin_lock(&inode_lock); + sync_list(&inode_dirty, &inode_in_use); + spin_unlock(&inode_lock); } -static /*inline*/ void _put_ibasket(struct inode * inode) +/* + * This is called by the filesystem to tell us + * that the inode is no longer useful. We just + * terminate it with extreme predjudice. + */ +void clear_inode(struct inode *inode) { - struct super_block * sb = inode->i_sb; - if(!(inode->i_status & ST_IBASKET)) { - inode->i_status |= ST_IBASKET; - insert_ibasket(&sb->s_ibasket, inode); - sb->s_ibasket_count++; - if(sb->s_ibasket_count > sb->s_ibasket_max) - (void)_free_ibasket(sb); - } + truncate_inode_pages(inode, 0); + wait_on_inode(inode); + if (IS_WRITABLE(inode) && inode->i_sb && inode->i_sb->dq_op) + inode->i_sb->dq_op->drop(inode); + + inode->i_state = 0; } -blocking void _clear_inode(struct inode * inode, int external, int verbose) +#define CAN_UNUSE(inode) \ + (((inode)->i_count == 0) && \ + ((inode)->i_nrpages == 0) && \ + (!(inode)->i_state)) + +static void invalidate_list(struct list_head *head, kdev_t dev) { -xcheck("_clear_inode",inode); - if(inode->i_status & ST_IBASKET) { - struct super_block * sb = inode->i_sb; - remove_ibasket(&sb->s_ibasket, inode); - sb->s_ibasket_count--; - inode->i_status &= ~ST_IBASKET; -#if 0 -printpath(inode->i_dentry); -printk(" put_inode\n"); -#endif - _io(sb->s_op->put_inode, inode, ST_TO_PUT|ST_TO_WRITE, ST_TO_PUT); - if(inode->i_status & ST_EMPTY) - return; + struct list_head *next; + + next = head->next; + for (;;) { + struct list_head * tmp = next; + struct inode * inode; + + next = next->next; + if (tmp == head) + break; + inode = list_entry(tmp, struct inode, i_list); + if (inode->i_dev != dev) + continue; + if (!CAN_UNUSE(inode)) + continue; + list_del(&inode->i_hash); + INIT_LIST_HEAD(&inode->i_hash); + list_del(&inode->i_list); + list_add(&inode->i_list, &inode_unused); } - if(inode->i_status & ST_HASHED) - remove_hash(&hashtable[hash(inode->i_dev, inode->i_ino)], inode); - if(inode->i_status & ST_AGED) { - /* "cannot happen" when called from an fs because at least - * the caller must use it. Can happen when called from - * invalidate_inodes(). */ - if(verbose) - printk("VFS: clearing aged inode\n"); - if(atomic_read(&inode->i_count)) - printk("VFS: aged inode is in use\n"); - remove_lru(&aged_i[inode->i_level], inode); - inodes_stat.aged_count[inode->i_level]--; - } - if(!external && inode->i_status & ST_IO) { - printk("VFS: clearing inode during IO operation\n"); - } - if(!(inode->i_status & ST_EMPTY)) { - remove_all(&all_i, inode); - inode->i_status = ST_EMPTY; - while(inode->i_dentry) { - d_del(inode->i_dentry, D_NO_CLEAR_INODE); - } - if(inode->i_pages) { - vfs_unlock(); /* may block, can that be revised? */ - truncate_inode_pages(inode, 0); - vfs_lock(); - } - insert_all(&empty_i, inode); - inodes_stat.nr_free_inodes++; - } else if(external) - printk("VFS: empty inode is unnecessarily cleared multiple " - "times by an fs\n"); - else - printk("VFS: clearing empty inode\n"); - inode->i_status = ST_EMPTY; - /* The inode is not really cleared any more here, but only once - * when taken from empty_i. This saves instructions and processor - * cache pollution. - */ } -void insert_inode_hash(struct inode * inode) +void invalidate_inodes(kdev_t dev) { -xcheck("insert_inode_hash",inode); - vfs_lock(); - if(!(inode->i_status & ST_HASHED)) { - insert_hash(&hashtable[hash(inode->i_dev, inode->i_ino)], inode); - inode->i_status |= ST_HASHED; - } else - printk("VFS: trying to hash an inode again\n"); - vfs_unlock(); + spin_lock(&inode_lock); + invalidate_list(&inode_in_use, dev); + invalidate_list(&inode_dirty, dev); + spin_unlock(&inode_lock); } -blocking struct inode * _get_empty_inode(void) +/* + * This is called with the inode lock held. It just looks at the last + * inode on the in-use list, and if the inode is trivially freeable + * we just move it to the unused list. + * + * Otherwise we just move the inode to be the first inode and expect to + * get back to the problem later.. + */ +static void try_to_free_inodes(void) { - struct inode * inode; - int retry = 0; - -retry: - inode = empty_i; - if(inode) { - remove_all(&empty_i, inode); - inodes_stat.nr_free_inodes--; - } else if(inodes_stat.nr_inodes < max_inodes || retry > 2) { - inode = grow_inodes(); - } - if(!inode) { - int level; - int usable = 0; - for(level = 0; level <= NR_LEVELS; level++) - if(aged_i[level]) { - inode = aged_i[level]->i_lru_prev; - /* Here is the picking strategy, tune this */ - if(aged_reused[level] < (usable++ ? - inodes_stat.aged_count[level] : - 2)) - break; - aged_reused[level] = 0; - } - if(inode) { - if(!(inode->i_status & ST_AGED)) - printk("VFS: inode aging inconsistency\n"); - if(atomic_read(&inode->i_count) + inode->i_ddir_count) - printk("VFS: i_count of aged inode is not zero\n"); - if(inode->i_dirt) - printk("VFS: Hey, somebody made my aged inode dirty\n"); - _clear_inode(inode, 0, 0); - goto retry; + struct list_head * tmp; + struct list_head *head = &inode_in_use; + + tmp = head->prev; + if (tmp != head) { + struct inode * inode; + + list_del(tmp); + inode = list_entry(tmp, struct inode, i_list); + if (CAN_UNUSE(inode)) { + list_del(&inode->i_hash); + INIT_LIST_HEAD(&inode->i_hash); + head = &inode_unused; } + list_add(tmp, head); } - if(!inode) { - vfs_unlock(); - schedule(); - if(retry > 10) - panic("VFS: cannot repair inode shortage"); - if(retry > 2) - printk("VFS: no free inodes\n"); - retry++; - vfs_lock(); - goto retry; - } -xcheck("get_empty_inode",inode); - memset(inode, 0, sizeof(struct inode)); - atomic_set(&inode->i_count, 1); - inode->i_nlink = 1; - sema_init(&inode->i_sem, 1); - inode->i_ino = ++last_inode; - inode->i_version = ++event; - insert_all(&all_i, inode); - return inode; } + -static inline blocking struct inode * _get_empty_inode_hashed(dev_t i_dev, - unsigned long i_ino) +static struct inode * find_inode(struct super_block * sb, unsigned long ino, struct list_head *head) { - struct inode ** base = &hashtable[hash(i_dev, i_ino)]; - struct inode * inode = *base; - if(inode) do { - if(inode->i_ino == i_ino && inode->i_dev == i_dev) { - atomic_inc(&inode->i_count); - printk("VFS: inode %lx is already in use\n", i_ino); - return inode; - } - inode = inode->i_hash_next; - } while(inode != *base); - inode = _get_empty_inode(); - inode->i_dev = i_dev; - inode->i_ino = i_ino; - insert_hash(base, inode); - inode->i_status |= ST_HASHED; + struct list_head *tmp; + struct inode * inode; + + tmp = head; + for (;;) { + tmp = tmp->next; + inode = NULL; + if (tmp == head) + break; + inode = list_entry(tmp, struct inode, i_hash); + if (inode->i_sb != sb) + continue; + if (inode->i_ino != ino) + continue; + inode->i_count++; + break; + } return inode; } -blocking struct inode * get_empty_inode_hashed(dev_t i_dev, unsigned long i_ino) +/* + * This just initializes the inode fields + * to known values before returning the inode.. + * + * i_sb, i_ino, i_count, i_state and the lists have + * been initialized elsewhere.. + */ +void clean_inode(struct inode *inode) { - struct inode * inode; - - vfs_lock(); - inode = _get_empty_inode_hashed(i_dev, i_ino); - vfs_unlock(); - return inode; + memset(&inode->u, 0, sizeof(inode->u)); + inode->i_sock = 0; + inode->i_op = NULL; + inode->i_nlink = 1; + inode->i_writecount = 0; + inode->i_size = 0; + memset(&inode->i_dquot, 0, sizeof(inode->i_dquot)); + sema_init(&inode->i_sem, 1); } -void _get_inode(struct inode * inode) +/* + * This gets called with I_LOCK held: it needs + * to read the inode and then unlock it + */ +static inline void read_inode(struct inode *inode, struct super_block *sb) { - if(inode->i_status & ST_IBASKET) { - inode->i_status &= ~ST_IBASKET; - remove_ibasket(&inode->i_sb->s_ibasket, inode); - inode->i_sb->s_ibasket_count--; - } - if(inode->i_status & ST_AGED) { - inode->i_status &= ~ST_AGED; - remove_lru(&aged_i[inode->i_level], inode); - inodes_stat.aged_count[inode->i_level]--; - aged_reused[inode->i_level]++; - if(S_ISDIR(inode->i_mode)) - /* make dirs less thrashable */ - inode->i_level = NR_LEVELS-1; - else if(inode->i_nlink > 1) - /* keep hardlinks totally separate */ - inode->i_level = NR_LEVELS; - else if(++inode->i_reuse_count >= age_table[inode->i_level] - && inode->i_level < NR_LEVELS-1) - inode->i_level++; - if(atomic_read(&inode->i_count) != 1) - printk("VFS: inode count was not zero\n"); - } else if(inode->i_status & ST_EMPTY) - printk("VFS: invalid reuse of empty inode\n"); + sb->s_op->read_inode(inode); + unlock_inode(inode); } -blocking struct inode * __iget(struct super_block * sb, - unsigned long i_ino, - int crossmntp) +struct inode * get_empty_inode(void) { - struct inode ** base; + static unsigned long last_ino = 0; struct inode * inode; - dev_t i_dev; - - if(!sb) - panic("VFS: iget with sb == NULL"); - i_dev = sb->s_dev; - if(!i_dev) - panic("VFS: sb->s_dev is NULL\n"); - base = &hashtable[hash(i_dev, i_ino)]; - vfs_lock(); - inode = *base; - if(inode) do { - if(inode->i_ino == i_ino && inode->i_dev == i_dev) { - atomic_inc(&inode->i_count); - _get_inode(inode); - - /* Allow concurrent writes/puts. This is in particular - * useful e.g. when syncing large chunks. - * I hope the i_dirty flag is everywhere set as soon - * as _any_ modifcation is made and _before_ - * giving up control, so no harm should occur if data - * is modified during writes, because it will be - * rewritten again (does a short inconsistency on the - * disk harm?) - */ - wait_io(inode, ST_TO_READ); - vfs_unlock(); - goto done; - } - inode = inode->i_hash_next; - } while(inode != *base); - inode = _get_empty_inode_hashed(i_dev, i_ino); - inode->i_sb = sb; - inode->i_flags = sb->s_flags; - if(sb->s_op && sb->s_op->read_inode) { - set_io(inode, 0, ST_TO_READ); /* do not wait at all */ - sb->s_op->read_inode(inode); - if(release_io(inode, ST_TO_READ)) - goto done; - } - vfs_unlock(); -done: - while(crossmntp && inode->i_mount) { - struct inode * tmp = inode->i_mount; - iinc(tmp); - iput(inode); - inode = tmp; + struct list_head * tmp; + + spin_lock(&inode_lock); + try_to_free_inodes(); + tmp = inode_unused.next; + if (tmp != &inode_unused) { + list_del(tmp); + inode = list_entry(tmp, struct inode, i_list); +add_new_inode: + inode->i_sb = NULL; + inode->i_ino = ++last_ino; + inode->i_count = 1; + list_add(&inode->i_list, &inode_in_use); + inode->i_state = 0; + spin_unlock(&inode_lock); + clean_inode(inode); + return inode; } -xcheck("_iget",inode); + + /* + * Warning: if this succeeded, we will now + * return with the inode lock. + */ + spin_unlock(&inode_lock); + inode = grow_inodes(); + if (inode) + goto add_new_inode; + return inode; } -blocking void __iput(struct inode * inode) +/* + * This is called with the inode lock held.. Be careful. + */ +static struct inode * get_new_inode(struct super_block *sb, unsigned long ino, struct list_head *head) { - struct super_block * sb; -xcheck("_iput",inode); - if(atomic_read(&inode->i_count) + inode->i_ddir_count < 0) - printk("VFS: i_count is negative\n"); - if((atomic_read(&inode->i_count) + inode->i_ddir_count) || - (inode->i_status & ST_FREEING)) { - return; - } - inode->i_status |= ST_FREEING; -#ifdef CONFIG_OMIRR - if(inode->i_status & ST_MODIFIED) { - inode->i_status &= ~ST_MODIFIED; - omirr_printall(inode, " W %ld ", CURRENT_TIME); - } -#endif - if(inode->i_pipe) { - free_page((unsigned long)PIPE_BASE(*inode)); - PIPE_BASE(*inode)= NULL; - } - if((sb = inode->i_sb)) { - if(sb->s_type && (sb->s_type->fs_flags & FS_NO_DCACHE)) { - while(inode->i_dentry) - d_del(inode->i_dentry, D_NO_CLEAR_INODE); - if(atomic_read(&inode->i_count) + inode->i_ddir_count) - goto done; - } - if(sb->s_op) { - if(inode->i_nlink <= 0 && inode->i_dent_count && - !(inode->i_status & (ST_EMPTY|ST_IBASKET)) && - (sb->s_type->fs_flags & FS_IBASKET)) { - _put_ibasket(inode); - goto done; - } - if(!inode->i_dent_count || - (sb->s_type->fs_flags & FS_NO_DCACHE)) { - _io(sb->s_op->put_inode, inode, - ST_TO_PUT|ST_TO_WRITE, ST_TO_PUT); - if(atomic_read(&inode->i_count) + inode->i_ddir_count) - goto done; - if(inode->i_nlink <= 0) { - if(!(inode->i_status & ST_EMPTY)) { - _clear_inode(inode, 0, 1); - } - goto done; - } - } - if(inode->i_dirt) { - inode->i_dirt = 0; - _io(sb->s_op->write_inode, inode, - ST_TO_PUT|ST_TO_WRITE, ST_TO_WRITE); - if(atomic_read(&inode->i_count) + inode->i_ddir_count) - goto done; - } - } - if(IS_WRITABLE(inode) && sb->dq_op) { - /* can operate in parallel to other ops ? */ - _io(sb->dq_op->drop, inode, 0, ST_TO_DROP); - if(atomic_read(&inode->i_count) + inode->i_ddir_count) - goto done; - } - } - if(inode->i_mmap) - printk("VFS: inode has mappings\n"); - if(inode->i_status & ST_AGED) { - printk("VFS: reaging inode\n"); -#if defined(DEBUG) -printpath(inode->i_dentry); -printk("\n"); -#endif - goto done; - } - if(!(inode->i_status & (ST_HASHED|ST_EMPTY))) { - _clear_inode(inode, 0, 1); - goto done; + struct inode * inode; + struct list_head * tmp = inode_unused.next; + + if (tmp != &inode_unused) { + list_del(tmp); + inode = list_entry(tmp, struct inode, i_list); +add_new_inode: + list_add(&inode->i_list, &inode_in_use); + list_add(&inode->i_hash, head); + inode->i_sb = sb; + inode->i_dev = sb->s_dev; + inode->i_ino = ino; + inode->i_flags = sb->s_flags; + inode->i_count = 1; + inode->i_state = 1 << I_LOCK; + spin_unlock(&inode_lock); + clean_inode(inode); + read_inode(inode, sb); + return inode; } - if(inode->i_status & ST_EMPTY) { - printk("VFS: aging an empty inode\n"); - goto done; + + /* + * Uhhuh.. We need to expand. Unlock for the allocation, + * but note that "grow_inodes()" will return with the + * lock held again if the allocation succeeded. + */ + spin_unlock(&inode_lock); + inode = grow_inodes(); + if (inode) { + /* We released the lock, so.. */ + struct inode * old = find_inode(sb, ino, head); + if (!old) + goto add_new_inode; + list_add(&inode->i_list, &inode_unused); + spin_unlock(&inode_lock); + wait_on_inode(old); + return old; } - insert_lru(&aged_i[inode->i_level], inode); - inodes_stat.aged_count[inode->i_level]++; - inode->i_status |= ST_AGED; -done: - inode->i_status &= ~ST_FREEING; + return inode; } -blocking void _iput(struct inode * inode) +static inline unsigned long hash(struct super_block *sb, unsigned long i_ino) { - vfs_lock(); - __iput(inode); - vfs_unlock(); + unsigned long tmp = i_ino | (unsigned long) sb; + tmp = tmp + (tmp >> HASH_BITS) + (tmp >> HASH_BITS*2); + return tmp & HASH_MASK; } -blocking void sync_inodes(kdev_t dev) +struct inode *iget(struct super_block *sb, unsigned long ino) { + struct list_head * head = inode_hashtable + hash(sb,ino); struct inode * inode; - vfs_lock(); - inode = all_i; - if(inode) do { -xcheck("sync_inodes",inode); - if(inode->i_dirt && (inode->i_dev == dev || !dev)) { - if(inode->i_sb && inode->i_sb->s_op && - !(inode->i_status & ST_FREEING)) { - inode->i_dirt = 0; - _io(inode->i_sb->s_op->write_inode, inode, - ST_IO, ST_TO_WRITE); - } - } - inode = inode->i_next; - } while(inode != all_i); - vfs_unlock(); + + spin_lock(&inode_lock); + inode = find_inode(sb, ino, head); + if (!inode) { + try_to_free_inodes(); + return get_new_inode(sb, ino, head); + } + spin_unlock(&inode_lock); + wait_on_inode(inode); + return inode; } -blocking int _check_inodes(kdev_t dev, int complain) +void insert_inode_hash(struct inode *inode) { - struct inode * inode; - int bad = 0; - - vfs_lock(); -startover: - inode = all_i; - if(inode) do { - struct inode * next; -xcheck("_check_inodes",inode); - next = inode->i_next; - if(inode->i_dev == dev) { - if(inode->i_dirt || atomic_read(&inode->i_count)) { - bad++; - } else { - _clear_inode(inode, 0, 0); - - /* _clear_inode() may recursively clear other - * inodes, probably also the next one. - */ - if(next->i_status & ST_EMPTY) - goto startover; - } - } - inode = next; - } while(inode != all_i); - vfs_unlock(); - if(complain && bad) - printk("VFS: %d inode(s) busy on removed device `%s'\n", - bad, kdevname(dev)); - return (bad == 0); + struct list_head *head = inode_hashtable + hash(inode->i_sb, inode->i_ino); + list_add(&inode->i_hash, head); } -/*inline*/ void invalidate_inodes(kdev_t dev) +void iput(struct inode *inode) { - /* Requires two passes, because of the new dcache holding - * directories with i_count > 1. - */ - (void)_check_inodes(dev, 0); - (void)_check_inodes(dev, 1); + if (inode) { + struct super_operations *op = NULL; + + if (inode->i_sb && inode->i_sb->s_op) + op = inode->i_sb->s_op; + if (op && op->put_inode) + op->put_inode(inode); + + spin_lock(&inode_lock); + if (!--inode->i_count) { + if (!inode->i_nlink) { + list_del(&inode->i_hash); + INIT_LIST_HEAD(&inode->i_hash); + list_del(&inode->i_list); + INIT_LIST_HEAD(&inode->i_list); + if (op && op->delete_inode) { + void (*delete)(struct inode *) = op->delete_inode; + spin_unlock(&inode_lock); + delete(inode); + spin_lock(&inode_lock); + } + } + if (list_empty(&inode->i_hash)) { + list_del(&inode->i_list); + list_add(&inode->i_list, &inode_unused); + } + } + spin_unlock(&inode_lock); + } } -/*inline*/ int fs_may_mount(kdev_t dev) +int bmap(struct inode * inode, int block) { - return _check_inodes(dev, 0); + if (inode->i_op && inode->i_op->bmap) + return inode->i_op->bmap(inode, block); + return 0; } -int fs_may_remount_ro(kdev_t dev) +/* + * Initialize the hash tables + */ +void inode_init(void) { - (void)dev; - return 1; /* not checked any more */ + int i; + struct list_head *head = inode_hashtable; + + i = HASH_SIZE; + do { + INIT_LIST_HEAD(head); + head++; + i--; + } while (i); } -int fs_may_umount(kdev_t dev, struct inode * mount_root) +/* + * FIXME! These need to go through the in-use inodes to + * check whether we can mount/umount/remount. + */ +int fs_may_mount(kdev_t dev) { - struct inode * inode; - vfs_lock(); - inode = all_i; - if(inode) do { -xcheck("fs_may_umount",inode); - if(inode->i_dev == dev && atomic_read(&inode->i_count)) - if(inode != mount_root || atomic_read(&inode->i_count) > - (inode->i_mount == inode ? 2 : 1)) { - vfs_unlock(); - return 0; - } - inode = inode->i_next; - } while(inode != all_i); - vfs_unlock(); return 1; } -extern struct inode_operations pipe_inode_operations; - -blocking struct inode * get_pipe_inode(void) +int fs_may_umount(struct super_block *sb, struct dentry * root) { - struct inode * inode = get_empty_inode(); - - PIPE_BASE(*inode) = (char*)__get_free_page(GFP_USER); - if(!(PIPE_BASE(*inode))) { - iput(inode); - return NULL; - } - inode->i_blksize = PAGE_SIZE; - inode->i_pipe = 1; - inode->i_mode = S_IFIFO | S_IRUSR | S_IWUSR; - atomic_inc(&inode->i_count); - inode->i_uid = current->fsuid; - inode->i_gid = current->fsgid; - inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; - inode->i_op = &pipe_inode_operations; - PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1; - - /* I hope this does not introduce security problems. - * Please check and give me response. - */ - { - char dummyname[32]; - struct qstr dummy = { dummyname, 0 }; - struct dentry * new; - sprintf(dummyname, ".anonymous-pipe-%06lud", inode->i_ino); - dummy.len = strlen(dummyname); - vfs_lock(); - new = d_alloc(the_root, dummy.len, 0); - if(new) - d_add(new, inode, &dummy, D_BASKET); - vfs_unlock(); - } - return inode; + shrink_dcache(); + return root->d_count == 1; } -int bmap(struct inode * inode, int block) +int fs_may_remount_ro(struct super_block *sb) { - if (inode->i_op && inode->i_op->bmap) - return inode->i_op->bmap(inode, block); - return 0; + return 1; } diff --git a/fs/ioctl.c b/fs/ioctl.c index 6766506a8..11798a238 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -20,28 +20,27 @@ static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg) { int error; int block; + struct inode * inode = filp->f_dentry->d_inode; switch (cmd) { case FIBMAP: - if (filp->f_inode->i_op == NULL) + if (inode->i_op == NULL) return -EBADF; - if (filp->f_inode->i_op->bmap == NULL) + if (inode->i_op->bmap == NULL) return -EINVAL; if ((error = get_user(block, (int *) arg)) != 0) return error; - block = filp->f_inode->i_op->bmap(filp->f_inode,block); + block = inode->i_op->bmap(inode,block); return put_user(block, (int *) arg); case FIGETBSZ: - if (filp->f_inode->i_sb == NULL) + if (inode->i_sb == NULL) return -EBADF; - return put_user(filp->f_inode->i_sb->s_blocksize, - (int *) arg); + return put_user(inode->i_sb->s_blocksize, (int *) arg); case FIONREAD: - return put_user(filp->f_inode->i_size - filp->f_pos, - (int *) arg); + return put_user(inode->i_size - filp->f_pos, (int *) arg); } if (filp->f_op && filp->f_op->ioctl) - return filp->f_op->ioctl(filp->f_inode, filp, cmd, arg); + return filp->f_op->ioctl(inode, filp, cmd, arg); return -ENOTTY; } @@ -91,10 +90,10 @@ asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) break; default: - if (filp->f_inode && S_ISREG(filp->f_inode->i_mode)) + if (filp->f_dentry && filp->f_dentry->d_inode && S_ISREG(filp->f_dentry->d_inode->i_mode)) error = file_ioctl(filp, cmd, arg); else if (filp->f_op && filp->f_op->ioctl) - error = filp->f_op->ioctl(filp->f_inode, filp, cmd, arg); + error = filp->f_op->ioctl(filp->f_dentry->d_inode, filp, cmd, arg); else error = -ENOTTY; } diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c index e22c3ca3b..48321d356 100644 --- a/fs/isofs/dir.c +++ b/fs/isofs/dir.c @@ -54,6 +54,7 @@ struct inode_operations isofs_dir_inode_operations = NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ isofs_bmap, /* bmap */ @@ -61,20 +62,6 @@ struct inode_operations isofs_dir_inode_operations = NULL /* permission */ }; -static int parent_inode_number(struct inode * inode, struct iso_directory_record * de) -{ - int inode_number = inode->i_ino; - - if ((inode->i_sb->u.isofs_sb.s_firstdatazone) != inode->i_ino) - inode_number = inode->u.isofs_i.i_backlink; - - if (inode_number != -1) - return inode_number; - - /* This should never happen, but who knows. Try to be forgiving */ - return isofs_lookup_grandparent(inode, find_rock_ridge_relocation(de, inode)); -} - static int isofs_name_translate(char * old, int len, char * new) { int i, c; @@ -196,9 +183,7 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, /* Handle the case of the '..' directory */ if (de->name_len[0] == 1 && de->name[0] == 1) { - inode_number = parent_inode_number(inode, de); - if (inode_number == -1) - break; + inode_number = filp->f_dentry->d_parent->d_inode->i_ino; if (filldir(dirent, "..", 2, filp->f_pos, inode_number) < 0) break; filp->f_pos += de_len; diff --git a/fs/isofs/file.c b/fs/isofs/file.c index 2742283f7..d14a558a0 100644 --- a/fs/isofs/file.c +++ b/fs/isofs/file.c @@ -47,6 +47,7 @@ struct inode_operations isofs_file_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ generic_readpage, /* readpage */ NULL, /* writepage */ isofs_bmap, /* bmap */ diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index d081a4cdd..436c22140 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -54,9 +54,10 @@ void isofs_put_super(struct super_block *sb) static struct super_operations isofs_sops = { isofs_read_inode, - NULL, /* notify_change */ NULL, /* write_inode */ NULL, /* put_inode */ + NULL, /* delete_inode */ + NULL, /* notify_change */ isofs_put_super, NULL, /* write_super */ isofs_statfs, @@ -481,12 +482,12 @@ struct super_block *isofs_read_super(struct super_block *s,void *data, s->u.isofs_sb.s_mode = opt.mode & 0777; s->s_blocksize = opt.blocksize; s->s_blocksize_bits = blocksize_bits; - s->s_mounted = iget(s, (isonum_733(rootp->extent) + + s->s_root = d_alloc_root(iget(s, (isonum_733(rootp->extent) + isonum_711(rootp->ext_attr_length)) - << s -> u.isofs_sb.s_log_zone_size); + << s -> u.isofs_sb.s_log_zone_size), NULL); unlock_super(s); - if (!(s->s_mounted)) { + if (!(s->s_root)) { s->s_dev = 0; printk("get root inode failed\n"); MOD_DEC_USE_COUNT; @@ -504,7 +505,7 @@ struct super_block *isofs_read_super(struct super_block *s,void *data, return NULL; } -void isofs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz) +int isofs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz) { struct statfs tmp; @@ -517,7 +518,7 @@ void isofs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz) tmp.f_files = sb->u.isofs_sb.s_ninodes; tmp.f_ffree = 0; tmp.f_namelen = NAME_MAX; - copy_to_user(buf, &tmp, bufsiz); + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } int isofs_bmap(struct inode * inode,int block) @@ -663,7 +664,6 @@ void isofs_read_inode(struct inode * inode) isonum_711 (raw_inode->ext_attr_length)) << inode -> i_sb -> u.isofs_sb.s_log_zone_size; - inode->u.isofs_i.i_backlink = 0xffffffff; /* Will be used for previous directory */ switch (inode->i_sb->u.isofs_sb.s_conversion){ case 'a': inode->u.isofs_i.i_file_format = ISOFS_FILE_UNKNOWN; /* File type */ @@ -735,7 +735,6 @@ void isofs_read_inode(struct inode * inode) /* With a data error we return this information */ inode->i_mtime = inode->i_atime = inode->i_ctime = 0; inode->u.isofs_i.i_first_extent = 0; - inode->u.isofs_i.i_backlink = 0xffffffff; inode->i_size = 0; inode->i_nlink = 1; inode->i_uid = inode->i_gid = 0; diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c index 155f4ae43..1c0be7fde 100644 --- a/fs/isofs/namei.c +++ b/fs/isofs/namei.c @@ -59,14 +59,13 @@ static int isofs_match(int len,const char * name, const char * compare, int dlen * entry - you'll have to do that yourself if you want to. */ static struct buffer_head * isofs_find_entry(struct inode * dir, - const char * name, int namelen, unsigned long * ino, unsigned long * ino_back) + const char * name, int namelen, unsigned long * ino) { unsigned long bufsize = ISOFS_BUFFER_SIZE(dir); unsigned char bufbits = ISOFS_BUFFER_BITS(dir); unsigned int block, i, f_pos, offset, inode_number; struct buffer_head * bh; unsigned int old_offset; - unsigned int backlink; int dlen, rrflag, match; char * dpnt; struct iso_directory_record * de; @@ -86,7 +85,6 @@ static struct buffer_head * isofs_find_entry(struct inode * dir, while (f_pos < dir->i_size) { de = (struct iso_directory_record *) (bh->b_data + offset); - backlink = dir->i_ino; inode_number = (block << bufbits) + (offset & (bufsize - 1)); /* If byte is zero, this is the end of file, or time to move to @@ -120,28 +118,6 @@ static struct buffer_head * isofs_find_entry(struct inode * dir, return 0; } - /* Handle the '.' case */ - - if (de->name[0]==0 && de->name_len[0]==1) { - inode_number = dir->i_ino; - backlink = 0; - } - - /* Handle the '..' case */ - - if (de->name[0]==1 && de->name_len[0]==1) { -#if 0 - printk("Doing .. (%d %d)", - dir->i_sb->s_firstdatazone, - dir->i_ino); -#endif - if((dir->i_sb->u.isofs_sb.s_firstdatazone) != dir->i_ino) - inode_number = dir->u.isofs_i.i_backlink; - else - inode_number = dir->i_ino; - backlink = 0; - } - dlen = de->name_len[0]; dpnt = de->name; /* Now convert the filename in the buffer to lower case */ @@ -183,16 +159,8 @@ static struct buffer_head * isofs_find_entry(struct inode * dir, inode_number = isofs_lookup_grandparent(dir, find_rock_ridge_relocation(de,dir)); - if(inode_number == -1){ - /* Should never happen */ - printk("Backlink not properly set %x %lx.\n", - isonum_733(de->extent), - dir->i_ino); - goto out; - } } *ino = inode_number; - *ino_back = backlink; return bh; } } @@ -201,62 +169,48 @@ static struct buffer_head * isofs_find_entry(struct inode * dir, return NULL; } -int isofs_lookup(struct inode * dir,const char * name, int len, - struct inode ** result) +int isofs_lookup(struct inode * dir, struct dentry * dentry) { - unsigned long ino, ino_back; + unsigned long ino; struct buffer_head * bh; char *lcname; + struct inode *inode; #ifdef DEBUG - printk("lookup: %x %d\n",dir->i_ino, len); + printk("lookup: %x %d\n",dir->i_ino, dentry->d_name.len); #endif - *result = NULL; if (!dir) return -ENOENT; - if (!S_ISDIR(dir->i_mode)) { - iput(dir); + if (!S_ISDIR(dir->i_mode)) return -ENOENT; - } /* If mounted with check=relaxed (and most likely norock), * then first convert this name to lower case. */ if (dir->i_sb->u.isofs_sb.s_name_check == 'r' && - (lcname = kmalloc(len, GFP_KERNEL)) != NULL) { + (lcname = kmalloc(dentry->d_name.len, GFP_KERNEL)) != NULL) { int i; char c; - for (i=0; i<len; i++) { - c = name[i]; + for (i=0; i<dentry->d_name.len; i++) { + c = dentry->d_name.name[i]; if (c >= 'A' && c <= 'Z') c |= 0x20; lcname[i] = c; } - bh = isofs_find_entry(dir,lcname,len, &ino, &ino_back); + bh = isofs_find_entry(dir, lcname, dentry->d_name.len, &ino); kfree(lcname); } else - bh = isofs_find_entry(dir,name,len, &ino, &ino_back); + bh = isofs_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &ino); - if (!bh) { - iput(dir); - return -ENOENT; - } - brelse(bh); + inode = NULL; + if (bh) { + brelse(bh); - if (!(*result = iget(dir->i_sb,ino))) { - iput(dir); - return -EACCES; + inode = iget(dir->i_sb,ino); + if (!inode) + return -EACCES; } - - /* We need this backlink for the ".." entry unless the name that we - * are looking up traversed a mount point (in which case the inode - * may not even be on an iso9660 filesystem, and writing to - * u.isofs_i would only cause memory corruption). - */ - if (ino_back && !(*result)->i_pipe && (*result)->i_sb == dir->i_sb) - (*result)->u.isofs_i.i_backlink = ino_back; - - iput(dir); + d_add(dentry, inode); return 0; } diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index 8c41d2f39..98814d220 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -172,7 +172,7 @@ reclaimer(void *ptr) /* First, reclaim all locks that have been granted previously. */ do { for (fl = file_lock_table; fl; fl = fl->fl_next) { - inode = fl->fl_file->f_inode; + inode = fl->fl_file->f_dentry->d_inode; if (inode->i_sb->s_magic == NFS_SUPER_MAGIC && nlm_cmp_addr(NFS_ADDR(inode), &host->h_addr) && fl->fl_u.nfs_fl.state != host->h_state diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index 1506a2ba6..d219de5ea 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -42,7 +42,7 @@ nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl) memset(argp, 0, sizeof(*argp)); argp->cookie = nlm_cookie++; argp->state = nsm_local_state; - lock->fh = *NFS_FH(fl->fl_file->f_inode); + lock->fh = *NFS_FH(fl->fl_file->f_dentry->d_inode); lock->caller = system_utsname.nodename; lock->oh.data = req->a_owner; lock->oh.len = sprintf(req->a_owner, "%d@%s", diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index b4d74f745..69a9eeb21 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -274,8 +274,8 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, int error; dprintk("lockd: nlmsvc_lock(%04x/%ld, ty=%d, pi=%d, %ld-%ld, bl=%d)\n", - file->f_file.f_inode->i_dev, - file->f_file.f_inode->i_ino, + file->f_file.f_dentry->d_inode->i_dev, + file->f_file.f_dentry->d_inode->i_ino, lock->fl.fl_type, lock->fl.fl_pid, lock->fl.fl_start, lock->fl.fl_end, @@ -344,8 +344,8 @@ nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock, struct file_lock *fl; dprintk("lockd: nlmsvc_testlock(%04x/%ld, ty=%d, %ld-%ld)\n", - file->f_file.f_inode->i_dev, - file->f_file.f_inode->i_ino, + file->f_file.f_dentry->d_inode->i_dev, + file->f_file.f_dentry->d_inode->i_ino, lock->fl.fl_type, lock->fl.fl_start, lock->fl.fl_end); @@ -375,8 +375,8 @@ nlmsvc_unlock(struct nlm_file *file, struct nlm_lock *lock) int error; dprintk("lockd: nlmsvc_unlock(%04x/%ld, pi=%d, %ld-%ld)\n", - file->f_file.f_inode->i_dev, - file->f_file.f_inode->i_ino, + file->f_file.f_dentry->d_inode->i_dev, + file->f_file.f_dentry->d_inode->i_ino, lock->fl.fl_pid, lock->fl.fl_start, lock->fl.fl_end); @@ -403,8 +403,8 @@ nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock) struct nlm_block *block; dprintk("lockd: nlmsvc_cancel(%04x/%ld, pi=%d, %ld-%ld)\n", - file->f_file.f_inode->i_dev, - file->f_file.f_inode->i_ino, + file->f_file.f_dentry->d_inode->i_dev, + file->f_file.f_dentry->d_inode->i_ino, lock->fl.fl_pid, lock->fl.fl_start, lock->fl.fl_end); diff --git a/fs/locks.c b/fs/locks.c index 8ca8aa183..909c2eacb 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -297,25 +297,34 @@ int fcntl_getlk(unsigned int fd, struct flock *l) { struct flock flock; struct file *filp; + struct dentry *dentry; + struct inode *inode; struct file_lock *fl,file_lock; int error; if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd])) - return (-EBADF); + return -EBADF; if (copy_from_user(&flock, l, sizeof(flock))) - return (-EFAULT); + return -EFAULT; if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK)) - return (-EINVAL); + return -EINVAL; - if (!filp->f_inode || !posix_make_lock(filp, &file_lock, &flock)) - return (-EINVAL); + dentry = filp->f_dentry; + if (!dentry) + return -EINVAL; + + inode = dentry->d_inode; + if (!inode) + return -EINVAL; + + if (!posix_make_lock(filp, &file_lock, &flock)) + return -EINVAL; if (filp->f_op->lock) { - error = filp->f_op->lock(filp->f_inode, filp, - F_GETLK, &file_lock); + error = filp->f_op->lock(inode, filp, F_GETLK, &file_lock); if (error < 0) - return (error); + return error; fl = &file_lock; } else { fl = posix_test_lock(filp, &file_lock); @@ -344,6 +353,7 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) struct file *filp; struct file_lock file_lock; struct flock flock; + struct dentry * dentry; struct inode *inode; int error; @@ -351,10 +361,13 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) */ if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd])) - return (-EBADF); - - if (!(inode = filp->f_inode)) - return (-EINVAL); + return -EBADF; + + if (!(dentry = filp->f_dentry)) + return -EINVAL; + + if (!(inode = dentry->d_inode)) + return -EINVAL; /* Don't allow mandatory locks on files that may be memory mapped * and shared. @@ -407,7 +420,7 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) } if (filp->f_op->lock != NULL) { - error = filp->f_op->lock(filp->f_inode, filp, cmd, &file_lock); + error = filp->f_op->lock(inode, filp, cmd, &file_lock); if (error < 0) return (error); } @@ -421,12 +434,14 @@ void locks_remove_locks(struct task_struct *task, struct file *filp) { struct file_lock file_lock, *fl; struct file_lock **before; + struct inode * inode; /* For POSIX locks we free all locks on this file for the given task. * For FLOCK we only free locks on this *open* file if it is the last * close on that file. */ - before = &filp->f_inode->i_flock; + inode = filp->f_dentry->d_inode; + before = &inode->i_flock; while ((fl = *before) != NULL) { if (((fl->fl_flags & FL_POSIX) && (fl->fl_owner == task)) || @@ -436,10 +451,9 @@ void locks_remove_locks(struct task_struct *task, struct file *filp) locks_delete_lock(before, 0); if (filp->f_op->lock) { file_lock.fl_type = F_UNLCK; - filp->f_op->lock(filp->f_inode, filp, - F_SETLK, &file_lock); + filp->f_op->lock(inode, filp, F_SETLK, &file_lock); /* List may have changed: */ - before = &filp->f_inode->i_flock; + before = &inode->i_flock; } } else { before = &fl->fl_next; @@ -454,7 +468,7 @@ posix_test_lock(struct file *filp, struct file_lock *fl) { struct file_lock *cfl; - for (cfl = filp->f_inode->i_flock; cfl; cfl = cfl->fl_next) { + for (cfl = filp->f_dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) { if (!(cfl->fl_flags & FL_POSIX)) continue; if (posix_locks_conflict(cfl, fl)) @@ -585,7 +599,7 @@ static int posix_make_lock(struct file *filp, struct file_lock *fl, start = filp->f_pos; break; case 2: /*SEEK_END*/ - start = filp->f_inode->i_size; + start = filp->f_dentry->d_inode->i_size; break; default: return (0); @@ -612,7 +626,7 @@ static int flock_make_lock(struct file *filp, struct file_lock *fl, { memset(fl, 0, sizeof(*fl)); - if (!filp->f_inode) /* just in case */ + if (!filp->f_dentry) /* just in case */ return (0); switch (cmd & ~LOCK_NB) { @@ -750,9 +764,10 @@ static int flock_lock_file(struct file *filp, struct file_lock *caller, struct file_lock *fl; struct file_lock *new_fl; struct file_lock **before; + struct inode * inode = filp->f_dentry->d_inode; int change = 0; - before = &filp->f_inode->i_flock; + before = &inode->i_flock; while (((fl = *before) != NULL) && (fl->fl_flags & FL_FLOCK)) { if (caller->fl_file == fl->fl_file) { if (caller->fl_type == fl->fl_type) @@ -772,7 +787,7 @@ static int flock_lock_file(struct file *filp, struct file_lock *caller, if ((new_fl = locks_alloc_lock(caller)) == NULL) return (-ENOLCK); repeat: - for (fl = filp->f_inode->i_flock; (fl != NULL) && (fl->fl_flags & FL_FLOCK); + for (fl = inode->i_flock; (fl != NULL) && (fl->fl_flags & FL_FLOCK); fl = fl->fl_next) { if (!flock_locks_conflict(new_fl, fl)) continue; @@ -801,7 +816,7 @@ repeat: } goto repeat; } - locks_insert_lock(&filp->f_inode->i_flock, new_fl); + locks_insert_lock(&inode->i_flock, new_fl); return (0); } @@ -825,11 +840,12 @@ int posix_lock_file(struct file *filp, struct file_lock *caller, struct file_lock *left = NULL; struct file_lock *right = NULL; struct file_lock **before; + struct inode * inode = filp->f_dentry->d_inode; int added = 0; if (caller->fl_type != F_UNLCK) { repeat: - for (fl = filp->f_inode->i_flock; fl != NULL; fl = fl->fl_next) { + for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { if (!(fl->fl_flags & FL_POSIX)) continue; if (!posix_locks_conflict(caller, fl)) @@ -852,7 +868,7 @@ int posix_lock_file(struct file *filp, struct file_lock *caller, /* Find the first old lock with the same owner as the new lock. */ - before = &filp->f_inode->i_flock; + before = &inode->i_flock; /* First skip locks owned by other processes. */ @@ -1054,7 +1070,7 @@ static char *lock_get_status(struct file_lock *fl, int id, char *pfx) char *p = temp; struct inode *inode; - inode = fl->fl_file->f_inode; + inode = fl->fl_file->f_dentry->d_inode; p += sprintf(p, "%d:%s ", id, pfx); if (fl->fl_flags & FL_POSIX) { diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c index 81ac9b047..f47f779d5 100644 --- a/fs/minix/bitmap.c +++ b/fs/minix/bitmap.c @@ -191,8 +191,8 @@ void minix_free_inode(struct inode * inode) printk("free_inode: inode has no device\n"); return; } - if (atomic_read(&inode->i_count) != 1) { - printk("free_inode: inode has count=%d\n",atomic_read(&inode->i_count)); + if (inode->i_count != 1) { + printk("free_inode: inode has count=%d\n",inode->i_count); return; } if (inode->i_nlink) { @@ -251,12 +251,12 @@ struct inode * minix_new_inode(const struct inode * dir) iput(inode); return NULL; } - atomic_set(&inode->i_count, 1); + inode->i_count = 1; inode->i_nlink = 1; inode->i_dev = sb->s_dev; inode->i_uid = current->fsuid; inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid; - inode->i_dirt = 1; + mark_inode_dirty(inode); inode->i_ino = j; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->i_op = NULL; diff --git a/fs/minix/dir.c b/fs/minix/dir.c index 439005f4e..ec5113c4a 100644 --- a/fs/minix/dir.c +++ b/fs/minix/dir.c @@ -50,6 +50,7 @@ struct inode_operations minix_dir_inode_operations = { minix_mknod, /* mknod */ minix_rename, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/minix/file.c b/fs/minix/file.c index 86cbca2b2..7ca7cb075 100644 --- a/fs/minix/file.c +++ b/fs/minix/file.c @@ -58,6 +58,7 @@ struct inode_operations minix_file_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ generic_readpage, /* readpage */ NULL, /* writepage */ minix_bmap, /* bmap */ @@ -120,6 +121,6 @@ static long minix_file_write(struct inode * inode, struct file * filp, inode->i_size = pos; inode->i_mtime = inode->i_ctime = CURRENT_TIME; filp->f_pos = pos; - inode->i_dirt = 1; + mark_inode_dirty(inode); return written; } diff --git a/fs/minix/inode.c b/fs/minix/inode.c index cbd735ef1..898f56f19 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -24,10 +24,8 @@ #include <asm/uaccess.h> #include <asm/bitops.h> -void minix_put_inode(struct inode *inode) +static void minix_delete_inode(struct inode *inode) { - if (inode->i_nlink) - return; inode->i_size = 0; minix_truncate(inode); minix_free_inode(inode); @@ -77,9 +75,10 @@ void minix_put_super(struct super_block *sb) static struct super_operations minix_sops = { minix_read_inode, - NULL, minix_write_inode, - minix_put_inode, + NULL, /* put_inode */ + minix_delete_inode, + NULL, /* notify_change */ minix_put_super, minix_write_super, minix_statfs, @@ -125,15 +124,13 @@ int minix_remount (struct super_block * sb, int * flags, char * data) * it really _is_ a minix filesystem, and to check the size * of the directory entry. */ -static const char * minix_checkroot(struct super_block *s) +static const char * minix_checkroot(struct super_block *s, struct inode *dir) { - struct inode * dir; struct buffer_head *bh; struct minix_dir_entry *de; const char * errmsg; int dirsize; - dir = s->s_mounted; if (!S_ISDIR(dir->i_mode)) return "root directory is not a directory"; @@ -172,7 +169,8 @@ struct super_block *minix_read_super(struct super_block *s,void *data, int i, block; kdev_t dev = s->s_dev; const char * errmsg; - + struct inode *root_inode; + if (32 != sizeof (struct minix_inode)) panic("bad V1 i-node size"); if (64 != sizeof(struct minix2_inode)) @@ -272,8 +270,9 @@ struct super_block *minix_read_super(struct super_block *s,void *data, /* set up enough so that it can read an inode */ s->s_dev = dev; s->s_op = &minix_sops; - s->s_mounted = iget(s,MINIX_ROOT_INO); - if (!s->s_mounted) { + root_inode = iget(s,MINIX_ROOT_INO); + s->s_root = d_alloc_root(root_inode, NULL); + if (!s->s_root) { s->s_dev = 0; brelse(bh); if (!silent) @@ -282,11 +281,11 @@ struct super_block *minix_read_super(struct super_block *s,void *data, return NULL; } - errmsg = minix_checkroot(s); + errmsg = minix_checkroot(s, root_inode); if (errmsg) { if (!silent) printk("MINIX-fs: %s\n", errmsg); - iput (s->s_mounted); + d_delete(s->s_root); /* XXX Is this enough? */ s->s_dev = 0; brelse (bh); MOD_DEC_USE_COUNT; @@ -307,7 +306,7 @@ struct super_block *minix_read_super(struct super_block *s,void *data, return s; } -void minix_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) +int minix_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) { struct statfs tmp; @@ -319,7 +318,7 @@ void minix_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) tmp.f_files = sb->u.minix_sb.s_ninodes; tmp.f_ffree = minix_count_free_inodes(sb); tmp.f_namelen = sb->u.minix_sb.s_namelen; - copy_to_user(buf, &tmp, bufsiz); + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } /* @@ -472,7 +471,7 @@ repeat: } *p = tmp; inode->i_ctime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); return result; } @@ -585,7 +584,7 @@ repeat: } *p = tmp; inode->i_ctime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); return result; } @@ -833,14 +832,12 @@ static struct buffer_head * V1_minix_update_inode(struct inode * inode) printk("Bad inode number on dev %s" ": %d is out of range\n", kdevname(inode->i_dev), ino); - inode->i_dirt = 0; return 0; } block = 2 + inode->i_sb->u.minix_sb.s_imap_blocks + inode->i_sb->u.minix_sb.s_zmap_blocks + (ino-1)/MINIX_INODES_PER_BLOCK; if (!(bh=bread(inode->i_dev, block, BLOCK_SIZE))) { printk("unable to read i-node block\n"); - inode->i_dirt = 0; return 0; } raw_inode = ((struct minix_inode *)bh->b_data) + @@ -855,7 +852,6 @@ static struct buffer_head * V1_minix_update_inode(struct inode * inode) raw_inode->i_zone[0] = kdev_t_to_nr(inode->i_rdev); else for (block = 0; block < 9; block++) raw_inode->i_zone[block] = inode->u.minix_i.u.i1_data[block]; - inode->i_dirt=0; mark_buffer_dirty(bh, 1); return bh; } @@ -874,14 +870,12 @@ static struct buffer_head * V2_minix_update_inode(struct inode * inode) printk("Bad inode number on dev %s" ": %d is out of range\n", kdevname(inode->i_dev), ino); - inode->i_dirt = 0; return 0; } block = 2 + inode->i_sb->u.minix_sb.s_imap_blocks + inode->i_sb->u.minix_sb.s_zmap_blocks + (ino-1)/MINIX2_INODES_PER_BLOCK; if (!(bh=bread(inode->i_dev, block, BLOCK_SIZE))) { printk("unable to read i-node block\n"); - inode->i_dirt = 0; return 0; } raw_inode = ((struct minix2_inode *)bh->b_data) + @@ -898,7 +892,6 @@ static struct buffer_head * V2_minix_update_inode(struct inode * inode) raw_inode->i_zone[0] = kdev_t_to_nr(inode->i_rdev); else for (block = 0; block < 10; block++) raw_inode->i_zone[block] = inode->u.minix_i.u.i2_data[block]; - inode->i_dirt=0; mark_buffer_dirty(bh, 1); return bh; } diff --git a/fs/minix/namei.c b/fs/minix/namei.c index b6041ad92..718d3dd07 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -47,9 +47,6 @@ static int minix_match(int len, const char * name, *offset += info->s_dirsize; if (!de->inode || len > info->s_namelen) return 0; - /* "" means "." ---> so paths like "/usr/lib//libc.a" work */ - if (!len && (de->name[0]=='.') && (de->name[1]=='\0')) - return 1; return namecompare(len,info->s_namelen,name,de->name); } @@ -104,31 +101,22 @@ static struct buffer_head * minix_find_entry(struct inode * dir, return NULL; } -int minix_lookup(struct inode * dir,const char * name, int len, - struct inode ** result) +int minix_lookup(struct inode * dir, struct dentry *dentry) { - int ino; + struct inode * inode = NULL; struct minix_dir_entry * de; struct buffer_head * bh; - *result = NULL; - if (!dir) - return -ENOENT; - if (!S_ISDIR(dir->i_mode)) { - iput(dir); - return -ENOENT; - } - if (!(bh = minix_find_entry(dir,name,len,&de))) { - iput(dir); - return -ENOENT; - } - ino = de->inode; - brelse(bh); - if (!(*result = iget(dir->i_sb,ino))) { - iput(dir); - return -EACCES; - } - iput(dir); + bh = minix_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de); + if (bh) { + unsigned long ino = le32_to_cpu(de->inode); + brelse (bh); + inode = iget(dir->i_sb, ino); + + if (!inode) + return -EACCES; + } + d_add(dentry, inode); return 0; } @@ -180,7 +168,7 @@ static int minix_add_entry(struct inode * dir, if (block*bh->b_size + offset > dir->i_size) { de->inode = 0; dir->i_size = block*bh->b_size + offset; - dir->i_dirt = 1; + mark_inode_dirty(dir); } if (de->inode) { if (namecompare(namelen, info->s_namelen, name, de->name)) { @@ -189,7 +177,7 @@ static int minix_add_entry(struct inode * dir, } } else { dir->i_mtime = dir->i_ctime = CURRENT_TIME; - dir->i_dirt = 1; + mark_inode_dirty(dir); for (i = 0; i < info->s_namelen ; i++) de->name[i] = (i < namelen) ? name[i] : 0; dir->i_version = ++event; @@ -208,42 +196,37 @@ static int minix_add_entry(struct inode * dir, return 0; } -int minix_create(struct inode * dir,const char * name, int len, int mode, - struct inode ** result) +int minix_create(struct inode * dir, struct dentry *dentry, int mode) { int error; struct inode * inode; struct buffer_head * bh; struct minix_dir_entry * de; - *result = NULL; if (!dir) return -ENOENT; inode = minix_new_inode(dir); - if (!inode) { - iput(dir); + if (!inode) return -ENOSPC; - } inode->i_op = &minix_file_inode_operations; inode->i_mode = mode; - inode->i_dirt = 1; - error = minix_add_entry(dir,name,len, &bh ,&de); + mark_inode_dirty(inode); + error = minix_add_entry(dir, dentry->d_name.name, + dentry->d_name.len, &bh ,&de); if (error) { inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput(inode); - iput(dir); return error; } de->inode = inode->i_ino; mark_buffer_dirty(bh, 1); brelse(bh); - iput(dir); - *result = inode; + d_instantiate(dentry, inode); return 0; } -int minix_mknod(struct inode * dir, const char * name, int len, int mode, int rdev) +int minix_mknod(struct inode * dir, struct dentry *dentry, int mode, int rdev) { int error; struct inode * inode; @@ -252,17 +235,15 @@ int minix_mknod(struct inode * dir, const char * name, int len, int mode, int rd if (!dir) return -ENOENT; - bh = minix_find_entry(dir,name,len,&de); + bh = minix_find_entry(dir, dentry->d_name.name, + dentry->d_name.len, &de); if (bh) { brelse(bh); - iput(dir); return -EEXIST; } inode = minix_new_inode(dir); - if (!inode) { - iput(dir); + if (!inode) return -ENOSPC; - } inode->i_uid = current->fsuid; inode->i_mode = mode; inode->i_op = NULL; @@ -283,24 +264,22 @@ int minix_mknod(struct inode * dir, const char * name, int len, int mode, int rd init_fifo(inode); if (S_ISBLK(mode) || S_ISCHR(mode)) inode->i_rdev = to_kdev_t(rdev); - inode->i_dirt = 1; - error = minix_add_entry(dir, name, len, &bh, &de); + mark_inode_dirty(inode); + error = minix_add_entry(dir, dentry->d_name.name, dentry->d_name.len, &bh, &de); if (error) { inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput(inode); - iput(dir); return error; } de->inode = inode->i_ino; mark_buffer_dirty(bh, 1); brelse(bh); - iput(dir); - iput(inode); + d_instantiate(dentry, inode); return 0; } -int minix_mkdir(struct inode * dir, const char * name, int len, int mode) +int minix_mkdir(struct inode * dir, struct dentry *dentry, int mode) { int error; struct inode * inode; @@ -308,33 +287,26 @@ int minix_mkdir(struct inode * dir, const char * name, int len, int mode) struct minix_dir_entry * de; struct minix_sb_info * info; - if (!dir || !dir->i_sb) { - iput(dir); + if (!dir || !dir->i_sb) return -EINVAL; - } info = &dir->i_sb->u.minix_sb; - bh = minix_find_entry(dir,name,len,&de); + bh = minix_find_entry(dir, dentry->d_name.name, + dentry->d_name.len, &de); if (bh) { brelse(bh); - iput(dir); return -EEXIST; } - if (dir->i_nlink >= MINIX_LINK_MAX) { - iput(dir); + if (dir->i_nlink >= MINIX_LINK_MAX) return -EMLINK; - } inode = minix_new_inode(dir); - if (!inode) { - iput(dir); + if (!inode) return -ENOSPC; - } inode->i_op = &minix_dir_inode_operations; inode->i_size = 2 * info->s_dirsize; dir_block = minix_bread(inode,0,1); if (!dir_block) { - iput(dir); inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput(inode); return -ENOSPC; } @@ -350,10 +322,10 @@ int minix_mkdir(struct inode * dir, const char * name, int len, int mode) inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask); if (dir->i_mode & S_ISGID) inode->i_mode |= S_ISGID; - inode->i_dirt = 1; - error = minix_add_entry(dir, name, len, &bh, &de); + mark_inode_dirty(inode); + error = minix_add_entry(dir, dentry->d_name.name, + dentry->d_name.len, &bh, &de); if (error) { - iput(dir); inode->i_nlink=0; iput(inode); return error; @@ -361,10 +333,9 @@ int minix_mkdir(struct inode * dir, const char * name, int len, int mode) de->inode = inode->i_ino; mark_buffer_dirty(bh, 1); dir->i_nlink++; - dir->i_dirt = 1; - iput(dir); - iput(inode); + mark_inode_dirty(dir); brelse(bh); + d_instantiate(dentry, inode); return 0; } @@ -427,7 +398,7 @@ bad_dir: return 1; } -int minix_rmdir(struct inode * dir, const char * name, int len) +int minix_rmdir(struct inode * dir, struct dentry *dentry) { int retval; struct inode * inode; @@ -435,13 +406,14 @@ int minix_rmdir(struct inode * dir, const char * name, int len) struct minix_dir_entry * de; inode = NULL; - bh = minix_find_entry(dir,name,len,&de); + bh = minix_find_entry(dir, dentry->d_name.name, + dentry->d_name.len, &de); retval = -ENOENT; if (!bh) goto end_rmdir; retval = -EPERM; - if (!(inode = iget(dir->i_sb, de->inode))) - goto end_rmdir; + inode = dentry->d_inode; + if ((dir->i_mode & S_ISVTX) && !fsuser() && current->fsuid != inode->i_uid && current->fsuid != dir->i_uid) @@ -462,7 +434,7 @@ int minix_rmdir(struct inode * dir, const char * name, int len) retval = -ENOENT; goto end_rmdir; } - if (atomic_read(&inode->i_count) > 1) { + if (inode->i_count > 1) { retval = -EBUSY; goto end_rmdir; } @@ -472,19 +444,18 @@ int minix_rmdir(struct inode * dir, const char * name, int len) dir->i_version = ++event; mark_buffer_dirty(bh, 1); inode->i_nlink=0; - inode->i_dirt=1; + mark_inode_dirty(inode); inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_nlink--; - dir->i_dirt=1; + mark_inode_dirty(dir); + d_delete(dentry); retval = 0; end_rmdir: - iput(dir); - iput(inode); brelse(bh); return retval; } -int minix_unlink(struct inode * dir, const char * name, int len) +int minix_unlink(struct inode * dir, struct dentry *dentry) { int retval; struct inode * inode; @@ -494,16 +465,16 @@ int minix_unlink(struct inode * dir, const char * name, int len) repeat: retval = -ENOENT; inode = NULL; - bh = minix_find_entry(dir,name,len,&de); + bh = minix_find_entry(dir, dentry->d_name.name, + dentry->d_name.len, &de); if (!bh) goto end_unlink; - if (!(inode = iget(dir->i_sb, de->inode))) - goto end_unlink; + inode = dentry->d_inode; + retval = -EPERM; if (S_ISDIR(inode->i_mode)) goto end_unlink; if (de->inode != inode->i_ino) { - iput(inode); brelse(bh); current->counter = 0; schedule(); @@ -527,19 +498,19 @@ repeat: dir->i_version = ++event; mark_buffer_dirty(bh, 1); dir->i_ctime = dir->i_mtime = CURRENT_TIME; - dir->i_dirt = 1; + mark_inode_dirty(dir); inode->i_nlink--; inode->i_ctime = dir->i_ctime; - inode->i_dirt = 1; + mark_inode_dirty(inode); + d_delete(dentry); /* This also frees the inode */ retval = 0; end_unlink: brelse(bh); - iput(inode); - iput(dir); return retval; } -int minix_symlink(struct inode * dir, const char * name, int len, const char * symname) +int minix_symlink(struct inode * dir, struct dentry *dentry, + const char * symname) { struct minix_dir_entry * de; struct inode * inode = NULL; @@ -547,17 +518,15 @@ int minix_symlink(struct inode * dir, const char * name, int len, const char * s int i; char c; - if (!(inode = minix_new_inode(dir))) { - iput(dir); + if (!(inode = minix_new_inode(dir))) return -ENOSPC; - } + inode->i_mode = S_IFLNK | 0777; inode->i_op = &minix_symlink_inode_operations; name_block = minix_bread(inode,0,1); if (!name_block) { - iput(dir); inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput(inode); return -ENOSPC; } @@ -568,93 +537,81 @@ int minix_symlink(struct inode * dir, const char * name, int len, const char * s mark_buffer_dirty(name_block, 1); brelse(name_block); inode->i_size = i; - inode->i_dirt = 1; - bh = minix_find_entry(dir,name,len,&de); + mark_inode_dirty(inode); + bh = minix_find_entry(dir, dentry->d_name.name, + dentry->d_name.len, &de); if (bh) { inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput(inode); brelse(bh); - iput(dir); return -EEXIST; } - i = minix_add_entry(dir, name, len, &bh, &de); + i = minix_add_entry(dir, dentry->d_name.name, + dentry->d_name.len, &bh, &de); if (i) { inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput(inode); - iput(dir); return i; } de->inode = inode->i_ino; mark_buffer_dirty(bh, 1); brelse(bh); - iput(dir); - iput(inode); + d_instantiate(dentry, inode); return 0; } -int minix_link(struct inode * oldinode, struct inode * dir, const char * name, int len) +int minix_link(struct inode * inode, struct inode * dir, + struct dentry *dentry) { int error; struct minix_dir_entry * de; struct buffer_head * bh; - if (S_ISDIR(oldinode->i_mode)) { - iput(oldinode); - iput(dir); + if (S_ISDIR(inode->i_mode)) return -EPERM; - } - if (oldinode->i_nlink >= MINIX_LINK_MAX) { - iput(oldinode); - iput(dir); + + if (inode->i_nlink >= MINIX_LINK_MAX) return -EMLINK; - } - bh = minix_find_entry(dir,name,len,&de); + + bh = minix_find_entry(dir, dentry->d_name.name, + dentry->d_name.len, &de); if (bh) { brelse(bh); - iput(dir); - iput(oldinode); return -EEXIST; } - error = minix_add_entry(dir, name, len, &bh, &de); + error = minix_add_entry(dir, dentry->d_name.name, + dentry->d_name.len, &bh, &de); if (error) { - iput(dir); - iput(oldinode); + brelse(bh); return error; } - de->inode = oldinode->i_ino; + de->inode = inode->i_ino; mark_buffer_dirty(bh, 1); brelse(bh); - iput(dir); - oldinode->i_nlink++; - oldinode->i_ctime = CURRENT_TIME; - oldinode->i_dirt = 1; - iput(oldinode); + inode->i_nlink++; + inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + d_instantiate(dentry, inode); return 0; } -static int subdir(struct inode * new_inode, struct inode * old_inode) +static int subdir(struct dentry * new_dentry, struct dentry * old_dentry) { - int ino; - int result; + int result = 0; - atomic_inc(&new_inode->i_count); - result = 0; for (;;) { - if (new_inode == old_inode) { - result = 1; - break; + if (new_dentry != old_dentry) { + struct dentry * parent = new_dentry->d_parent; + if (parent == new_dentry) + break; + new_dentry = parent; + continue; } - if (new_inode->i_dev != old_inode->i_dev) - break; - ino = new_inode->i_ino; - if (minix_lookup(new_inode,"..",2,&new_inode)) - break; - if (new_inode->i_ino == ino) - break; + result = 1; + break; } - iput(new_inode); return result; } @@ -671,8 +628,8 @@ static int subdir(struct inode * new_inode, struct inode * old_inode) * Anybody can rename anything with this: the permission checks are left to the * higher-level routines. */ -static int do_minix_rename(struct inode * old_dir, const char * old_name, int old_len, - struct inode * new_dir, const char * new_name, int new_len) +static int do_minix_rename(struct inode * old_dir, struct dentry *old_dentry, + struct inode * new_dir, struct dentry *new_dentry) { struct inode * old_inode, * new_inode; struct buffer_head * old_bh, * new_bh, * dir_bh; @@ -686,28 +643,26 @@ try_again: brelse(old_bh); brelse(new_bh); brelse(dir_bh); - iput(old_inode); - iput(new_inode); current->counter = 0; schedule(); start_up: old_inode = new_inode = NULL; old_bh = new_bh = dir_bh = NULL; - old_bh = minix_find_entry(old_dir,old_name,old_len,&old_de); + old_bh = minix_find_entry(old_dir, old_dentry->d_name.name, + old_dentry->d_name.len, &old_de); retval = -ENOENT; if (!old_bh) goto end_rename; - old_inode = __iget(old_dir->i_sb, old_de->inode,0); /* don't cross mnt-points */ - if (!old_inode) - goto end_rename; + old_inode = old_dentry->d_inode; retval = -EPERM; if ((old_dir->i_mode & S_ISVTX) && current->fsuid != old_inode->i_uid && current->fsuid != old_dir->i_uid && !fsuser()) goto end_rename; - new_bh = minix_find_entry(new_dir,new_name,new_len,&new_de); + new_inode = new_dentry->d_inode; + new_bh = minix_find_entry(new_dir, new_dentry->d_name.name, + new_dentry->d_name.len, &new_de); if (new_bh) { - new_inode = __iget(new_dir->i_sb, new_de->inode, 0); if (!new_inode) { brelse(new_bh); new_bh = NULL; @@ -722,13 +677,13 @@ start_up: if (!S_ISDIR(old_inode->i_mode)) goto end_rename; retval = -EINVAL; - if (subdir(new_dir, old_inode)) + if (subdir(new_dentry, old_dentry)) goto end_rename; retval = -ENOTEMPTY; if (!empty_dir(new_inode)) goto end_rename; retval = -EBUSY; - if (atomic_read(&new_inode->i_count) > 1) + if (new_inode->i_count > 1) goto end_rename; } retval = -EPERM; @@ -741,7 +696,7 @@ start_up: if (new_inode && !S_ISDIR(new_inode->i_mode)) goto end_rename; retval = -EINVAL; - if (subdir(new_dir, old_inode)) + if (subdir(new_dentry, old_dentry)) goto end_rename; retval = -EIO; dir_bh = minix_bread(old_inode,0,0); @@ -754,7 +709,10 @@ start_up: goto end_rename; } if (!new_bh) { - retval = minix_add_entry(new_dir,new_name,new_len,&new_bh,&new_de); + retval = minix_add_entry(new_dir, + new_dentry->d_name.name, + new_dentry->d_name.len, + &new_bh, &new_de); if (retval) goto end_rename; } @@ -769,15 +727,15 @@ start_up: old_de->inode = 0; new_de->inode = old_inode->i_ino; old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; - old_dir->i_dirt = 1; + mark_inode_dirty(old_dir); old_dir->i_version = ++event; new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME; - new_dir->i_dirt = 1; + mark_inode_dirty(new_dir); new_dir->i_version = ++event; if (new_inode) { new_inode->i_nlink--; new_inode->i_ctime = CURRENT_TIME; - new_inode->i_dirt = 1; + mark_inode_dirty(new_inode); } mark_buffer_dirty(old_bh, 1); mark_buffer_dirty(new_bh, 1); @@ -785,24 +743,23 @@ start_up: PARENT_INO(dir_bh->b_data) = new_dir->i_ino; mark_buffer_dirty(dir_bh, 1); old_dir->i_nlink--; - old_dir->i_dirt = 1; + mark_inode_dirty(old_dir); if (new_inode) { new_inode->i_nlink--; - new_inode->i_dirt = 1; + mark_inode_dirty(new_inode); } else { new_dir->i_nlink++; - new_dir->i_dirt = 1; + mark_inode_dirty(new_dir); } } + /* Update the dcache */ + d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name); + d_delete(new_dentry); retval = 0; end_rename: brelse(dir_bh); brelse(old_bh); brelse(new_bh); - iput(old_inode); - iput(new_inode); - iput(old_dir); - iput(new_dir); return retval; } @@ -815,8 +772,8 @@ end_rename: * the same device that races occur: many renames can happen at once, as long * as they are on different partitions. */ -int minix_rename(struct inode * old_dir, const char * old_name, int old_len, - struct inode * new_dir, const char * new_name, int new_len) +int minix_rename(struct inode * old_dir, struct dentry *old_dentry, + struct inode * new_dir, struct dentry *new_dentry) { static struct wait_queue * wait = NULL; static int lock = 0; @@ -825,8 +782,8 @@ int minix_rename(struct inode * old_dir, const char * old_name, int old_len, while (lock) sleep_on(&wait); lock = 1; - result = do_minix_rename(old_dir, old_name, old_len, - new_dir, new_name, new_len); + result = do_minix_rename(old_dir, old_dentry, + new_dir, new_dentry); lock = 0; wake_up(&wait); return result; diff --git a/fs/minix/symlink.c b/fs/minix/symlink.c index 92539cded..9f759ecc9 100644 --- a/fs/minix/symlink.c +++ b/fs/minix/symlink.c @@ -15,6 +15,7 @@ #include <asm/uaccess.h> static int minix_readlink(struct inode *, char *, int); +static struct dentry *minix_follow_link(struct inode *, struct dentry *); /* * symlinks can't do much... @@ -31,6 +32,7 @@ struct inode_operations minix_symlink_inode_operations = { NULL, /* mknod */ NULL, /* rename */ minix_readlink, /* readlink */ + minix_follow_link, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -38,6 +40,21 @@ struct inode_operations minix_symlink_inode_operations = { NULL /* permission */ }; +static struct dentry * minix_follow_link(struct inode * inode, struct dentry * base) +{ + struct buffer_head * bh; + + bh = minix_bread(inode, 0, 0); + if (!bh) { + dput(base); + return ERR_PTR(-EIO); + } + UPDATE_ATIME(inode); + base = lookup_dentry(bh->b_data, base, 1); + brelse(bh); + return base; +} + static int minix_readlink(struct inode * inode, char * buffer, int buflen) { struct buffer_head * bh; @@ -47,7 +64,6 @@ static int minix_readlink(struct inode * inode, char * buffer, int buflen) if (buflen > 1023) buflen = 1023; bh = minix_bread(inode, 0, 0); - iput(inode); if (!bh) return 0; i = 0; diff --git a/fs/minix/truncate.c b/fs/minix/truncate.c index a6d3d4b5e..298d6c155 100644 --- a/fs/minix/truncate.c +++ b/fs/minix/truncate.c @@ -58,7 +58,7 @@ repeat: continue; } *p = 0; - inode->i_dirt = 1; + mark_inode_dirty(inode); if (bh) { mark_buffer_clean(bh); brelse(bh); @@ -167,7 +167,7 @@ repeat: else { tmp = *p; *p = 0; - inode->i_dirt = 1; + mark_inode_dirty(inode); minix_free_block(inode->i_sb,tmp); } brelse(dind_bh); @@ -191,7 +191,7 @@ void V1_minix_truncate(struct inode * inode) schedule(); } inode->i_mtime = inode->i_ctime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); } /* @@ -220,7 +220,7 @@ repeat: continue; } *p = 0; - inode->i_dirt = 1; + mark_inode_dirty(inode); if (bh) { mark_buffer_clean(bh); brelse(bh); @@ -329,7 +329,7 @@ repeat: else { tmp = *p; *p = 0; - inode->i_dirt = 1; + mark_inode_dirty(inode); minix_free_block(inode->i_sb,tmp); } brelse(dind_bh); @@ -374,7 +374,7 @@ repeat: else { tmp = *p; *p = 0; - inode->i_dirt = 1; + mark_inode_dirty(inode); minix_free_block(inode->i_sb,tmp); } brelse(tind_bh); @@ -402,7 +402,7 @@ static void V2_minix_truncate(struct inode * inode) schedule(); } inode->i_mtime = inode->i_ctime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); } /* diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index bcf6782d0..9481a763c 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c @@ -280,7 +280,7 @@ static int msdos_create_entry(struct inode *dir, const char *name,int len, * XXX all times should be set by caller upon successful completion. */ dir->i_ctime = dir->i_mtime = CURRENT_TIME; - dir->i_dirt = 1; + mark_inode_dirty(dir); memcpy(de->name,name,MSDOS_NAME); memset(de->unused, 0, sizeof(de->unused)); de->attr = is_dir ? ATTR_DIR : ATTR_ARCH; @@ -295,7 +295,7 @@ static int msdos_create_entry(struct inode *dir, const char *name,int len, if (!*result) return -EIO; (*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime = CURRENT_TIME; - (*result)->i_dirt = 1; + mark_inode_dirty(*result); return 0; } @@ -369,7 +369,7 @@ static int msdos_empty(struct inode *dir) struct buffer_head *bh; struct msdos_dir_entry *de; - if (atomic_read(&dir->i_count) > 1) + if (dir->i_count > 1) return -EBUSY; if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */ pos = 0; @@ -415,7 +415,8 @@ int msdos_rmdir(struct inode *dir,const char *name,int len) inode->i_nlink = 0; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_nlink--; - inode->i_dirt = dir->i_dirt = 1; + mark_inode_dirty(inode); + mark_inode_dirty(dir); de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, bh, 1); res = 0; @@ -465,7 +466,7 @@ int msdos_mkdir(struct inode *dir,const char *name,int len,int mode) dot->i_size = inode->i_size; /* doesn't grow in the 2nd create_entry */ MSDOS_I(dot)->i_start = MSDOS_I(inode)->i_start; dot->i_nlink = inode->i_nlink; - dot->i_dirt = 1; + mark_inode_dirty(dot); iput(dot); if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,2,1,0,&dot)) < 0) goto mkdir_error; @@ -473,7 +474,7 @@ int msdos_mkdir(struct inode *dir,const char *name,int len,int mode) dot->i_size = dir->i_size; MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start; dot->i_nlink = dir->i_nlink; - dot->i_dirt = 1; + mark_inode_dirty(dot); MSDOS_I(inode)->i_busy = 0; iput(dot); iput(inode); @@ -519,7 +520,8 @@ static int msdos_unlinkx( inode->i_nlink = 0; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; MSDOS_I(inode)->i_busy = 1; - inode->i_dirt = dir->i_dirt = 1; + mark_inode_dirty(inode); + mark_inode_dirty(dir); de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, bh, 1); unlink_done: @@ -580,11 +582,11 @@ static int rename_same_dir(struct inode *old_dir,char *old_name,int old_len, } if (S_ISDIR(new_inode->i_mode)) { new_dir->i_nlink--; - new_dir->i_dirt = 1; + mark_inode_dirty(new_dir); } new_inode->i_nlink = 0; MSDOS_I(new_inode)->i_busy = 1; - new_inode->i_dirt = 1; + mark_inode_dirty(new_inode); new_de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, new_bh, 1); iput(new_inode); @@ -675,7 +677,7 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len, } new_inode->i_nlink = 0; MSDOS_I(new_inode)->i_busy = 1; - new_inode->i_dirt = 1; + mark_inode_dirty(new_inode); new_de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, new_bh, 1); } @@ -696,14 +698,14 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len, } if (exists && S_ISDIR(new_inode->i_mode)) { new_dir->i_nlink--; - new_dir->i_dirt = 1; + mark_inode_dirty(new_dir); } msdos_read_inode(free_inode); MSDOS_I(old_inode)->i_busy = 1; MSDOS_I(old_inode)->i_linked = free_inode; MSDOS_I(free_inode)->i_oldlink = old_inode; fat_cache_inval_inode(old_inode); - old_inode->i_dirt = 1; + mark_inode_dirty(old_inode); old_de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, old_bh, 1); fat_mark_buffer_dirty(sb, free_bh, 1); @@ -711,7 +713,7 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len, MSDOS_I(new_inode)->i_depend = free_inode; MSDOS_I(free_inode)->i_old = new_inode; /* Two references now exist to free_inode so increase count */ - atomic_inc(&free_inode->i_count); + free_inode->i_count++; /* free_inode is put after putting new_inode and old_inode */ iput(new_inode); fat_brelse(sb, new_bh); @@ -726,7 +728,7 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len, } dotdot_de->start = MSDOS_I(dotdot_inode)->i_start = MSDOS_I(new_dir)->i_start; - dotdot_inode->i_dirt = 1; + mark_inode_dirty(dotdot_inode); fat_mark_buffer_dirty(sb, dotdot_bh, 1); old_dir->i_nlink--; new_dir->i_nlink++; @@ -793,6 +795,7 @@ struct inode_operations msdos_dir_inode_operations = { NULL, /* mknod */ msdos_rename, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ fat_bmap, /* bmap */ diff --git a/fs/namei.c b/fs/namei.c index 198179b98..2ec173f0d 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -12,7 +12,6 @@ * lookup logic. */ -#include <linux/config.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -20,10 +19,7 @@ #include <linux/fcntl.h> #include <linux/stat.h> #include <linux/mm.h> -#include <linux/dalloc.h> -#include <linux/nametrans.h> #include <linux/proc_fs.h> -#include <linux/omirr.h> #include <linux/smp.h> #include <linux/smp_lock.h> @@ -37,10 +33,6 @@ #undef DEBUG /* some other debugging */ -/* local flags for __namei() */ -#define NAM_SEMLOCK 8 /* set a semlock on the last dir */ -#define NAM_TRANSCREATE 16 /* last component may be created, try "=CREATE#" suffix*/ -#define NAM_NO_TRAILSLASH 32 /* disallow trailing slashes by returning EISDIR */ #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE]) /* [Feb-1997 T. Schoebel-Theuer] @@ -56,7 +48,7 @@ * is done solely in the VFS level, such that <fs>_follow_link() is not * used any more and could be removed in future. As a side effect, * dir_namei(), _namei() and follow_link() are now replaced with a single - * function __namei() that can handle all the special cases of the former + * function lookup_dentry() that can handle all the special cases of the former * code. * * With the new dcache, the pathname is stored at each inode, at least as @@ -95,13 +87,13 @@ static inline char * get_page(void) char * res; down(&quicklock); res = quicklist; - if(res) { + if (res) { #ifdef DEBUG char * tmp = res; int i; for(i=0; i<quickcount; i++) tmp = *(char**)tmp; - if(tmp) + if (tmp) printk("bad quicklist %x\n", (int)tmp); #endif quicklist = *(char**)res; @@ -113,9 +105,21 @@ static inline char * get_page(void) return res; } +/* + * Kernel pointers have redundant information, so we can use a + * scheme where we can return either an error code or a dentry + * pointer with the same return value. + * + * This should be a per-architecture thing, to allow different + * error and pointer decisions. + */ +#define ERR_PTR(err) ((void *)((long)(err))) +#define PTR_ERR(ptr) ((long)(ptr)) +#define IS_ERR(ptr) ((unsigned long)(ptr) > (unsigned long)(-1000)) + inline void putname(char * name) { - if(name) { + if (name) { down(&quicklock); *(char**)name = quicklist; quicklist = name; @@ -154,20 +158,22 @@ static inline int do_getname(const char *filename, char *page) return retval; } -int getname(const char * filename, char **result) +char * getname(const char * filename) { - char *tmp; - int retval; + char *tmp, *result; + result = ERR_PTR(-ENOMEM); tmp = get_page(); - if(!tmp) - return -ENOMEM; - retval = do_getname(filename, tmp); - if (retval < 0) - putname(tmp); - else - *result = tmp; - return retval; + if (tmp) { + int retval = do_getname(filename, tmp); + + result = tmp; + if (retval < 0) { + putname(tmp); + result = ERR_PTR(retval); + } + } + return result; } /* @@ -222,417 +228,207 @@ void put_write_access(struct inode * inode) inode->i_writecount--; } -static /*inline */ int concat(struct qstr * name, struct qstr * appendix, char * buf) +/* + * This is called when everything else fails, and we actually have + * to go to the low-level filesystem to find out what we should do.. + * + * We get the directory semaphore, and after getting that we also + * make sure that nobody added the entry to the dcache in the meantime.. + */ +static struct dentry * real_lookup(struct dentry * parent, struct qstr * name) { - int totallen = name->len; - if(name->len > MAX_TRANS_FILELEN || - appendix->len > MAX_TRANS_SUFFIX) { - return -ENAMETOOLONG; + struct dentry * result; + struct inode *dir = parent->d_inode; + + result = ERR_PTR(-ENOTDIR); + if (dir->i_op && dir->i_op->lookup) { + down(&dir->i_sem); + result = d_lookup(parent, name); + if (!result) { + int error; + result = d_alloc(parent, name); + error = dir->i_op->lookup(dir, result); + if (error) { + d_free(result); + result = ERR_PTR(error); + } + } + up(&dir->i_sem); } - memcpy(buf, name->name, name->len); - memcpy(buf + name->len, appendix->name, appendix->len); - totallen += appendix->len; - buf[totallen] = '\0'; - return totallen; + return result; } -/* Internal lookup() using the new generic dcache. - * buf must only be supplied if appendix!=NULL. - */ -static int cached_lookup(struct inode * dir, struct qstr * name, - struct qstr * appendix, char * buf, - struct qstr * res_name, struct dentry ** res_entry, - struct inode ** result) +/* Internal lookup() using the new generic dcache. */ +static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name) { - struct qstr tmp = { name->name, name->len }; - int error; - struct dentry * cached; - - *result = NULL; - if(name->len >= D_MAXLEN) - return -ENAMETOOLONG; - vfs_lock(); - cached = d_lookup(dir, name, appendix); - if(cached) { - struct inode *inode = NULL; + struct dentry * dentry = d_lookup(parent, name); - if(cached->u.d_inode && (inode = d_inode(&cached))) { - error = 0; - if(appendix && res_name) { - tmp.len = error = concat(name, appendix, buf); - tmp.name = buf; - if(error > 0) - error = 0; - } - } else { - error = -ENOENT; + if (dentry) { + if (dentry->d_revalidate) { + /* spin_unlock(&dentry_lock); */ + dentry = dentry->d_revalidate(dentry); + /* spin_lock(&dentry_lock); */ } - vfs_unlock(); - if(res_entry) - *res_entry = cached; - /* Since we are bypassing the iget() mechanism, we have to - * fabricate the act of crossing any mount points. + /* + * The parent d_count _should_ be at least 2: one for the + * dentry we found, and one for the fact that we are using + * it. */ - if(!error && inode && inode->i_mount) { - do { - struct inode *mnti = inode->i_mount; - iinc(mnti); - iput(inode); - inode = mnti; - } while(inode->i_mount); + if (parent->d_count <= 1) { + printk("lookup of %s success in %s, but parent count is %d\n", + dentry->d_name.name, parent->d_name.name, parent->d_count); } - *result = inode; - goto done; - } else - vfs_unlock(); - - if(appendix) { - tmp.len = error = concat(name, appendix, buf); - tmp.name = buf; - if(error < 0) - goto done; - } - atomic_inc(&dir->i_count); - error = dir->i_op->lookup(dir, tmp.name, tmp.len, result); - if(dir->i_dentry && tmp.len && - (!error || (error == -ENOENT && (!dir->i_sb || !dir->i_sb->s_type || - !(dir->i_sb->s_type->fs_flags & FS_NO_DCACHE))))) { - struct dentry * res; - vfs_lock(); - res = d_entry(dir->i_dentry, &tmp, error ? NULL : *result); - vfs_unlock(); - if(res_entry) - *res_entry = res; } -done: - if(res_name) { - if(error) { - res_name->name = name->name; - res_name->len = name->len; - } else { - res_name->name = tmp.name; - res_name->len = tmp.len; - } - } - return error; + return dentry; } -#ifdef CONFIG_TRANS_NAMES -/* If a normal filename is seen, try to determine whether a - * "#keyword=context#" file exists and return the new filename. - * If the name is to be created (create_mode), check whether a - * "#keyword=CREATE" name exists and optionally return the corresponding - * context name even if it didn't exist before. +/* + * "." and ".." are special - ".." especially so because it has to be able + * to know about the current root directory and parent relationships */ -static int check_suffixes(struct inode * dir, struct qstr * name, - int create_mode, char * buf, - struct qstr * res_name, struct dentry ** res_entry, - struct inode ** result) +static struct dentry * reserved_lookup(struct dentry * parent, struct qstr * name) { - struct translations * trans; - char * env; - struct qstr * suffixes; - int i; - int error = -ENOENT; - - if(!buf) - panic("buf==NULL"); - env = env_transl(); -#ifdef CONFIG_TRANS_RESTRICT - if(!env && dir->i_gid != CONFIG_TRANS_GID) { - return error; - } -#endif - trans = get_translations(env); - suffixes = create_mode ? trans->c_name : trans->name; - for(i = 0; i < trans->count; i++) { - error = cached_lookup(dir, name, &suffixes[i], - buf, res_name, res_entry, result); - if(!error) { - if(res_name && create_mode) { - /* buf == res_name->name, but is writable */ - memcpy(buf + name->len, - trans->name[i].name, - trans->name[i].len); - res_name->len = name->len + trans->name[i].len; - buf[res_name->len] = '\0'; - } + struct dentry *result = NULL; + if (name->name[0] == '.') { + switch (name->len) { + default: break; + case 2: + if (name->name[1] != '.') + break; + + if (parent != current->fs->root) + parent = parent->d_covers->d_parent; + /* fallthrough */ + case 1: + result = parent; } } - if(env) - free_page((unsigned long)trans); - return error; -} - -#endif - -/* Any operations involving reserved names at the VFS level should go here. */ -static /*inline*/ int reserved_lookup(struct inode * dir, struct qstr * name, - int create_mode, char * buf, - struct inode ** result) -{ - int error = -ENOENT; - if(name->name[0] == '.') { - if(name->len == 1) { - *result = dir; - error = 0; - } else if (name->len==2 && name->name[1] == '.') { - if (dir == current->fs->root) { - *result = dir; - error = 0; - } - else if(dir->i_dentry) { - error = 0; - *result = dir->i_dentry->d_parent->u.d_inode; - if(!*result) { - printk("dcache parent directory is lost"); - error = -ESTALE; /* random error */ - } - } - } - if(!error) - atomic_inc(&(*result)->i_count); - } - return error; + return result; } /* In difference to the former version, lookup() no longer eats the dir. */ -static /*inline*/ int lookup(struct inode * dir, struct qstr * name, int create_mode, - char * buf, struct qstr * res_name, - struct dentry ** res_entry, struct inode ** result) +static struct dentry * lookup(struct dentry * dir, struct qstr * name) { - int perm; - - *result = NULL; - perm = -ENOENT; - if (!dir) - goto done; + int err; + struct dentry * result; /* Check permissions before traversing mount-points. */ - perm = permission(dir,MAY_EXEC); - if (perm) - goto done; - perm = reserved_lookup(dir, name, create_mode, buf, result); - if(!perm) { - if(res_name) { - res_name->name = name->name; - res_name->len = name->len; - } - goto done; - } - perm = -ENOTDIR; - if (!dir->i_op || !dir->i_op->lookup) - goto done; -#ifdef CONFIG_TRANS_NAMES /* try suffixes */ - perm = check_suffixes(dir, name, 0, buf, res_name, res_entry, result); - if(perm) /* try original name */ -#endif - perm = cached_lookup(dir, name, NULL, buf, res_name, res_entry, result); -#ifdef CONFIG_TRANS_NAMES - if(perm == -ENOENT && create_mode) { /* try the =CREATE# suffix */ - struct inode * dummy; - if(!check_suffixes(dir, name, 1, buf, res_name, NULL, &dummy)) { - iput(dummy); - } + err = permission(dir->d_inode, MAY_EXEC); + result = ERR_PTR(err); + if (err) + goto done_error; + + result = reserved_lookup(dir, name); + if (result) + goto done_noerror; + + result = cached_lookup(dir, name); + if (result) + goto done_noerror; + + result = real_lookup(dir, name); + + if (!IS_ERR(result)) { +done_noerror: + result = dget(result->d_mounts); } -#endif -done: - return perm; +done_error: + return result; } -/* [8-Feb-97 T. Schoebel-Theuer] follow_link() modified for generic operation - * on the VFS layer: first call <fs>_readlink() and then open_namei(). - * All <fs>_follow_link() are not used any more and may be eliminated - * (by Linus; I refrained in order to not break other patches). - * Single exeption is procfs, where proc_follow_link() is used - * internally (and perhaps should be rewritten). - * Note: [partly obsolete] I removed parameters flag and mode, since now - * __namei() is called instead of open_namei(). In the old semantics, - * the _last_ instance of open_namei() did the real create() if O_CREAT was - * set and the name existed already in form of a symlink. This has been - * simplified now, and also the semantics when combined with O_EXCL has changed. - **************************************************************************** - * [13-Feb-97] Complete rewrite -> functionality of reading symlinks factored - * out into _read_link(). The above notes remain valid in principle. +/* + * This should check "link_count", but doesn't do that yet.. */ -static /*inline*/ int _read_link(struct inode * inode, char ** linkname, int loopcount) +static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry) { - unsigned long old_fs; - int error; + struct inode * inode = dentry->d_inode; - error = -ENOSYS; - if (!inode->i_op || !inode->i_op->readlink) - goto done; - error = -ELOOP; - if (current->link_count + loopcount > 10) - goto done; - error = -ENOMEM; - if(!*linkname && !(*linkname = get_page())) - goto done; - if (DO_UPDATE_ATIME(inode)) { - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; - } - atomic_inc(&inode->i_count); - old_fs = get_fs(); - set_fs(KERNEL_DS); - error = inode->i_op->readlink(inode, *linkname, PAGE_SIZE); - set_fs(old_fs); - if(!error) { - error = -ENOENT; /* ? or other error code ? */ - } else if(error > 0) { - (*linkname)[error] = '\0'; - error = 0; + if (inode && inode->i_op && inode->i_op->follow_link) { + struct dentry *result; + + /* This eats the base */ + result = inode->i_op->follow_link(inode, base); + base = dentry; + dentry = result; } -done: - iput(inode); - return error; + dput(base); + return dentry; } -/* [13-Feb-97 T. Schoebel-Theuer] complete rewrite: - * merged dir_name(), _namei() and follow_link() into one new routine - * that obeys all the special cases hidden in the old routines in a - * (hopefully) systematic way: - * parameter retrieve_mode is bitwise or'ed of the ST_* flags. - * if res_inode is a NULL pointer, dont try to retrieve the last component - * at all. Parameters with prefix last_ are used only if res_inode is - * non-NULL and refer to the last component of the path only. +/* + * Name resolution. + * + * This is the basic name resolution function, turning a pathname + * into the final dentry. */ -int __namei(int retrieve_mode, const char * name, struct inode * base, - char * buf, struct inode ** res_dir, struct inode ** res_inode, - struct qstr * last_name, struct dentry ** last_entry, - int * last_error) +struct dentry * lookup_dentry(const char * name, struct dentry * base, int follow_link) { - char c; - struct qstr this; - char * linkname = NULL; - char * oldlinkname = NULL; - int trail_flag = 0; - int loopcount = 0; - int error; -#ifdef DEBUG - if(last_name) { - last_name->name = "(Uninitialized)"; - last_name->len = 15; - } -#endif -again: - error = -ENOENT; - this.name = name; - if (this.name[0] == '/') { - if(base) - iput(base); - if (__prefix_namei(retrieve_mode, this.name, base, buf, - res_dir, res_inode, - last_name, last_entry, last_error) == 0) - return 0; - base = current->fs->root; - atomic_inc(&base->i_count); - this.name++; + struct dentry * dentry; + + if (*name == '/') { + if (base) + dput(base); + base = dget(current->fs->root); + do { + name++; + } while (*name == '/'); } else if (!base) { - base = current->fs->pwd; - atomic_inc(&base->i_count); + base = dget(current->fs->pwd); } + + if (!*name) + goto return_base; + + /* At this point we know we have a real path component. */ for(;;) { - struct inode * inode; - const char * tmp = this.name; int len; + unsigned long hash; + struct qstr this; + char c, follow; - for(len = 0; (c = *tmp++) && (c != '/'); len++) ; - this.len = len; - if(!c) + dentry = ERR_PTR(-ENOENT); + if (!base->d_inode) break; - while((c = *tmp) == '/') /* remove embedded/trailing slashes */ - tmp++; - if(!c) { - trail_flag = 1; - if(retrieve_mode & NAM_NO_TRAILSLASH) { - error = -EISDIR; - goto alldone; - } - break; - } -#if 0 - if(atomic_read(&base->i_count) == 0) - printk("vor lookup this=%s tmp=%s\n", this.name, tmp); -#endif - error = lookup(base, &this, 0, buf, NULL, NULL, &inode); -#if 0 - if(atomic_read(&base->i_count) == 0) - printk("nach lookup this=%s tmp=%s\n", this.name, tmp); -#endif - if (error) - goto alldone; - if(S_ISLNK(inode->i_mode)) { - error = _read_link(inode, &linkname, loopcount); - if(error) - goto alldone; - current->link_count++; - error = __namei((retrieve_mode & - ~(NAM_SEMLOCK|NAM_TRANSCREATE|NAM_NO_TRAILSLASH)) - | NAM_FOLLOW_LINK, - linkname, base, buf, - &base, &inode, NULL, NULL, NULL); - current->link_count--; - if(error) - goto alldone; - } -#if 0 - if(atomic_read(&base->i_count) == 0) - printk("this=%s tmp=%s\n", this.name, tmp); -#endif - this.name = tmp; - iput(base); - base = inode; - } - if(res_inode) { - if(retrieve_mode & NAM_SEMLOCK) - down(&base->i_sem); - error = lookup(base, &this, retrieve_mode & NAM_TRANSCREATE, - buf, last_name, last_entry, res_inode); - if(!error && S_ISLNK((*res_inode)->i_mode) && - ((retrieve_mode & NAM_FOLLOW_LINK) || - (trail_flag && (retrieve_mode & NAM_FOLLOW_TRAILSLASH)))) { - char * tmp; - - error = _read_link(*res_inode, &linkname, loopcount); - if(error) - goto lastdone; - if(retrieve_mode & NAM_SEMLOCK) - up(&base->i_sem); - /* exchange pages */ - name = tmp = linkname; - linkname = oldlinkname; oldlinkname = tmp; - loopcount++; - goto again; /* Tail recursion elimination "by hand", - * uses less dynamic memory. - */ - - /* Note that trail_flag is not reset, so it - * does not matter in a symlink chain where a - * trailing slash indicates a directory endpoint. - */ - } - if(!error && trail_flag && !S_ISDIR((*res_inode)->i_mode)) { - iput(*res_inode); - error = -ENOTDIR; - } - lastdone: - if(last_error) { - *last_error = error; - error = 0; + this.name = name; + hash = init_name_hash(); + len = 0; + c = *name; + do { + len++; name++; + hash = partial_name_hash(c, hash); + c = *name; + } while (c && (c != '/')); + + this.len = len; + this.hash = end_name_hash(hash); + + /* remove trailing slashes? */ + follow = follow_link; + if (c) { + follow |= c; + do { + c = *++name; + } while (c == '/'); } + + dentry = lookup(base, &this); + if (IS_ERR(dentry)) + break; + + if (!follow) + break; + + base = do_follow_link(base, dentry); + if (c && !IS_ERR(base)) + continue; + +return_base: + return base; } -alldone: - if(!error && res_dir) - *res_dir = base; - else - iput(base); - putname(linkname); - putname(oldlinkname); - return error; + dput(base); + return dentry; } /* @@ -641,24 +437,41 @@ alldone: * is used by most simple commands to get the inode of a specified name. * Open, link etc use their own routines, but this is enough for things * like 'chmod' etc. + * + * namei exists in two versions: namei/lnamei. The only difference is + * that namei follows links, while lnamei does not. */ +struct dentry * __namei(const char *pathname, int follow_link) +{ + char *name; + struct dentry *dentry; + + name = getname(pathname); + dentry = (struct dentry *) name; + if (!IS_ERR(name)) { + dentry = lookup_dentry(name, NULL, follow_link); + putname(name); + if (!IS_ERR(dentry)) { + if (!dentry->d_inode) { + dput(dentry); + dentry = ERR_PTR(-ENOENT); + } + } + } + return dentry; +} -/* [Feb 1997 T.Schoebel-Theuer] lnamei() completely removed; can be - * simulated when calling with retrieve_mode==NAM_FOLLOW_TRAILSLASH. - */ -int namei(int retrieve_mode, const char *pathname, struct inode **res_inode) +static inline struct inode *get_parent(struct dentry *dentry) { - int error; - char * tmp; + return dentry->d_parent->d_inode; +} - error = getname(pathname, &tmp); - if (!error) { - char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - error = __namei(retrieve_mode, tmp, NULL, - buf, NULL, res_inode, NULL, NULL, NULL); - putname(tmp); - } - return error; +static inline struct inode *lock_parent(struct dentry *dentry) +{ + struct inode *dir = dentry->d_parent->d_inode; + + down(&dir->i_sem); + return dir; } /* @@ -674,175 +487,157 @@ int namei(int retrieve_mode, const char *pathname, struct inode **res_inode) * which is a lot more logical, and also allows the "no perm" needed * for symlinks (where the permissions are checked later). */ -int open_namei(const char * pathname, int flag, int mode, - struct inode ** res_inode, struct inode * base) +struct dentry * open_namei(const char * pathname, int flag, int mode) { - char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr last; - int error; - int lasterror; - struct inode * dir, * inode; - int namei_mode; + int acc_mode, error; + struct inode *inode; + struct dentry *dentry; mode &= S_IALLUGO & ~current->fs->umask; mode |= S_IFREG; - namei_mode = NAM_FOLLOW_LINK; - if(flag & O_CREAT) - namei_mode |= NAM_SEMLOCK|NAM_TRANSCREATE|NAM_NO_TRAILSLASH; - error = __namei(namei_mode, pathname, base, buf, - &dir, &inode, &last, NULL, &lasterror); - if (error) - goto exit; - error = lasterror; + dentry = lookup_dentry(pathname, NULL, 1); + if (IS_ERR(dentry)) + return dentry; + + acc_mode = ACC_MODE(flag); if (flag & O_CREAT) { - if (!error) { - if (flag & O_EXCL) { + struct inode *dir; + + dir = lock_parent(dentry); + /* + * The existence test must be done _after_ getting the directory + * semaphore - the dentry might otherwise change. + */ + if (dentry->d_inode) { + error = 0; + if (flag & O_EXCL) error = -EEXIST; - } } else if (IS_RDONLY(dir)) error = -EROFS; else if (!dir->i_op || !dir->i_op->create) error = -EACCES; - else if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) - ; /* error is already set! */ - else { - d_del(d_lookup(dir, &last, NULL), D_REMOVE); - atomic_inc(&dir->i_count); /* create eats the dir */ + else if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) == 0) { if (dir->i_sb && dir->i_sb->dq_op) dir->i_sb->dq_op->initialize(dir, -1); - error = dir->i_op->create(dir, last.name, last.len, - mode, res_inode); -#ifdef CONFIG_OMIRR - if(!error) - omirr_print(dir->i_dentry, NULL, &last, - " c %ld %d ", CURRENT_TIME, mode); -#endif - up(&dir->i_sem); - goto exit_dir; + error = dir->i_op->create(dir, dentry, mode); + /* Don't check for write permission */ + acc_mode = 0; } up(&dir->i_sem); + if (error) + goto exit; } + + error = -ENOENT; + inode = dentry->d_inode; + if (!inode) + goto exit; + + error = -EISDIR; + if (S_ISDIR(inode->i_mode) && (flag & FMODE_WRITE)) + goto exit; + + error = permission(inode,acc_mode); if (error) - goto exit_inode; + goto exit; - if (S_ISDIR(inode->i_mode) && (flag & 2)) { - error = -EISDIR; - goto exit_inode; - } - if ((error = permission(inode,ACC_MODE(flag))) != 0) { - goto exit_inode; - } + /* + * FIFO's, sockets and device files are special: they don't + * actually live on the filesystem itself, and as such you + * can write to them even if the filesystem is read-only. + */ if (S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { - /* - * 2-Feb-1995 Bruce Perens <Bruce@Pixar.com> - * Allow opens of Unix domain sockets and FIFOs for write on - * read-only filesystems. Their data does not live on the disk. - * - * If there was something like IS_NODEV(inode) for - * pipes and/or sockets I'd check it here. - */ flag &= ~O_TRUNC; - } - else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { - if (IS_NODEV(inode)) { - error = -EACCES; - goto exit_inode; - } + } else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { + error = -EACCES; + if (IS_NODEV(inode)) + goto exit; + flag &= ~O_TRUNC; } else { - if (IS_RDONLY(inode) && (flag & 2)) { - error = -EROFS; - goto exit_inode; - } + error = -EROFS; + if (IS_RDONLY(inode) && (flag & 2)) + goto exit; } /* * An append-only file must be opened in append mode for writing. */ - if (IS_APPEND(inode) && ((flag & FMODE_WRITE) && !(flag & O_APPEND))) { - error = -EPERM; - goto exit_inode; - } + error = -EPERM; + if (IS_APPEND(inode) && ((flag & FMODE_WRITE) && !(flag & O_APPEND))) + goto exit; + if (flag & O_TRUNC) { - if ((error = get_write_access(inode))) - goto exit_inode; + error = get_write_access(inode); + if (error) + goto exit; + /* * Refuse to truncate files with mandatory locks held on them. */ error = locks_verify_locked(inode); - if (error) - goto exit_inode; - if (inode->i_sb && inode->i_sb->dq_op) - inode->i_sb->dq_op->initialize(inode, -1); + if (!error) { + if (inode->i_sb && inode->i_sb->dq_op) + inode->i_sb->dq_op->initialize(inode, -1); - error = do_truncate(inode, 0); + error = do_truncate(inode, 0); + } put_write_access(inode); + if (error) + goto exit; } else if (flag & FMODE_WRITE) if (inode->i_sb && inode->i_sb->dq_op) inode->i_sb->dq_op->initialize(inode, -1); -exit_inode: - if(error) { - if(!lasterror) - iput(inode); - } else - *res_inode = inode; -exit_dir: - iput(dir); + + return dentry; + exit: - return error; + dput(dentry); + return ERR_PTR(error); } -int do_mknod(const char * filename, int mode, dev_t dev) +struct dentry * do_mknod(const char * filename, int mode, dev_t dev) { - char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr last; - int error, lasterror; - struct inode * dir; - struct inode * inode; + int error; + struct inode *dir; + struct dentry *dentry, *retval; mode &= ~current->fs->umask; - error = __namei(NAM_FOLLOW_LINK|NAM_TRANSCREATE|NAM_NO_TRAILSLASH, - filename, NULL, buf, - &dir, &inode, &last, NULL, &lasterror); + dentry = lookup_dentry(filename, NULL, 1); + if (IS_ERR(dentry)) + return dentry; + + dir = lock_parent(dentry); + + retval = ERR_PTR(-EEXIST); + if (dentry->d_inode) + goto exit_lock; + + retval = ERR_PTR(-EROFS); + if (IS_RDONLY(dir)) + goto exit_lock; + + error = permission(dir,MAY_WRITE | MAY_EXEC); + retval = ERR_PTR(error); if (error) - goto exit; - if(!lasterror) { - error = -EEXIST; - goto exit_inode; - } - if (!last.len) { - error = -ENOENT; - goto exit_inode; - } - if (IS_RDONLY(dir)) { - error = -EROFS; - goto exit_inode; - } - if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) - goto exit_inode; - if (!dir->i_op || !dir->i_op->mknod) { - error = -ENOSYS; /* instead of EPERM, what does Posix say? */ - goto exit_inode; - } - atomic_inc(&dir->i_count); + goto exit_lock; + + retval = ERR_PTR(-EPERM); + if (!dir->i_op || !dir->i_op->mknod) + goto exit_lock; + if (dir->i_sb && dir->i_sb->dq_op) dir->i_sb->dq_op->initialize(dir, -1); - down(&dir->i_sem); - d_del(d_lookup(dir, &last, NULL), D_REMOVE); - error = dir->i_op->mknod(dir, last.name, last.len, mode, dev); -#ifdef CONFIG_OMIRR - if(!error) - omirr_print(dir->i_dentry, NULL, &last, " n %ld %d %d ", - CURRENT_TIME, mode, dev); -#endif + error = dir->i_op->mknod(dir, dentry, mode, dev); + retval = ERR_PTR(error); + if (!error) + retval = dget(dentry); + +exit_lock: up(&dir->i_sem); -exit_inode: - if(!lasterror) - iput(inode); - iput(dir); -exit: - return error; + dput(dentry); + return retval; } asmlinkage int sys_mknod(const char * filename, int mode, dev_t dev) @@ -864,68 +659,63 @@ asmlinkage int sys_mknod(const char * filename, int mode, dev_t dev) default: goto out; } - error = getname(filename,&tmp); - if (!error) { - error = do_mknod(tmp,mode,dev); + tmp = getname(filename); + error = PTR_ERR(tmp); + if (!IS_ERR(tmp)) { + struct dentry * dentry = do_mknod(tmp,mode,dev); putname(tmp); + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + dput(dentry); + error = 0; + } } out: unlock_kernel(); return error; } -/* [Feb-97 T. Schoebel-Theuer] remove_trailing_slashes() is now obsolete, - * its functionality is handled by observing trailing slashes in __namei(). +/* + * Look out: this function may change a normal dentry + * into a directory dentry (different size).. */ static inline int do_mkdir(const char * pathname, int mode) { - char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr last; - int error, lasterror; - struct inode * dir; - struct inode * inode; + int error; + struct inode *dir; + struct dentry *dentry; - mode &= 0777 & ~current->fs->umask; + dentry = lookup_dentry(pathname, NULL, 1); + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) + goto exit; + + dir = lock_parent(dentry); + + error = -EEXIST; + if (dentry->d_inode) + goto exit_lock; + + error = -EROFS; + if (IS_RDONLY(dir)) + goto exit_lock; - error = __namei(NAM_FOLLOW_LINK|NAM_TRANSCREATE, pathname, NULL, buf, - &dir, &inode, &last, NULL, &lasterror); + error = permission(dir,MAY_WRITE | MAY_EXEC); if (error) - goto exit; - if(!lasterror) { - error = -EEXIST; - goto exit_inode; - } - if (!last.len) { - error = -ENOENT; - goto exit_inode; - } - if (IS_RDONLY(dir)) { - error = -EROFS; - goto exit_inode; - } - if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) - goto exit_inode; - if (!dir->i_op || !dir->i_op->mkdir) { - error = -ENOSYS; /* instead of EPERM, what does Posix say? */ - goto exit_inode; - } - atomic_inc(&dir->i_count); + goto exit_lock; + + error = -EPERM; + if (!dir->i_op || !dir->i_op->mkdir) + goto exit_lock; + if (dir->i_sb && dir->i_sb->dq_op) dir->i_sb->dq_op->initialize(dir, -1); - down(&dir->i_sem); - d_del(d_lookup(dir, &last, NULL), D_REMOVE); - mode &= 01777 & ~current->fs->umask; - error = dir->i_op->mkdir(dir, last.name, last.len, mode); -#ifdef CONFIG_OMIRR - if(!error) - omirr_print(dir->i_dentry, NULL, &last, " d %ld %d ", - CURRENT_TIME, mode); -#endif + mode &= 0777 & ~current->fs->umask; + error = dir->i_op->mkdir(dir, dentry, mode); + +exit_lock: up(&dir->i_sem); -exit_inode: - if(!lasterror) - iput(inode); - iput(dir); + dput(dentry); exit: return error; } @@ -936,8 +726,9 @@ asmlinkage int sys_mkdir(const char * pathname, int mode) char * tmp; lock_kernel(); - error = getname(pathname,&tmp); - if (!error) { + tmp = getname(pathname); + error = PTR_ERR(tmp); + if (!IS_ERR(tmp)) { error = do_mkdir(tmp,mode); putname(tmp); } @@ -945,124 +736,54 @@ asmlinkage int sys_mkdir(const char * pathname, int mode) return error; } -#if 0 /* We need a "deletefs", someone please write it. -DaveM */ -/* Perhaps this could be moved out into a new file. */ -static void basket_name(struct inode * dir, struct dentry * entry) -{ - char prefix[32]; - struct qstr prename = { prefix, 14 }; - struct qstr entname = { entry->d_name, entry->d_len }; - struct inode * inode; - struct dentry * old = entry; /* dummy */ - int i; - if(!entry || !(inode = d_inode(&entry))) - return; -#if 0 - if(atomic_read(&inode->i_count) > 2) { - extern void printpath(struct dentry *entry); - - printk("Caution: in use "); - if(inode->i_dentry) - printpath(inode->i_dentry); - printk(" i_nlink=%d i_count=%d i_ddir_count=%d i_dent_count=%d\n", - inode->i_nlink, atomic_read(&inode->i_count), - inode->i_ddir_count, inode->i_dent_count); - } -#endif - vfs_lock(); - for(i = 1; old; i++) { - sprintf(prefix, ".deleted-%04d.", i); - old = d_lookup(dir, &prename, &entname); - } - d_move(entry, dir, &prename, &entname); - vfs_unlock(); - iput(inode); -} -#endif - static inline int do_rmdir(const char * name) { - char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr last; - struct dentry * lastent = NULL; int error; - struct inode * dir; - struct inode * inode; - - /* [T.Schoebel-Theuer] I'm not sure which flags to use here. - * Try the following on different platforms: - * [0] rm -rf test test2 - * [1] ln -s test2 test - * [2] mkdir test || mkdir test2 - * [3] rmdir test && mkdir test2 - * [4] rmdir test/ - * Now the rusults: - * cmd | HP-UX | SunOS | Solaris | Old Linux | New Linux | - * ---------------------------------------------------------------- - * [2] | (OK) | EEXIST | EEXIST | EEXIST | (OK) - * [3] | ENOTDIR | ENOTDIR | ENOTDIR | ENOTDIR | ENOTDIR - * [4] | (OK) | EINVAL | ENOTDIR | ENOTDIR | (OK) - * So I implemented the HP-UX semantics. If this is not right - * for Posix compliancy, change the flags accordingly. If Posix - * let the question open, I'd suggest to stay at the new semantics. - * I'd even make case [3] work by adding 2 to the flags parameter - * if Posix tolerates that. - */ - error = __namei(NAM_FOLLOW_TRAILSLASH, name, NULL, buf, - &dir, &inode, &last, &lastent, NULL); - if (error) + struct inode *dir; + struct dentry *dentry; + + dentry = lookup_dentry(name, NULL, 0); + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto exit; - if (IS_RDONLY(dir)) { - error = -EROFS; - goto exit_dir; - } - if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) - goto exit_dir; + + dir = lock_parent(dentry); + error = -ENOENT; + if (!dentry->d_inode) + goto exit_lock; + + error = -EROFS; + if (IS_RDONLY(dir)) + goto exit_lock; + + error = permission(dir,MAY_WRITE | MAY_EXEC); + if (error) + goto exit_lock; + /* * A subdirectory cannot be removed from an append-only directory. */ - if (IS_APPEND(dir)) { - error = -EPERM; - goto exit_dir; - } - if (!dir->i_op || !dir->i_op->rmdir) { - error = -ENOSYS; /* was EPERM */ - goto exit_dir; - } + error = -EPERM; + if (IS_APPEND(dir)) + goto exit_lock; + /* Disallow removals of mountpoints. */ - if(inode->i_mount) { - error = -EBUSY; - goto exit_dir; - } + error = -EBUSY; + if (dentry->d_covers != dentry) + goto exit_lock; + + error = -EPERM; + if (!dir->i_op || !dir->i_op->rmdir) + goto exit_lock; + if (dir->i_sb && dir->i_sb->dq_op) dir->i_sb->dq_op->initialize(dir, -1); - down(&dir->i_sem); -#if 0 - if(lastent && d_isbasket(lastent)) { - d_del(lastent, D_REMOVE); - error = 0; - goto exit_lock; - } -#endif - atomic_inc(&dir->i_count); - error = dir->i_op->rmdir(dir, last.name, last.len); -#ifdef CONFIG_OMIRR - if(!error) - omirr_print(lastent, NULL, NULL, " r %ld ", CURRENT_TIME); -#endif -#if 0 - if(!error && lastent) - basket_name(dir, lastent); + error = dir->i_op->rmdir(dir, dentry); + exit_lock: -#else - if(!error && lastent) - d_del(lastent, D_REMOVE); -#endif up(&dir->i_sem); -exit_dir: - iput(inode); - iput(dir); + dput(dentry); exit: return error; } @@ -1073,8 +794,9 @@ asmlinkage int sys_rmdir(const char * pathname) char * tmp; lock_kernel(); - error = getname(pathname,&tmp); - if (!error) { + tmp = getname(pathname); + error = PTR_ERR(tmp); + if (!IS_ERR(tmp)) { error = do_rmdir(tmp); putname(tmp); } @@ -1084,90 +806,44 @@ asmlinkage int sys_rmdir(const char * pathname) static inline int do_unlink(const char * name) { - char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr last; - struct dentry * lastent = NULL; int error; - struct inode * dir; - struct inode * inode; - - /* HP-UX shows a strange behaviour: - * touch y; ln -s y x; rm x/ - * this succeeds and removes the file y, not the symlink x! - * Solaris and old Linux remove the symlink instead, and - * old SunOS complains ENOTDIR. - * I chose the SunOS behaviour (by not using NAM_FOLLOW_TRAILSLASH), - * but I'm not shure whether I should. - * The current code generally prohibits using trailing slashes with - * non-directories if the name already exists, but not if - * it is to be newly created. - * Perhaps this should be further strengthened (by introducing - * an additional flag bit indicating whether trailing slashes are - * allowed) to get it as consistant as possible, but I don't know - * what Posix says. - */ - error = __namei(NAM_NO_TRAILSLASH, name, NULL, buf, - &dir, &inode, &last, &lastent, NULL); - if (error) + struct inode *dir; + struct dentry *dentry; + + dentry = lookup_dentry(name, NULL, 0); + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto exit; - if (IS_RDONLY(dir)) { - error = -EROFS; - goto exit_dir; - } - if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) - goto exit_dir; + + dir = lock_parent(dentry); + + error = -EROFS; + if (IS_RDONLY(dir)) + goto exit_lock; + + error = permission(dir,MAY_WRITE | MAY_EXEC); + if (error) + goto exit_lock; + /* * A file cannot be removed from an append-only directory. */ - if (IS_APPEND(dir)) { - error = -EPERM; - goto exit_dir; - } - if (!dir->i_op || !dir->i_op->unlink) { - error = -ENOSYS; /* was EPERM */ - goto exit_dir; - } + error = -EPERM; + if (IS_APPEND(dir)) + goto exit_lock; + + error = -EPERM; + if (!dir->i_op || !dir->i_op->unlink) + goto exit_lock; + if (dir->i_sb && dir->i_sb->dq_op) dir->i_sb->dq_op->initialize(dir, -1); - down(&dir->i_sem); -#if 0 - if(atomic_read(&inode->i_count) > 1) { - extern void printpath(struct dentry *entry); - - printk("Fire "); - if(lastent) - printpath(lastent); - printk(" i_nlink=%d i_count=%d i_ddir_count=%d i_dent_count=%d\n", - inode->i_nlink, atomic_read(&inode->i_count), - inode->i_ddir_count, inode->i_dent_count); - } -#endif -#if 0 - if(lastent && d_isbasket(lastent)) { - d_del(lastent, D_REMOVE); - error = 0; - goto exit_lock; - } -#endif - atomic_inc(&dir->i_count); - error = dir->i_op->unlink(dir, last.name, last.len); -#ifdef CONFIG_OMIRR - if(!error) - omirr_print(lastent, NULL, NULL, " u %ld ", CURRENT_TIME); -#endif -#if 0 - if(!error && lastent) - basket_name(dir, lastent); + error = dir->i_op->unlink(dir, dentry); + exit_lock: -#else - if(!error && lastent) - d_del(lastent, D_REMOVE); -#endif up(&dir->i_sem); -exit_dir: - iput(inode); - iput(dir); + dput(dentry); exit: return error; } @@ -1178,8 +854,9 @@ asmlinkage int sys_unlink(const char * pathname) char * tmp; lock_kernel(); - error = getname(pathname,&tmp); - if (!error) { + tmp = getname(pathname); + error = PTR_ERR(tmp); + if (!IS_ERR(tmp)) { error = do_unlink(tmp); putname(tmp); } @@ -1189,62 +866,41 @@ asmlinkage int sys_unlink(const char * pathname) static inline int do_symlink(const char * oldname, const char * newname) { - char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr last; - int error, lasterror; - struct inode * dir; - struct inode * inode; - - /* The following works on HP-UX and Solaris, by producing - * a symlink chain: - * rm -rf ? ; mkdir z ; ln -s z y ; ln -s y x/ - * Under old SunOS, the following occurs: - * ln: x/: No such file or directory - * Under old Linux, very strange things occur: - * ln: cannot create symbolic link `x//y' to `y': No such file or directory - * This is very probably a bug, but may be caused by the ln program - * when checking for a directory target. - * - * I'm not shure whether to add NAM_NO_TRAILSLASH to inhibit trailing - * slashes in the target generally. - */ - error = __namei(NAM_TRANSCREATE, newname, NULL, buf, - &dir, &inode, &last, NULL, &lasterror); - if (error) + int error; + struct inode *dir; + struct dentry *dentry; + + dentry = lookup_dentry(newname, NULL, 0); + + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto exit; - if(!lasterror) { - iput(inode); - error = -EEXIST; - goto exit_dir; - } - if (!last.len) { - error = -ENOENT; - goto exit_dir; - } - if (IS_RDONLY(dir)) { - error = -EROFS; - goto exit_dir; - } - if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0) - goto exit_dir; - if (!dir->i_op || !dir->i_op->symlink) { - error = -ENOSYS; /* was EPERM */ - goto exit_dir; - } - atomic_inc(&dir->i_count); + + error = -EEXIST; + if (dentry->d_inode) + goto exit; + + dir = lock_parent(dentry); + + error = -EROFS; + if (IS_RDONLY(dir)) + goto exit_lock; + + error = permission(dir,MAY_WRITE | MAY_EXEC); + if (error) + goto exit_lock; + + error = -EPERM; + if (!dir->i_op || !dir->i_op->symlink) + goto exit_lock; + if (dir->i_sb && dir->i_sb->dq_op) dir->i_sb->dq_op->initialize(dir, -1); - down(&dir->i_sem); - d_del(d_lookup(dir, &last, NULL), D_REMOVE); - error = dir->i_op->symlink(dir, last.name, last.len, oldname); -#ifdef CONFIG_OMIRR - if(!error) - omirr_print(dir->i_dentry, NULL, &last, - " s %ld %s\0", CURRENT_TIME, oldname); -#endif + error = dir->i_op->symlink(dir, dentry, oldname); + +exit_lock: up(&dir->i_sem); -exit_dir: - iput(dir); + dput(dentry); exit: return error; } @@ -1252,13 +908,16 @@ exit: asmlinkage int sys_symlink(const char * oldname, const char * newname) { int error; - char * from, * to; + char * from; lock_kernel(); - error = getname(oldname,&from); - if (!error) { - error = getname(newname,&to); - if (!error) { + from = getname(oldname); + error = PTR_ERR(from); + if (!IS_ERR(from)) { + char * to; + to = getname(newname); + error = PTR_ERR(to); + if (!IS_ERR(to)) { error = do_symlink(from,to); putname(to); } @@ -1270,73 +929,63 @@ asmlinkage int sys_symlink(const char * oldname, const char * newname) static inline int do_link(const char * oldname, const char * newname) { - char oldbuf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - char newbuf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr oldlast; - struct qstr newlast; - struct dentry * oldent = NULL; - struct inode * oldinode; - struct inode * newinode; - struct inode * newdir; - int error, lasterror; - - error = __namei(NAM_FOLLOW_LINK|NAM_NO_TRAILSLASH, - oldname, NULL, oldbuf, - NULL, &oldinode, &oldlast, &oldent, NULL); - if (error) + struct dentry *old_dentry, *new_dentry; + struct inode *dir, *inode; + int error; + + old_dentry = lookup_dentry(oldname, NULL, 1); + error = PTR_ERR(old_dentry); + if (IS_ERR(old_dentry)) goto exit; - error = __namei(NAM_FOLLOW_LINK|NAM_TRANSCREATE, newname, NULL, newbuf, - &newdir, &newinode, &newlast, NULL, &lasterror); + new_dentry = lookup_dentry(newname, NULL, 1); + error = PTR_ERR(new_dentry); + if (IS_ERR(new_dentry)) + goto exit_old; + + dir = lock_parent(new_dentry); + + error = -ENOENT; + inode = old_dentry->d_inode; + if (!inode) + goto exit_lock; + + error = -EEXIST; + if (new_dentry->d_inode) + goto exit_lock; + + error = -EROFS; + if (IS_RDONLY(dir)) + goto exit_lock; + + error = -EXDEV; + if (dir->i_dev != inode->i_dev) + goto exit_lock; + + error = permission(dir, MAY_WRITE | MAY_EXEC); if (error) - goto old_exit; - if(!lasterror) { - iput(newinode); - error = -EEXIST; - goto new_exit; - } - if (!newlast.len) { - error = -EPERM; - goto new_exit; - } - if (IS_RDONLY(newdir)) { - error = -EROFS; - goto new_exit; - } - if (newdir->i_dev != oldinode->i_dev) { - error = -EXDEV; - goto new_exit; - } - if ((error = permission(newdir,MAY_WRITE | MAY_EXEC)) != 0) - goto new_exit; + goto exit_lock; + /* * A link to an append-only or immutable file cannot be created. */ - if (IS_APPEND(oldinode) || IS_IMMUTABLE(oldinode)) { - error = -EPERM; - goto new_exit; - } - if (!newdir->i_op || !newdir->i_op->link) { - error = -ENOSYS; /* was EPERM */ - goto new_exit; - } - atomic_inc(&oldinode->i_count); - atomic_inc(&newdir->i_count); - if (newdir->i_sb && newdir->i_sb->dq_op) - newdir->i_sb->dq_op->initialize(newdir, -1); - down(&newdir->i_sem); - d_del(d_lookup(newdir, &newlast, NULL), D_REMOVE); - error = newdir->i_op->link(oldinode, newdir, newlast.name, newlast.len); -#ifdef CONFIG_OMIRR - if(!error) - omirr_print(oldent, newdir->i_dentry, &newlast, - " l %ld ", CURRENT_TIME); -#endif - up(&newdir->i_sem); -new_exit: - iput(newdir); -old_exit: - iput(oldinode); + error = -EPERM; + if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) + goto exit_lock; + + error = -EPERM; + if (!dir->i_op || !dir->i_op->link) + goto exit_lock; + + if (dir->i_sb && dir->i_sb->dq_op) + dir->i_sb->dq_op->initialize(dir, -1); + error = dir->i_op->link(inode, dir, new_dentry); + +exit_lock: + up(&dir->i_sem); + dput(new_dentry); +exit_old: + dput(old_dentry); exit: return error; } @@ -1344,13 +993,16 @@ exit: asmlinkage int sys_link(const char * oldname, const char * newname) { int error; - char * from, * to; + char * from; lock_kernel(); - error = getname(oldname,&from); - if (!error) { - error = getname(newname,&to); - if (!error) { + from = getname(oldname); + error = PTR_ERR(from); + if (!IS_ERR(from)) { + char * to; + to = getname(newname); + error = PTR_ERR(to); + if (!IS_ERR(to)) { error = do_link(from,to); putname(to); } @@ -1360,105 +1012,111 @@ asmlinkage int sys_link(const char * oldname, const char * newname) return error; } -static inline int do_rename(const char * oldname, const char * newname) +/* + * Whee.. Deadlock country. Happily there is only one VFS + * operation that does this.. + */ +static inline void double_down(struct semaphore *s1, struct semaphore *s2) { - char oldbuf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr oldlast; - char newbuf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2]; - struct qstr newlast; - struct dentry * oldent = NULL; - struct inode * olddir, * newdir; - struct inode * oldinode, * newinode; - int error, newlasterror; - - error = __namei(NAM_FOLLOW_TRAILSLASH, oldname, NULL, oldbuf, - &olddir, &oldinode, &oldlast, &oldent, NULL); - if (error) - goto exit; - if ((error = permission(olddir,MAY_WRITE | MAY_EXEC)) != 0) - goto old_exit; - if (!oldlast.len || (oldlast.name[0] == '.' && - (oldlast.len == 1 || (oldlast.name[1] == '.' && - oldlast.len == 2)))) { - error = -EPERM; - goto old_exit; + if ((unsigned long) s1 < (unsigned long) s2) { + down(s1); + down(s2); + } else if (s1 == s2) { + down(s1); + atomic_dec(&s1->count); + } else { + down(s2); + down(s1); } - /* Disallow moves of mountpoints. */ - if(oldinode->i_mount) { - error = -EBUSY; - goto old_exit; +} + +static inline int is_reserved(struct dentry *dentry) +{ + if (dentry->d_name.name[0] == '.') { + switch (dentry->d_name.len) { + case 2: + if (dentry->d_name.name[1] != '.') + break; + /* fallthrough */ + case 1: + return 1; + } } + return 0; +} + +static inline int do_rename(const char * oldname, const char * newname) +{ + int error; + struct inode * old_dir, * new_dir; + struct dentry * old_dentry, *new_dentry; + + old_dentry = lookup_dentry(oldname, NULL, 0); + + error = PTR_ERR(old_dentry); + if (IS_ERR(old_dentry)) + goto exit; + + new_dentry = lookup_dentry(newname, NULL, 0); + + error = PTR_ERR(new_dentry); + if (IS_ERR(new_dentry)) + goto exit_old; + + new_dir = get_parent(new_dentry); + old_dir = get_parent(old_dentry); + + double_down(&new_dir->i_sem, &old_dir->i_sem); - error = __namei(NAM_FOLLOW_LINK|NAM_TRANSCREATE, newname, NULL, newbuf, - &newdir, &newinode, &newlast, NULL, &newlasterror); + error = -ENOENT; + if (!old_dentry->d_inode) + goto exit_lock; + + error = permission(old_dir,MAY_WRITE | MAY_EXEC); if (error) - goto old_exit; - if ((error = permission(newdir,MAY_WRITE | MAY_EXEC)) != 0) - goto new_exit; - if (!newlast.len || (newlast.name[0] == '.' && - (newlast.len == 1 || (newlast.name[1] == '.' && - newlast.len == 2)))) { - error = -EPERM; - goto new_exit; - } - if (newdir->i_dev != olddir->i_dev) { - error = -EXDEV; - goto new_exit; - } - if (IS_RDONLY(newdir) || IS_RDONLY(olddir)) { - error = -EROFS; - goto new_exit; - } + goto exit_lock; + error = permission(new_dir,MAY_WRITE | MAY_EXEC); + if (error) + goto exit_lock; + + error = -EPERM; + if (is_reserved(new_dentry) || is_reserved(old_dentry)) + goto exit_lock; + + /* Disallow moves of mountpoints. */ + error = -EBUSY; + if (old_dentry->d_covers != old_dentry) + goto exit_lock; + + error = -EXDEV; + if (new_dir->i_dev != old_dir->i_dev) + goto exit_lock; + + error = -EROFS; + if (IS_RDONLY(new_dir) || IS_RDONLY(old_dir)) + goto exit_lock; + /* * A file cannot be removed from an append-only directory. */ - if (IS_APPEND(olddir)) { - error = -EPERM; - goto new_exit; - } - if (!olddir->i_op || !olddir->i_op->rename) { - error = -ENOSYS; /* was EPERM */ - goto new_exit; - } -#ifdef CONFIG_TRANS_NAMES - /* if oldname has been translated, but newname not (and - * has not already a suffix), take over the suffix from oldname. - */ - if(oldlast.name == oldbuf && newlast.name != newbuf && - newlast.name[newlast.len-1] != '#') { - int i = oldlast.len - 2; - while (i > 0 && oldlast.name[i] != '#') - i--; - memcpy(newbuf, newlast.name, newlast.len); - memcpy(newbuf+newlast.len, oldlast.name+i, oldlast.len - i); - newlast.len += oldlast.len - i; - newlast.name = newbuf; - } -#endif - atomic_inc(&olddir->i_count); - atomic_inc(&newdir->i_count); - if (newdir->i_sb && newdir->i_sb->dq_op) - newdir->i_sb->dq_op->initialize(newdir, -1); - down(&newdir->i_sem); - error = olddir->i_op->rename(olddir, oldlast.name, oldlast.len, - newdir, newlast.name, newlast.len); -#ifdef CONFIG_OMIRR - if(!error) - omirr_print(oldent, newdir->i_dentry, &newlast, - " m %ld ", CURRENT_TIME); -#endif - if(!error) { - d_del(d_lookup(newdir, &newlast, NULL), D_REMOVE); - d_move(d_lookup(olddir, &oldlast, NULL), newdir, &newlast, NULL); - } - up(&newdir->i_sem); -new_exit: - if(!newlasterror) - iput(newinode); - iput(newdir); -old_exit: - iput(oldinode); - iput(olddir); + error = -EPERM; + if (IS_APPEND(old_dir)) + goto exit_lock; + + error = -EPERM; + if (!old_dir->i_op || !old_dir->i_op->rename) + goto exit_lock; + + if (new_dir->i_sb && new_dir->i_sb->dq_op) + new_dir->i_sb->dq_op->initialize(new_dir, -1); + error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); + +exit_lock: + up(&new_dir->i_sem); + up(&old_dir->i_sem); + dput(new_dentry); +exit_old: + dput(old_dentry); exit: return error; } @@ -1466,13 +1124,16 @@ exit: asmlinkage int sys_rename(const char * oldname, const char * newname) { int error; - char * from, * to; + char * from; lock_kernel(); - error = getname(oldname,&from); - if (!error) { - error = getname(newname,&to); - if (!error) { + from = getname(oldname); + error = PTR_ERR(from); + if (!IS_ERR(from)) { + char * to; + to = getname(newname); + error = PTR_ERR(to); + if (!IS_ERR(to)) { error = do_rename(from,to); putname(to); } diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 72ca3e6dd..83309f3a6 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -408,7 +408,7 @@ int ncp_current_malloced; static struct file_system_type ncp_fs_type = { "ncpfs", - FS_NO_DCACHE, + 0 /* FS_NO_DCACHE doesn't work correctly */, ncp_read_super, NULL }; diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c index 8e814d153..937f40cec 100644 --- a/fs/ncpfs/mmap.c +++ b/fs/ncpfs/mmap.c @@ -132,8 +132,8 @@ int ncp_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma) inode->i_atime = CURRENT_TIME; inode->i_dirt = 1; } - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + + vma->vm_dentry = dget(file->f_dentry); vma->vm_ops = &ncp_file_mmap; return 0; } diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 71835c255..b10331c6a 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -42,16 +42,15 @@ struct nfs_dirent { static int nfs_dir_open(struct inode * inode, struct file * file); static long nfs_dir_read(struct inode *, struct file *, char *, unsigned long); static int nfs_readdir(struct inode *, struct file *, void *, filldir_t); -static int nfs_lookup(struct inode *, const char *, int, struct inode **); -static int nfs_create(struct inode *, const char *, int, int, struct inode **); -static int nfs_mkdir(struct inode *, const char *, int, int); -static int nfs_rmdir(struct inode *, const char *, int); -static int nfs_unlink(struct inode *, const char *, int); -static int nfs_symlink(struct inode *, const char *, int, const char *); -static int nfs_link(struct inode *, struct inode *, const char *, int); -static int nfs_mknod(struct inode *, const char *, int, int, int); -static int nfs_rename(struct inode *, const char *, int, - struct inode *, const char *, int); +static int nfs_lookup(struct inode *, struct dentry *); +static int nfs_create(struct inode *, struct dentry *, int); +static int nfs_mkdir(struct inode *, struct dentry *, int); +static int nfs_rmdir(struct inode *, struct dentry *); +static int nfs_unlink(struct inode *, struct dentry *); +static int nfs_symlink(struct inode *, struct dentry *, const char *); +static int nfs_link(struct inode *, struct inode *, struct dentry *); +static int nfs_mknod(struct inode *, struct dentry *, int, int); +static int nfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); static struct file_operations nfs_dir_operations = { NULL, /* lseek - default */ @@ -78,6 +77,7 @@ struct inode_operations nfs_dir_inode_operations = { nfs_mknod, /* mknod */ nfs_rename, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -328,488 +328,267 @@ nfs_free_dircache(void) } -/* - * Lookup caching is a big win for performance but this is just - * a trial to see how well it works on a small scale. - * For example, bash does a lookup on ".." 13 times for each path - * element when running pwd. Yes, hard to believe but true. - * Try pwd in a filesystem mounted with noac. - * - * It trades a little cpu time and memory for a lot of network bandwidth. - * Since the cache is not hashed yet, it is a good idea not to make it too - * large because every lookup looks through the entire cache even - * though most of them will fail. - * - * FIXME: The lookup cache should also cache failed lookups. This can - * be a considerable win on diskless clients. - */ - -static struct nfs_lookup_cache_entry { - kdev_t dev; - ino_t inode; - char filename[NFS_MAXNAMLEN + 1]; - struct nfs_fh fhandle; - struct nfs_fattr fattr; - unsigned long expiration_date; -} nfs_lookup_cache[NFS_LOOKUP_CACHE_SIZE]; - -static struct nfs_lookup_cache_entry *nfs_lookup_cache_index(struct inode *dir, - const char *filename) -{ - struct nfs_lookup_cache_entry *entry; - int i; - - for (i = 0; i < NFS_LOOKUP_CACHE_SIZE; i++) { - entry = nfs_lookup_cache + i; - if (entry->dev == dir->i_dev - && entry->inode == dir->i_ino - && !strncmp(filename, entry->filename, NFS_MAXNAMLEN)) - return entry; - } - return NULL; -} - -static int nfs_lookup_cache_lookup(struct inode *dir, const char *filename, - struct nfs_fh *fhandle, - struct nfs_fattr *fattr) -{ - static int nfs_lookup_cache_in_use = 0; - - struct nfs_lookup_cache_entry *entry; - - dfprintk(LOOKUPCACHE, "NFS: lookup_cache_lookup(%x/%ld, %s)\n", - dir->i_dev, dir->i_ino, filename); - if (!nfs_lookup_cache_in_use) { - memset(nfs_lookup_cache, 0, sizeof(nfs_lookup_cache)); - nfs_lookup_cache_in_use = 1; - } - if ((entry = nfs_lookup_cache_index(dir, filename))) { - if (jiffies > entry->expiration_date) { - entry->dev = 0; - return 0; - } - *fhandle = entry->fhandle; - *fattr = entry->fattr; - return 1; - } - return 0; -} - -static void nfs_lookup_cache_add(struct inode *dir, const char *filename, - struct nfs_fh *fhandle, - struct nfs_fattr *fattr) -{ - static int nfs_lookup_cache_pos = 0; - struct nfs_lookup_cache_entry *entry; - - dfprintk(LOOKUPCACHE, "NFS: lookup_cache_add(%x/%ld, %s\n", - dir->i_dev, dir->i_ino, filename); - - /* compensate for bug in SGI NFS server */ - if (fattr->size == -1 || fattr->uid == -1 || fattr->gid == -1 - || fattr->atime.seconds == -1 || fattr->mtime.seconds == -1) - return; - if (!(entry = nfs_lookup_cache_index(dir, filename))) { - entry = nfs_lookup_cache + nfs_lookup_cache_pos++; - if (nfs_lookup_cache_pos == NFS_LOOKUP_CACHE_SIZE) - nfs_lookup_cache_pos = 0; - } - - entry->dev = dir->i_dev; - entry->inode = dir->i_ino; - strcpy(entry->filename, filename); - entry->fhandle = *fhandle; - entry->fattr = *fattr; - entry->expiration_date = jiffies + (S_ISDIR(fattr->mode) - ? NFS_SERVER(dir)->acdirmin : NFS_SERVER(dir)->acregmin); -} - -static void nfs_lookup_cache_remove(struct inode *dir, struct inode *inode, - const char *filename) -{ - struct nfs_lookup_cache_entry *entry; - kdev_t dev; - ino_t fileid; - int i; - - if (inode) { - dev = inode->i_dev; - fileid = inode->i_ino; - } - else if ((entry = nfs_lookup_cache_index(dir, filename))) { - dev = entry->dev; - fileid = entry->fattr.fileid; - } - else - return; - - dfprintk(LOOKUPCACHE, "NFS: lookup_cache_remove(%x/%ld)\n", - dev, (long)fileid); - - for (i = 0; i < NFS_LOOKUP_CACHE_SIZE; i++) { - entry = nfs_lookup_cache + i; - if (entry->dev == dev && entry->fattr.fileid == fileid) - entry->dev = 0; - } -} - -static void nfs_lookup_cache_refresh(struct inode *file, - struct nfs_fattr *fattr) -{ - struct nfs_lookup_cache_entry *entry; - kdev_t dev = file->i_dev; - int fileid = file->i_ino; - int i; - - for (i = 0; i < NFS_LOOKUP_CACHE_SIZE; i++) { - entry = nfs_lookup_cache + i; - if (entry->dev == dev && entry->fattr.fileid == fileid) - entry->fattr = *fattr; - } -} - -static int nfs_lookup(struct inode *dir, const char *__name, int len, - struct inode **result) +static int nfs_lookup(struct inode *dir, struct dentry * dentry) { + struct inode *inode; struct nfs_fh fhandle; struct nfs_fattr fattr; - char name[len > NFS_MAXNAMLEN? 1 : len+1]; + int len = dentry->d_name.len; int error; dfprintk(VFS, "NFS: lookup(%x/%ld, %.*s)\n", - dir->i_dev, dir->i_ino, len, __name); + dir->i_dev, dir->i_ino, len, dentry->d_name.name); - *result = NULL; if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_lookup: inode is NULL or not a directory\n"); - iput(dir); return -ENOENT; } - if (len > NFS_MAXNAMLEN) { - iput(dir); + + if (len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - } - memcpy(name,__name,len); - name[len] = '\0'; - if (len == 0 || (len == 1 && name[0] == '.')) { /* cheat for "" and "." */ - *result = dir; - return 0; - } - if ((NFS_SERVER(dir)->flags & NFS_MOUNT_NOAC) - || !nfs_lookup_cache_lookup(dir, name, &fhandle, &fattr)) { - if ((error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir), - name, &fhandle, &fattr))) { - iput(dir); - return error; - } - nfs_lookup_cache_add(dir, name, &fhandle, &fattr); - } - if (!(*result = nfs_fhget(dir->i_sb, &fhandle, &fattr))) { - iput(dir); - return -EACCES; - } - iput(dir); + + error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name, &fhandle, &fattr); + + inode = NULL; + if (!error) { + error = -ENOENT; + inode = nfs_fhget(dir->i_sb, &fhandle, &fattr); + if (!inode) + return -EACCES; + } else if (error != -ENOENT) + return error; + + d_add(dentry, inode); return 0; } -static int nfs_create(struct inode *dir, const char *name, int len, int mode, - struct inode **result) +static int nfs_create(struct inode *dir, struct dentry * dentry, int mode) { struct nfs_sattr sattr; struct nfs_fattr fattr; struct nfs_fh fhandle; + struct inode *inode; int error; dfprintk(VFS, "NFS: create(%x/%ld, %s\n", - dir->i_dev, dir->i_ino, name); + dir->i_dev, dir->i_ino, dentry->d_name.name); - *result = NULL; if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_create: inode is NULL or not a directory\n"); - iput(dir); return -ENOENT; } - if (len > NFS_MAXNAMLEN) { - iput(dir); + + if (dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - } + sattr.mode = mode; sattr.uid = sattr.gid = sattr.size = (unsigned) -1; sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; - if ((error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir), - name, &sattr, &fhandle, &fattr))) { - iput(dir); + error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir), + dentry->d_name.name, &sattr, &fhandle, &fattr); + + if (error) return error; - } - if (!(*result = nfs_fhget(dir->i_sb, &fhandle, &fattr))) { - iput(dir); + + inode = nfs_fhget(dir->i_sb, &fhandle, &fattr); + if (!inode) return -EACCES; - } - nfs_lookup_cache_add(dir, name, &fhandle, &fattr); - nfs_invalidate_dircache(dir); - iput(dir); + + d_instantiate(dentry, inode); return 0; } -static int nfs_mknod(struct inode *dir, const char *name, int len, - int mode, int rdev) +static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev) { struct nfs_sattr sattr; struct nfs_fattr fattr; struct nfs_fh fhandle; + struct inode *inode; int error; dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n", - dir->i_dev, dir->i_ino, name); + dir->i_dev, dir->i_ino, dentry->d_name.name); if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_mknod: inode is NULL or not a directory\n"); - iput(dir); return -ENOENT; } - if (len > NFS_MAXNAMLEN) { - iput(dir); + + if (dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - } + sattr.mode = mode; - sattr.uid = sattr.gid = (unsigned) -1; + sattr.uid = sattr.gid = sattr.size = (unsigned) -1; if (S_ISCHR(mode) || S_ISBLK(mode)) sattr.size = rdev; /* get out your barf bag */ - else - sattr.size = (unsigned) -1; + sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir), - name, &sattr, &fhandle, &fattr); - if (!error) - nfs_lookup_cache_add(dir, name, &fhandle, &fattr); - nfs_invalidate_dircache(dir); - iput(dir); - return error; + dentry->d_name.name, &sattr, &fhandle, &fattr); + + if (error) + return error; + + inode = nfs_fhget(dir->i_sb, &fhandle, &fattr); + if (!inode) + return -EACCES; + + d_instantiate(dentry, inode); + return 0; } -static int nfs_mkdir(struct inode *dir, const char *name, int len, int mode) +static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) { struct nfs_sattr sattr; struct nfs_fattr fattr; struct nfs_fh fhandle; + struct inode * inode; int error; dfprintk(VFS, "NFS: mkdir(%x/%ld, %s\n", - dir->i_dev, dir->i_ino, name); + dir->i_dev, dir->i_ino, dentry->d_name.name); if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_mkdir: inode is NULL or not a directory\n"); - iput(dir); return -ENOENT; } - if (len > NFS_MAXNAMLEN) { - iput(dir); + + if (dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - } + sattr.mode = mode; sattr.uid = sattr.gid = sattr.size = (unsigned) -1; sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; + error = nfs_proc_mkdir(NFS_SERVER(dir), NFS_FH(dir), - name, &sattr, &fhandle, &fattr); - if (!error) - nfs_lookup_cache_add(dir, name, &fhandle, &fattr); - nfs_invalidate_dircache(dir); - iput(dir); - return error; + dentry->d_name.name, &sattr, &fhandle, &fattr); + + if (error) + return error; + + inode = nfs_fhget(dir->i_sb, &fhandle, &fattr); + if (!inode) + return -EACCES; + + d_instantiate(dentry, inode); + return 0; } -static int nfs_rmdir(struct inode *dir, const char *name, int len) +static int nfs_rmdir(struct inode *dir, struct dentry *dentry) { int error; dfprintk(VFS, "NFS: rmdir(%x/%ld, %s\n", - dir->i_dev, dir->i_ino, name); + dir->i_dev, dir->i_ino, dentry->d_name.name); if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_rmdir: inode is NULL or not a directory\n"); - iput(dir); return -ENOENT; } - if (len > NFS_MAXNAMLEN) { - iput(dir); - return -ENAMETOOLONG; - } - error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dir), name); - if (!error) - nfs_lookup_cache_remove(dir, NULL, name); - nfs_invalidate_dircache(dir); - iput(dir); - return error; -} - -static int nfs_sillyrename(struct inode *dir, const char *name, int len) -{ - struct inode *inode; - char silly[16]; - int slen, ret; - - atomic_inc(&dir->i_count); - if (nfs_lookup(dir, name, len, &inode) < 0) - return -EIO; /* arbitrary */ - - if (atomic_read(&inode->i_count) == 1) { - iput(inode); - return -EIO; - } - if (NFS_RENAMED_DIR(inode)) { - iput(NFS_RENAMED_DIR(inode)); - NFS_RENAMED_DIR(inode) = NULL; - iput(inode); - return -EIO; - } - slen = sprintf(silly, ".nfs%ld", inode->i_ino); - if (len == slen && !strncmp(name, silly, len)) { - iput(inode); - return -EIO; /* DWIM */ - } + if (dentry->d_name.len > NFS_MAXNAMLEN) + return -ENAMETOOLONG; - dfprintk(VFS, "NFS: sillyrename(%x/%ld, %s)\n", - dir->i_dev, dir->i_ino, name); + error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name); + if (error) + return error; - ret = nfs_proc_rename(NFS_SERVER(dir), NFS_FH(dir), name, - NFS_FH(dir), silly); - if (ret >= 0) { - nfs_lookup_cache_remove(dir, NULL, name); - nfs_lookup_cache_remove(dir, NULL, silly); - NFS_RENAMED_DIR(inode) = dir; - atomic_inc(&dir->i_count); - } - nfs_invalidate_dircache(dir); - iput(inode); - return ret; + d_delete(dentry); + return 0; } /* - * When releasing the inode, finally remove any unlinked but open files. - * Note that we have to clear the set of pending signals temporarily; - * otherwise the RPC call will fail. + * We should do silly-rename here, but I'm too lazy to fix + * up the directory entry implications of it.. */ -void nfs_sillyrename_cleanup(struct inode *inode) -{ - unsigned long oldsig; - struct inode *dir = NFS_RENAMED_DIR(inode); - char silly[14]; - int error, slen; - - dfprintk(VFS, "NFS: sillyrename cleanup(%x/%ld)\n", - inode->i_dev, inode->i_ino); - - oldsig = current->signal; - current->signal = 0; - - slen = sprintf(silly, ".nfs%ld", inode->i_ino); - error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), silly); - if (error < 0) - printk("NFS: silly_rename cleanup failed (err %d)\n", -error); - - nfs_lookup_cache_remove(dir, NULL, silly); - nfs_invalidate_dircache(dir); - NFS_RENAMED_DIR(inode) = NULL; - iput(dir); - - current->signal |= oldsig; -} - -static int nfs_unlink(struct inode *dir, const char *name, int len) +static int nfs_unlink(struct inode *dir, struct dentry *dentry) { int error; dfprintk(VFS, "NFS: unlink(%x/%ld, %s)\n", - dir->i_dev, dir->i_ino, name); + dir->i_dev, dir->i_ino, dentry->d_name.name); if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_unlink: inode is NULL or not a directory\n"); - iput(dir); return -ENOENT; } - if (len > NFS_MAXNAMLEN) { - iput(dir); + + if (dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - } - if ((error = nfs_sillyrename(dir, name, len)) < 0) { - error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), name); - if (!error) - nfs_lookup_cache_remove(dir, NULL, name); - } - nfs_invalidate_dircache(dir); - iput(dir); - return error; + + error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name); + if (error) + return error; + + d_delete(dentry); + return 0; } -static int nfs_symlink(struct inode *dir, const char *name, int len, - const char *symname) +static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct nfs_sattr sattr; + struct nfs_fattr fattr; + struct nfs_fh fhandle; + struct inode * inode; int error; dfprintk(VFS, "NFS: symlink(%x/%ld, %s, %s)\n", - dir->i_dev, dir->i_ino, name, symname); + dir->i_dev, dir->i_ino, dentry->d_name.name, symname); if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_symlink: inode is NULL or not a directory\n"); - iput(dir); return -ENOENT; } - if (len > NFS_MAXNAMLEN) { - iput(dir); + + if (dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - } - if (strlen(symname) > NFS_MAXPATHLEN) { - iput(dir); + + if (strlen(symname) > NFS_MAXPATHLEN) return -ENAMETOOLONG; - } + sattr.mode = S_IFLNK | S_IRWXUGO; /* SunOS 4.1.2 crashes without this! */ sattr.uid = sattr.gid = sattr.size = (unsigned) -1; sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; + error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dir), - name, symname, &sattr); - nfs_invalidate_dircache(dir); - iput(dir); - return error; + dentry->d_name.name, symname, &sattr); + + if (error) + return error; + + inode = nfs_fhget(dir->i_sb, &fhandle, &fattr); + if (!inode) + return -EACCES; + + d_instantiate(dentry, inode); + return 0; } -static int nfs_link(struct inode *oldinode, struct inode *dir, - const char *name, int len) +static int nfs_link(struct inode *inode, struct inode *dir, struct dentry *dentry) { int error; dfprintk(VFS, "NFS: link(%x/%ld -> %x/%ld, %s)\n", - oldinode->i_dev, oldinode->i_ino, - dir->i_dev, dir->i_ino, name); + inode->i_dev, inode->i_ino, + dir->i_dev, dir->i_ino, dentry->d_name.name); - if (!oldinode) { - printk("nfs_link: old inode is NULL\n"); - iput(oldinode); - iput(dir); - return -ENOENT; - } if (!dir || !S_ISDIR(dir->i_mode)) { printk("nfs_link: dir is NULL or not a directory\n"); - iput(oldinode); - iput(dir); return -ENOENT; } - if (len > NFS_MAXNAMLEN) { - iput(oldinode); - iput(dir); + + if (dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - } - error = nfs_proc_link(NFS_SERVER(oldinode), NFS_FH(oldinode), - NFS_FH(dir), name); - if (!error) { - nfs_lookup_cache_remove(dir, oldinode, NULL); - NFS_READTIME(oldinode) = 0; /* force getattr */ - } - nfs_invalidate_dircache(dir); - iput(oldinode); - iput(dir); - return error; + + error = nfs_proc_link(NFS_SERVER(inode), NFS_FH(inode), + NFS_FH(dir), dentry->d_name.name); + + if (error) + return error; + + inode->i_count++; + d_instantiate(dentry, inode); + return 0; } /* @@ -821,45 +600,39 @@ static int nfs_link(struct inode *oldinode, struct inode *dir, * rename the old file using the silly_rename stuff. This way, the original * file in old_dir will go away when the last process iput()s the inode. */ -static int nfs_rename(struct inode *old_dir, const char *old_name, int old_len, - struct inode *new_dir, const char *new_name, int new_len) +static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) { int error; dfprintk(VFS, "NFS: rename(%x/%ld, %s -> %x/%ld, %s)\n", - old_dir->i_dev, old_dir->i_ino, old_name, - new_dir->i_dev, new_dir->i_ino, new_name); + old_dir->i_dev, old_dir->i_ino, old_dentry->d_name.name, + new_dir->i_dev, new_dir->i_ino, new_dentry->d_name.name); if (!old_dir || !S_ISDIR(old_dir->i_mode)) { printk("nfs_rename: old inode is NULL or not a directory\n"); - iput(old_dir); - iput(new_dir); return -ENOENT; } + if (!new_dir || !S_ISDIR(new_dir->i_mode)) { printk("nfs_rename: new inode is NULL or not a directory\n"); - iput(old_dir); - iput(new_dir); return -ENOENT; } - if (old_len > NFS_MAXNAMLEN || new_len > NFS_MAXNAMLEN) { - iput(old_dir); - iput(new_dir); + + if (old_dentry->d_name.len > NFS_MAXNAMLEN || new_dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - } error = nfs_proc_rename(NFS_SERVER(old_dir), - NFS_FH(old_dir), old_name, - NFS_FH(new_dir), new_name); - if (!error) { - nfs_lookup_cache_remove(old_dir, NULL, old_name); - nfs_lookup_cache_remove(new_dir, NULL, new_name); - } - nfs_invalidate_dircache(old_dir); - nfs_invalidate_dircache(new_dir); - iput(old_dir); - iput(new_dir); - return error; + NFS_FH(old_dir), old_dentry->d_name.name, + NFS_FH(new_dir), new_dentry->d_name.name); + + if (error) + return error; + + /* Update the dcache */ + d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name); + d_delete(new_dentry); + return 0; } /* @@ -873,8 +646,7 @@ void nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) int was_empty; dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d)\n", - inode->i_dev, inode->i_ino, - atomic_read(&inode->i_count)); + inode->i_dev, inode->i_ino, inode->i_count); if (!inode || !fattr) { printk("nfs_refresh_inode: inode or fattr is NULL\n"); @@ -925,6 +697,4 @@ void nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) init_fifo(inode); } else inode->i_op = NULL; - nfs_lookup_cache_refresh(inode, fattr); } - diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 56540bbdc..eb4735a6d 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -69,6 +69,7 @@ struct inode_operations nfs_file_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ nfs_readpage, /* readpage */ nfs_writepage, /* writepage */ NULL, /* bmap */ @@ -142,7 +143,7 @@ nfs_file_write(struct inode *inode, struct file *file, int result; dfprintk(VFS, "nfs: write(%x/%ld (%d), %lu@%lu)\n", - inode->i_dev, inode->i_ino, atomic_read(&inode->i_count), + inode->i_dev, inode->i_ino, inode->i_count, count, (unsigned long) file->f_pos); if (!inode) { @@ -179,11 +180,11 @@ nfs_lock(struct inode *inode, struct file *filp, int cmd, struct file_lock *fl) int status; dprintk("NFS: nfs_lock(f=%4x/%ld, t=%x, fl=%x, r=%ld:%ld)\n", - filp->f_inode->i_dev, filp->f_inode->i_ino, + filp->f_dentry->d_inode->i_dev, filp->f_dentry->d_inode->i_ino, fl->fl_type, fl->fl_flags, fl->fl_start, fl->fl_end); - if (!(inode = filp->f_inode)) + if (!(inode = filp->f_dentry->d_inode)) return -EINVAL; /* No mandatory locks over NFS */ diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 5ab9600e9..cf52a0d56 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -36,15 +36,17 @@ static int nfs_notify_change(struct inode *, struct iattr *); static void nfs_put_inode(struct inode *); +static void nfs_delete_inode(struct inode *); static void nfs_put_super(struct super_block *); static void nfs_read_inode(struct inode *); -static void nfs_statfs(struct super_block *, struct statfs *, int bufsiz); +static int nfs_statfs(struct super_block *, struct statfs *, int bufsiz); static struct super_operations nfs_sops = { nfs_read_inode, /* read inode */ - nfs_notify_change, /* notify change */ NULL, /* write inode */ nfs_put_inode, /* put inode */ + nfs_delete_inode, /* delete inode */ + nfs_notify_change, /* notify change */ nfs_put_super, /* put superblock */ NULL, /* write superblock */ nfs_statfs, /* stat filesystem */ @@ -73,11 +75,16 @@ static void nfs_put_inode(struct inode * inode) { dprintk("NFS: put_inode(%x/%ld)\n", inode->i_dev, inode->i_ino); +} - if (NFS_RENAMED_DIR(inode)) - nfs_sillyrename_cleanup(inode); - if (inode->i_pipe) - clear_inode(inode); +/* + * This should do any silly-rename cleanups once we + * get silly-renaming working again.. + */ +static void +nfs_delete_inode(struct inode * inode) +{ + dprintk("NFS: delete_inode(%x/%ld)\n", inode->i_dev, inode->i_ino); } void @@ -230,7 +237,8 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent) /* Unlock super block and try to get root fh attributes */ unlock_super(sb); - if ((sb->s_mounted = nfs_fhget(sb, &data->root, NULL)) != NULL) { + sb->s_root = d_alloc_root(nfs_fhget(sb, &data->root, NULL), NULL); + if (sb->s_root != NULL) { /* We're airborne */ if (!(server->flags & NFS_MOUNT_NONLM)) lockd_up(); @@ -250,7 +258,7 @@ failure: return NULL; } -static void +static int nfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) { int error; @@ -271,7 +279,7 @@ nfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) tmp.f_files = 0; tmp.f_ffree = 0; tmp.f_namelen = NAME_MAX; - copy_to_user(buf, &tmp, bufsiz); + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } /* @@ -317,7 +325,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle, } dprintk("NFS: fhget(%x/%ld ct=%d)\n", inode->i_dev, inode->i_ino, - atomic_read(&inode->i_count)); + inode->i_count); return inode; } @@ -364,7 +372,6 @@ nfs_notify_change(struct inode *inode, struct iattr *attr) nfs_truncate_dirty_pages(inode, sattr.size); nfs_refresh_inode(inode, &fattr); } - inode->i_dirt = 0; return error; } @@ -435,7 +442,7 @@ done: */ static struct file_system_type nfs_fs_type = { "nfs", - FS_NO_DCACHE, + 0 /* FS_NO_DCACHE - this doesn't work right now*/, nfs_read_super, NULL }; diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c index add3309f3..51cca7c48 100644 --- a/fs/nfs/nfsroot.c +++ b/fs/nfs/nfsroot.c @@ -1,5 +1,5 @@ /* - * $Id: nfsroot.c,v 1.37 1997/06/04 08:28:10 davem Exp $ + * $Id: nfsroot.c,v 1.3 1997/06/17 13:26:56 ralf Exp $ * * Copyright (C) 1995, 1996 Gero Kuhlmann <gero@gkminix.han.de> * @@ -78,7 +78,6 @@ #include <asm/param.h> #include <linux/utsname.h> -#include <linux/nametrans.h> #include <linux/in.h> #include <linux/if.h> #include <linux/inet.h> @@ -833,9 +832,6 @@ __initfunc(static void root_do_bootp_ext(u8 *ext)) root_bootp_string(nfs_path, ext+1, *ext, NFS_MAXPATHLEN); break; } -#ifdef CONFIG_TRANS_NAMES - translations_dirty = 1; -#endif } @@ -1258,9 +1254,6 @@ __initfunc(static void root_nfs_addrs(char *addrs)) system_utsname.domainname[0] = '\0'; user_dev_name[0] = '\0'; bootp_flag = rarp_flag = 1; -#ifdef CONFIG_TRANS_NAMES - translations_dirty = 1; -#endif /* The following is just a shortcut for automatic IP configuration */ if (!strcmp(addrs, "bootp")) { @@ -1306,9 +1299,6 @@ __initfunc(static void root_nfs_addrs(char *addrs)) } strncpy(system_utsname.nodename, ip, __NEW_UTS_LEN); system_utsname.nodename[__NEW_UTS_LEN] = '\0'; -#ifdef CONFIG_TRANS_NAMES - translations_dirty = 1; -#endif break; case 5: strncpy(user_dev_name, ip, IFNAMSIZ); @@ -1342,9 +1332,6 @@ __initfunc(static int root_nfs_setup(void)) if (!system_utsname.nodename[0]) { strncpy(system_utsname.nodename, in_ntoa(myaddr), __NEW_UTS_LEN); system_utsname.nodename[__NEW_UTS_LEN] = '\0'; -#ifdef CONFIG_TRANS_NAMES - translations_dirty = 1; -#endif } /* Set the correct netmask */ diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 2c3b59036..4ce61f731 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -150,7 +150,6 @@ nfs_readpage_result(struct rpc_task *task) fail++; dprintk("NFS: %d successful reads, %d failures\n", succ, fail); } - iput(req->ra_inode); clear_bit(PG_locked, &page->flags); wake_up(&page->wait); @@ -188,7 +187,6 @@ nfs_readpage_async(struct inode *inode, struct page *page) nfs_readpage_result, req); if (result >= 0) { - atomic_inc(&inode->i_count); atomic_inc(&page->count); return 0; } diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index a22f96239..3d545f7d8 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -19,6 +19,7 @@ #include <asm/uaccess.h> static int nfs_readlink(struct inode *, char *, int); +static struct dentry *nfs_follow_link(struct inode *, struct dentry *); /* * symlinks can't do much... @@ -35,6 +36,7 @@ struct inode_operations nfs_symlink_inode_operations = { NULL, /* mknod */ NULL, /* rename */ nfs_readlink, /* readlink */ + nfs_follow_link, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -55,7 +57,6 @@ static int nfs_readlink(struct inode *inode, char *buffer, int buflen) buflen = NFS_MAXPATHLEN; error = nfs_proc_readlink(NFS_SERVER(inode), NFS_FH(inode), &mem, &res, &len, buflen); - iput(inode); if (! error) { copy_to_user(buffer, res, len); put_user('\0', buffer + len); @@ -64,3 +65,36 @@ static int nfs_readlink(struct inode *inode, char *buffer, int buflen) kfree(mem); return error; } + +static struct dentry * nfs_follow_link(struct inode * inode, struct dentry *base) +{ + int error; + unsigned int len; + char *res; + void *mem; + char *path; + + dfprintk(VFS, "nfs: follow_link(%x/%ld)\n", inode->i_dev, inode->i_ino); + + error = nfs_proc_readlink(NFS_SERVER(inode), NFS_FH(inode), &mem, + &res, &len, NFS_MAXPATHLEN); + + if (error) { + dput(base); + kfree(mem); + return ERR_PTR(error); + } + path = kmalloc(len + 1, GFP_KERNEL); + if (!path) { + dput(base); + kfree(mem); + return ERR_PTR(-ENOMEM); + } + memcpy(path, res, len); + path[len] = 0; + kfree(mem); + + base = lookup_dentry(path, base, 1); + kfree(path); + return base; +} diff --git a/fs/nfs/write.c b/fs/nfs/write.c index f27d083e4..9241c679e 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -338,7 +338,7 @@ create_write_request(struct inode *inode, struct page *page, wreq->wb_page = page; wreq->wb_offset = offset; wreq->wb_bytes = bytes; - atomic_inc(&inode->i_count); + atomic_inc(&page->count); append_write_request(&NFS_WRITEBACK(inode), wreq); @@ -695,7 +695,6 @@ nfs_check_error(struct inode *inode) status = req->wb_task.tk_status; remove_write_request(&nfs_failed_requests, req); - iput(req->wb_inode); kfree(req); return status; } @@ -788,7 +787,6 @@ nfs_wback_result(struct rpc_task *task) dprintk("NFS: %4d saving write failure code\n", task->tk_pid); append_write_request(&nfs_failed_requests, req); - atomic_inc(&inode->i_count); } clear_bit(PG_uptodate, &page->flags); } else if (!WB_CANCELLED(req)) { @@ -818,6 +816,5 @@ nfs_wback_result(struct rpc_task *task) kfree(req); free_page(page_address(page)); - iput(inode); nr_write_requests--; } diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index a3b29313a..c83150b5f 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -331,7 +331,7 @@ exp_rootfh(struct svc_client *clp, dev_t dev, ino_t ino, struct knfs_fh *f) if (!(exp = exp_get(clp, dev, ino))) return -EPERM; - atomic_inc(&exp->ex_inode->i_count); + exp->ex_inode->i_count++; fh_compose(&fh, exp, exp->ex_inode); memcpy(f, &fh.fh_handle, sizeof(struct knfs_fh)); fh_put(&fh); diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index a68fca997..b6fdb460d 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -120,13 +120,13 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, dotdot = (len == 2 && name[0] == '.' && name[1] == '.'); if (dotdot) { if (dirp == current->fs->root) { - atomic_inc(&dirp->i_count); + dirp->i_count++; *resfh = *fhp; return 0; } if (dirp->i_dev == exp->ex_dev && dirp->i_ino == exp->ex_ino) { - atomic_inc(&dirp->i_count); + dirp->i_count++; *resfh = *fhp; return 0; } @@ -144,12 +144,12 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, if (perm != 0) return perm; if (!len) { - atomic_inc(&dirp->i_count); + dirp->i_count++; *resfh = *fhp; return 0; } - atomic_inc(&dirp->i_count); /* lookup eats the dirp inode */ + dirp->i_count++; /* lookup eats the dirp inode */ err = dirp->i_op->lookup(dirp, name, len, &inode); if (err) @@ -162,7 +162,7 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, if (!dotdot && (sb = inode->i_sb) && (inode == sb->s_mounted)) { iput(inode); inode = sb->s_covered; - atomic_inc(&inode->i_count); + inode->i_count++; } fh_compose(resfh, exp, inode); @@ -291,7 +291,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, } } - atomic_inc(&inode->i_count); + inode->i_count++; return 0; } @@ -304,7 +304,7 @@ nfsd_close(struct file *filp) struct inode *inode; inode = filp->f_inode; - if (!atomic_read(&inode->i_count)) + if (!inode->i_count) printk(KERN_WARNING "nfsd: inode count == 0!\n"); if (filp->f_op && filp->f_op->release) filp->f_op->release(inode, filp); @@ -533,7 +533,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, fh_lock(fhp); /* lock directory */ dirp = fhp->fh_inode; - atomic_inc(&dirp->i_count); /* dirop eats the inode */ + dirp->i_count++; /* dirop eats the inode */ switch (type) { case S_IFREG: @@ -568,7 +568,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, * If the VFS call doesn't return the inode, look it up now. */ if (inode == NULL) { - atomic_inc(&dirp->i_count); + dirp->i_count++; err = dirp->i_op->lookup(dirp, fname, flen, &inode); if (err < 0) return -nfserrno(err); /* Huh?! */ @@ -643,7 +643,7 @@ nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp) if (!inode->i_op || !inode->i_op->readlink) return nfserr_io; - atomic_inc(&inode->i_count); + inode->i_count++; oldfs = get_fs(); set_fs(KERNEL_DS); err = inode->i_op->readlink(inode, buf, *lenp); set_fs(oldfs); @@ -680,7 +680,7 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, return nfserr_perm; fh_lock(fhp); /* lock inode */ - atomic_inc(&dirp->i_count); + dirp->i_count++; err = dirp->i_op->symlink(dirp, fname, flen, path); fh_unlock(fhp); /* unlock inode */ @@ -693,7 +693,7 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, /* * Okay, now look up the inode of the new symlink. */ - atomic_inc(&dirp->i_count); /* lookup eats the dirp inode */ + dirp->i_count++; /* lookup eats the dirp inode */ err = dirp->i_op->lookup(dirp, fname, flen, &inode); if (err) return nfserrno(-err); @@ -730,7 +730,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, return nfserr_perm; fh_lock(ffhp); /* lock directory inode */ - atomic_inc(&dirp->i_count); + dirp->i_count++; err = dirp->i_op->link(dest, dirp, fname, len); fh_unlock(ffhp); /* unlock inode */ @@ -770,8 +770,8 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, return nfserr_perm; fh_lock(tfhp); /* lock destination directory */ - atomic_inc(&tdir->i_count); - atomic_inc(&fdir->i_count); + tdir->i_count++; + fdir->i_count++; err = fdir->i_op->rename(fdir, fname, flen, tdir, tname, tlen); fh_unlock(tfhp); /* unlock inode */ @@ -805,12 +805,12 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, if (type == S_IFDIR) { if (!dirp->i_op || !dirp->i_op->rmdir) return nfserr_notdir; - atomic_inc(&dirp->i_count); + dirp->i_count++; err = dirp->i_op->rmdir(dirp, fname, flen); } else { /* other than S_IFDIR */ if (!dirp->i_op || !dirp->i_op->unlink) return nfserr_perm; - atomic_inc(&dirp->i_count); + dirp->i_count++; err = dirp->i_op->unlink(dirp, fname, flen); } @@ -4,7 +4,6 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ -#include <linux/config.h> #include <linux/vfs.h> #include <linux/types.h> #include <linux/utime.h> @@ -21,32 +20,27 @@ #include <linux/file.h> #include <linux/smp.h> #include <linux/smp_lock.h> -#include <linux/omirr.h> #include <asm/uaccess.h> #include <asm/bitops.h> asmlinkage int sys_statfs(const char * path, struct statfs * buf) { - struct inode * inode; + struct dentry * dentry; int error; lock_kernel(); - error = verify_area(VERIFY_WRITE, buf, sizeof(struct statfs)); - if (error) - goto out; - error = namei(NAM_FOLLOW_LINK, path, &inode); - if (error) - goto out; - error = -ENOSYS; - if (!inode->i_sb->s_op->statfs) { - iput(inode); - goto out; + dentry = namei(path); + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + struct inode * inode = dentry->d_inode; + + error = -ENOSYS; + if (inode->i_sb->s_op->statfs) + error = inode->i_sb->s_op->statfs(inode->i_sb, buf, sizeof(struct statfs)); + + dput(dentry); } - inode->i_sb->s_op->statfs(inode->i_sb, buf, sizeof(struct statfs)); - iput(inode); - error = 0; -out: unlock_kernel(); return error; } @@ -54,6 +48,7 @@ out: asmlinkage int sys_fstatfs(unsigned int fd, struct statfs * buf) { struct inode * inode; + struct dentry * dentry; struct file * file; int error; @@ -63,7 +58,9 @@ asmlinkage int sys_fstatfs(unsigned int fd, struct statfs * buf) goto out; if (fd >= NR_OPEN || !(file = current->files->fd[fd])) error = -EBADF; - else if (!(inode = file->f_inode)) + else if (!(dentry = file->f_dentry)) + error = -ENOENT; + else if (!(inode = dentry->d_inode)) error = -ENOENT; else if (!inode->i_sb) error = -ENODEV; @@ -90,7 +87,6 @@ int do_truncate(struct inode *inode, unsigned long length) vmtruncate(inode, length); if (inode->i_op && inode->i_op->truncate) inode->i_op->truncate(inode); - inode->i_status |= ST_MODIFIED; } up(&inode->i_sem); return error; @@ -98,33 +94,37 @@ int do_truncate(struct inode *inode, unsigned long length) asmlinkage int sys_truncate(const char * path, unsigned long length) { + struct dentry * dentry; struct inode * inode; int error; lock_kernel(); - error = namei(NAM_FOLLOW_LINK, path, &inode); - if (error) + dentry = namei(path); + + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto out; + inode = dentry->d_inode; error = -EACCES; if (S_ISDIR(inode->i_mode)) - goto iput_and_out; + goto dput_and_out; error = permission(inode,MAY_WRITE); if (error) - goto iput_and_out; + goto dput_and_out; error = -EROFS; if (IS_RDONLY(inode)) - goto iput_and_out; + goto dput_and_out; error = -EPERM; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) - goto iput_and_out; + goto dput_and_out; error = get_write_access(inode); if (error) - goto iput_and_out; + goto dput_and_out; error = locks_verify_area(FLOCK_VERIFY_WRITE, inode, NULL, length < inode->i_size ? length : inode->i_size, @@ -135,8 +135,8 @@ asmlinkage int sys_truncate(const char * path, unsigned long length) error = do_truncate(inode, length); } put_write_access(inode); -iput_and_out: - iput(inode); +dput_and_out: + dput(dentry); out: unlock_kernel(); return error; @@ -145,13 +145,16 @@ out: asmlinkage int sys_ftruncate(unsigned int fd, unsigned long length) { struct inode * inode; + struct dentry *dentry; struct file * file; int error; lock_kernel(); if (fd >= NR_OPEN || !(file = current->files->fd[fd])) error = -EBADF; - else if (!(inode = file->f_inode)) + else if (!(dentry = file->f_dentry)) + error = -ENOENT; + else if (!(inode = dentry->d_inode)) error = -ENOENT; else if (S_ISDIR(inode->i_mode) || !(file->f_mode & FMODE_WRITE)) error = -EACCES; @@ -184,17 +187,21 @@ asmlinkage int sys_ftruncate(unsigned int fd, unsigned long length) asmlinkage int sys_utime(char * filename, struct utimbuf * times) { int error; + struct dentry * dentry; struct inode * inode; struct iattr newattrs; lock_kernel(); - /* Hmm, should I always follow symlinks or not ? */ - error = namei(NAM_FOLLOW_LINK, filename, &inode); - if (error) + dentry = namei(filename); + + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto out; + inode = dentry->d_inode; + error = -EROFS; if (IS_RDONLY(inode)) - goto iput_and_out; + goto dput_and_out; /* Don't worry, the checks are done in inode_change_ok() */ newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; @@ -203,22 +210,17 @@ asmlinkage int sys_utime(char * filename, struct utimbuf * times) if (!error) error = get_user(newattrs.ia_mtime, ×->modtime); if (error) - goto iput_and_out; + goto dput_and_out; newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET; } else { if (current->fsuid != inode->i_uid && (error = permission(inode,MAY_WRITE)) != 0) - goto iput_and_out; + goto dput_and_out; } error = notify_change(inode, &newattrs); -#ifdef CONFIG_OMIRR - if(!error) - omirr_printall(inode, " U %ld %ld %ld ", CURRENT_TIME, - newattrs.ia_atime, newattrs.ia_mtime); -#endif -iput_and_out: - iput(inode); +dput_and_out: + dput(dentry); out: unlock_kernel(); return error; @@ -233,38 +235,39 @@ out: asmlinkage int sys_utimes(char * filename, struct timeval * utimes) { int error; + struct dentry * dentry; struct inode * inode; struct iattr newattrs; lock_kernel(); - error = namei(NAM_FOLLOW_LINK, filename, &inode); - if (error) + dentry = namei(filename); + + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto out; + inode = dentry->d_inode; + error = -EROFS; if (IS_RDONLY(inode)) - goto iput_and_out; + goto dput_and_out; + /* Don't worry, the checks are done in inode_change_ok() */ newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; if (utimes) { struct timeval times[2]; error = -EFAULT; if (copy_from_user(×, utimes, sizeof(times))) - goto iput_and_out; + goto dput_and_out; 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 ((error = permission(inode,MAY_WRITE)) != 0) - goto iput_and_out; + goto dput_and_out; } error = notify_change(inode, &newattrs); -#ifdef CONFIG_OMIRR - if(!error) - omirr_printall(inode, " U %ld %ld %ld ", CURRENT_TIME, - newattrs.ia_atime, newattrs.ia_mtime); -#endif -iput_and_out: - iput(inode); +dput_and_out: + dput(dentry); out: unlock_kernel(); return error; @@ -276,7 +279,7 @@ out: */ asmlinkage int sys_access(const char * filename, int mode) { - struct inode * inode; + struct dentry * dentry; int old_fsuid, old_fsgid; int res = -EINVAL; @@ -287,11 +290,14 @@ asmlinkage int sys_access(const char * filename, int mode) old_fsgid = current->fsgid; current->fsuid = current->uid; current->fsgid = current->gid; - res = namei(NAM_FOLLOW_LINK, filename, &inode); - if (!res) { - res = permission(inode, mode); - iput(inode); + + dentry = namei(filename); + res = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + res = permission(dentry->d_inode, mode); + dput(dentry); } + current->fsuid = old_fsuid; current->fsgid = old_fsgid; out: @@ -301,24 +307,34 @@ out: asmlinkage int sys_chdir(const char * filename) { - struct inode * inode; - struct inode * tmpi; int error; + struct inode *inode; + struct dentry *dentry, *tmp; lock_kernel(); - error = namei(NAM_FOLLOW_LINK, filename, &inode); - if (error) + + dentry = namei(filename); + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto out; + + inode = dentry->d_inode; + error = -ENOTDIR; if (!S_ISDIR(inode->i_mode)) - goto iput_and_out; - if ((error = permission(inode,MAY_EXEC)) != 0) - goto iput_and_out; - - /* exchange inodes */ - tmpi = current->fs->pwd; current->fs->pwd = inode; inode = tmpi; -iput_and_out: - iput(inode); + goto dput_and_out; + + error = permission(inode,MAY_EXEC); + if (error) + goto dput_and_out; + + /* exchange dentries */ + tmp = current->fs->pwd; + current->fs->pwd = dentry; + dentry = tmp; + +dput_and_out: + dput(dentry); out: unlock_kernel(); return error; @@ -326,24 +342,38 @@ out: asmlinkage int sys_fchdir(unsigned int fd) { - struct inode * inode; - struct file * file; - int error = -EBADF; + struct file *file; + struct dentry *dentry; + struct inode *inode; + int error; lock_kernel(); + + error = -EBADF; if (fd >= NR_OPEN || !(file = current->files->fd[fd])) goto out; + error = -ENOENT; - if (!(inode = file->f_inode)) + if (!(dentry = file->f_dentry)) goto out; + if (!(inode = dentry->d_inode)) + goto out; + error = -ENOTDIR; if (!S_ISDIR(inode->i_mode)) goto out; - if ((error = permission(inode,MAY_EXEC)) != 0) + + error = permission(inode,MAY_EXEC); + if (error) goto out; - iput(current->fs->pwd); - current->fs->pwd = inode; - atomic_inc(&inode->i_count); + + { + struct dentry *tmp; + + tmp = current->fs->pwd; + current->fs->pwd = dget(dentry); + dput(tmp); + } out: unlock_kernel(); return error; @@ -351,24 +381,39 @@ out: asmlinkage int sys_chroot(const char * filename) { - struct inode * inode; - struct inode * tmpi; int error; + struct inode *inode; + struct dentry *dentry, *tmp; lock_kernel(); - error = namei(NAM_FOLLOW_LINK, filename, &inode); - if (error) + + dentry = namei(filename); + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto out; + + inode = dentry->d_inode; + error = -ENOTDIR; if (!S_ISDIR(inode->i_mode)) - goto iput_and_out; + goto dput_and_out; + + error = permission(inode,MAY_EXEC); + if (error) + goto dput_and_out; + error = -EPERM; if (!fsuser()) - goto iput_and_out; - tmpi = current->fs->root; current->fs->root = inode; inode = tmpi; + goto dput_and_out; + + /* exchange dentries */ + tmp = current->fs->root; + current->fs->root = dentry; + dentry = tmp; error = 0; -iput_and_out: - iput(inode); + +dput_and_out: + dput(dentry); out: unlock_kernel(); return error; @@ -377,6 +422,7 @@ out: asmlinkage int sys_fchmod(unsigned int fd, mode_t mode) { struct inode * inode; + struct dentry * dentry; struct file * file; struct iattr newattrs; int err = -EBADF; @@ -385,7 +431,9 @@ asmlinkage int sys_fchmod(unsigned int fd, mode_t mode) if (fd >= NR_OPEN || !(file = current->files->fd[fd])) goto out; err = -ENOENT; - if (!(inode = file->f_inode)) + if (!(dentry = file->f_dentry)) + goto out; + if (!(inode = dentry->d_inode)) goto out; err = -EROFS; if (IS_RDONLY(inode)) @@ -397,12 +445,7 @@ asmlinkage int sys_fchmod(unsigned int fd, mode_t mode) mode = inode->i_mode; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; - inode->i_dirt = 1; err = notify_change(inode, &newattrs); -#ifdef CONFIG_OMIRR - if(!err) - omirr_printall(inode, " M %ld %ld ", CURRENT_TIME, newattrs.ia_mode); -#endif out: unlock_kernel(); return err; @@ -410,54 +453,51 @@ out: asmlinkage int sys_chmod(const char * filename, mode_t mode) { + struct dentry * dentry; struct inode * inode; int error; struct iattr newattrs; lock_kernel(); - /* I'm not sure whether to use NAM_FOLLOW_TRAILSLASH instead, - * because permissions on symlinks now can never be changed, - * but on the other hand they are never needed. - */ - error = namei(NAM_FOLLOW_LINK, filename, &inode); - if (error) + dentry = namei(filename); + + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto out; + inode = dentry->d_inode; + error = -EROFS; if (IS_RDONLY(inode)) - goto iput_and_out; + goto dput_and_out; + error = -EPERM; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) - goto iput_and_out; + goto dput_and_out; + if (mode == (mode_t) -1) mode = inode->i_mode; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; - inode->i_dirt = 1; error = notify_change(inode, &newattrs); -#ifdef CONFIG_OMIRR - if(!error) - omirr_printall(inode, " M %ld %ld ", CURRENT_TIME, newattrs.ia_mode); -#endif -iput_and_out: - iput(inode); + +dput_and_out: + dput(dentry); out: unlock_kernel(); return error; } -asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group) +static int chown_common(struct dentry * dentry, uid_t user, gid_t group) { struct inode * inode; - struct file * file; struct iattr newattrs; - int error = -EBADF; + int error; - lock_kernel(); - if (fd >= NR_OPEN || !(file = current->files->fd[fd])) - goto out; error = -ENOENT; - if (!(inode = file->f_inode)) + if (!(inode = dentry->d_inode)) { + printk("chown_common: NULL inode\n"); goto out; + } error = -EROFS; if (IS_RDONLY(inode)) goto out; @@ -489,7 +529,6 @@ asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group) newattrs.ia_mode &= ~S_ISGID; newattrs.ia_valid |= ATTR_MODE; } - inode->i_dirt = 1; if (inode->i_sb && inode->i_sb->dq_op) { inode->i_sb->dq_op->initialize(inode, -1); error = -EDQUOT; @@ -500,80 +539,70 @@ asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group) inode->i_sb->dq_op->transfer(inode, &newattrs, 1); } else error = notify_change(inode, &newattrs); -#ifdef CONFIG_OMIRR - if(!error) - omirr_printall(inode, " O %d %d ", CURRENT_TIME, - newattrs.ia_uid, newattrs.ia_gid); -#endif out: - unlock_kernel(); return error; } +asmlinkage int sys_lchown(const char * filename, uid_t user, gid_t group) +{ + struct dentry * dentry; + int error; + + lock_kernel(); + dentry = lnamei(filename); + + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) + goto out; + + error = chown_common(dentry, user, group); + + dput(dentry); +out: + unlock_kernel(); + return(error); +} + asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group) { - struct inode * inode; + struct dentry * dentry; int error; - struct iattr newattrs; lock_kernel(); - error = namei(NAM_FOLLOW_TRAILSLASH, filename, &inode); - if (error) + dentry = namei(filename); + + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto out; - error = -EROFS; - if (IS_RDONLY(inode)) - goto iput_and_out; - error = -EPERM; - if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) - goto iput_and_out; - if (user == (uid_t) -1) - user = inode->i_uid; - if (group == (gid_t) -1) - group = inode->i_gid; - newattrs.ia_mode = inode->i_mode; - newattrs.ia_uid = user; - newattrs.ia_gid = group; - newattrs.ia_valid = ATTR_UID | ATTR_GID | ATTR_CTIME; - /* - * If the owner has been changed, remove the setuid bit - */ - if (inode->i_mode & S_ISUID) { - newattrs.ia_mode &= ~S_ISUID; - newattrs.ia_valid |= ATTR_MODE; - } - /* - * If the group has been changed, remove the setgid bit - * - * Don't remove the setgid bit if no group execute bit. - * This is a file marked for mandatory locking. - */ - if (((inode->i_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))) { - newattrs.ia_mode &= ~S_ISGID; - newattrs.ia_valid |= ATTR_MODE; - } - inode->i_dirt = 1; - if (inode->i_sb->dq_op) { - inode->i_sb->dq_op->initialize(inode, -1); - error = -EDQUOT; - if (inode->i_sb->dq_op->transfer(inode, &newattrs, 0)) - goto iput_and_out; - error = notify_change(inode, &newattrs); - if (error) - inode->i_sb->dq_op->transfer(inode, &newattrs, 1); - } else - error = notify_change(inode, &newattrs); -#ifdef CONFIG_OMIRR - if(!error) - omirr_printall(inode, " O %d %d ", CURRENT_TIME, - newattrs.ia_uid, newattrs.ia_gid); -#endif -iput_and_out: - iput(inode); + + error = chown_common(dentry, user, group); + + dput(dentry); out: unlock_kernel(); return(error); } +asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group) +{ + struct dentry * dentry; + struct file * file; + int error = -EBADF; + + lock_kernel(); + if (fd >= NR_OPEN || !(file = current->files->fd[fd])) + goto out; + error = -ENOENT; + if (!(dentry = file->f_dentry)) + goto out; + + error = chown_common(dentry, user, group); + +out: + unlock_kernel(); + return error; +} + /* * Note that while the flag value (low two bits) for sys_open means: * 00 - read-only @@ -591,6 +620,7 @@ out: static int do_open(const char * filename,int flags,int mode, int fd) { struct inode * inode; + struct dentry * dentry; struct file * f; int flag,error; @@ -603,16 +633,18 @@ static int do_open(const char * filename,int flags,int mode, int fd) flag++; if (flag & O_TRUNC) flag |= 2; - error = open_namei(filename,flag,mode,&inode,NULL); - if (error) + dentry = open_namei(filename,flag,mode); + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto cleanup_file; + inode = dentry->d_inode; if (f->f_mode & FMODE_WRITE) { error = get_write_access(inode); if (error) - goto cleanup_inode; + goto cleanup_dentry; } - f->f_inode = inode; + f->f_dentry = dentry; f->f_pos = 0; f->f_reada = 0; f->f_op = NULL; @@ -631,8 +663,8 @@ static int do_open(const char * filename,int flags,int mode, int fd) cleanup_all: if (f->f_mode & FMODE_WRITE) put_write_access(inode); -cleanup_inode: - iput(inode); +cleanup_dentry: + dput(dentry); cleanup_file: put_filp(f); return error; @@ -666,14 +698,15 @@ asmlinkage int sys_open(const char * filename,int flags,int mode) int fd, error; lock_kernel(); - fd = get_unused_fd(); - if (fd < 0) { - error = fd; + error = get_unused_fd(); + if (error < 0) goto out; - } - error = getname(filename, &tmp); - if (!error) { - error = do_open(tmp,flags,mode, fd); + + fd = error; + tmp = getname(filename); + error = PTR_ERR(tmp); + if (!IS_ERR(tmp)) { + error = do_open(tmp,flags,mode,fd); putname(tmp); if (!error) { error = fd; @@ -704,31 +737,35 @@ asmlinkage int sys_creat(const char * pathname, int mode) #endif -int __fput(struct file *filp, struct inode *inode) +int __fput(struct file *filp) { int error = 0; + struct dentry * dentry = filp->f_dentry; + struct inode * inode = dentry->d_inode; if (filp->f_op && filp->f_op->release) error = filp->f_op->release(inode,filp); - filp->f_inode = NULL; + filp->f_dentry = NULL; if (filp->f_mode & FMODE_WRITE) put_write_access(inode); - iput(inode); + dput(dentry); return error; } int close_fp(struct file *filp) { + struct dentry *dentry; struct inode *inode; if (filp->f_count == 0) { printk("VFS: Close: file count is 0\n"); return 0; } - inode = filp->f_inode; + dentry = filp->f_dentry; + inode = dentry->d_inode; if (inode) locks_remove_locks(current, filp); - return fput(filp, inode); + return fput(filp); } asmlinkage int sys_close(unsigned int fd) @@ -75,10 +75,7 @@ static long pipe_read(struct inode * inode, struct file * filp, PIPE_LOCK(*inode)--; wake_up_interruptible(&PIPE_WAIT(*inode)); if (read) { - if (DO_UPDATE_ATIME(inode)) { - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; - } + UPDATE_ATIME(inode); return read; } if (PIPE_WRITERS(*inode)) @@ -132,7 +129,7 @@ static long pipe_write(struct inode * inode, struct file * filp, free = 1; } inode->i_ctime = inode->i_mtime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); return written; } @@ -168,7 +165,7 @@ static int pipe_ioctl(struct inode *pino, struct file * filp, static unsigned int pipe_poll(struct file * filp, poll_table * wait) { unsigned int mask; - struct inode * inode = filp->f_inode; + struct inode * inode = filp->f_dentry->d_inode; poll_wait(&PIPE_WAIT(*inode), wait); mask = POLLIN | POLLRDNORM; @@ -189,7 +186,7 @@ static unsigned int pipe_poll(struct file * filp, poll_table * wait) static unsigned int fifo_poll(struct file * filp, poll_table * wait) { unsigned int mask; - struct inode * inode = filp->f_inode; + struct inode * inode = filp->f_dentry->d_inode; poll_wait(&PIPE_WAIT(*inode), wait); mask = POLLIN | POLLRDNORM; @@ -221,7 +218,7 @@ static long connect_read(struct inode * inode, struct file * filp, static unsigned int connect_poll(struct file * filp, poll_table * wait) { - struct inode * inode = filp->f_inode; + struct inode * inode = filp->f_dentry->d_inode; poll_wait(&PIPE_WAIT(*inode), wait); if (!PIPE_EMPTY(*inode)) { @@ -233,18 +230,26 @@ static unsigned int connect_poll(struct file * filp, poll_table * wait) return POLLOUT | POLLWRNORM; } -static int pipe_read_release(struct inode * inode, struct file * filp) +static int pipe_release(struct inode * inode) { - PIPE_READERS(*inode)--; + if (!PIPE_READERS(*inode) && !PIPE_WRITERS(*inode)) { + free_page((unsigned long) PIPE_BASE(*inode)); + PIPE_BASE(*inode) = NULL; + } wake_up_interruptible(&PIPE_WAIT(*inode)); return 0; } +static int pipe_read_release(struct inode * inode, struct file * filp) +{ + PIPE_READERS(*inode)--; + return pipe_release(inode); +} + static int pipe_write_release(struct inode * inode, struct file * filp) { PIPE_WRITERS(*inode)--; - wake_up_interruptible(&PIPE_WAIT(*inode)); - return 0; + return pipe_release(inode); } static int pipe_rdwr_release(struct inode * inode, struct file * filp) @@ -253,8 +258,7 @@ static int pipe_rdwr_release(struct inode * inode, struct file * filp) PIPE_READERS(*inode)--; if (filp->f_mode & FMODE_WRITE) PIPE_WRITERS(*inode)--; - wake_up_interruptible(&PIPE_WAIT(*inode)); - return 0; + return pipe_release(inode); } static int pipe_read_open(struct inode * inode, struct file * filp) @@ -373,6 +377,42 @@ struct file_operations rdwr_pipe_fops = { NULL }; +static struct inode * get_pipe_inode(void) +{ + extern struct inode_operations pipe_inode_operations; + struct inode *inode = get_empty_inode(); + + if (inode) { + unsigned long page = __get_free_page(GFP_USER); + + if (!page) { + iput(inode); + inode = NULL; + } else { + PIPE_BASE(*inode) = (char *) page; + inode->i_op = &pipe_inode_operations; + PIPE_WAIT(*inode) = NULL; + PIPE_START(*inode) = PIPE_LEN(*inode) = 0; + PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0; + PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1; + PIPE_LOCK(*inode) = 0; + /* + * Mark the inode dirty from the very beginning, + * that way it will never be moved to the dirty + * list because "make_inode_dirty()" will think + * that it already _is_ on the dirty list. + */ + inode->i_state = 1 << I_DIRTY; + inode->i_mode = S_IFIFO | S_IRUSR | S_IWUSR; + inode->i_uid = current->fsuid; + inode->i_gid = current->fsgid; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->i_blksize = PAGE_SIZE; + } + } + return inode; +} + struct inode_operations pipe_inode_operations = { &rdwr_pipe_fops, NULL, /* create */ @@ -422,12 +462,14 @@ int do_pipe(int *fd) goto close_f12_inode_i; j = error; - f1->f_inode = f2->f_inode = inode; + f1->f_dentry = f2->f_dentry = dget(d_alloc_root(inode, NULL)); + /* read file */ f1->f_pos = f2->f_pos = 0; f1->f_flags = O_RDONLY; f1->f_op = &read_pipe_fops; f1->f_mode = 1; + /* write file */ f2->f_flags = O_WRONLY; f2->f_op = &write_pipe_fops; @@ -441,7 +483,6 @@ int do_pipe(int *fd) close_f12_inode_i: put_unused_fd(i); close_f12_inode: - atomic_dec(&inode->i_count); iput(inode); close_f12: put_filp(f2); diff --git a/fs/proc/Makefile b/fs/proc/Makefile index 6f336245d..68e2a3485 100644 --- a/fs/proc/Makefile +++ b/fs/proc/Makefile @@ -8,7 +8,7 @@ # Note 2! The CFLAGS definitions are now in the main makefile... O_TARGET := proc.o -O_OBJS := inode.o root.o base.o generic.o mem.o link.o arbitrary.o fd.o array.o \ +O_OBJS := inode.o root.o base.o generic.o mem.o link.o fd.o array.o \ kmsg.o scsi.o proc_tty.o ifdef CONFIG_OMIRR O_OBJS := $(O_OBJS) omirr.o diff --git a/fs/proc/arbitrary.c b/fs/proc/arbitrary.c deleted file mode 100644 index 1e18e594e..000000000 --- a/fs/proc/arbitrary.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * $Id: arbitrary.c,v 1.2 1997/06/05 01:27:47 davem Exp $ - * - * linux/fs/proc/arbitrary.c - lookup() for arbitrary inodes. - * Copyright (C) 1997, Thomas Schoebel-Theuer, - * <schoebel@informatik.uni-stuttgart.de>. - */ - -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/kdev_t.h> -#include <linux/fs.h> - -/* Format of dev/inode pairs that can be used as file names: - * [<dev_number_in_hex]:<inode_number_in_decimal> - * (the same format that is already in use in /proc/<pid>/exe, - * /proc/<pid>/cwd and /proc/<pid>/root). - */ -/* Note that readdir does not supply such names, so they must be used - * either "blind" or must be queried another way, for example - * as result of a virtual symlink (see linux/proc/link.c). - */ -int proc_arbitrary_lookup(struct inode * dir, const char * name, - int len, struct inode ** result) -{ - int dev, ino; - char * ptr = (char*)name; - kdev_t kdev; - int i; - int error = -EINVAL; - - if(*ptr++ != '[') - goto done; - dev = simple_strtoul(ptr, &ptr, 16); - if(*ptr++ != ']') - goto done; - if(*ptr++ != ':') - goto done; - ino = simple_strtoul(ptr, &ptr, 0); - if((long)ptr - (long)name != len) - goto done; - - error = -ENOENT; - kdev = to_kdev_t(dev); - if(!kdev) - goto done; - for(i = 0; i < NR_SUPER; i++) - if(super_blocks[i].s_dev == kdev) - break; - if(i < NR_SUPER) { - *result = iget(&super_blocks[i], ino); - if(*result) - error = 0; - } -done: - iput(dir); - return error; -} diff --git a/fs/proc/array.c b/fs/proc/array.c index 518ef1b4c..773b96873 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -612,7 +612,7 @@ static inline char * task_mem(struct task_struct *p, char *buffer) for (vma = mm->mmap; vma; vma = vma->vm_next) { unsigned long len = (vma->vm_end - vma->vm_start) >> 10; - if (!vma->vm_inode) { + if (!vma->vm_dentry) { data += len; if (vma->vm_flags & VM_GROWSDOWN) stack += len; @@ -970,12 +970,11 @@ static long read_maps (int pid, struct file * file, *cp++ = flags & VM_MAYSHARE ? 's' : 'p'; *cp++ = 0; - if (map->vm_inode != NULL) { - dev = map->vm_inode->i_dev; - ino = map->vm_inode->i_ino; - } else { - dev = 0; - ino = 0; + dev = 0; + ino = 0; + if (map->vm_dentry != NULL) { + dev = map->vm_dentry->d_inode->i_dev; + ino = map->vm_dentry->d_inode->i_ino; } len = sprintf(line, @@ -1237,6 +1236,7 @@ struct inode_operations proc_array_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -1282,6 +1282,7 @@ struct inode_operations proc_arraylong_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/proc/base.c b/fs/proc/base.c index b983e73f6..7e9a65e08 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -42,6 +42,7 @@ static struct inode_operations proc_base_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/proc/fd.c b/fs/proc/fd.c index 884631db8..1e1fb494f 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -14,7 +14,7 @@ #include <linux/stat.h> static int proc_readfd(struct inode *, struct file *, void *, filldir_t); -static int proc_lookupfd(struct inode *,const char *,int,struct inode **); +static int proc_lookupfd(struct inode *, struct dentry *); static struct file_operations proc_fd_operations = { NULL, /* lseek - default */ @@ -44,6 +44,7 @@ struct inode_operations proc_fd_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -51,39 +52,36 @@ struct inode_operations proc_fd_inode_operations = { NULL /* permission */ }; -static int proc_lookupfd(struct inode * dir, const char * name, int len, - struct inode ** result) +/* + * NOTE! Normally we'd indicate that a file does not + * exist by creating a negative dentry and returning + * a successful return code. However, for this case + * we do not want to create negative dentries, because + * the state of the world can change behind our backs. + * + * Thus just return -ENOENT instead. + */ +static int proc_lookupfd(struct inode * dir, struct dentry * dentry) { unsigned int ino, pid, fd, c; struct task_struct * p; struct super_block * sb; + struct inode *inode; + const char *name; + int len; - *result = NULL; ino = dir->i_ino; pid = ino >> 16; ino &= 0x0000ffff; if (!dir) return -ENOENT; sb = dir->i_sb; - if (!pid || ino != PROC_PID_FD || !S_ISDIR(dir->i_mode)) { - iput(dir); + if (!pid || ino != PROC_PID_FD || !S_ISDIR(dir->i_mode)) return -ENOENT; - } - if (!len || (name[0] == '.' && (len == 1 || - (name[1] == '.' && len == 2)))) { - if (len < 2) { - *result = dir; - return 0; - } - if (!(*result = proc_get_inode(sb, (pid << 16)+PROC_PID_INO, &proc_pid))) { - iput(dir); - return -ENOENT; - } - iput(dir); - return 0; - } - iput(dir); + fd = 0; + len = dentry->d_name.len; + name = dentry->d_name.name; while (len-- > 0) { c = *name - '0'; name++; @@ -111,13 +109,16 @@ static int proc_lookupfd(struct inode * dir, const char * name, int len, if (fd >= NR_OPEN || !p->files || !p->files->fd[fd] || - !p->files->fd[fd]->f_inode) + !p->files->fd[fd]->f_dentry) return -ENOENT; ino = (pid << 16) + (PROC_PID_FD_DIR << 8) + fd; - if (!(*result = proc_get_inode(sb, ino, NULL))) + inode = proc_get_inode(sb, ino, NULL); + if (!inode) return -ENOENT; + + d_add(dentry, inode); return 0; } @@ -155,7 +156,7 @@ static int proc_readfd(struct inode * inode, struct file * filp, for (fd -= 2 ; fd < NR_OPEN; fd++, filp->f_pos++) { if (!p->files) break; - if (!p->files->fd[fd] || !p->files->fd[fd]->f_inode) + if (!p->files->fd[fd] || !p->files->fd[fd]->f_dentry) continue; j = NUMBUF; diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 1424dd1ef..358060020 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -23,6 +23,15 @@ static long proc_file_write(struct inode * inode, struct file * file, static long long proc_file_lseek(struct inode * inode, struct file * file, long long offset, int orig); +int proc_match(int len, const char *name,struct proc_dir_entry * de) +{ + if (!de || !de->low_ino) + return 0; + if (de->namelen != len) + return 0; + return !memcmp(name, de->name, len); +} + static struct file_operations proc_file_operations = { proc_file_lseek, /* lseek */ proc_file_read, /* read */ @@ -51,6 +60,7 @@ struct inode_operations proc_file_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -73,6 +83,7 @@ struct inode_operations proc_net_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 943137bf4..4bc4a4f17 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -25,8 +25,14 @@ static void proc_put_inode(struct inode *inode) && proc_openprom_use) (*proc_openprom_use)(inode, 0); #endif - if (inode->i_nlink) - return; +} + +/* + * Does this ever happen? + */ +static void proc_delete_inode(struct inode *inode) +{ + printk("proc_delete_inode()?\n"); inode->i_size = 0; } @@ -39,9 +45,10 @@ static void proc_put_super(struct super_block *sb) static struct super_operations proc_sops = { proc_read_inode, - NULL, proc_write_inode, proc_put_inode, + proc_delete_inode, + NULL, proc_put_super, NULL, proc_statfs, @@ -131,16 +138,17 @@ struct super_block *proc_read_super(struct super_block *s,void *data, s->s_magic = PROC_SUPER_MAGIC; s->s_op = &proc_sops; unlock_super(s); - if (!(s->s_mounted = proc_get_inode(s, PROC_ROOT_INO, &proc_root))) { + s->s_root = d_alloc_root(proc_get_inode(s, PROC_ROOT_INO, &proc_root), NULL); + if (!s->s_root) { s->s_dev = 0; printk("get root inode failed\n"); return NULL; } - parse_options(data, &s->s_mounted->i_uid, &s->s_mounted->i_gid); + parse_options(data, &s->s_root->d_inode->i_uid, &s->s_root->d_inode->i_gid); return s; } -void proc_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) +int proc_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) { struct statfs tmp; @@ -152,7 +160,7 @@ void proc_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) tmp.f_files = 0; tmp.f_ffree = 0; tmp.f_namelen = NAME_MAX; - copy_to_user(buf, &tmp, bufsiz); + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } void proc_read_inode(struct inode * inode) @@ -200,5 +208,4 @@ void proc_read_inode(struct inode * inode) void proc_write_inode(struct inode * inode) { - inode->i_dirt=0; } diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c index 6ef386ffa..1cc6a9c83 100644 --- a/fs/proc/kmsg.c +++ b/fs/proc/kmsg.c @@ -70,6 +70,7 @@ struct inode_operations proc_kmsg_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/proc/link.c b/fs/proc/link.c index 695ed9bba..c25fd702b 100644 --- a/fs/proc/link.c +++ b/fs/proc/link.c @@ -14,9 +14,9 @@ #include <linux/mm.h> #include <linux/proc_fs.h> #include <linux/stat.h> -#include <linux/dalloc.h> static int proc_readlink(struct inode *, char *, int); +static struct dentry * proc_follow_link(struct inode *, struct dentry *); /* * PLAN9_SEMANTICS won't work any more: it used an ugly hack that broke @@ -52,6 +52,7 @@ struct inode_operations proc_link_inode_operations = { NULL, /* mknod */ NULL, /* rename */ proc_readlink, /* readlink */ + proc_follow_link, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -59,59 +60,52 @@ struct inode_operations proc_link_inode_operations = { NULL /* permission */ }; -/* [Feb-1997 T. Schoebel-Theuer] This is no longer called from the - * VFS, but only from proc_readlink(). All the functionality - * should the moved there (without using temporary inodes any more) - * and then it could be eliminated. - */ -static int proc_follow_link(struct inode * dir, struct inode * inode, - int flag, int mode, struct inode ** res_inode) +static struct dentry * proc_follow_link(struct inode *inode, struct dentry *base) { - unsigned int pid, ino; - struct task_struct * p; - struct inode * new_inode; + struct task_struct *p; + struct dentry * result; + int ino, pid; int error; - *res_inode = NULL; - if (dir) - iput(dir); - if (!inode) - return -ENOENT; - if ((error = permission(inode, MAY_EXEC)) != 0){ - iput(inode); - return error; - } + /* We don't need a base pointer in the /proc filesystem */ + dput(base); + + error = permission(inode, MAY_EXEC); + result = ERR_PTR(error); + if (error) + return result; + ino = inode->i_ino; pid = ino >> 16; ino &= 0x0000ffff; p = find_task_by_pid(pid); - if (!p) { - iput(inode); - return -ENOENT; - } - new_inode = NULL; + result = ERR_PTR(-ENOENT); + if (!p) + return result; + switch (ino) { case PROC_PID_CWD: - if (!p->fs) + if (!p->fs || !p->fs->pwd) break; - new_inode = p->fs->pwd; + result = dget(p->fs->pwd); break; + case PROC_PID_ROOT: - if (!p->fs) + if (!p->fs || !p->fs->root) break; - new_inode = p->fs->root; + result = dget(p->fs->root); break; + case PROC_PID_EXE: { struct vm_area_struct * vma; if (!p->mm) break; vma = p->mm->mmap; while (vma) { - if (vma->vm_flags & VM_EXECUTABLE) { - new_inode = vma->vm_inode; - break; - } + if (vma->vm_flags & VM_EXECUTABLE) + return dget(vma->vm_dentry); + vma = vma->vm_next; } break; @@ -122,45 +116,38 @@ static int proc_follow_link(struct inode * dir, struct inode * inode, if (!p->files) break; ino &= 0xff; - if (ino < NR_OPEN && p->files->fd[ino]) { - new_inode = p->files->fd[ino]->f_inode; - } + if (ino >= NR_OPEN) + break; + if (!p->files->fd[ino]) + break; + if (!p->files->fd[ino]->f_dentry) + break; + result = dget(p->files->fd[ino]->f_dentry); break; } } - iput(inode); - if (!new_inode) - return -ENOENT; - *res_inode = new_inode; - atomic_inc(&new_inode->i_count); - return 0; + return result; } static int proc_readlink(struct inode * inode, char * buffer, int buflen) { - int error = proc_follow_link(NULL, inode, 0, 0, &inode); + int error; + struct dentry * dentry = proc_follow_link(inode, NULL); - if (error) - return error; - if (!inode) - return -EIO; - - /* This will return *one* of the alias names (which is not quite - * correct). I have to rethink the problem, so this is only a - * quick hack... - */ - if(inode->i_dentry) { - char * tmp = (char*)__get_free_page(GFP_KERNEL); - int len = d_path(inode->i_dentry, current->fs->root, tmp); - int min = buflen<PAGE_SIZE ? buflen : PAGE_SIZE; - if(len <= min) - min = len+1; - copy_to_user(buffer, tmp, min); - free_page((unsigned long)tmp); - error = len; - } else { - error= -ENOENT; + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + error = -ENOENT; + if (dentry) { + char * tmp = (char*)__get_free_page(GFP_KERNEL); + int len = d_path(dentry, current->fs->root, tmp); + int min = buflen<PAGE_SIZE ? buflen : PAGE_SIZE; + if(len <= min) + min = len+1; + dput(dentry); + copy_to_user(buffer, tmp, min); + free_page((unsigned long)tmp); + error = len; + } } - iput(inode); return error; } diff --git a/fs/proc/mem.c b/fs/proc/mem.c index a64ead624..97acb5ee8 100644 --- a/fs/proc/mem.c +++ b/fs/proc/mem.c @@ -328,6 +328,7 @@ struct inode_operations proc_mem_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/proc/net.c b/fs/proc/net.c index 3bc5c339c..257487569 100644 --- a/fs/proc/net.c +++ b/fs/proc/net.c @@ -111,6 +111,7 @@ struct inode_operations proc_net_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/proc/omirr.c b/fs/proc/omirr.c index 0e6377fb2..041e493d2 100644 --- a/fs/proc/omirr.c +++ b/fs/proc/omirr.c @@ -7,7 +7,6 @@ #include <linux/string.h> #include <linux/mm.h> #include <linux/fs.h> -#include <linux/dalloc.h> #include <linux/omirr.h> #include <asm/uaccess.h> @@ -288,6 +287,7 @@ struct inode_operations proc_omirr_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/proc/openpromfs.c b/fs/proc/openpromfs.c index 7d741cfaf..346e72ed1 100644 --- a/fs/proc/openpromfs.c +++ b/fs/proc/openpromfs.c @@ -1,7 +1,7 @@ -/* $Id: openpromfs.c,v 1.15 1997/06/05 01:28:11 davem Exp $ +/* $Id: openpromfs.c,v 1.18 1997/07/17 02:24:01 davem Exp $ * openpromfs.c: /proc/openprom handling routines * - * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ #include <linux/module.h> @@ -68,7 +68,7 @@ static long nodenum_read(struct inode *inode, struct file *file, if (count < 0 || !inode->u.generic_ip) return -EINVAL; - sprintf (buffer, "%8.8x\n", (u32)(inode->u.generic_ip)); + sprintf (buffer, "%8.8x\n", (u32)(long)(inode->u.generic_ip)); if (file->f_pos >= 9) return 0; if (count > 9 - file->f_pos) @@ -86,27 +86,28 @@ static long property_read(struct inode *inode, struct file *filp, char *p; u32 *q; openprom_property *op; + char buffer[64]; if (filp->f_pos >= 0xffffff) return -EINVAL; if (!filp->private_data) { - node = nodes[(u16)((uint)inode->u.generic_ip)].node; - i = ((u32)inode->u.generic_ip) >> 16; - if ((u16)((uint)inode->u.generic_ip) == aliases) { + node = nodes[(u16)((long)inode->u.generic_ip)].node; + i = ((u32)(long)inode->u.generic_ip) >> 16; + if ((u16)((long)inode->u.generic_ip) == aliases) { if (i >= aliases_nodes) p = 0; else p = alias_names [i]; } else - for (p = prom_firstprop (node); + for (p = prom_firstprop (node, buffer); i && p && *p; - p = prom_nextprop (node, p), i--) + p = prom_nextprop (node, p, buffer), i--) /* nothing */ ; if (!p || !*p) return -EIO; i = prom_getproplen (node, p); if (i < 0) { - if ((u16)((uint)inode->u.generic_ip) == aliases) + if ((u16)((long)inode->u.generic_ip) == aliases) i = 0; else return -EIO; @@ -155,7 +156,7 @@ static long property_read(struct inode *inode, struct file *filp, if (count > i - k) count = i - k; if (op->flag & OPP_STRING) { if (!k) { - *buf = '\''; + __put_user('\'', buf); k++; count--; } @@ -170,9 +171,9 @@ static long property_read(struct inode *inode, struct file *filp, k += j; } if (count) - buf [k++ - filp->f_pos] = '\''; + __put_user('\'', &buf [k++ - filp->f_pos]); if (count > 1) - buf [k++ - filp->f_pos] = '\n'; + __put_user('\n', &buf [k++ - filp->f_pos]); } else if (op->flag & OPP_BINARY) { char buffer[10]; u32 *first, *last; @@ -186,26 +187,26 @@ static long property_read(struct inode *inode, struct file *filp, if (first == last) { sprintf (buffer, "%08x.", *first); - memcpy (buf, buffer + first_off, last_cnt - first_off); + copy_to_user (buf, buffer + first_off, last_cnt - first_off); buf += last_cnt - first_off; } else { for (q = first; q <= last; q++) { sprintf (buffer, "%08x.", *q); if (q == first) { - memcpy (buf, buffer + first_off, - 9 - first_off); + copy_to_user (buf, buffer + first_off, + 9 - first_off); buf += 9 - first_off; } else if (q == last) { - memcpy (buf, buffer, last_cnt); + copy_to_user (buf, buffer, last_cnt); buf += last_cnt; } else { - memcpy (buf, buffer, 9); + copy_to_user (buf, buffer, 9); buf += 9; } } } if (last == (u32 *)(op->value + op->len - 4) && last_cnt == 9) - *(buf - 1) = '\n'; + __put_user('\n', (buf - 1)); k += count; } count = k - filp->f_pos; @@ -242,8 +243,10 @@ static long property_write(struct inode *inode, struct file *filp, for (i = 0; i < count; i++, j++) { if (j == 9) j = 0; if (!j) { - if (buf [i] != '.') { - if (buf [i] != '\n') { + char ctmp; + __get_user(ctmp, &buf[i]); + if (ctmp != '.') { + if (ctmp != '\n') { if (op->flag & OPP_BINARY) return -EINVAL; else @@ -255,10 +258,12 @@ static long property_write(struct inode *inode, struct file *filp, } } } else { - if (buf [i] < '0' || - (buf [i] > '9' && buf [i] < 'A') || - (buf [i] > 'F' && buf [i] < 'a') || - buf [i] > 'f') { + char ctmp; + __get_user(ctmp, &buf[i]); + if (ctmp < '0' || + (ctmp > '9' && ctmp < 'A') || + (ctmp > 'F' && ctmp < 'a') || + ctmp > 'f') { if (op->flag & OPP_BINARY) return -EINVAL; else @@ -292,8 +297,8 @@ static long property_write(struct inode *inode, struct file *filp, last_cnt = (k + count) % 9; if (first + 1 == last) { memset (tmp, '0', 8); - memcpy (tmp + first_off, buf, (count + first_off > 8) ? - 8 - first_off : count); + copy_from_user (tmp + first_off, buf, + (count + first_off > 8) ? 8 - first_off : count); mask = 0xffffffff; mask2 = 0xffffffff; for (j = 0; j < first_off; j++) @@ -312,8 +317,8 @@ static long property_write(struct inode *inode, struct file *filp, if (q == first) { if (first_off < 8) { memset (tmp, '0', 8); - memcpy (tmp + first_off, buf, - 8 - first_off); + copy_from_user (tmp + first_off, buf, + 8 - first_off); mask = 0xffffffff; for (j = 0; j < first_off; j++) mask >>= 1; @@ -324,7 +329,7 @@ static long property_write(struct inode *inode, struct file *filp, } else if ((q == last - 1) && last_cnt && (last_cnt < 8)) { memset (tmp, '0', 8); - memcpy (tmp, buf, last_cnt); + copy_from_user (tmp, buf, last_cnt); mask = 0xffffffff; for (j = 0; j < 8 - last_cnt; j++) mask <<= 1; @@ -332,7 +337,10 @@ static long property_write(struct inode *inode, struct file *filp, *q |= simple_strtoul (tmp, 0, 16); buf += last_cnt; } else { - *q = simple_strtoul (buf, 0, 16); + char tchars[17]; /* XXX yuck... */ + + copy_from_user(tchars, buf, 16); + *q = simple_strtoul (tchars, 0, 16); buf += 9; } } @@ -347,12 +355,15 @@ static long property_write(struct inode *inode, struct file *filp, write_try_string: if (!(op->flag & OPP_BINARY)) { if (!(op->flag & (OPP_QUOTED | OPP_NOTQUOTED))) { + char ctmp; + /* No way, if somebody starts writing from the middle, * we don't know whether he uses quotes around or not */ if (k > 0) return -EINVAL; - if (*buf == '\'') { + __get_user(ctmp, buf); + if (ctmp == '\'') { op->flag |= OPP_QUOTED; buf++; count--; @@ -383,7 +394,7 @@ write_try_string: kfree (b); } p = op->value + filp->f_pos - ((op->flag & OPP_QUOTED) ? 1 : 0); - memcpy (p, buf, count); + copy_from_user (p, buf, count); op->flag |= OPP_DIRTY; for (i = 0; i < count; i++, p++) if (*p == '\n') { @@ -414,8 +425,8 @@ int property_release (struct inode *inode, struct file *filp) if (!op) return 0; - node = nodes[(u16)((uint)inode->u.generic_ip)].node; - if ((u16)((uint)inode->u.generic_ip) == aliases) { + node = nodes[(u16)((long)inode->u.generic_ip)].node; + if ((u16)((long)inode->u.generic_ip) == aliases) { if ((op->flag & OPP_DIRTY) && (op->flag & OPP_STRING)) { char *p = op->name; int i = (op->value - op->name) - strlen (op->name) - 1; @@ -484,6 +495,7 @@ static struct inode_operations openpromfs_prop_inode_ops = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -516,6 +528,7 @@ static struct inode_operations openpromfs_nodenum_inode_ops = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -548,6 +561,7 @@ static struct inode_operations openprom_alias_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -604,6 +618,7 @@ static int openpromfs_lookup(struct inode * dir, const char * name, int len, int i; struct inode *inode; struct openpromfs_dev *d = NULL; + char buffer2[64]; *result = NULL; if (!dir || !S_ISDIR(dir->i_mode)) { @@ -659,9 +674,9 @@ static int openpromfs_lookup(struct inode * dir, const char * name, int len, if (!ino) { int j = NODEP2INO(NODE(dir->i_ino).first_prop); if (dirnode != aliases) { - for (p = prom_firstprop (n); + for (p = prom_firstprop (n, buffer2); p && *p; - p = prom_nextprop (n, p)) { + p = prom_nextprop (n, p, buffer2)) { j++; if ((len == strlen (p)) && !strncmp (p, name, len)) { @@ -720,7 +735,7 @@ static int openpromfs_lookup(struct inode * dir, const char * name, int len, inode->i_mode = S_IFREG | S_IRUGO; inode->i_op = &openpromfs_nodenum_inode_ops; inode->i_nlink = 1; - inode->u.generic_ip = (void *)(n); + inode->u.generic_ip = (void *)(long)(n); break; case OPFSL_PROPERTY: if ((dirnode == options) && (len == 17) @@ -737,7 +752,7 @@ static int openpromfs_lookup(struct inode * dir, const char * name, int len, inode->i_nlink = 1; if (inode->i_size < 0) inode->i_size = 0; - inode->u.generic_ip = (void *)(((u16)dirnode) | + inode->u.generic_ip = (void *)(long)(((u16)dirnode) | (((u16)(ino - NODEP2INO(NODE(dir->i_ino).first_prop) - 1)) << 16)); break; case OPFSL_DEVICE: @@ -761,6 +776,7 @@ static int openpromfs_readdir(struct inode * inode, struct file * filp, u16 node; char *p; struct openpromfs_dev *d; + char buffer2[64]; if (!inode || !S_ISDIR (inode->i_mode)) return -ENOTDIR; ino = inode->i_ino; @@ -813,9 +829,9 @@ static int openpromfs_readdir(struct inode * inode, struct file * filp, } } } else { - for (p = prom_firstprop (n); + for (p = prom_firstprop (n, buffer2); p && *p; - p = prom_nextprop (n, p)) { + p = prom_nextprop (n, p, buffer2)) { j++; if (i) i--; else { @@ -877,7 +893,7 @@ static int openpromfs_create (struct inode *dir, const char *name, int len, inode->i_op = &openpromfs_prop_inode_ops; inode->i_nlink = 1; if (inode->i_size < 0) inode->i_size = 0; - inode->u.generic_ip = (void *)(((u16)aliases) | + inode->u.generic_ip = (void *)(long)(((u16)aliases) | (((u16)(aliases_nodes - 1)) << 16)); *result = inode; return 0; @@ -940,6 +956,7 @@ static u16 get_nodes (u16 parent, u32 node) { char *p; u16 n = last_node++, i; + char buffer[64]; if (check_space (n) < 0) return 0xffff; @@ -961,15 +978,15 @@ static u16 get_nodes (u16 parent, u32 node) } } if (n != aliases) - for (p = prom_firstprop (node); + for (p = prom_firstprop (node, buffer); p && p != (char *)-1 && *p; - p = prom_nextprop (node, p)) + p = prom_nextprop (node, p, buffer)) first_prop++; else { char *q; - for (p = prom_firstprop (node); + for (p = prom_firstprop (node, buffer); p && p != (char *)-1 && *p; - p = prom_nextprop (node, p)) { + p = prom_nextprop (node, p, buffer)) { if (aliases_nodes == ALIASES_NNODES) break; for (i = 0; i < aliases_nodes; i++) @@ -1012,7 +1029,7 @@ void openpromfs_use (struct inode *inode, int inc) static int usec = 0; if (inc) { - if (atomic_read(&inode->i_count) == 1) + if (inode->i_count == 1) usec++; else if (root_fresh && inode->i_ino == PROC_OPENPROM_FIRST) { root_fresh = 0; @@ -1025,10 +1042,10 @@ void openpromfs_use (struct inode *inode, int inc) usec--; } printk ("openpromfs_use: %d %d %d %d\n", - inode->i_ino, inc, usec, atomic_read(&inode->i_count)); + inode->i_ino, inc, usec, inode->i_count); #else if (inc) { - if (atomic_read(&inode->i_count) == 1) + if (inode->i_count == 1) MOD_INC_USE_COUNT; else if (root_fresh && inode->i_ino == PROC_OPENPROM_FIRST) { root_fresh = 0; @@ -1059,8 +1076,10 @@ EXPORT_NO_SYMBOLS; int init_module (void) #endif { +#ifndef __sparc_v9__ if (!romvec->pv_romvers) return RET(ENODEV); +#endif nodes = (openpromfs_node *)__get_free_pages(GFP_KERNEL, 0, 0); if (!nodes) { printk (KERN_WARNING "/proc/openprom: can't get free page\n"); diff --git a/fs/proc/procfs_syms.c b/fs/proc/procfs_syms.c index 71c29dd75..d3077ea79 100644 --- a/fs/proc/procfs_syms.c +++ b/fs/proc/procfs_syms.c @@ -38,7 +38,7 @@ EXPORT_SYMBOL(proc_openprom_deregister); static struct file_system_type proc_fs_type = { "proc", - FS_NO_DCACHE, + 0 /* FS_NO_DCACHE doesn't work correctly */, proc_read_super, NULL }; diff --git a/fs/proc/root.c b/fs/proc/root.c index f42557d2c..2b456ca57 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -24,7 +24,7 @@ #define FIRST_PROCESS_ENTRY 256 static int proc_root_readdir(struct inode *, struct file *, void *, filldir_t); -static int proc_root_lookup(struct inode *,const char *,int,struct inode **); +static int proc_root_lookup(struct inode *,struct dentry *); static unsigned char proc_alloc_map[PROC_NDYNAMIC / 8] = {0}; @@ -64,6 +64,7 @@ struct inode_operations proc_dir_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -104,6 +105,7 @@ static struct inode_operations proc_root_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -149,14 +151,14 @@ struct proc_dir_entry proc_sys_root = { #if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE) static int (*proc_openprom_defreaddir_ptr)(struct inode *, struct file *, void *, filldir_t); -static int (*proc_openprom_deflookup_ptr)(struct inode *, const char *, int, struct inode **); +static int (*proc_openprom_deflookup_ptr)(struct inode *, struct qstr *, struct inode **); void (*proc_openprom_use)(struct inode *, int) = 0; static struct openpromfs_dev *proc_openprom_devices = NULL; static ino_t proc_openpromdev_ino = PROC_OPENPROMD_FIRST; struct inode_operations * proc_openprom_register(int (*readdir)(struct inode *, struct file *, void *, filldir_t), - int (*lookup)(struct inode *, const char *, int, struct inode **), + int (*lookup)(struct inode *, struct qstr *, struct inode **), void (*use)(struct inode *, int), struct openpromfs_dev ***devices) { @@ -218,15 +220,13 @@ proc_openprom_defreaddir(struct inode * inode, struct file * filp, } static int -proc_openprom_deflookup(struct inode * dir,const char * name, int len, - struct inode ** result) +proc_openprom_deflookup(struct inode * dir, struct qstr *str, struct inode ** result) { request_module("openpromfs"); if (proc_openprom_inode_operations.lookup != proc_openprom_deflookup) return proc_openprom_inode_operations.lookup - (dir, name, len, result); - iput(dir); + (dir, str, result); return -ENOENT; } #endif @@ -264,6 +264,7 @@ struct inode_operations proc_openprom_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -350,7 +351,6 @@ static int proc_self_readlink(struct inode * inode, char * buffer, int buflen) int len; char tmp[30]; - iput(inode); len = sprintf(tmp, "%d", current->pid); if (buflen < len) len = buflen; @@ -358,6 +358,15 @@ static int proc_self_readlink(struct inode * inode, char * buffer, int buflen) return len; } +static struct dentry * proc_self_follow_link(struct inode *inode, struct dentry *base) +{ + int len; + char tmp[30]; + + len = sprintf(tmp, "%d", current->pid); + return lookup_dentry(tmp, base, 1); +} + static struct inode_operations proc_self_inode_operations = { NULL, /* no file-ops */ NULL, /* create */ @@ -370,6 +379,7 @@ static struct inode_operations proc_self_inode_operations = { NULL, /* mknod */ NULL, /* rename */ proc_self_readlink, /* readlink */ + proc_self_follow_link, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -611,93 +621,49 @@ void proc_root_init(void) proc_tty_init(); } - -int proc_match(int len,const char * name,struct proc_dir_entry * de) -{ - if (!de || !de->low_ino) - return 0; - /* "" means "." ---> so paths like "/usr/lib//libc.a" work */ - if (!len && (de->name[0]=='.') && (de->name[1]=='\0')) - return 1; - if (de->namelen != len) - return 0; - return !memcmp(name, de->name, len); -} - -int proc_lookup(struct inode * dir,const char * name, int len, - struct inode ** result) +/* + * Don't create negative dentries here, return -ENOENT by hand + * instead. + */ +int proc_lookup(struct inode * dir, struct dentry *dentry) { + struct inode *inode; struct proc_dir_entry * de; - int ino; - *result = NULL; - if (!dir || !S_ISDIR(dir->i_mode)) { - iput(dir); + if (!dir || !S_ISDIR(dir->i_mode)) return -ENOTDIR; - } de = (struct proc_dir_entry *) dir->u.generic_ip; - if (!de) { - iput(dir); - return -EINVAL; - } - - /* Either remove this as soon as possible due to security problems, - * or uncomment the root-only usage. - */ - - /* Allow generic inode lookups everywhere. - * No other name in /proc must begin with a '['. - */ - if(/*!current->uid &&*/ name[0] == '[') - return proc_arbitrary_lookup(dir,name,len,result); - - /* Special case "." and "..": they aren't on the directory list */ - *result = dir; - if (!len) - return 0; - if (name[0] == '.') { - if (len == 1) - return 0; - if (name[1] == '.' && len == 2) { - struct inode * inode; - inode = proc_get_inode(dir->i_sb, de->parent->low_ino, de->parent); - iput(dir); - if (!inode) - return -EINVAL; - *result = inode; - return 0; + inode = NULL; + if (de) { + for (de = de->subdir; de ; de = de->next) { + if (!de || !de->low_ino) + continue; + if (de->namelen != dentry->d_name.len) + continue; + if (!memcmp(dentry->d_name.name, de->name, de->namelen)) { + int ino = de->low_ino | (dir->i_ino & ~(0xffff)); + inode = proc_get_inode(dir->i_sb, ino, de); + if (!inode) + return -EINVAL; + break; + } } } - - *result = NULL; - for (de = de->subdir; de ; de = de->next) { - if (proc_match(len, name, de)) - break; - } - if (!de) { - iput(dir); + if (!inode) return -ENOENT; - } - ino = de->low_ino | (dir->i_ino & ~(0xffff)); - - if (!(*result = proc_get_inode(dir->i_sb, ino, de))) { - iput(dir); - return -EINVAL; - } - iput(dir); + d_add(dentry, inode); return 0; } -static int proc_root_lookup(struct inode * dir,const char * name, int len, - struct inode ** result) +static int proc_root_lookup(struct inode * dir, struct dentry * dentry) { unsigned int pid, c; - int ino, retval; struct task_struct *p; - - atomic_inc(&dir->i_count); + const char *name; + struct inode *inode; + int len; if (dir->i_ino == PROC_ROOT_INO) { /* check for safety... */ dir->i_nlink = proc_root.nlink; @@ -710,13 +676,12 @@ static int proc_root_lookup(struct inode * dir,const char * name, int len, read_unlock(&tasklist_lock); } - retval = proc_lookup(dir, name, len, result); - if (retval != -ENOENT) { - iput(dir); - return retval; - } + if (!proc_lookup(dir, dentry)) + return 0; pid = 0; + name = dentry->d_name.name; + len = dentry->d_name.len; while (len-- > 0) { c = *name - '0'; name++; @@ -732,16 +697,14 @@ static int proc_root_lookup(struct inode * dir,const char * name, int len, } } p = find_task_by_pid(pid); - if (!pid || !p) { - iput(dir); - return -ENOENT; - } - ino = (pid << 16) + PROC_PID_INO; - if (!(*result = proc_get_inode(dir->i_sb, ino, &proc_pid))) { - iput(dir); - return -EINVAL; + inode = NULL; + if (pid && p) { + unsigned long ino = (pid << 16) + PROC_PID_INO; + inode = proc_get_inode(dir->i_sb, ino, &proc_pid); + if (!inode) + return -EINVAL; } - iput(dir); + d_add(dentry, inode); return 0; } diff --git a/fs/proc/scsi.c b/fs/proc/scsi.c index fd629a75c..b1e77398c 100644 --- a/fs/proc/scsi.c +++ b/fs/proc/scsi.c @@ -69,6 +69,7 @@ struct inode_operations proc_scsi_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/read_write.c b/fs/read_write.c index 81b19ac30..6c422b7f4 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -60,13 +60,15 @@ asmlinkage long sys_lseek(unsigned int fd, off_t offset, unsigned int origin) { long retval; struct file * file; + struct dentry * dentry; struct inode * inode; lock_kernel(); retval = -EBADF; if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || - !(inode = file->f_inode)) + !(dentry = file->f_dentry) || + !(inode = dentry->d_inode)) goto bad; retval = -EINVAL; if (origin > 2) @@ -83,6 +85,7 @@ asmlinkage int sys_llseek(unsigned int fd, unsigned long offset_high, { long retval; struct file * file; + struct dentry * dentry; struct inode * inode; long long offset; @@ -90,7 +93,8 @@ asmlinkage int sys_llseek(unsigned int fd, unsigned long offset_high, retval = -EBADF; if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || - !(inode = file->f_inode)) + !(dentry = file->f_dentry) || + !(inode = dentry->d_inode)) goto bad; retval = -EINVAL; if (origin > 2) @@ -115,6 +119,7 @@ asmlinkage long sys_read(unsigned int fd, char * buf, unsigned long count) { int error; struct file * file; + struct dentry * dentry; struct inode * inode; long (*read)(struct inode *, struct file *, char *, unsigned long); @@ -123,7 +128,10 @@ asmlinkage long sys_read(unsigned int fd, char * buf, unsigned long count) file = fget(fd); if (!file) goto bad_file; - inode = file->f_inode; + dentry = file->f_dentry; + if (!dentry) + goto bad_file; + inode = dentry->d_inode; if (!inode) goto out; error = -EBADF; @@ -137,7 +145,7 @@ asmlinkage long sys_read(unsigned int fd, char * buf, unsigned long count) goto out; error = read(inode,file,buf,count); out: - fput(file, inode); + fput(file); bad_file: unlock_kernel(); return error; @@ -147,6 +155,7 @@ asmlinkage long sys_write(unsigned int fd, const char * buf, unsigned long count { int error; struct file * file; + struct dentry * dentry; struct inode * inode; long (*write)(struct inode *, struct file *, const char *, unsigned long); @@ -155,7 +164,10 @@ asmlinkage long sys_write(unsigned int fd, const char * buf, unsigned long count file = fget(fd); if (!file) goto bad_file; - inode = file->f_inode; + dentry = file->f_dentry; + if (!dentry) + goto out; + inode = dentry->d_inode; if (!inode) goto out; if (!(file->f_mode & 2)) @@ -168,10 +180,9 @@ asmlinkage long sys_write(unsigned int fd, const char * buf, unsigned long count goto out; down(&inode->i_sem); error = write(inode,file,buf,count); - inode->i_status |= ST_MODIFIED; up(&inode->i_sem); out: - fput(file, inode); + fput(file); bad_file: unlock_kernel(); return error; @@ -264,8 +275,6 @@ static long do_readv_writev(int type, struct inode * inode, struct file * file, if (nr != len) break; } - if(fn == (IO_fn_t) file->f_op->write) - inode->i_status |= ST_MODIFIED; if (iov != iovstack) kfree(iov); return retval; @@ -274,14 +283,30 @@ static long do_readv_writev(int type, struct inode * inode, struct file * file, asmlinkage long sys_readv(unsigned long fd, const struct iovec * vector, unsigned long count) { struct file * file; + struct dentry * dentry; struct inode * inode; - long err = -EBADF; + long err; lock_kernel(); - if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || !(inode=file->f_inode)) + err = -EBADF; + if (fd >= NR_OPEN) + goto out; + + file = current->files->fd[fd]; + if (!file) goto out; + if (!(file->f_mode & 1)) goto out; + + dentry = file->f_dentry; + if (!dentry) + goto out; + + inode = dentry->d_inode; + if (!inode) + goto out; + err = do_readv_writev(VERIFY_WRITE, inode, file, vector, count); out: unlock_kernel(); @@ -290,15 +315,32 @@ out: asmlinkage long sys_writev(unsigned long fd, const struct iovec * vector, unsigned long count) { - int error = -EBADF; + long error; struct file * file; + struct dentry * dentry; struct inode * inode; lock_kernel(); - if (fd >= NR_OPEN || !(file = current->files->fd[fd]) || !(inode=file->f_inode)) + error = -EBADF; + + if (fd >= NR_OPEN) + goto out; + + file = current->files->fd[fd]; + if (!file) goto out; + if (!(file->f_mode & 2)) goto out; + + dentry = file->f_dentry; + if (!dentry) + goto out; + + inode = dentry->d_inode; + if (!inode) + goto out; + down(&inode->i_sem); error = do_readv_writev(VERIFY_READ, inode, file, vector, count); up(&inode->i_sem); diff --git a/fs/readdir.c b/fs/readdir.c index a86398ac3..2a2b0a707 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -1,35 +1,20 @@ /* - * fs/readdir.c + * linux/fs/readdir.c * * Copyright (C) 1995 Linus Torvalds */ -#include <linux/config.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/stat.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> -#ifdef CONFIG_TRANS_NAMES -#include <linux/nametrans.h> -#endif -#include <linux/dalloc.h> #include <linux/smp.h> #include <linux/smp_lock.h> #include <asm/uaccess.h> -/* [T.Schoebel-Theuer] I am assuming that directories never get too large. - * The problem is that getdents() delivers d_offset's that can be used - * for lseek() by the user, so I must encode the status information for - * name translation and dcache baskets in the offset. - * Note that the linux man page getdents(2) does not mention that - * the d_offset is fs-specific and can be used for lseek(). - */ -#define BASKET_BIT (1<<30) /* 31 is already used by affs */ -#define TRANS_BIT (1<<29) - /* * Traditional linux readdir() handling.. * @@ -50,9 +35,6 @@ struct old_linux_dirent { struct readdir_callback { struct old_linux_dirent * dirent; - struct file * file; - int translate; - off_t oldoffset; int count; }; @@ -65,60 +47,49 @@ static int fillonedir(void * __buf, const char * name, int namlen, off_t offset, return -EINVAL; buf->count++; dirent = buf->dirent; - copy_to_user(dirent->d_name, name, namlen); - put_user(0, dirent->d_name + namlen); -#ifdef CONFIG_TRANS_NAMES - if(!buf->translate) { - char * cut; -#ifdef CONFIG_TRANS_RESTRICT - struct inode * inode = buf->file->f_inode; - cut = testname(inode && inode->i_gid != CONFIG_TRANS_GID, dirent->d_name); -#else - cut = testname(1, dirent->d_name); -#endif - if(cut) { - put_user(0, cut); - buf->translate = 1; - } - } -#endif put_user(ino, &dirent->d_ino); put_user(offset, &dirent->d_offset); put_user(namlen, &dirent->d_namlen); + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); return 0; } asmlinkage int old_readdir(unsigned int fd, void * dirent, unsigned int count) { - int error = -EBADF; + int error; struct file * file; + struct dentry * dentry; + struct inode * inode; struct readdir_callback buf; - off_t oldpos; lock_kernel(); - if (fd >= NR_OPEN || !(file = current->files->fd[fd])) + error = -EBADF; + if (fd >= NR_OPEN) goto out; - error = -ENOTDIR; - if (!file->f_op || !file->f_op->readdir) + + file = current->files->fd[fd]; + if (!file) goto out; - error = verify_area(VERIFY_WRITE, dirent, sizeof(struct old_linux_dirent)); - if (error) + + dentry = file->f_dentry; + if (!dentry) goto out; - oldpos = file->f_pos; - buf.file = file; - buf.dirent = dirent; + + inode = dentry->d_inode; + if (!inode) + goto out; + buf.count = 0; - buf.translate = 0; - if(file->f_pos & TRANS_BIT) { - file->f_pos &= ~TRANS_BIT; - buf.translate = 1; - } - error = file->f_op->readdir(file->f_inode, file, &buf, fillonedir); + buf.dirent = dirent; + + error = -ENOTDIR; + if (!file->f_op || !file->f_op->readdir) + goto out; + + error = file->f_op->readdir(inode, file, &buf, fillonedir); if (error < 0) goto out; - if(buf.translate) { - file->f_pos = oldpos | TRANS_BIT; - } error = buf.count; out: unlock_kernel(); @@ -139,11 +110,8 @@ struct linux_dirent { struct getdents_callback { struct linux_dirent * current_dir; struct linux_dirent * previous; - struct file * file; int count; - int error; - int restricted; - int do_preload; + int error; }; static int filldir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino) @@ -152,51 +120,18 @@ static int filldir(void * __buf, const char * name, int namlen, off_t offset, in struct getdents_callback * buf = (struct getdents_callback *) __buf; int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1); - /* Do not touch buf->error any more if everything is ok! */ + buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) - return (buf->error = -EINVAL); -#ifdef CONFIG_DCACHE_PRELOAD - if(buf->do_preload && (name[0] != '.' || namlen > 2)) { - struct qstr qname = { name, namlen }; - struct inode * dir = buf->file->f_inode; - d_entry_preliminary(dir->i_dentry, &qname, ino); - } -#endif + return -EINVAL; + dirent = buf->previous; + if (dirent) + put_user(offset, &dirent->d_off); dirent = buf->current_dir; - copy_to_user(dirent->d_name, name, namlen); - put_user(0, dirent->d_name + namlen); -#ifdef CONFIG_TRANS_NAMES - { - char * cut; -#ifdef CONFIG_TRANS_RESTRICT - cut = testname(buf->restricted, dirent->d_name); -#else - cut = testname(1, dirent->d_name); -#endif - if(cut) { - int newlen = (int)cut - (int)dirent->d_name; - int newreclen = ROUND_UP(NAME_OFFSET(dirent) + newlen + 1); - /* Either both must fit or none. This way we need - * no status information in f_pos */ - if (reclen+newlen > buf->count) - return -EINVAL; - put_user(0, cut); - put_user(ino, &dirent->d_ino); - put_user(newreclen, &dirent->d_reclen); - put_user(offset, &dirent->d_off); - ((char *) dirent) += newreclen; - buf->count -= newreclen; - put_user(offset, &dirent->d_off); - copy_to_user(dirent->d_name, name, namlen); - put_user(0, dirent->d_name + namlen); - } - } -#endif + buf->previous = dirent; put_user(ino, &dirent->d_ino); put_user(reclen, &dirent->d_reclen); - if (buf->previous) - put_user(buf->file->f_pos, &buf->previous->d_off); - buf->previous = dirent; + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); ((char *) dirent) += reclen; buf->current_dir = dirent; buf->count -= reclen; @@ -206,84 +141,45 @@ static int filldir(void * __buf, const char * name, int namlen, off_t offset, in asmlinkage int sys_getdents(unsigned int fd, void * dirent, unsigned int count) { struct file * file; + struct dentry * dentry; + struct inode * inode; + struct linux_dirent * lastdirent; struct getdents_callback buf; - int error = -EBADF; + int error; lock_kernel(); - if (fd >= NR_OPEN || !(file = current->files->fd[fd])) + error = -EBADF; + if (fd >= NR_OPEN) goto out; - error = -ENOTDIR; - if (!file->f_op || !file->f_op->readdir) + + file = current->files->fd[fd]; + if (!file) goto out; - error = verify_area(VERIFY_WRITE, dirent, count); - if (error) + + dentry = file->f_dentry; + if (!dentry) goto out; - buf.file = file; + + inode = dentry->d_inode; + if (!inode) + goto out; + buf.current_dir = (struct linux_dirent *) dirent; buf.previous = NULL; buf.count = count; buf.error = 0; - buf.restricted = 0; -#ifdef CONFIG_TRANS_RESTRICT - buf.restricted = file->f_inode && file->f_inode->i_gid != CONFIG_TRANS_GID; -#endif - buf.do_preload = 0; -#ifdef CONFIG_DCACHE_PRELOAD - if(file->f_inode && file->f_inode->i_dentry && - !(file->f_inode->i_sb->s_type->fs_flags & (FS_NO_DCACHE|FS_NO_PRELIM)) && - !(file->f_inode->i_dentry->d_flag & D_PRELOADED)) - buf.do_preload = 1; -#endif - - if(!(file->f_pos & BASKET_BIT)) { - int oldcount; - do { - oldcount = buf.count; - error = file->f_op->readdir(file->f_inode, file, &buf, filldir); - if (error < 0) - goto out; - } while(!buf.error && buf.count != oldcount); - } - if(!buf.error) { - int nr = 0; - struct dentry * list = file->f_inode ? - d_basket(file->f_inode->i_dentry) : NULL; - struct dentry * ptr = list; -#ifdef CONFIG_DCACHE_PRELOAD - if(buf.do_preload) { - buf.do_preload = 0; - file->f_inode->i_dentry->d_flag |= D_PRELOADED; - } -#endif - if(ptr) { - if(!(file->f_pos & BASKET_BIT)) - file->f_pos = BASKET_BIT; - do { - struct dentry * next = ptr->d_basket_next; - struct inode * inode; - /* vfs_locks() are missing here */ - inode = d_inode(&ptr); - if(inode) { - nr++; - if(nr > (file->f_pos & ~BASKET_BIT)) { - int err = filldir(&buf, ptr->d_name, - ptr->d_len, - file->f_pos, - inode->i_ino); - if(err) - break; - file->f_pos++; - } - iput(inode); - } - ptr = next; - } while(ptr != list); - } - } - if (!buf.previous) { - error = buf.error; - } else { - put_user(file->f_pos, &buf.previous->d_off); + + error = -ENOTDIR; + if (!file->f_op || !file->f_op->readdir) + goto out; + + error = file->f_op->readdir(inode, file, &buf, filldir); + if (error < 0) + goto out; + lastdirent = buf.previous; + error = buf.error; + if (lastdirent) { + put_user(file->f_pos, &lastdirent->d_off); error = count - buf.count; } out: diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c index 0ddda855d..5d936ee59 100644 --- a/fs/romfs/inode.c +++ b/fs/romfs/inode.c @@ -584,7 +584,7 @@ static struct super_operations romfs_ops = { static struct file_system_type romfs_fs_type = { "romfs", - (FS_REQUIRES_DEV | FS_NO_DCACHE), /* Can dcache be used? */ + FS_REQUIRES_DEV, romfs_read_super, NULL }; diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index 20738b0d2..75d3dbff7 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -431,7 +431,7 @@ int smb_current_vmalloced; static struct file_system_type smb_fs_type = { "smbfs", - FS_NO_DCACHE, + 0 /* FS_NO_DCACHE doesn't work correctly */, smb_read_super, NULL }; diff --git a/fs/smbfs/mmap.c b/fs/smbfs/mmap.c index 472fad6de..20a2d0569 100644 --- a/fs/smbfs/mmap.c +++ b/fs/smbfs/mmap.c @@ -119,8 +119,8 @@ smb_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma) inode->i_atime = CURRENT_TIME; inode->i_dirt = 1; } - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + + vma->vm_dentry = dget(file->f_dentry); vma->vm_ops = &smb_file_mmap; return 0; } @@ -48,8 +48,6 @@ static int cp_old_stat(struct inode * inode, struct __old_kernel_stat * statbuf) tmp.st_gid = inode->i_gid; tmp.st_rdev = kdev_t_to_nr(inode->i_rdev); tmp.st_size = inode->i_size; - if (inode->i_pipe) - tmp.st_size = PIPE_SIZE(*inode); tmp.st_atime = inode->i_atime; tmp.st_mtime = inode->i_mtime; tmp.st_ctime = inode->i_ctime; @@ -72,8 +70,6 @@ static int cp_new_stat(struct inode * inode, struct stat * statbuf) tmp.st_gid = inode->i_gid; tmp.st_rdev = kdev_t_to_nr(inode->i_rdev); tmp.st_size = inode->i_size; - if (inode->i_pipe) - tmp.st_size = PIPE_SIZE(*inode); tmp.st_atime = inode->i_atime; tmp.st_mtime = inode->i_mtime; tmp.st_ctime = inode->i_ctime; @@ -116,6 +112,7 @@ static int cp_new_stat(struct inode * inode, struct stat * statbuf) return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; } + #if !defined(__alpha__) && !defined(__sparc__) /* * For backward compatibility? Maybe this should be moved @@ -123,17 +120,21 @@ static int cp_new_stat(struct inode * inode, struct stat * statbuf) */ asmlinkage int sys_stat(char * filename, struct __old_kernel_stat * statbuf) { - struct inode * inode; + struct dentry * dentry; int error; lock_kernel(); - error = namei(NAM_FOLLOW_LINK, filename, &inode); - if (error) - goto out; - if ((error = do_revalidate(inode)) == 0) - error = cp_old_stat(inode,statbuf); - iput(inode); -out: + dentry = namei(filename); + + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + struct inode * inode = dentry->d_inode; + error = do_revalidate(inode); + if (!error) + error = cp_old_stat(inode, statbuf); + + dput(dentry); + } unlock_kernel(); return error; } @@ -141,17 +142,21 @@ out: asmlinkage int sys_newstat(char * filename, struct stat * statbuf) { - struct inode * inode; + struct dentry * dentry; int error; lock_kernel(); - error = namei(NAM_FOLLOW_LINK, filename, &inode); - if (error) - goto out; - if ((error = do_revalidate(inode)) == 0) - error = cp_new_stat(inode,statbuf); - iput(inode); -out: + dentry = namei(filename); + + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + struct inode * inode = dentry->d_inode; + error = do_revalidate(inode); + if (!error) + error = cp_new_stat(inode,statbuf); + + dput(dentry); + } unlock_kernel(); return error; } @@ -164,17 +169,21 @@ out: */ asmlinkage int sys_lstat(char * filename, struct __old_kernel_stat * statbuf) { - struct inode * inode; + struct dentry * dentry; int error; lock_kernel(); - error = namei(NAM_FOLLOW_TRAILSLASH, filename, &inode); - if (error) - goto out; - if ((error = do_revalidate(inode)) == 0) - error = cp_old_stat(inode,statbuf); - iput(inode); -out: + dentry = lnamei(filename); + + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + struct inode * inode = dentry->d_inode; + error = do_revalidate(inode); + if (!error) + error = cp_old_stat(inode, statbuf); + + dput(dentry); + } unlock_kernel(); return error; } @@ -183,17 +192,21 @@ out: asmlinkage int sys_newlstat(char * filename, struct stat * statbuf) { - struct inode * inode; + struct dentry * dentry; int error; lock_kernel(); - error = namei(NAM_FOLLOW_TRAILSLASH, filename, &inode); - if (error) - goto out; - if ((error = do_revalidate(inode)) == 0) - error = cp_new_stat(inode,statbuf); - iput(inode); -out: + dentry = lnamei(filename); + + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + struct inode * inode = dentry->d_inode; + error = do_revalidate(inode); + if (!error) + error = cp_new_stat(inode,statbuf); + + dput(dentry); + } unlock_kernel(); return error; } @@ -207,17 +220,19 @@ out: asmlinkage int sys_fstat(unsigned int fd, struct __old_kernel_stat * statbuf) { struct file * f; - struct inode * inode; - int ret = -EBADF; + int err = -EBADF; lock_kernel(); - if (fd >= NR_OPEN || !(f=current->files->fd[fd]) || !(inode=f->f_inode)) - goto out; - if ((ret = do_revalidate(inode)) == 0) - ret = cp_old_stat(inode,statbuf); -out: + if (fd < NR_OPEN && (f = current->files->fd[fd]) != NULL) { + struct dentry * dentry = f->f_dentry; + struct inode * inode = dentry->d_inode; + + err = do_revalidate(inode); + if (!err) + err = cp_old_stat(inode,statbuf); + } unlock_kernel(); - return ret; + return err; } #endif @@ -225,45 +240,43 @@ out: asmlinkage int sys_newfstat(unsigned int fd, struct stat * statbuf) { struct file * f; - struct inode * inode; int err = -EBADF; lock_kernel(); - if (fd >= NR_OPEN || !(f=current->files->fd[fd]) || !(inode=f->f_inode)) - goto out; - if ((err = do_revalidate(inode)) == 0) - err = cp_new_stat(inode,statbuf); -out: + if (fd < NR_OPEN && (f = current->files->fd[fd]) != NULL) { + struct dentry * dentry = f->f_dentry; + struct inode * inode = dentry->d_inode; + + err = do_revalidate(inode); + if (!err) + err = cp_new_stat(inode,statbuf); + } unlock_kernel(); return err; } asmlinkage int sys_readlink(const char * path, char * buf, int bufsiz) { - struct inode * inode; - int error = -EINVAL; + struct dentry * dentry; + int error; - lock_kernel(); if (bufsiz <= 0) - goto out; - error = verify_area(VERIFY_WRITE,buf,bufsiz); - if (error) - goto out; - error = namei(NAM_FOLLOW_TRAILSLASH, path, &inode); - if (error) - goto out; - error = -EINVAL; - if (!inode->i_op || !inode->i_op->readlink || - !S_ISLNK(inode->i_mode) || (error = do_revalidate(inode)) < 0) { - iput(inode); - goto out; - } - if (!IS_RDONLY(inode)) { - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; + return -EINVAL; + + lock_kernel(); + dentry = lnamei(path); + + error = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + struct inode * inode = dentry->d_inode; + + error = -EINVAL; + if (inode->i_op && inode->i_op->readlink && !(error = do_revalidate(inode))) { + UPDATE_ATIME(inode); + error = inode->i_op->readlink(inode,buf,bufsiz); + } + dput(dentry); } - error = inode->i_op->readlink(inode,buf,bufsiz); -out: unlock_kernel(); return error; } diff --git a/fs/super.c b/fs/super.c index ec47301aa..f143f9348 100644 --- a/fs/super.c +++ b/fs/super.c @@ -33,7 +33,6 @@ #include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/fd.h> -#include <linux/dalloc.h> #include <linux/init.h> #include <asm/system.h> @@ -102,13 +101,13 @@ struct vfsmount *add_vfsmnt(kdev_t dev, const char *dev_name, const char *dir_na lptr->mnt_dev = dev; sema_init(&lptr->mnt_sem, 1); - if (dev_name && !getname(dev_name, &tmp)) { + if (dev_name && !IS_ERR(tmp = getname(dev_name))) { if ((lptr->mnt_devname = (char *) kmalloc(strlen(tmp)+1, GFP_KERNEL)) != (char *)NULL) strcpy(lptr->mnt_devname, tmp); putname(tmp); } - if (dir_name && !getname(dir_name, &tmp)) { + if (dir_name && !IS_ERR(tmp = getname(dir_name))) { if ((lptr->mnt_dirname = (char *) kmalloc(strlen(tmp)+1, GFP_KERNEL)) != (char *)NULL) strcpy(lptr->mnt_dirname, tmp); @@ -198,9 +197,11 @@ static int fs_index(const char * __name) char * name; int err, index; - err = getname(__name, &name); - if (err) + name = getname(__name); + err = PTR_ERR(name); + if (IS_ERR(name)) return err; + index = 0; for (tmp = file_systems ; tmp ; tmp = tmp->next) { if (strcmp(tmp->name, name) == 0) { @@ -454,26 +455,6 @@ struct super_block * get_super(kdev_t dev) return NULL; } -void put_super(kdev_t dev) -{ - struct super_block * sb; - - if (dev == ROOT_DEV) { - printk("VFS: Root device %s: prepare for armageddon\n", - kdevname(dev)); - return; - } - if (!(sb = get_super(dev))) - return; - if (sb->s_covered) { - printk("VFS: Mounted device %s - tssk, tssk\n", - kdevname(dev)); - return; - } - if (sb->s_op && sb->s_op->put_super) - sb->s_op->put_super(sb); -} - asmlinkage int sys_ustat(dev_t dev, struct ustat * ubuf) { struct super_block *s; @@ -535,7 +516,6 @@ static struct super_block * read_super(kdev_t dev,const char *name,int flags, return NULL; } s->s_dev = dev; - s->s_covered = NULL; s->s_rd_only = 0; s->s_dirt = 0; s->s_type = type; @@ -570,18 +550,45 @@ void put_unnamed_dev(kdev_t dev) kdevname(dev)); } +static void d_umount(struct dentry *dentry) +{ + struct dentry * covers = dentry->d_covers; + + if (covers != dentry) { + covers->d_mounts = covers; + dentry->d_covers = dentry; + dput(covers); + dput(dentry); + } +} + +static void d_mount(struct dentry *covers, struct dentry *dentry) +{ + if (covers->d_mounts != covers) { + printk("VFS: mount - already mounted\n"); + return; + } + covers->d_mounts = dentry; + dentry->d_covers = covers; +} + static int do_umount(kdev_t dev,int unmount_root) { struct super_block * sb; int retval; + sb = get_super(dev); + if (!sb) + return -ENOENT; + + if (!sb->s_root) + return -ENOENT; + if (dev==ROOT_DEV && !unmount_root) { /* * Special case for "unmounting" root. We just try to remount * it readonly, and sync() the device. */ - if (!(sb=get_super(dev))) - return -ENOENT; if (!(sb->s_flags & MS_RDONLY)) { /* * Make sure all quotas are turned off on this device we need to mount @@ -597,35 +604,51 @@ static int do_umount(kdev_t dev,int unmount_root) } return 0; } - if (!(sb=get_super(dev)) || !(sb->s_covered)) - return -ENOENT; - if (!sb->s_covered->i_mount) - printk("VFS: umount(%s): mounted inode has i_mount=NULL\n", - kdevname(dev)); - while(sb->s_ibasket) - free_ibasket(sb); - if(sb->s_mounted->i_dentry) - d_del(sb->s_mounted->i_dentry, D_NO_CLEAR_INODE); + /* * Before checking if the filesystem is still busy make sure the kernel * doesn't hold any quotafiles open on that device. If the umount fails * too bad there are no quotas running anymore. Turn them on again by hand. */ quota_off(dev, -1); - if (!fs_may_umount(dev, sb->s_mounted)) + if (!fs_may_umount(sb, sb->s_root)) return -EBUSY; - sb->s_covered->i_mount = NULL; - iput(sb->s_covered); - sb->s_covered = NULL; - iput(sb->s_mounted); - sb->s_mounted = NULL; - if (sb->s_op && sb->s_op->write_super && sb->s_dirt) - sb->s_op->write_super(sb); - put_super(dev); + + /* clean up dcache .. */ + d_umount(sb->s_root); + sb->s_root = NULL; + + if (sb->s_op) { + if (sb->s_op->write_super && sb->s_dirt) + sb->s_op->write_super(sb); + if (sb->s_op->put_super) + sb->s_op->put_super(sb); + } remove_vfsmnt(dev); return 0; } +static int umount_dev(kdev_t dev) +{ + int retval; + struct inode * inode = get_empty_inode(); + + inode->i_rdev = dev; + if (MAJOR(dev) >= MAX_BLKDEV) + return -ENXIO; + + retval = do_umount(dev,0); + if (!retval) { + fsync_dev(dev); + if (dev != ROOT_DEV) { + blkdev_release(inode); + put_unnamed_dev(dev); + } + } + iput(inode); + return retval; +} + /* * Now umount can handle mount points as well as block devices. * This is important for filesystems which use unnamed block devices. @@ -639,55 +662,36 @@ static int do_umount(kdev_t dev,int unmount_root) asmlinkage int sys_umount(char * name) { - struct inode * inode; - kdev_t dev; - struct inode * dummy_inode = NULL; - int retval = -EPERM; + struct dentry * dentry; + int retval; - lock_kernel(); if (!suser()) - goto out; - retval = namei(NAM_FOLLOW_LINK, name, &inode); - if (retval) { - retval = namei(NAM_FOLLOW_TRAILSLASH, name, &inode); - if (retval) - goto out; - } - if (S_ISBLK(inode->i_mode)) { - dev = inode->i_rdev; - retval = -EACCES; - if (IS_NODEV(inode)) { - iput(inode); - goto out; - } - } else { - retval = -EINVAL; - if (!inode->i_sb || inode != inode->i_sb->s_mounted) { - iput(inode); - goto out; - } - dev = inode->i_sb->s_dev; - iput(inode); - inode = dummy_inode = get_empty_inode(); - inode->i_rdev = dev; - } - retval = -ENXIO; - if (MAJOR(dev) >= MAX_BLKDEV) { - iput(inode); - goto out; - } - retval = do_umount(dev,0); - if (!retval) { - fsync_dev(dev); - if (dev != ROOT_DEV) { - blkdev_release (inode); - put_unnamed_dev(dev); + return -EPERM; + + lock_kernel(); + dentry = namei(name); + retval = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + struct inode * inode = dentry->d_inode; + kdev_t dev = inode->i_rdev; + + retval = 0; + if (S_ISBLK(inode->i_mode)) { + if (IS_NODEV(inode)) + retval = -EACCES; + } else { + struct super_block *sb = inode->i_sb; + retval = -EINVAL; + if (sb && inode == sb->s_root->d_inode) { + dev = sb->s_dev; + retval = 0; + } } + dput(dentry); + + if (!retval) + retval = umount_dev(dev); } - iput(inode); - if (!retval) - fsync_dev(dev); -out: unlock_kernel(); return retval; } @@ -715,45 +719,39 @@ out: int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const char * type, int flags, void * data) { - struct inode * dir_i = NULL; + struct dentry * dir_d = NULL; struct super_block * sb; struct vfsmount *vfsmnt; int error; - int override = 0; - - if(dir_name) { - char c; - get_user(c, dir_name); - override = (c == '!'); - } if (!(flags & MS_RDONLY) && dev && is_read_only(dev)) return -EACCES; /*flags |= MS_RDONLY;*/ - if(override) - dir_name++; - error = namei(NAM_FOLLOW_LINK, dir_name, &dir_i); - if (error) + + dir_d = namei(dir_name); + error = PTR_ERR(dir_d); + if (IS_ERR(dir_d)) return error; - if (!override && (atomic_read(&dir_i->i_count) != 1 || dir_i->i_mount)) { - iput(dir_i); + + if (dir_d->d_covers != dir_d) { + dput(dir_d); return -EBUSY; } - if (!S_ISDIR(dir_i->i_mode)) { - iput(dir_i); + if (!S_ISDIR(dir_d->d_inode->i_mode)) { + dput(dir_d); return -ENOTDIR; } - if (!fs_may_mount(dev) && !override) { - iput(dir_i); + if (!fs_may_mount(dev)) { + dput(dir_d); return -EBUSY; } sb = read_super(dev,type,flags,data,0); if (!sb) { - iput(dir_i); + dput(dir_d); return -EINVAL; } - if (sb->s_covered) { - iput(dir_i); + if (sb->s_root->d_covers != sb->s_root) { + dput(dir_d); return -EBUSY; } vfsmnt = add_vfsmnt(dev, dev_name, dir_name); @@ -761,25 +759,8 @@ int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const cha vfsmnt->mnt_sb = sb; vfsmnt->mnt_flags = flags; } - { - struct dentry * old = dir_i->i_dentry; - struct dentry * new; - vfs_lock(); - new = d_alloc(old->d_parent, old->d_len, 1); - if(new) { - struct qstr copy = { old->d_name, old->d_len }; - d_add(new, sb->s_mounted, ©, D_DUPLICATE); - vfs_unlock(); - } else { - printk("VFS: cannot setup dentry for mount\n"); - iput(dir_i); - return -ENOMEM; - } - vfs_unlock(); - } - sb->s_covered = dir_i; - dir_i->i_mount = sb->s_mounted; - return 0; /* we don't iput(dir_i) - see umount */ + d_mount(dir_d, sb->s_root); + return 0; /* we don't dput(dir) - see umount */ } @@ -799,7 +780,7 @@ static int do_remount_sb(struct super_block *sb, int flags, char *data) /*flags |= MS_RDONLY;*/ /* If we are remounting RDONLY, make sure there are no rw files open */ if ((flags & MS_RDONLY) && !(sb->s_flags & MS_RDONLY)) - if (!fs_may_remount_ro(sb->s_dev)) + if (!fs_may_remount_ro(sb)) return -EBUSY; sb->s_flags = (flags & ~MS_RDONLY) | (sb->s_flags & MS_RDONLY); if (sb->s_op && sb->s_op->remount_fs) { @@ -817,18 +798,19 @@ static int do_remount_sb(struct super_block *sb, int flags, char *data) static int do_remount(const char *dir,int flags,char *data) { - struct inode *dir_i; + struct dentry *dentry; int retval; - retval = namei(NAM_FOLLOW_LINK, dir, &dir_i); - if (retval) - return retval; - if (dir_i != dir_i->i_sb->s_mounted) { - iput(dir_i); - return -EINVAL; + dentry = namei(dir); + retval = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + struct super_block * sb = dentry->d_inode->i_sb; + + retval = -EINVAL; + if (dentry == sb->s_root) + retval = do_remount_sb(sb, flags, data); + dput(dentry); } - retval = do_remount_sb(dir_i->i_sb, flags, data); - iput(dir_i); return retval; } @@ -879,7 +861,8 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type, unsigned long new_flags, void * data) { struct file_system_type * fstype; - struct inode * inode; + struct dentry * dentry = NULL; + struct inode * inode = NULL; struct file_operations * fops; kdev_t dev; int retval = -EPERM; @@ -911,58 +894,54 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type, goto out; t = fstype->name; fops = NULL; - if ((fstype->fs_flags & FS_REQUIRES_DEV)) { - retval = namei(NAM_FOLLOW_LINK, dev_name, &inode); - if (retval) + if (fstype->fs_flags & FS_REQUIRES_DEV) { + dentry = namei(dev_name); + retval = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto out; + + inode = dentry->d_inode; retval = -ENOTBLK; - if (!S_ISBLK(inode->i_mode)) { - iput(inode); - goto out; - } + if (!S_ISBLK(inode->i_mode)) + goto dput_and_out; + retval = -EACCES; - if (IS_NODEV(inode)) { - iput(inode); - goto out; - } + if (IS_NODEV(inode)) + goto dput_and_out; + dev = inode->i_rdev; retval = -ENXIO; - if (MAJOR(dev) >= MAX_BLKDEV) { - iput(inode); - goto out; - } + if (MAJOR(dev) >= MAX_BLKDEV) + goto dput_and_out; + fops = get_blkfops(MAJOR(dev)); retval = -ENOTBLK; - if (!fops) { - iput(inode); - goto out; - } + if (!fops) + goto dput_and_out; + if (fops->open) { struct file dummy; /* allows read-write or read-only flag */ memset(&dummy, 0, sizeof(dummy)); - dummy.f_inode = inode; + dummy.f_dentry = dentry; dummy.f_mode = (new_flags & MS_RDONLY) ? 1 : 3; retval = fops->open(inode, &dummy); - if (retval) { - iput(inode); - goto out; - } + if (retval) + goto dput_and_out; } } else { retval = -EMFILE; if (!(dev = get_unnamed_dev())) goto out; - inode = NULL; } + page = 0; if ((new_flags & MS_MGC_MSK) == MS_MGC_VAL) { flags = new_flags & ~MS_MGC_MSK; retval = copy_mount_options(data, &page); if (retval < 0) { put_unnamed_dev(dev); - iput(inode); - goto out; + goto dput_and_out; } } retval = do_mount(dev,dev_name,dir_name,t,flags,(void *) page); @@ -971,7 +950,8 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type, fops->release(inode, NULL); put_unnamed_dev(dev); } - iput(inode); +dput_and_out: + dput(dentry); out: unlock_kernel(); return retval; @@ -982,7 +962,7 @@ __initfunc(static void do_mount_root(void)) struct file_system_type * fs_type; struct super_block * sb; struct vfsmount *vfsmnt; - struct inode * inode, * d_inode = NULL; + struct inode * d_inode = NULL; struct file filp; int retval; @@ -1001,15 +981,11 @@ __initfunc(static void do_mount_root(void)) sb->s_dev = get_unnamed_dev(); sb->s_flags = root_mountflags & ~MS_RDONLY; if (nfs_root_mount(sb) >= 0) { - inode = sb->s_mounted; - atomic_add(3, &inode->i_count); - sb->s_covered = inode; sb->s_rd_only = 0; sb->s_dirt = 0; sb->s_type = fs_type; - current->fs->pwd = inode; - current->fs->root = inode; - (void)d_alloc_root(inode); + current->fs->root = dget(sb->s_root); + current->fs->pwd = dget(sb->s_root); ROOT_DEV = sb->s_dev; printk (KERN_NOTICE "VFS: Mounted root (nfs filesystem).\n"); vfsmnt = add_vfsmnt(ROOT_DEV, "/dev/root", "/"); @@ -1042,7 +1018,7 @@ __initfunc(static void do_mount_root(void)) memset(&filp, 0, sizeof(filp)); d_inode = get_empty_inode(); d_inode->i_rdev = ROOT_DEV; - filp.f_inode = d_inode; + filp.f_dentry = NULL; if ( root_mountflags & MS_RDONLY) filp.f_mode = 1; /* read only */ else @@ -1066,15 +1042,9 @@ __initfunc(static void do_mount_root(void)) continue; sb = read_super(ROOT_DEV,fs_type->name,root_mountflags,NULL,1); if (sb) { - inode = sb->s_mounted; - - /* NOTE! it is logically used 4 times, not 1 */ - atomic_add(3, &inode->i_count); - sb->s_covered = inode; sb->s_flags = root_mountflags; - current->fs->pwd = inode; - current->fs->root = inode; - (void)d_alloc_root(inode); + current->fs->root = dget(sb->s_root); + current->fs->pwd = dget(sb->s_root); printk ("VFS: Mounted root (%s filesystem)%s.\n", fs_type->name, (sb->s_flags & MS_RDONLY) ? " readonly" : ""); @@ -1106,8 +1076,7 @@ __initfunc(static int do_change_root(kdev_t new_root_dev,const char *put_old)) { kdev_t old_root_dev; struct vfsmount *vfsmnt; - struct inode *old_root,*old_pwd,*inode; - unsigned long old_fs; + struct dentry *old_root,*old_pwd,*dir_d = NULL; int error; old_root = current->fs->root; @@ -1119,24 +1088,29 @@ __initfunc(static int do_change_root(kdev_t new_root_dev,const char *put_old)) } ROOT_DEV = new_root_dev; do_mount_root(); - old_fs = get_fs(); - set_fs(get_ds()); - error = namei(NAM_FOLLOW_LINK, put_old, &inode); - if (error) inode = NULL; - set_fs(old_fs); - if (!error && (atomic_read(&inode->i_count) != 1 || inode->i_mount)) + dir_d = lookup_dentry(put_old, NULL, 1); + if (IS_ERR(dir_d)) { + error = PTR_ERR(dir_d); + } else if (!dir_d->d_inode) { + dput(dir_d); + error = -ENOENT; + } else { + error = 0; + } + if (!error && dir_d->d_covers != dir_d) { + dput(dir_d); error = -EBUSY; - if (!error && !S_ISDIR(inode->i_mode)) + } + if (!error && !S_ISDIR(dir_d->d_inode->i_mode)) { + dput(dir_d); error = -ENOTDIR; - iput(old_root); /* current->fs->root */ - iput(old_pwd); /* current->fs->pwd */ + } + dput(old_root); + dput(old_pwd); if (error) { int umount_error; - if (inode) iput(inode); printk(KERN_NOTICE "Trying to unmount old root ... "); - old_root->i_mount = old_root; - /* does this belong into do_mount_root ? */ umount_error = do_umount(old_root_dev,1); if (umount_error) printk(KERN_ERR "error %d\n",umount_error); else { @@ -1145,16 +1119,16 @@ __initfunc(static int do_change_root(kdev_t new_root_dev,const char *put_old)) } return umount_error ? error : 0; } - iput(old_root); /* sb->s_covered */ remove_vfsmnt(old_root_dev); vfsmnt = add_vfsmnt(old_root_dev,"/dev/root.old",put_old); if (!vfsmnt) printk(KERN_CRIT "Trouble: add_vfsmnt failed\n"); else { - vfsmnt->mnt_sb = old_root->i_sb; - vfsmnt->mnt_sb->s_covered = inode; + vfsmnt->mnt_sb = old_root->d_inode->i_sb; + d_mount(dir_d,vfsmnt->mnt_sb->s_root); vfsmnt->mnt_flags = vfsmnt->mnt_sb->s_flags; } - inode->i_mount = old_root; + d_umount(old_root); + d_mount(dir_d,old_root); return 0; } diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c index 8b942a5b1..3dd0931cf 100644 --- a/fs/sysv/dir.c +++ b/fs/sysv/dir.c @@ -57,6 +57,7 @@ struct inode_operations sysv_dir_inode_operations = { sysv_mknod, /* mknod */ sysv_rename, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ diff --git a/fs/sysv/file.c b/fs/sysv/file.c index da07ef7a8..1a28e4c2a 100644 --- a/fs/sysv/file.c +++ b/fs/sysv/file.c @@ -64,6 +64,7 @@ struct inode_operations sysv_file_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ generic_readpage, /* readpage */ NULL, /* writepage */ sysv_bmap, /* bmap */ @@ -194,7 +195,7 @@ long sysv_file_read(struct inode * inode, struct file * filp, filp->f_reada = 1; if (!IS_RDONLY(inode)) { inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); } return read; } @@ -255,7 +256,7 @@ static long sysv_file_write(struct inode * inode, struct file * filp, pos += c; if (pos > inode->i_size) { inode->i_size = pos; - inode->i_dirt = 1; + mark_inode_dirty(inode); } written += c; buf += c; @@ -265,6 +266,6 @@ static long sysv_file_write(struct inode * inode, struct file * filp, } inode->i_mtime = inode->i_ctime = CURRENT_TIME; filp->f_pos = pos; - inode->i_dirt = 1; + mark_inode_dirty(inode); return written; } diff --git a/fs/sysv/ialloc.c b/fs/sysv/ialloc.c index 97bc7284f..fa0b3cf95 100644 --- a/fs/sysv/ialloc.c +++ b/fs/sysv/ialloc.c @@ -62,9 +62,8 @@ void sysv_free_inode(struct inode * inode) printk("sysv_free_inode: inode has no device\n"); return; } - if (atomic_read(&inode->i_count) != 1) { - printk("sysv_free_inode: inode has count=%d\n", - atomic_read(&inode->i_count)); + if (inode->i_count != 1) { + printk("sysv_free_inode: inode has count=%d\n", inode->i_count); return; } if (inode->i_nlink) { @@ -150,12 +149,12 @@ struct inode * sysv_new_inode(const struct inode * dir) mark_buffer_dirty(sb->sv_bh1, 1); /* super-block has been modified */ if (sb->sv_bh1 != sb->sv_bh2) mark_buffer_dirty(sb->sv_bh2, 1); sb->s_dirt = 1; /* and needs time stamp */ - atomic_set(&inode->i_count, 1); + inode->i_count = 1; inode->i_nlink = 1; inode->i_dev = sb->s_dev; inode->i_uid = current->fsuid; inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid; - inode->i_dirt = 1; + mark_inode_dirty(inode); inode->i_ino = ino; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->i_op = NULL; @@ -166,7 +165,7 @@ struct inode * sysv_new_inode(const struct inode * dir) inode->i_size = 0; /* ditto */ sysv_write_inode(inode); /* ensure inode not allocated again */ /* FIXME: caller may call this too. */ - inode->i_dirt = 1; /* cleared by sysv_write_inode() */ + mark_inode_dirty(inode); /* cleared by sysv_write_inode() */ /* That's it. */ (*sb->sv_sb_total_free_inodes)--; mark_buffer_dirty(sb->sv_bh2, 1); /* super-block has been modified again */ diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index f8c6a1b38..b2d7edfbc 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -344,6 +344,7 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data, struct buffer_head *bh; const char *found; kdev_t dev = sb->s_dev; + struct inode *root_inode; if (1024 != sizeof (struct xenix_super_block)) panic("Xenix FS: bad super-block size"); @@ -483,9 +484,10 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data, /* set up enough so that it can read an inode */ sb->s_dev = dev; sb->s_op = &sysv_sops; - sb->s_mounted = iget(sb,SYSV_ROOT_INO); + root_inode = iget(sb,SYSV_ROOT_INO); + sb->s_root = d_alloc_root(root_inode, NULL); unlock_super(sb); - if (!sb->s_mounted) { + if (!sb->s_root) { printk("SysV FS: get root inode failed\n"); sysv_put_super(sb); return NULL; @@ -534,7 +536,7 @@ void sysv_put_super(struct super_block *sb) MOD_DEC_USE_COUNT; } -void sysv_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) +int sysv_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) { struct statfs tmp; @@ -547,7 +549,7 @@ void sysv_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) tmp.f_ffree = sysv_count_free_inodes(sb); /* free file nodes in fs */ tmp.f_namelen = SYSV_NAMELEN; /* Don't know what value to put in tmp.f_fsid */ /* file system id */ - copy_to_user(buf, &tmp, bufsiz); + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } @@ -667,7 +669,7 @@ repeat: } *p = tmp; inode->i_ctime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); return result; } @@ -900,13 +902,11 @@ static struct buffer_head * sysv_update_inode(struct inode * inode) printk("Bad inode number on dev %s" ": %d is out of range\n", kdevname(inode->i_dev), ino); - inode->i_dirt = 0; return 0; } block = sb->sv_firstinodezone + ((ino-1) >> sb->sv_inodes_per_block_bits); if (!(bh = sv_bread(sb,inode->i_dev,block))) { printk("unable to read i-node block\n"); - inode->i_dirt = 0; return 0; } raw_inode = (struct sysv_inode *) bh->b_data + ((ino-1) & sb->sv_inodes_per_block_1); @@ -937,7 +937,6 @@ static struct buffer_head * sysv_update_inode(struct inode * inode) else for (block = 0; block < 10+1+1+1; block++) write3byte(&raw_inode->i_a.i_addb[3*block],inode->u.sysv_i.i_data[block]); - inode->i_dirt=0; mark_buffer_dirty(bh, 1); return bh; } diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c index d1b67ab5f..b3586b58f 100644 --- a/fs/sysv/namei.c +++ b/fs/sysv/namei.c @@ -175,7 +175,7 @@ static int sysv_add_entry(struct inode * dir, if (pos > dir->i_size) { de->inode = 0; dir->i_size = pos; - dir->i_dirt = 1; + mark_inode_dirty(dir); } if (de->inode) { if (namecompare(namelen, SYSV_NAMELEN, name, de->name)) { @@ -184,7 +184,7 @@ static int sysv_add_entry(struct inode * dir, } } else { dir->i_mtime = dir->i_ctime = CURRENT_TIME; - dir->i_dirt = 1; + mark_inode_dirty(dir); for (i = 0; i < SYSV_NAMELEN ; i++) de->name[i] = (i < namelen) ? name[i] : 0; mark_buffer_dirty(bh, 1); @@ -219,11 +219,11 @@ int sysv_create(struct inode * dir,const char * name, int len, int mode, } inode->i_op = &sysv_file_inode_operations; inode->i_mode = mode; - inode->i_dirt = 1; + mark_inode_dirty(inode); error = sysv_add_entry(dir,name,len, &bh ,&de); if (error) { inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput(inode); iput(dir); return error; @@ -276,11 +276,11 @@ int sysv_mknod(struct inode * dir, const char * name, int len, int mode, int rde init_fifo(inode); if (S_ISBLK(mode) || S_ISCHR(mode)) inode->i_rdev = to_kdev_t(rdev); - inode->i_dirt = 1; + mark_inode_dirty(inode); error = sysv_add_entry(dir, name, len, &bh, &de); if (error) { inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput(inode); iput(dir); return error; @@ -325,7 +325,7 @@ int sysv_mkdir(struct inode * dir, const char * name, int len, int mode) if (!dir_block) { iput(dir); inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput(inode); return -ENOSPC; } @@ -341,7 +341,7 @@ int sysv_mkdir(struct inode * dir, const char * name, int len, int mode) inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask); if (dir->i_mode & S_ISGID) inode->i_mode |= S_ISGID; - inode->i_dirt = 1; + mark_inode_dirty(inode); error = sysv_add_entry(dir, name, len, &bh, &de); if (error) { iput(dir); @@ -352,7 +352,7 @@ int sysv_mkdir(struct inode * dir, const char * name, int len, int mode) de->inode = inode->i_ino; mark_buffer_dirty(bh, 1); dir->i_nlink++; - dir->i_dirt = 1; + mark_inode_dirty(dir); iput(dir); iput(inode); brelse(bh); @@ -454,7 +454,7 @@ int sysv_rmdir(struct inode * dir, const char * name, int len) retval = -ENOENT; goto end_rmdir; } - if (atomic_read(&inode->i_count) > 1) { + if (inode->i_count > 1) { retval = -EBUSY; goto end_rmdir; } @@ -463,10 +463,10 @@ int sysv_rmdir(struct inode * dir, const char * name, int len) de->inode = 0; mark_buffer_dirty(bh, 1); inode->i_nlink=0; - inode->i_dirt=1; + mark_inode_dirty(inode); dir->i_nlink--; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; - dir->i_dirt=1; + mark_inode_dirty(dir); retval = 0; end_rmdir: iput(dir); @@ -517,10 +517,10 @@ repeat: de->inode = 0; mark_buffer_dirty(bh, 1); dir->i_ctime = dir->i_mtime = CURRENT_TIME; - dir->i_dirt = 1; + mark_inode_dirty(dir); inode->i_nlink--; inode->i_ctime = dir->i_ctime; - inode->i_dirt = 1; + mark_inode_dirty(inode); retval = 0; end_unlink: brelse(bh); @@ -550,7 +550,7 @@ int sysv_symlink(struct inode * dir, const char * name, int len, const char * sy if (!name_block) { iput(dir); inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput(inode); return -ENOSPC; } @@ -563,11 +563,11 @@ int sysv_symlink(struct inode * dir, const char * name, int len, const char * sy mark_buffer_dirty(name_block, 1); brelse(name_block); inode->i_size = i; - inode->i_dirt = 1; + mark_inode_dirty(inode); bh = sysv_find_entry(dir,name,len,&de); if (bh) { inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput(inode); brelse(bh); iput(dir); @@ -576,7 +576,7 @@ int sysv_symlink(struct inode * dir, const char * name, int len, const char * sy i = sysv_add_entry(dir, name, len, &bh, &de); if (i) { inode->i_nlink--; - inode->i_dirt = 1; + mark_inode_dirty(inode); iput(inode); iput(dir); return i; @@ -624,7 +624,7 @@ int sysv_link(struct inode * oldinode, struct inode * dir, const char * name, in iput(dir); oldinode->i_nlink++; oldinode->i_ctime = CURRENT_TIME; - oldinode->i_dirt = 1; + mark_inode_dirty(oldinode); iput(oldinode); return 0; } @@ -635,7 +635,7 @@ static int subdir(struct inode * new_inode, struct inode * old_inode) int ino; int result; - atomic_inc(&new_inode->i_count); + new_inode->i_count++; result = 0; for (;;) { if (new_inode == old_inode) { @@ -667,8 +667,8 @@ static int subdir(struct inode * new_inode, struct inode * old_inode) * Anybody can rename anything with this: the permission checks are left to the * higher-level routines. */ -static int do_sysv_rename(struct inode * old_dir, const char * old_name, int old_len, - struct inode * new_dir, const char * new_name, int new_len) +static int do_sysv_rename(struct inode * old_dir, struct dentry * old_dentry, + struct inode * new_dir, struct dentry * new_dentry) { struct inode * old_inode, * new_inode; struct buffer_head * old_bh, * new_bh, * dir_bh; @@ -687,21 +687,21 @@ try_again: start_up: old_inode = new_inode = NULL; old_bh = new_bh = dir_bh = NULL; - old_bh = sysv_find_entry(old_dir,old_name,old_len,&old_de); + old_bh = sysv_find_entry(old_dir,old_dentry->d_name.name, + old_dentry->d_name.len,&old_de); retval = -ENOENT; if (!old_bh) goto end_rename; - old_inode = __iget(old_dir->i_sb, old_de->inode, 0); /* don't cross mnt-points */ - if (!old_inode) - goto end_rename; + old_inode = old_dentry->d_inode;/* don't cross mnt-points */ retval = -EPERM; if ((old_dir->i_mode & S_ISVTX) && current->fsuid != old_inode->i_uid && current->fsuid != old_dir->i_uid && !fsuser()) goto end_rename; - new_bh = sysv_find_entry(new_dir,new_name,new_len,&new_de); + new_inode = new_dentry->d_inode; + new_bh = sysv_find_entry(new_dir,new_dentry->d_name.name, + new_dentry->d_name.len,&new_de); if (new_bh) { - new_inode = __iget(new_dir->i_sb, new_de->inode, 0); if (!new_inode) { brelse(new_bh); new_bh = NULL; @@ -722,7 +722,7 @@ start_up: if (!empty_dir(new_inode)) goto end_rename; retval = -EBUSY; - if (atomic_read(&new_inode->i_count) > 1) + if (new_inode->i_count > 1) goto end_rename; } retval = -EPERM; @@ -748,7 +748,8 @@ start_up: goto end_rename; } if (!new_bh) { - retval = sysv_add_entry(new_dir,new_name,new_len,&new_bh,&new_de); + retval = sysv_add_entry(new_dir,new_dentry->d_name.name, + new_dentry->d_name.len,&new_bh,&new_de); if (retval) goto end_rename; } @@ -763,13 +764,13 @@ start_up: old_de->inode = 0; new_de->inode = old_inode->i_ino; old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; - old_dir->i_dirt = 1; + mark_inode_dirty(old_dir); new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME; - new_dir->i_dirt = 1; + mark_inode_dirty(new_dir); if (new_inode) { new_inode->i_nlink--; new_inode->i_ctime = CURRENT_TIME; - new_inode->i_dirt = 1; + mark_inode_dirty(new_inode); } mark_buffer_dirty(old_bh, 1); mark_buffer_dirty(new_bh, 1); @@ -777,13 +778,13 @@ start_up: PARENT_INO(dir_bh->b_data) = new_dir->i_ino; mark_buffer_dirty(dir_bh, 1); old_dir->i_nlink--; - old_dir->i_dirt = 1; + mark_inode_dirty(old_dir); if (new_inode) { new_inode->i_nlink--; - new_inode->i_dirt = 1; + mark_inode_dirty(new_inode); } else { new_dir->i_nlink++; - new_dir->i_dirt = 1; + mark_inode_dirty(new_dir); } } retval = 0; @@ -807,8 +808,8 @@ end_rename: * the same device that races occur: many renames can happen at once, as long * as they are on different partitions. */ -int sysv_rename(struct inode * old_dir, const char * old_name, int old_len, - struct inode * new_dir, const char * new_name, int new_len) +int sysv_rename(struct inode * old_dir, struct dentry * old_dentry, + struct inode * new_dir, struct dentry * new_dentry) { static struct wait_queue * wait = NULL; static int lock = 0; @@ -817,8 +818,8 @@ int sysv_rename(struct inode * old_dir, const char * old_name, int old_len, while (lock) sleep_on(&wait); lock = 1; - result = do_sysv_rename(old_dir, old_name, old_len, - new_dir, new_name, new_len); + result = do_sysv_rename(old_dir, old_dentry, + new_dir, new_dentry); lock = 0; wake_up(&wait); return result; diff --git a/fs/sysv/symlink.c b/fs/sysv/symlink.c index 4e8a5e349..d76c3fa66 100644 --- a/fs/sysv/symlink.c +++ b/fs/sysv/symlink.c @@ -21,6 +21,7 @@ #include <asm/uaccess.h> static int sysv_readlink(struct inode *, char *, int); +static struct dentry *sysv_follow_link(struct inode *, struct dentry *); /* * symlinks can't do much... @@ -37,6 +38,7 @@ struct inode_operations sysv_symlink_inode_operations = { NULL, /* mknod */ NULL, /* rename */ sysv_readlink, /* readlink */ + sysv_follow_link, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -44,6 +46,21 @@ struct inode_operations sysv_symlink_inode_operations = { NULL /* permission */ }; +static struct dentry *sysv_follow_link(struct inode * inode, struct dentry * base) +{ + struct buffer_head * bh; + + bh = sysv_file_bread(inode, 0, 0); + if (!bh) { + dput(base); + return ERR_PTR(-EIO); + } + UPDATE_ATIME(inode); + base = lookup_dentry(bh->b_data, base, 1); + brelse(bh); + return base; +} + static int sysv_readlink(struct inode * inode, char * buffer, int buflen) { struct buffer_head * bh; @@ -54,7 +71,6 @@ static int sysv_readlink(struct inode * inode, char * buffer, int buflen) if (buflen > inode->i_sb->sv_block_size_1) buflen = inode->i_sb->sv_block_size_1; bh = sysv_file_bread(inode, 0, 0); - iput(inode); if (!bh) return 0; bh_data = bh->b_data; diff --git a/fs/sysv/truncate.c b/fs/sysv/truncate.c index 0eeb10d30..433c39cae 100644 --- a/fs/sysv/truncate.c +++ b/fs/sysv/truncate.c @@ -64,7 +64,7 @@ repeat: continue; } *p = 0; - inode->i_dirt = 1; + mark_inode_dirty(inode); brelse(bh); sysv_free_block(sb,block); } @@ -257,12 +257,14 @@ done: static int trunc_all(struct inode * inode) { struct super_block * sb; + char * res; sb = inode->i_sb; + res = (char *)test_bit(I_DIRTY,&inode->i_state); return trunc_direct(inode) - | trunc_indirect(inode,sb->sv_ind0_size,&inode->u.sysv_i.i_data[10],0,&inode->i_dirt) - | trunc_dindirect(inode,sb->sv_ind1_size,&inode->u.sysv_i.i_data[11],0,&inode->i_dirt) - | trunc_tindirect(inode,sb->sv_ind2_size,&inode->u.sysv_i.i_data[12],0,&inode->i_dirt); + | trunc_indirect(inode,sb->sv_ind0_size,&inode->u.sysv_i.i_data[10],0,res) + | trunc_dindirect(inode,sb->sv_ind1_size,&inode->u.sysv_i.i_data[11],0,res) + | trunc_tindirect(inode,sb->sv_ind2_size,&inode->u.sysv_i.i_data[12],0,res); } @@ -285,5 +287,5 @@ void sysv_truncate(struct inode * inode) schedule(); } inode->i_mtime = inode->i_ctime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); } diff --git a/fs/ufs/ufs_file.c b/fs/ufs/ufs_file.c index 74ae1a470..ef7858c8f 100644 --- a/fs/ufs/ufs_file.c +++ b/fs/ufs/ufs_file.c @@ -6,7 +6,7 @@ * Laboratory for Computer Science Research Computing Facility * Rutgers, The State University of New Jersey * - * $Id: ufs_file.c,v 1.8 1997/06/05 01:29:09 davem Exp $ + * $Id: ufs_file.c,v 1.2 1997/06/17 13:27:28 ralf Exp $ * */ @@ -41,6 +41,7 @@ struct inode_operations ufs_file_inode_operations = { NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ generic_readpage, /* readpage */ NULL, /* writepage */ ufs_bmap, /* bmap */ diff --git a/fs/ufs/ufs_inode.c b/fs/ufs/ufs_inode.c index f0fdd5d5f..d8c8c6896 100644 --- a/fs/ufs/ufs_inode.c +++ b/fs/ufs/ufs_inode.c @@ -6,7 +6,7 @@ * Laboratory for Computer Science Research Computing Facility * Rutgers, The State University of New Jersey * - * $Id: ufs_inode.c,v 1.8 1997/06/04 08:28:28 davem Exp $ + * $Id: ufs_inode.c,v 1.2 1997/06/17 13:27:29 ralf Exp $ * */ @@ -19,8 +19,7 @@ void ufs_print_inode(struct inode * inode) printk("ino %lu mode 0%6.6o lk %d uid %d gid %d" " sz %lu blks %lu cnt %u\n", inode->i_ino, inode->i_mode, inode->i_nlink, inode->i_uid, - inode->i_gid, inode->i_size, inode->i_blocks, - atomic_read(&inode->i_count)); + inode->i_gid, inode->i_size, inode->i_blocks, inode->i_count); printk(" db <0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x" " 0x%x 0x%x 0x%x 0x%x>\n", inode->u.ufs_i.i_data[0], inode->u.ufs_i.i_data[1], diff --git a/fs/ufs/ufs_namei.c b/fs/ufs/ufs_namei.c index 03ea2dde1..64ea3a866 100644 --- a/fs/ufs/ufs_namei.c +++ b/fs/ufs/ufs_namei.c @@ -6,7 +6,7 @@ * Laboratory for Computer Science Research Computing Facility * Rutgers, The State University of New Jersey * - * $Id: ufs_namei.c,v 1.7 1996/06/01 14:56:49 ecd Exp $ + * $Id: ufs_namei.c,v 1.1.1.1 1997/06/01 03:16:19 ralf Exp $ * */ @@ -35,12 +35,14 @@ static int ufs_match (int len, const char * const name, struct ufs_direct * d) } /* XXX - this is a mess, especially for endianity */ -int ufs_lookup (struct inode * dir, const char * name, int len, +int ufs_lookup (struct inode * dir, struct qstr *qname, struct inode ** result) { unsigned long int lfragno, fragno; struct buffer_head * bh; struct ufs_direct * d; + const char *name = qname->name; + int len = qname->len; if (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) printk("Passed name: %s\nPassed length: %d\n", name, len); diff --git a/fs/ufs/ufs_super.c b/fs/ufs/ufs_super.c index 342722237..f08292a23 100644 --- a/fs/ufs/ufs_super.c +++ b/fs/ufs/ufs_super.c @@ -8,7 +8,7 @@ * * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) * - * $Id: ufs_super.c,v 1.24 1997/06/04 08:28:29 davem Exp $ + * $Id: ufs_super.c,v 1.2 1997/06/17 13:27:29 ralf Exp $ * */ @@ -254,7 +254,7 @@ ufs_read_super(struct super_block * sb, void * data, int silent) sb->u.ufs_sb.s_lmask = ~((ufs_swab32(usb->fs_fmask) - ufs_swab32(usb->fs_bmask)) >> ufs_swab32(usb->fs_fshift)); sb->u.ufs_sb.s_fsfrag = ufs_swab32(usb->fs_frag); /* XXX - rename this later */ - sb->s_mounted = iget(sb, UFS_ROOTINO); + sb->s_root = d_alloc_root(iget(sb, UFS_ROOTINO), NULL); #ifdef DEBUG_UFS_SUPER printk("ufs_read_super: inopb %u\n", sb->u.ufs_sb.s_inopb); diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c index 161bc791e..b3263b42d 100644 --- a/fs/vfat/namei.c +++ b/fs/vfat/namei.c @@ -877,7 +877,7 @@ static int vfat_find(struct inode *dir,const char *name,int len, PRINTK(("vfat_find: create file 4\n")); dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME; - dir->i_dirt = 1; + mark_inode_dirty(dir); PRINTK(("vfat_find: create file 5\n")); @@ -1010,7 +1010,7 @@ static int vfat_create_entry(struct inode *dir,const char *name,int len, return -EIO; (*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime = CURRENT_TIME; - (*result)->i_dirt = 1; + mark_inode_dirty(*result); (*result)->i_version = ++event; dir->i_version = event; @@ -1046,7 +1046,7 @@ static int vfat_create_a_dotdir(struct inode *dir,struct inode *parent, * XXX all times should be set by caller upon successful completion. */ dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; - dir->i_dirt = 1; + mark_inode_dirty(dir); memcpy(de->name,name,MSDOS_NAME); memset(de->unused, 0, sizeof(de->unused)); de->lcase = 0; @@ -1062,7 +1062,7 @@ static int vfat_create_a_dotdir(struct inode *dir,struct inode *parent, vfat_read_inode(dot); if (!dot) return -EIO; dot->i_mtime = dot->i_atime = CURRENT_TIME; - dot->i_dirt = 1; + mark_inode_dirty(dot); if (isdot) { dot->i_size = dir->i_size; MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start; @@ -1125,7 +1125,7 @@ static int vfat_empty(struct inode *dir) struct buffer_head *bh; struct msdos_dir_entry *de; - if (atomic_read(&dir->i_count) > 1) + if (dir->i_count > 1) return -EBUSY; if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */ pos = 0; @@ -1173,7 +1173,8 @@ static int vfat_rmdir_free_ino(struct inode *dir,struct buffer_head *bh, inode->i_mtime = dir->i_mtime = CURRENT_TIME; inode->i_atime = dir->i_atime = CURRENT_TIME; dir->i_nlink--; - inode->i_dirt = dir->i_dirt = 1; + mark_inode_dirty(dir); + mark_inode_dirty(inode); de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, bh, 1); iput(inode); @@ -1196,7 +1197,8 @@ static int vfat_unlink_free_ino(struct inode *dir,struct buffer_head *bh, inode->i_atime = dir->i_atime = CURRENT_TIME; dir->i_version = ++event; MSDOS_I(inode)->i_busy = 1; - inode->i_dirt = dir->i_dirt = 1; + mark_inode_dirty(dir); + mark_inode_dirty(inode); de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, bh, 1); @@ -1478,7 +1480,7 @@ int vfat_rename(struct inode *old_dir,const char *old_name,int old_len, MSDOS_I(new_inode)->i_oldlink = old_inode; fat_cache_inval_inode(old_inode); PRINTK(("vfat_rename 15: old_slots=%d\n",old_slots)); - old_inode->i_dirt = 1; + mark_inode_dirty(old_inode); old_dir->i_version = ++event; /* remove the old entry */ @@ -1511,7 +1513,7 @@ int vfat_rename(struct inode *old_dir,const char *old_name,int old_len, } dotdot_de->start = MSDOS_I(dotdot_inode)->i_start = MSDOS_I(new_dir)->i_start; - dotdot_inode->i_dirt = 1; + mark_inode_dirty(dotdot_inode); fat_mark_buffer_dirty(sb, dotdot_bh, 1); old_dir->i_nlink--; new_dir->i_nlink++; diff --git a/include/asm-alpha/ioctls.h b/include/asm-alpha/ioctls.h index bb4f71de8..0337b92b3 100644 --- a/include/asm-alpha/ioctls.h +++ b/include/asm-alpha/ioctls.h @@ -83,6 +83,8 @@ #define TIOCGETD 0x5424 #define TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */ #define TIOCTTYGSTRUCT 0x5426 /* For debugging only */ +#define TIOCSBRK 0x5427 /* BSD compatibility */ +#define TIOCCBRK 0x5428 /* BSD compatibility */ #define TIOCSERCONFIG 0x5453 #define TIOCSERGWILD 0x5454 diff --git a/include/asm-alpha/pgtable.h b/include/asm-alpha/pgtable.h index 2f40ad673..7d80c32f8 100644 --- a/include/asm-alpha/pgtable.h +++ b/include/asm-alpha/pgtable.h @@ -517,4 +517,7 @@ extern inline pte_t mk_swap_pte(unsigned long type, unsigned long offset) #define SWP_OFFSET(entry) ((entry) >> 40) #define SWP_ENTRY(type,offset) pte_val(mk_swap_pte((type),(offset))) +#define module_map vmalloc +#define module_unmap vfree + #endif /* _ALPHA_PGTABLE_H */ diff --git a/include/asm-i386/ioctls.h b/include/asm-i386/ioctls.h index 60e0806e5..ae04e1a55 100644 --- a/include/asm-i386/ioctls.h +++ b/include/asm-i386/ioctls.h @@ -44,6 +44,9 @@ #define TIOCGETD 0x5424 #define TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */ #define TIOCTTYGSTRUCT 0x5426 /* For debugging only */ +#define TIOCSBRK 0x5427 /* BSD compatibility */ +#define TIOCCBRK 0x5428 /* BSD compatibility */ + #define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ #define FIOCLEX 0x5451 #define FIOASYNC 0x5452 diff --git a/include/asm-i386/pgtable.h b/include/asm-i386/pgtable.h index 7d973530c..347611f1b 100644 --- a/include/asm-i386/pgtable.h +++ b/include/asm-i386/pgtable.h @@ -491,4 +491,7 @@ extern inline void update_mmu_cache(struct vm_area_struct * vma, #define SWP_OFFSET(entry) ((entry) >> 8) #define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << 8)) +#define module_map vmalloc +#define module_unmap vfree + #endif /* _I386_PAGE_H */ diff --git a/include/asm-i386/unistd.h b/include/asm-i386/unistd.h index 069c7a1a8..66efafee9 100644 --- a/include/asm-i386/unistd.h +++ b/include/asm-i386/unistd.h @@ -175,6 +175,8 @@ #define __NR_query_module 167 #define __NR_poll 168 #define __NR_nfsservctl 169 +#define __NR_setresgid 170 +#define __NR_getresgid 171 /* user-visible error numbers are in the range -1 - -122: see <asm-i386/errno.h> */ diff --git a/include/asm-m68k/hardirq.h b/include/asm-m68k/hardirq.h index 512e0b054..ab3ca802e 100644 --- a/include/asm-m68k/hardirq.h +++ b/include/asm-m68k/hardirq.h @@ -12,4 +12,6 @@ extern unsigned int local_irq_count[NR_CPUS]; #define hardirq_enter(cpu) (local_irq_count[cpu]++) #define hardirq_exit(cpu) (local_irq_count[cpu]--) +#define synchronize_irq() do { } while (0) + #endif diff --git a/include/asm-m68k/ioctls.h b/include/asm-m68k/ioctls.h index 77ad866b8..b4a95af61 100644 --- a/include/asm-m68k/ioctls.h +++ b/include/asm-m68k/ioctls.h @@ -44,6 +44,9 @@ #define TIOCGETD 0x5424 #define TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */ #define TIOCTTYGSTRUCT 0x5426 /* For debugging only */ +#define TIOCSBRK 0x5427 /* BSD compatibility */ +#define TIOCCBRK 0x5428 /* BSD compatibility */ + #define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ #define FIOCLEX 0x5451 #define FIOASYNC 0x5452 diff --git a/include/asm-m68k/pgtable.h b/include/asm-m68k/pgtable.h index 9463700a3..589dfe956 100644 --- a/include/asm-m68k/pgtable.h +++ b/include/asm-m68k/pgtable.h @@ -95,38 +95,23 @@ extern void cache_push_v (unsigned long vaddr, int len); extern inline void flush_cache_mm(struct mm_struct *mm) { -#if FLUSH_VIRTUAL_CACHE_040 - if (mm == current->mm) __flush_cache_all(); -#else - if (mm == current->mm) __flush_cache_030(); -#endif + if (mm == current->mm) + __flush_cache_030(); } extern inline void flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end) { - if (mm == current->mm){ -#if FLUSH_VIRTUAL_CACHE_040 - if (CPU_IS_040_OR_060) - cache_push_v(start, end-start); - else -#endif + if (mm == current->mm) __flush_cache_030(); - } } extern inline void flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr) { - if (vma->vm_mm == current->mm){ -#if FLUSH_VIRTUAL_CACHE_040 - if (CPU_IS_040_OR_060) - cache_push_v(vmaddr, PAGE_SIZE); - else -#endif + if (vma->vm_mm == current->mm) __flush_cache_030(); - } } /* Push the page at kernel virtual address and clear the icache */ @@ -783,4 +768,7 @@ extern inline void update_mmu_cache(struct vm_area_struct * vma, #endif /* __ASSEMBLY__ */ +#define module_map vmalloc +#define module_unmap vfree + #endif /* _M68K_PGTABLE_H */ diff --git a/include/asm-mips/ioctls.h b/include/asm-mips/ioctls.h index 9ec2c0215..5648eae7f 100644 --- a/include/asm-mips/ioctls.h +++ b/include/asm-mips/ioctls.h @@ -96,6 +96,8 @@ #define TCSBRKP 0x5486 /* Needed for POSIX tcsendbreak() */ #define TIOCTTYGSTRUCT 0x5487 /* For debugging only */ +#define TIOCSBRK 0x5427 /* BSD compatibility */ +#define TIOCCBRK 0x5428 /* BSD compatibility */ #define TIOCSERCONFIG 0x5488 #define TIOCSERGWILD 0x5489 diff --git a/include/asm-mips/namei.h b/include/asm-mips/namei.h index 0ff96ac00..b9c0aea15 100644 --- a/include/asm-mips/namei.h +++ b/include/asm-mips/namei.h @@ -13,6 +13,8 @@ /* Only one at this time. */ #define IRIX32_EMUL "usr/gnemul/irix/" +#if 0 /* XXX FIXME */ + extern int __namei(int, const char *, struct inode *, char *, struct inode **, struct inode **, struct qstr *, struct dentry **, int *); @@ -44,6 +46,8 @@ __prefix_namei(int retrieve_mode, const char * name, struct inode * base, return 0; } +#endif /* XXX FIXME */ + #else /* !defined(CONFIG_BINFMT_IRIX) */ #define __prefix_namei(retrieve_mode, name, base, buf, res_dir, res_inode, \ diff --git a/include/asm-mips/pgtable.h b/include/asm-mips/pgtable.h index ebeb6e67c..4f3876cdb 100644 --- a/include/asm-mips/pgtable.h +++ b/include/asm-mips/pgtable.h @@ -491,6 +491,9 @@ extern void (*update_mmu_cache)(struct vm_area_struct *vma, #define SWP_OFFSET(entry) ((entry) >> 15) #define SWP_ENTRY(type,offset) (((type) << 8) | ((offset) << 15)) +#define module_map vmalloc +#define module_unmap vfree + /* TLB operations. */ extern inline void tlb_probe(void) { diff --git a/include/asm-mips/system.h b/include/asm-mips/system.h index daf477459..2c752a0c0 100644 --- a/include/asm-mips/system.h +++ b/include/asm-mips/system.h @@ -108,13 +108,13 @@ __restore_flags(int flags) #define save_and_cli(x) __save_and_cli(x) #define restore_flags(x) __restore_flags(x) -#define sync_mem() \ -__asm__ __volatile__( \ - ".set\tnoreorder\n\t" \ - "sync\n\t" \ - ".set\treorder" \ - : /* no output */ \ - : /* no input */ \ +#define mb() \ +__asm__ __volatile__( \ + "# prevent instructions being moved around\n\t" \ + ".set\tnoreorder\n\t" \ + ".set\treorder" \ + : /* no output */ \ + : /* no input */ \ : "memory") #if !defined (__LANGUAGE_ASSEMBLY__) diff --git a/include/asm-mips/unistd.h b/include/asm-mips/unistd.h index 4cf30a110..4517106ca 100644 --- a/include/asm-mips/unistd.h +++ b/include/asm-mips/unistd.h @@ -1177,11 +1177,13 @@ #define __NR_query_module (__NR_Linux + 187) #define __NR_poll (__NR_Linux + 188) #define __NR_nfsservctl (__NR_Linux + 189) +#define __NR_setresgid (__NR_Linux + 190) +#define __NR_getresgid (__NR_Linux + 191) /* * Offset of the last Linux flavoured syscall */ -#define __NR_Linux_syscalls 189 +#define __NR_Linux_syscalls 191 #ifndef __LANGUAGE_ASSEMBLY__ diff --git a/include/asm-ppc/ioctls.h b/include/asm-ppc/ioctls.h index 52a3d45bd..f56e53db7 100644 --- a/include/asm-ppc/ioctls.h +++ b/include/asm-ppc/ioctls.h @@ -83,6 +83,8 @@ #define TIOCGETD 0x5424 #define TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */ #define TIOCTTYGSTRUCT 0x5426 /* For debugging only */ +#define TIOCSBRK 0x5427 /* BSD compatibility */ +#define TIOCCBRK 0x5428 /* BSD compatibility */ #define TIOCSERCONFIG 0x5453 #define TIOCSERGWILD 0x5454 diff --git a/include/asm-ppc/pgtable.h b/include/asm-ppc/pgtable.h index 243a0b115..e9c400345 100644 --- a/include/asm-ppc/pgtable.h +++ b/include/asm-ppc/pgtable.h @@ -361,4 +361,7 @@ extern inline void update_mmu_cache(struct vm_area_struct * vma, #define SWP_OFFSET(entry) ((entry) >> 8) #define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << 8)) +#define module_map vmalloc +#define module_unmap vfree + #endif /* _PPC_PAGE_H */ diff --git a/include/asm-sparc/asi.h b/include/asm-sparc/asi.h index f81ab33b9..59fcd4337 100644 --- a/include/asm-sparc/asi.h +++ b/include/asm-sparc/asi.h @@ -1,4 +1,4 @@ -/* $Id: asi.h,v 1.16 1996/04/25 06:12:43 davem Exp $ */ +/* $Id: asi.h,v 1.17 1997/06/24 15:48:10 jj Exp $ */ #ifndef _SPARC_ASI_H #define _SPARC_ASI_H @@ -69,7 +69,7 @@ /* Block-copy operations are available only on certain V8 cpus. */ #define ASI_M_BCOPY 0x17 /* Block copy */ -/* These affect only the ICACHE and are Ross HyperSparc specific. */ +/* These affect only the ICACHE and are Ross HyperSparc and TurboSparc specific. */ #define ASI_M_IFLUSH_PAGE 0x18 /* Flush I Cache Line (page); wo, ss */ #define ASI_M_IFLUSH_SEG 0x19 /* Flush I Cache Line (seg); wo, ss */ #define ASI_M_IFLUSH_REGION 0x1A /* Flush I Cache Line (region); wo, ss */ @@ -97,7 +97,7 @@ /* This is ROSS HyperSparc only. */ #define ASI_M_FLUSH_IWHOLE 0x31 /* Flush entire ICACHE; wo, ss */ -/* Tsunami/Viking i/d cache flash clear. */ +/* Tsunami/Viking/TurboSparc i/d cache flash clear. */ #define ASI_M_IC_FLCLEAR 0x36 #define ASI_M_DC_FLCLEAR 0x37 diff --git a/include/asm-sparc/ioctls.h b/include/asm-sparc/ioctls.h index 80eff02ea..ccc5e7fce 100644 --- a/include/asm-sparc/ioctls.h +++ b/include/asm-sparc/ioctls.h @@ -63,8 +63,8 @@ /* 119 is the non-posix getpgrp tty ioctl */ #define __TIOCCDTR _IO('t', 120) /* SunOS Specific */ #define __TIOCSDTR _IO('t', 121) /* SunOS Specific */ -#define __TIOCCBRK _IO('t', 122) /* SunOS Specific */ -#define __TIOCSBRK _IO('t', 123) /* SunOS Specific */ +#define TIOCCBRK _IO('t', 122) +#define TIOCSBRK _IO('t', 123) #define __TIOCLGET _IOW('t', 124, int) /* SunOS Specific */ #define __TIOCLSET _IOW('t', 125, int) /* SunOS Specific */ #define __TIOCLBIC _IOW('t', 126, int) /* SunOS Specific */ diff --git a/include/asm-sparc/mbus.h b/include/asm-sparc/mbus.h index e5e5a18c8..5f2749015 100644 --- a/include/asm-sparc/mbus.h +++ b/include/asm-sparc/mbus.h @@ -1,4 +1,4 @@ -/* $Id: mbus.h,v 1.8 1996/08/29 09:48:21 davem Exp $ +/* $Id: mbus.h,v 1.9 1997/06/24 15:48:12 jj Exp $ * mbus.h: Various defines for MBUS modules. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -25,7 +25,8 @@ enum mbus_module { Viking_30 = 10, Viking_35 = 11, Viking_new = 12, - SRMMU_INVAL_MOD = 13, + TurboSparc = 13, + SRMMU_INVAL_MOD = 14, }; extern enum mbus_module srmmu_modtype; @@ -71,6 +72,7 @@ extern unsigned int hwbug_bitmask; /* Fujitsu */ #define FMI_AURORA 0x4 /* MB8690x, a Swift module... */ +#define FMI_TURBO 0x5 /* MB86907, a TurboSparc module... */ /* For multiprocessor support we need to be able to obtain the CPU id and * the MBUS Module id. diff --git a/include/asm-sparc/namei.h b/include/asm-sparc/namei.h index e74a76bce..8ce53ae35 100644 --- a/include/asm-sparc/namei.h +++ b/include/asm-sparc/namei.h @@ -1,4 +1,4 @@ -/* $Id: namei.h,v 1.5 1997/06/07 08:32:54 ecd Exp $ +/* $Id: namei.h,v 1.6 1997/07/17 02:24:25 davem Exp $ * linux/include/asm-sparc/namei.h * * Routines to handle famous /usr/gnemul/s*. @@ -11,6 +11,8 @@ #define SPARC_BSD_EMUL "usr/gnemul/sunos/" #define SPARC_SOL_EMUL "usr/gnemul/solaris/" +#if 0 /* XXX FIXME */ + extern int __namei(int, const char *, struct inode *, char *, struct inode **, struct inode **, struct qstr *, struct dentry **, int *); @@ -44,4 +46,6 @@ __prefix_namei(int retrieve_mode, const char * name, struct inode * base, return 0; } +#endif /* XXX FIXME */ + #endif /* __SPARC_NAMEI_H */ diff --git a/include/asm-sparc/oplib.h b/include/asm-sparc/oplib.h index 40c6de10b..bb404745f 100644 --- a/include/asm-sparc/oplib.h +++ b/include/asm-sparc/oplib.h @@ -1,4 +1,4 @@ -/* $Id: oplib.h,v 1.15 1997/03/18 18:00:18 jj Exp $ +/* $Id: oplib.h,v 1.16 1997/06/27 14:55:04 jj Exp $ * oplib.h: Describes the interface and available routines in the * Linux Prom library. * @@ -272,12 +272,12 @@ extern int prom_searchsiblings(int node_start, char *name); /* Return the first property type, as a string, for the given node. * Returns a null string on error. */ -extern char *prom_firstprop(int node); +extern char *prom_firstprop(int node, char *buffer); /* Returns the next property after the passed property for the given * node. Returns null string on failure. */ -extern char *prom_nextprop(int node, char *prev_property); +extern char *prom_nextprop(int node, char *prev_property, char *buffer); /* Returns 1 if the specified node has given property. */ extern int prom_node_has_property(int node, char *property); diff --git a/include/asm-sparc/pgtable.h b/include/asm-sparc/pgtable.h index 8b84e1dce..de8ce5687 100644 --- a/include/asm-sparc/pgtable.h +++ b/include/asm-sparc/pgtable.h @@ -1,4 +1,4 @@ -/* $Id: pgtable.h,v 1.60 1997/04/14 17:05:16 jj Exp $ */ +/* $Id: pgtable.h,v 1.62 1997/06/27 14:55:00 jj Exp $ */ #ifndef _SPARC_PGTABLE_H #define _SPARC_PGTABLE_H @@ -368,7 +368,7 @@ extern __inline__ void add_to_ctx_list(struct ctx_list *head, struct ctx_list *e #define add_to_free_ctxlist(entry) add_to_ctx_list(&ctx_free, entry) #define add_to_used_ctxlist(entry) add_to_ctx_list(&ctx_used, entry) -extern __inline__ unsigned int +extern __inline__ unsigned long __get_phys (unsigned long addr) { switch (sparc_cpu_model){ @@ -394,4 +394,7 @@ __get_iospace (unsigned long addr) } } +#define module_map vmalloc +#define module_unmap vfree + #endif /* !(_SPARC_PGTABLE_H) */ diff --git a/include/asm-sparc/turbosparc.h b/include/asm-sparc/turbosparc.h new file mode 100644 index 000000000..b3cdc7d78 --- /dev/null +++ b/include/asm-sparc/turbosparc.h @@ -0,0 +1,114 @@ +/* $Id: turbosparc.h,v 1.1 1997/07/18 06:29:12 ralf Exp $ + * turbosparc.h: Defines specific to the TurboSparc module. + * This is SRMMU stuff. + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ +#ifndef _SPARC_TURBOSPARC_H +#define _SPARC_TURBOSPARC_H + +#include <asm/asi.h> +#include <asm/pgtsrmmu.h> + +/* Bits in the SRMMU control register for TurboSparc modules. + * + * ------------------------------------------------------------------- + * |impl-vers| RSV| PMC |PE|PC| RSV |BM| RFR |IC|DC|PSO|RSV|ICS|NF|ME| + * ------------------------------------------------------------------- + * 31 24 23-21 20-19 18 17 16-15 14 13-10 9 8 7 6-3 2 1 0 + * + * BM: Boot Mode -- 0 = not in boot mode, 1 = in boot mode + * + * This indicates whether the TurboSparc is in boot-mode or not. + * + * IC: Instruction Cache -- 0 = off, 1 = on + * DC: Data Cache -- 0 = off, 1 = 0n + * + * These bits enable the on-cpu TurboSparc split I/D caches. + * + * ICS: ICache Snooping -- 0 = disable, 1 = enable snooping of icache + * NF: No Fault -- 0 = faults generate traps, 1 = faults don't trap + * ME: MMU enable -- 0 = mmu not translating, 1 = mmu translating + * + */ + +#define TURBOSPARC_MMUENABLE 0x00000001 +#define TURBOSPARC_NOFAULT 0x00000002 +#define TURBOSPARC_ICSNOOP 0x00000004 +#define TURBOSPARC_PSO 0x00000080 +#define TURBOSPARC_DCENABLE 0x00000100 /* Enable data cache */ +#define TURBOSPARC_ICENABLE 0x00000200 /* Enable instruction cache */ +#define TURBOSPARC_BMODE 0x00004000 +#define TURBOSPARC_PARITYODD 0x00020000 /* Parity odd, if enabled */ +#define TURBOSPARC_PCENABLE 0x00040000 /* Enable parity checking */ + +/* Bits in the CPU configuration register for TurboSparc modules. + * + * ------------------------------------------------------- + * |IOClk|SNP|AXClk| RAH | WS | RSV |SBC|WT|uS2|SE|SCC| + * ------------------------------------------------------- + * 31 30 29-28 27-26 25-23 22-8 7-6 5 4 3 2-0 + * + */ + +#define TURBOSPARC_SCENABLE 0x00000008 /* Secondary cache enable */ +#define TURBOSPARC_uS2 0x00000010 /* Swift compatibility mode */ +#define TURBOSPARC_WTENABLE 0x00000020 /* Write thru for dcache */ +#define TURBOSPARC_SNENABLE 0x40000000 /* DVMA snoop enable */ + +#ifndef __ASSEMBLY__ + +/* Bits [13:5] select one of 512 instruction cache tags */ +extern __inline__ void turbosparc_inv_insn_tag(unsigned long addr) +{ + __asm__ __volatile__("sta %%g0, [%0] %1\n\t" : : + "r" (addr), "i" (ASI_M_TXTC_TAG)); +} + +/* Bits [13:5] select one of 512 data cache tags */ +extern __inline__ void turbosparc_inv_data_tag(unsigned long addr) +{ + __asm__ __volatile__("sta %%g0, [%0] %1\n\t" : : + "r" (addr), "i" (ASI_M_DATAC_TAG)); +} + +extern __inline__ void turbosparc_flush_icache(void) +{ + __asm__ __volatile__("sta %%g0, [%%g0] %0\n\t" : : + "i" (ASI_M_IC_FLCLEAR)); +} + +extern __inline__ void turbosparc_flush_dcache(void) +{ + unsigned long addr; + + for(addr = 0; addr < 0x4000; addr += 0x20) + turbosparc_inv_data_tag(addr); +} + +extern __inline__ void turbosparc_idflash_clear(void) +{ + turbosparc_flush_icache(); turbosparc_flush_dcache(); +} + +extern __inline__ void turbosparc_set_ccreg(unsigned long regval) +{ + __asm__ __volatile__("sta %0, [%1] %2\n\t" : : + "r" (regval), "r" (0x600), + "i" (ASI_M_MMUREGS)); +} + +extern __inline__ unsigned long turbosparc_get_ccreg(void) +{ + unsigned long regval; + + __asm__ __volatile__("lda [%1] %2, %0\n\t" : + "=r" (regval) : + "r" (0x600), + "i" (ASI_M_MMUREGS)); + return regval; +} + +#endif /* !__ASSEMBLY__ */ + +#endif /* !(_SPARC_TURBOSPARC_H) */ diff --git a/include/asm-sparc64/asm_offsets.h b/include/asm-sparc64/asm_offsets.h index 70e6c37a4..18cf7c541 100644 --- a/include/asm-sparc64/asm_offsets.h +++ b/include/asm-sparc64/asm_offsets.h @@ -104,14 +104,8 @@ #define ASIZ_task_it_virt_incr 0x00000008 #define AOFF_task_real_timer 0x000001f8 #define ASIZ_task_real_timer 0x00000028 -#define AOFF_task_utime 0x00000220 -#define ASIZ_task_utime 0x00000008 -#define AOFF_task_stime 0x00000228 -#define ASIZ_task_stime 0x00000008 -#define AOFF_task_cutime 0x00000230 -#define ASIZ_task_cutime 0x00000008 -#define AOFF_task_cstime 0x00000238 -#define ASIZ_task_cstime 0x00000008 +#define AOFF_task_times 0x00000220 +#define ASIZ_task_times 0x00000020 #define AOFF_task_start_time 0x00000240 #define ASIZ_task_start_time 0x00000008 #define AOFF_task_min_flt 0x00000248 @@ -151,24 +145,24 @@ #define AOFF_task_ldt 0x00000370 #define ASIZ_task_ldt 0x00000008 #define AOFF_task_tss 0x00000380 -#define ASIZ_task_tss 0x00000600 -#define AOFF_task_fs 0x00000980 +#define ASIZ_task_tss 0x000004c0 +#define AOFF_task_fs 0x00000840 #define ASIZ_task_fs 0x00000008 -#define AOFF_task_files 0x00000988 +#define AOFF_task_files 0x00000848 #define ASIZ_task_files 0x00000008 -#define AOFF_task_mm 0x00000990 +#define AOFF_task_mm 0x00000850 #define ASIZ_task_mm 0x00000008 -#define AOFF_task_sig 0x00000998 +#define AOFF_task_sig 0x00000858 #define ASIZ_task_sig 0x00000008 -#define AOFF_task_has_cpu 0x000009a0 +#define AOFF_task_has_cpu 0x00000860 #define ASIZ_task_has_cpu 0x00000004 -#define AOFF_task_processor 0x000009a4 +#define AOFF_task_processor 0x00000864 #define ASIZ_task_processor 0x00000004 -#define AOFF_task_last_processor 0x000009a8 +#define AOFF_task_last_processor 0x00000868 #define ASIZ_task_last_processor 0x00000004 -#define AOFF_task_lock_depth 0x000009ac +#define AOFF_task_lock_depth 0x0000086c #define ASIZ_task_lock_depth 0x00000004 -#define AOFF_task_sigmask_lock 0x000009b0 +#define AOFF_task_sigmask_lock 0x00000870 #define ASIZ_task_sigmask_lock 0x00000000 #define AOFF_mm_mmap 0x00000000 #define ASIZ_mm_mmap 0x00000008 @@ -216,41 +210,37 @@ #define ASIZ_mm_def_flags 0x00000008 #define AOFF_mm_cpu_vm_mask 0x000000b8 #define ASIZ_mm_cpu_vm_mask 0x00000008 -#define AOFF_thread_float_regs 0x00000000 -#define ASIZ_thread_float_regs 0x00000100 -#define AOFF_thread_fsr 0x00000100 -#define ASIZ_thread_fsr 0x00000008 -#define AOFF_thread_ksp 0x00000108 +#define AOFF_thread_ksp 0x00000000 #define ASIZ_thread_ksp 0x00000008 -#define AOFF_thread_kpc 0x00000110 +#define AOFF_thread_kpc 0x00000008 #define ASIZ_thread_kpc 0x00000008 -#define AOFF_thread_wstate 0x00000118 +#define AOFF_thread_wstate 0x00000010 #define ASIZ_thread_wstate 0x00000008 -#define AOFF_thread_cwp 0x00000120 -#define ASIZ_thread_cwp 0x00000008 -#define AOFF_thread_ctx 0x00000128 -#define ASIZ_thread_ctx 0x00000008 -#define AOFF_thread_reg_window 0x00000130 +#define AOFF_thread_cwp 0x00000018 +#define ASIZ_thread_cwp 0x00000004 +#define AOFF_thread_ctx 0x0000001c +#define ASIZ_thread_ctx 0x00000004 +#define AOFF_thread_flags 0x00000020 +#define ASIZ_thread_flags 0x00000004 +#define AOFF_thread_new_signal 0x00000024 +#define ASIZ_thread_new_signal 0x00000004 +#define AOFF_thread_current_ds 0x00000028 +#define ASIZ_thread_current_ds 0x00000008 +#define AOFF_thread_w_saved 0x00000030 +#define ASIZ_thread_w_saved 0x00000008 +#define AOFF_thread_kregs 0x00000038 +#define ASIZ_thread_kregs 0x00000008 +#define AOFF_thread_reg_window 0x00000040 #define ASIZ_thread_reg_window 0x00000400 -#define AOFF_thread_rwbuf_stkptrs 0x00000530 +#define AOFF_thread_rwbuf_stkptrs 0x00000440 #define ASIZ_thread_rwbuf_stkptrs 0x00000040 -#define AOFF_thread_w_saved 0x00000570 -#define ASIZ_thread_w_saved 0x00000008 -#define AOFF_thread_flags 0x00000578 -#define ASIZ_thread_flags 0x00000008 -#define AOFF_thread_sig_address 0x00000580 +#define AOFF_thread_sig_address 0x00000480 #define ASIZ_thread_sig_address 0x00000008 -#define AOFF_thread_sig_desc 0x00000588 +#define AOFF_thread_sig_desc 0x00000488 #define ASIZ_thread_sig_desc 0x00000008 -#define AOFF_thread_sstk_info 0x00000590 +#define AOFF_thread_sstk_info 0x00000490 #define ASIZ_thread_sstk_info 0x00000010 -#define AOFF_thread_current_ds 0x000005a0 -#define ASIZ_thread_current_ds 0x00000004 -#define AOFF_thread_new_signal 0x000005a4 -#define ASIZ_thread_new_signal 0x00000004 -#define AOFF_thread_kregs 0x000005a8 -#define ASIZ_thread_kregs 0x00000008 -#define AOFF_thread_core_exec 0x000005b0 +#define AOFF_thread_core_exec 0x000004a0 #define ASIZ_thread_core_exec 0x00000020 #endif /* __ASM_OFFSETS_H__ */ diff --git a/include/asm-sparc64/atomic.h b/include/asm-sparc64/atomic.h index ec496fa17..12baf0222 100644 --- a/include/asm-sparc64/atomic.h +++ b/include/asm-sparc64/atomic.h @@ -1,8 +1,8 @@ -/* $Id: atomic.h,v 1.14 1997/04/16 05:57:06 davem Exp $ +/* $Id: atomic.h,v 1.15 1997/07/03 09:18:09 davem Exp $ * atomic.h: Thankfully the V9 is at least reasonable for this * stuff. * - * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1996, 1997 David S. Miller (davem@caip.rutgers.edu) */ #ifndef __ARCH_SPARC64_ATOMIC__ @@ -22,73 +22,63 @@ typedef struct { int counter; } atomic_t; extern __inline__ void atomic_add(int i, atomic_t *v) { - unsigned long temp0, temp1; __asm__ __volatile__(" - lduw [%3], %0 -1: - add %0, %2, %1 - cas [%3], %0, %1 - cmp %0, %1 - bne,a,pn %%icc, 1b - lduw [%3], %0 -2: -" : "=&r" (temp0), "=&r" (temp1) +1: lduw [%1], %%g1 + add %%g1, %0, %%g2 + cas [%1], %%g1, %%g2 + sub %%g1, %%g2, %%g1 + brnz,pn %%g1, 1b + nop" + : /* No outputs */ : "HIr" (i), "r" (__atomic_fool_gcc(v)) - : "cc"); + : "g1", "g2"); } extern __inline__ void atomic_sub(int i, atomic_t *v) { - unsigned long temp0, temp1; __asm__ __volatile__(" - lduw [%3], %0 -1: - sub %0, %2, %1 - cas [%3], %0, %1 - cmp %0, %1 - bne,a,pn %%icc, 1b - lduw [%3], %0 -2: -" : "=&r" (temp0), "=&r" (temp1) +1: lduw [%1], %%g1 + sub %%g1, %0, %%g2 + cas [%1], %%g1, %%g2 + sub %%g1, %%g2, %%g1 + brnz,pn %%g1, 1b + nop" + : /* No outputs */ : "HIr" (i), "r" (__atomic_fool_gcc(v)) - : "cc"); + : "g1", "g2"); } /* Same as above, but return the result value. */ extern __inline__ int atomic_add_return(int i, atomic_t *v) { - unsigned long temp0, oldval; + unsigned long oldval; __asm__ __volatile__(" - lduw [%3], %0 -1: - add %0, %2, %1 - cas [%3], %0, %1 - cmp %0, %1 - bne,a,pn %%icc, 1b - lduw [%3], %0 -2: -" : "=&r" (temp0), "=&r" (oldval) +1: lduw [%2], %%g1 + add %%g1, %1, %%g2 + cas [%2], %%g1, %%g2 + sub %%g1, %%g2, %%g1 + brnz,pn %%g1, 1b + add %%g2, %1, %0" + : "=&r" (oldval) : "HIr" (i), "r" (__atomic_fool_gcc(v)) - : "cc"); - return (((int)oldval) + 1); + : "g1", "g2"); + return (int)oldval; } extern __inline__ int atomic_sub_return(int i, atomic_t *v) { - unsigned long temp0, oldval; + unsigned long oldval; __asm__ __volatile__(" - lduw [%3], %0 -1: - sub %0, %2, %1 - cas [%3], %0, %1 - cmp %0, %1 - bne,a,pn %%icc, 1b - lduw [%3], %0 -2: -" : "=&r" (temp0), "=&r" (oldval) +1: lduw [%2], %%g1 + sub %%g1, %1, %%g2 + cas [%2], %%g1, %%g2 + sub %%g1, %%g2, %%g1 + brnz,pn %%g1, 1b + sub %%g2, %1, %0" + : "=&r" (oldval) : "HIr" (i), "r" (__atomic_fool_gcc(v)) - : "cc"); - return (((int)oldval) - 1); + : "g1", "g2"); + return (int)oldval; } #define atomic_dec_return(v) atomic_sub_return(1,(v)) diff --git a/include/asm-sparc64/bitops.h b/include/asm-sparc64/bitops.h index 5060d88ae..f0d11e6ef 100644 --- a/include/asm-sparc64/bitops.h +++ b/include/asm-sparc64/bitops.h @@ -1,4 +1,4 @@ -/* $Id: bitops.h,v 1.16 1997/05/28 13:48:56 jj Exp $ +/* $Id: bitops.h,v 1.19 1997/07/08 10:17:37 davem Exp $ * bitops.h: Bit string operations on the V9. * * Copyright 1996 David S. Miller (davem@caip.rutgers.edu) @@ -23,21 +23,21 @@ extern __inline__ unsigned long test_and_set_bit(unsigned long nr, void *addr) { unsigned long oldbit; unsigned long temp0, temp1; - unsigned int * m = ((unsigned int *) addr) + (nr >> 5); + unsigned long * m = ((unsigned long *) addr) + (nr >> 6); __asm__ __volatile__(" - lduw [%4], %0 + ldx [%4], %0 1: andcc %0, %3, %2 - bne,pn %%icc, 2f + bne,pn %%xcc, 2f xor %0, %3, %1 - cas [%4], %0, %1 + casx [%4], %0, %1 cmp %0, %1 - bne,a,pn %%icc, 1b - lduw [%4], %0 + bne,a,pn %%xcc, 1b + ldx [%4], %0 2: " : "=&r" (temp0), "=&r" (temp1), "=&r" (oldbit) - : "HIr" (1UL << (nr & 31)), "r" (m) + : "HIr" (1UL << (nr & 63)), "r" (m) : "cc"); return oldbit != 0; } @@ -51,21 +51,21 @@ extern __inline__ unsigned long test_and_clear_bit(unsigned long nr, void *addr) { unsigned long oldbit; unsigned long temp0, temp1; - unsigned int * m = ((unsigned int *) addr) + (nr >> 5); + unsigned long * m = ((unsigned long *) addr) + (nr >> 6); __asm__ __volatile__(" - lduw [%4], %0 + ldx [%4], %0 1: andcc %0, %3, %2 - be,pn %%icc, 2f + be,pn %%xcc, 2f xor %0, %3, %1 - cas [%4], %0, %1 + casx [%4], %0, %1 cmp %0, %1 - bne,a,pn %%icc, 1b - lduw [%4], %0 + bne,a,pn %%xcc, 1b + ldx [%4], %0 2: " : "=&r" (temp0), "=&r" (temp1), "=&r" (oldbit) - : "HIr" (1UL << (nr & 31)), "r" (m) + : "HIr" (1UL << (nr & 63)), "r" (m) : "cc"); return oldbit != 0; } @@ -79,19 +79,19 @@ extern __inline__ unsigned long test_and_change_bit(unsigned long nr, void *addr { unsigned long oldbit; unsigned long temp0, temp1; - unsigned int * m = ((unsigned int *) addr) + (nr >> 5); + unsigned long * m = ((unsigned long *) addr) + (nr >> 6); __asm__ __volatile__(" - lduw [%4], %0 + ldx [%4], %0 1: and %0, %3, %2 xor %0, %3, %1 - cas [%4], %0, %1 + casx [%4], %0, %1 cmp %0, %1 - bne,a,pn %%icc, 1b - lduw [%4], %0 + bne,a,pn %%xcc, 1b + ldx [%4], %0 " : "=&r" (temp0), "=&r" (temp1), "=&r" (oldbit) - : "HIr" (1UL << (nr & 31)), "r" (m) + : "HIr" (1UL << (nr & 63)), "r" (m) : "cc"); return oldbit != 0; } @@ -103,7 +103,7 @@ extern __inline__ void change_bit(unsigned long nr, void *addr) extern __inline__ unsigned long test_bit(int nr, __const__ void *addr) { - return 1UL & (((__const__ int *) addr)[nr >> 5] >> (nr & 31)); + return 1UL & (((__const__ long *) addr)[nr >> 6] >> (nr & 63)); } /* The easy/cheese version for now. */ @@ -121,7 +121,7 @@ extern __inline__ unsigned long ffz(unsigned long word) : "0" (word) : "g1", "g2"); #else -#ifdef EASY_CHEESE_VERSION +#if 1 /* def EASY_CHEESE_VERSION */ result = 0; while(word & 1) { result++; @@ -177,13 +177,11 @@ extern __inline__ unsigned long find_next_zero_bit(void *addr, unsigned long siz size -= 64; result += 64; } - offset = size >> 6; - size &= 63UL; - while (offset) { + while (size & ~63UL) { if (~(tmp = *(p++))) goto found_middle; result += 64; - offset--; + size -= 64; } if (!size) return result; @@ -260,22 +258,12 @@ extern __inline__ int test_le_bit(int nr, __const__ void * addr) #define find_first_zero_le_bit(addr, size) \ find_next_zero_le_bit((addr), (size), 0) -extern __inline__ unsigned long __swab64(unsigned long value) -{ - return (((value>>56) & 0x00000000000000ff) | - ((value>>40) & 0x000000000000ff00) | - ((value>>24) & 0x0000000000ff0000) | - ((value>>8) & 0x00000000ff000000) | - ((value<<8) & 0x000000ff00000000) | - ((value<<24) & 0x0000ff0000000000) | - ((value<<40) & 0x00ff000000000000) | - ((value<<56) & 0xff00000000000000)); -} - extern __inline__ unsigned long __swab64p(unsigned long *addr) { unsigned long ret; - __asm__ __volatile__ ("ldxa [%1] %2, %0" : "=r" (ret) : "r" (addr), "i" (ASI_PL)); + __asm__ __volatile__ ("ldxa [%1] %2, %0" + : "=r" (ret) + : "r" (addr), "i" (ASI_PL)); return ret; } @@ -299,13 +287,11 @@ extern __inline__ unsigned long find_next_zero_le_bit(void *addr, unsigned long size -= 64; result += 64; } - offset = size >> 6; - size &= 63UL; - while(offset) { + while(size & ~63) { if(~(tmp = __swab64p(p++))) goto found_middle; result += 64; - offset--; + size -= 64; } if(!size) return result; diff --git a/include/asm-sparc64/byteorder.h b/include/asm-sparc64/byteorder.h index 2325ef29c..dce2db246 100644 --- a/include/asm-sparc64/byteorder.h +++ b/include/asm-sparc64/byteorder.h @@ -1,4 +1,4 @@ -/* $Id: byteorder.h,v 1.5 1997/05/28 11:35:41 jj Exp $ */ +/* $Id: byteorder.h,v 1.6 1997/06/14 17:35:07 davem Exp $ */ #ifndef _SPARC64_BYTEORDER_H #define _SPARC64_BYTEORDER_H @@ -56,21 +56,27 @@ extern __inline__ __u64 cpu_to_le64(__u64 value) extern __inline__ __u16 cpu_to_le16p(__u16 *addr) { __u16 ret; - __asm__ __volatile__ ("lduha [%1] %2, %0" : "=r" (ret) : "r" (addr), "i" (ASI_PL)); + __asm__ __volatile__ ("lduha [%1] %2, %0" + : "=r" (ret) + : "r" (addr), "i" (ASI_PL)); return ret; } extern __inline__ __u32 cpu_to_le32p(__u32 *addr) { __u32 ret; - __asm__ __volatile__ ("lduwa [%1] %2, %0" : "=r" (ret) : "r" (addr), "i" (ASI_PL)); + __asm__ __volatile__ ("lduwa [%1] %2, %0" + : "=r" (ret) + : "r" (addr), "i" (ASI_PL)); return ret; } extern __inline__ __u64 cpu_to_le64p(__u64 *addr) { __u64 ret; - __asm__ __volatile__ ("ldxa [%1] %2, %0" : "=r" (ret) : "r" (addr), "i" (ASI_PL)); + __asm__ __volatile__ ("ldxa [%1] %2, %0" + : "=r" (ret) + : "r" (addr), "i" (ASI_PL)); return ret; } extern __inline__ __u16 cpu_to_be16p(__u16 *addr) { return *addr; } diff --git a/include/asm-sparc64/checksum.h b/include/asm-sparc64/checksum.h index d04abac7e..b1ff474c3 100644 --- a/include/asm-sparc64/checksum.h +++ b/include/asm-sparc64/checksum.h @@ -1,4 +1,4 @@ -/* $Id: checksum.h,v 1.8 1997/05/29 12:45:03 jj Exp $ */ +/* $Id: checksum.h,v 1.9 1997/06/26 04:05:17 davem Exp $ */ #ifndef __SPARC64_CHECKSUM_H #define __SPARC64_CHECKSUM_H @@ -108,31 +108,30 @@ extern __inline__ unsigned short ip_fast_csum(__const__ unsigned char *iph, * both operands. */ __asm__ __volatile__(" - sub %2, 4, %%g7 - lduw [%1 + 0x00], %0 - lduw [%1 + 0x04], %%g2 - lduw [%1 + 0x08], %%g3 - addcc %%g2, %0, %0 - addccc %%g3, %0, %0 - lduw [%1 + 0x0c], %%g2 - lduw [%1 + 0x10], %%g3 - addccc %%g2, %0, %0 - addc %0, %%g0, %0 -1: - addcc %%g3, %0, %0 - add %1, 4, %1 - addccc %0, %%g0, %0 - subcc %%g7, 1, %%g7 - be,a,pt %%icc, 2f - sll %0, 16, %%g2 - ba,pt %%xcc, 1b - lduw [%1 + 0x10], %%g3 -2: - addcc %0, %%g2, %%g2 - srl %%g2, 16, %0 - addc %0, %%g0, %0 - xnor %%g0, %0, %0 - srl %0, 0, %0 + sub %2, 4, %%g7 ! IEU0 + lduw [%1 + 0x00], %0 ! Load Group + lduw [%1 + 0x04], %%g2 ! Load Group + lduw [%1 + 0x08], %%g3 ! Load Group + addcc %%g2, %0, %0 ! IEU1 1 Load Bubble + Group + lduw [%1 + 0x0c], %%g2 ! Load + addccc %%g3, %0, %0 ! Sngle Group no Bubble + lduw [%1 + 0x10], %%g3 ! Load Group + addccc %%g2, %0, %0 ! Sngle Group no Bubble + addc %0, %%g0, %0 ! Sngle Group +1: addcc %%g3, %0, %0 ! IEU1 Group no Bubble + add %1, 4, %1 ! IEU0 + addccc %0, %%g0, %0 ! Sngle Group no Bubble + subcc %%g7, 1, %%g7 ! IEU1 Group + be,a,pt %%icc, 2f ! CTI + sll %0, 16, %%g2 ! IEU0 + lduw [%1 + 0x10], %%g3 ! Load Group + ba,pt %%xcc, 1b ! CTI + nop ! IEU0 +2: addcc %0, %%g2, %%g2 ! IEU1 Group + srl %%g2, 16, %0 ! IEU0 Group regdep XXX Scheisse! + addc %0, %%g0, %0 ! Sngle Group + xnor %%g0, %0, %0 ! IEU0 Group + srl %0, 0, %0 ! IEU0 Group XXX Scheisse! " : "=r" (sum), "=&r" (iph) : "r" (ihl), "1" (iph) : "g2", "g3", "g7", "cc"); diff --git a/include/asm-sparc64/delay.h b/include/asm-sparc64/delay.h index 7923b5014..f70d99b68 100644 --- a/include/asm-sparc64/delay.h +++ b/include/asm-sparc64/delay.h @@ -1,4 +1,4 @@ -/* $Id: delay.h,v 1.4 1997/04/10 23:32:44 davem Exp $ +/* $Id: delay.h,v 1.5 1997/06/18 12:36:23 jj Exp $ * delay.h: Linux delay routines on the V9. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu). @@ -12,7 +12,9 @@ extern unsigned long loops_per_sec; extern __inline__ void __delay(unsigned long loops) { __asm__ __volatile__(" - cmp %0, 0 + b,pt %%xcc, 1f + cmp %0, 0 + .align 32 1: bne,pt %%xcc, 1b subcc %0, 1, %0 diff --git a/include/asm-sparc64/elf.h b/include/asm-sparc64/elf.h index 9a43b6c3f..1cef89ff1 100644 --- a/include/asm-sparc64/elf.h +++ b/include/asm-sparc64/elf.h @@ -1,4 +1,4 @@ -/* $Id: elf.h,v 1.6 1997/05/17 11:51:27 davem Exp $ */ +/* $Id: elf.h,v 1.7 1997/06/14 21:28:07 davem Exp $ */ #ifndef __ASM_SPARC64_ELF_H #define __ASM_SPARC64_ELF_H @@ -32,7 +32,9 @@ typedef unsigned long elf_fpregset_t; /* * This is used to ensure we don't load something for the wrong architecture. */ +#ifndef elf_check_arch #define elf_check_arch(x) ((x) == ELF_ARCH) /* Might be EM_SPARC64 or EM_SPARC */ +#endif #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 8192 diff --git a/include/asm-sparc64/fbio.h b/include/asm-sparc64/fbio.h index 3d8713468..6d2f1e730 100644 --- a/include/asm-sparc64/fbio.h +++ b/include/asm-sparc64/fbio.h @@ -48,10 +48,6 @@ struct fbtype { }; #define FBIOGTYPE _IOR('F', 0, struct fbtype) -/* Used by FBIOPUTCMAP - * - * XXX 32-bit binary compatability item... -DaveM - */ struct fbcmap { int index; /* first element (0 origin) */ int count; @@ -104,7 +100,6 @@ struct fbcurpos { #define FB_CUR_SETSHAPE 0x10 /* set shape */ #define FB_CUR_SETALL 0x1F /* all of the above */ -/* XXX 32-bit binary compatability item... -DaveM */ struct fbcursor { short set; /* what to set, choose from the list above */ short enable; /* cursor on/off */ @@ -143,7 +138,6 @@ struct fb_wid_item { __u32 wi_attrs; __u32 wi_values[32]; }; -/* XXX 32-bit binary compatability item... -DaveM */ struct fb_wid_list { __u32 wl_flags; __u32 wl_count; @@ -155,6 +149,21 @@ struct fb_wid_list { #define FBIO_WID_PUT _IOW('F', 32, struct fb_wid_list) #define FBIO_WID_GET _IOWR('F', 33, struct fb_wid_list) +/* Creator ioctls */ +#define FFB_IOCTL ('F'<<8) +#define FFB_SYS_INFO (FFB_IOCTL|80) +#define FFB_CLUTREAD (FFB_IOCTL|81) +#define FFB_CLUTPOST (FFB_IOCTL|82) +#define FFB_SETDIAGMODE (FFB_IOCTL|83) +#define FFB_GETMONITORID (FFB_IOCTL|84) +#define FFB_GETVIDEOMODE (FFB_IOCTL|85) +#define FFB_SETVIDEOMODE (FFB_IOCTL|86) +#define FFB_SETSERVER (FFB_IOCTL|87) +#define FFB_SETOVCTL (FFB_IOCTL|88) +#define FFB_GETOVCTL (FFB_IOCTL|89) +#define FFB_GETSAXNUM (FFB_IOCTL|90) +#define FFB_FBDEBUG (FFB_IOCTL|91) + /* Cg14 ioctls */ #define MDI_IOCTL ('M'<<8) #define MDI_RESET (MDI_IOCTL|1) @@ -179,16 +188,15 @@ struct mdi_cfginfo { */ #define MDI_CLEAR_XLUT (MDI_IOCTL|9) -/* leo ioctls */ -struct leo_clut_alloc { +/* leo & ffb ioctls */ +struct fb_clut_alloc { __u32 clutid; /* Set on return */ __u32 flag; __u32 index; }; -/* XXX 32-bit binary compatability item... -DaveM */ -struct leo_clut { -#define LEO_CLUT_WAIT 0x00000001 /* Not yet implemented */ +struct fb_clut { +#define FB_CLUT_WAIT 0x00000001 /* Not yet implemented */ __u32 flag; __u32 clutid; __u32 offset; @@ -197,10 +205,21 @@ struct leo_clut { char * green; char * blue; }; -#define LEO_CLUTALLOC _IOWR('L', 53, struct leo_clut_alloc) -#define LEO_CLUTFREE _IOW('L', 54, struct leo_clut_alloc) -#define LEO_CLUTREAD _IOW('L', 55, struct leo_clut) -#define LEO_CLUTPOST _IOW('L', 56, struct leo_clut) + +struct fb_clut32 { + __u32 flag; + __u32 clutid; + __u32 offset; + __u32 count; + __u32 red; + __u32 green; + __u32 blue; +}; + +#define LEO_CLUTALLOC _IOWR('L', 53, struct fb_clut_alloc) +#define LEO_CLUTFREE _IOW('L', 54, struct fb_clut_alloc) +#define LEO_CLUTREAD _IOW('L', 55, struct fb_clut) +#define LEO_CLUTPOST _IOW('L', 56, struct fb_clut) #define LEO_SETGAMMA _IOW('L', 68, int) /* Not yet implemented */ #define LEO_GETGAMMA _IOR('L', 69, int) /* Not yet implemented */ diff --git a/include/asm-sparc64/floppy.h b/include/asm-sparc64/floppy.h index c7aa7cc81..bbef85483 100644 --- a/include/asm-sparc64/floppy.h +++ b/include/asm-sparc64/floppy.h @@ -1,4 +1,4 @@ -/* $Id: floppy.h,v 1.2 1997/03/14 21:05:25 jj Exp $ +/* $Id: floppy.h,v 1.3 1997/07/11 03:03:22 davem Exp $ * asm-sparc64/floppy.h: Sparc specific parts of the Floppy driver. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -315,7 +315,7 @@ static int sun_floppy_init(void) /* printk("DOR @0x%p\n", &sun_fdc->dor_82077); */ /* P3 */ /* Success... */ - return (int) sun_fdc; + return (int) ((unsigned long)sun_fdc); } static int sparc_eject(void) diff --git a/include/asm-sparc64/fpumacro.h b/include/asm-sparc64/fpumacro.h index f6323254d..dab134472 100644 --- a/include/asm-sparc64/fpumacro.h +++ b/include/asm-sparc64/fpumacro.h @@ -21,68 +21,44 @@ extern __inline__ void fprs_write(unsigned long val) __asm__ __volatile__("wr %0, 0x0, %%fprs" : : "r" (val)); } -extern __inline__ void fpsave32(unsigned int *fpregs, unsigned long *fsr) +extern __inline__ void fpsave(unsigned long *fpregs, + unsigned long *fsr, + unsigned long *gsr) { __asm__ __volatile__ (" - wr %%g0, %2, %%asi - stx %%fsr, [%1] - stda %%f0, [%0] %%asi - stda %%f16, [%0 + 64] %%asi - " : : "r" (fpregs), "r" (fsr), "i" (ASI_BLK_P)); -} - -extern __inline__ void fpload32(unsigned int *fpregs, unsigned long *fsr) -{ - __asm__ __volatile__ (" - wr %%g0, %2, %%asi - ldda [%0] %%asi, %%f0 - ldda [%0 + 64] %%asi, %%f16 - ldx [%1], %%fsr - " : : "r" (fpregs), "r" (fsr), "i" (ASI_BLK_P)); -} - -extern __inline__ void fpsave64hi(unsigned int *fpregs, unsigned long *fsr) -{ - __asm__ __volatile__ (" - wr %%g0, %2, %%asi - stx %%fsr, [%1] - stda %%f32, [%0 + 128] %%asi - stda %%f48, [%0 + 192] %%asi - " : : "r" (fpregs), "r" (fsr), "i" (ASI_BLK_P)); -} - -extern __inline__ void fpload64hi(unsigned int *fpregs, unsigned long *fsr) -{ - __asm__ __volatile__ (" - wr %%g0, %2, %%asi - ldda [%0 + 128] %%asi, %%f32 - ldda [%0 + 192] %%asi, %%f48 - ldx [%1], %%fsr - " : : "r" (fpregs), "r" (fsr), "i" (ASI_BLK_P)); -} - -extern __inline__ void fpsave(unsigned int *fpregs, unsigned long *fsr) -{ - __asm__ __volatile__ (" - wr %%g0, %2, %%asi + wr %%g0, %3, %%asi + rd %%gsr, %%g1 + membar #LoadStore | #StoreStore stx %%fsr, [%1] + stx %%g1, [%2] stda %%f0, [%0] %%asi stda %%f16, [%0 + 64] %%asi stda %%f32, [%0 + 128] %%asi stda %%f48, [%0 + 192] %%asi - " : : "r" (fpregs), "r" (fsr), "i" (ASI_BLK_P)); + membar #Sync +" : /* No outputs */ + : "r" (fpregs), "r" (fsr), "r" (gsr), "i" (ASI_BLK_P) + : "g1"); } -extern __inline__ void fpload(unsigned int *fpregs, unsigned long *fsr) +extern __inline__ void fpload(unsigned long *fpregs, + unsigned long *fsr, + unsigned long *gsr) { __asm__ __volatile__ (" - wr %%g0, %2, %%asi + wr %%g0, %3, %%asi + membar #StoreLoad | #LoadLoad ldda [%0] %%asi, %%f0 ldda [%0 + 64] %%asi, %%f16 ldda [%0 + 128] %%asi, %%f32 ldda [%0 + 192] %%asi, %%f48 ldx [%1], %%fsr - " : : "r" (fpregs), "r" (fsr), "i" (ASI_BLK_P)); + ldx [%2], %%g1 + wr %%g1, 0, %%gsr + membar #Sync +" : /* No outputs */ + : "r" (fpregs), "r" (fsr), "r" (gsr), "i" (ASI_BLK_P) + : "g1"); } #endif /* !(_SPARC64_FPUMACRO_H) */ diff --git a/include/asm-sparc64/fs_mount.h b/include/asm-sparc64/fs_mount.h deleted file mode 100644 index 3ad7ad698..000000000 --- a/include/asm-sparc64/fs_mount.h +++ /dev/null @@ -1,44 +0,0 @@ -/* $Id: fs_mount.h,v 1.2 1997/04/18 14:34:46 jj Exp $ - * fs_mount.h: Definitions for mount structure conversions. - * - * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) - */ - -#ifndef __ASM_FS_MOUNT_H -#define __ASM_FS_MOUNT_H - -#if defined(CONFIG_SPARC32_COMPAT) || defined(CONFIG_SPARC32_COMPAT_MODULE) - -#include <linux/sched.h> - -/* We need this to convert 32bit mount structures to 64bit */ - -extern void *do_ncp_super_data_conv(void *raw_data); -extern void *do_smb_super_data_conv(void *raw_data); - -extern __inline__ void *ncp_super_data_conv(void *raw_data) -{ - if (current->tss.flags & SPARC_FLAG_32BIT) - return do_ncp_super_data_conv(raw_data); - else - return raw_data; -} - -extern __inline__ void *smb_super_data_conv(void *raw_data) -{ - if (current->tss.flags & SPARC_FLAG_32BIT) - return do_smb_super_data_conv(raw_data); - else - return raw_data; -} - -#else /* CONFIG_SPARC32_COMPAT* */ - -#define ncp_super_data_conv(__x) __x -#define smb_super_data_conv(__x) __x - -#endif /* CONFIG_SPARC32_COMPAT* */ - -#define nfs_super_data_conv(__x) __x - -#endif /* __ASM_FS_MOUNT_H */ diff --git a/include/asm-sparc64/hardirq.h b/include/asm-sparc64/hardirq.h index 4680a4095..03ee543b1 100644 --- a/include/asm-sparc64/hardirq.h +++ b/include/asm-sparc64/hardirq.h @@ -13,8 +13,8 @@ extern unsigned int local_irq_count[NR_CPUS]; #ifndef __SMP__ -#define hardirq_trylock(cpu) (++local_irq_count[cpu], (cpu)==0) -#define hardirq_endlock(cpu) (--local_irq_count[cpu]) +#define hardirq_trylock(cpu) (local_irq_count[cpu] == 0) +#define hardirq_endlock(cpu) do { } while(0) #define hardirq_enter(cpu) (local_irq_count[cpu]++) #define hardirq_exit(cpu) (local_irq_count[cpu]--) diff --git a/include/asm-sparc64/head.h b/include/asm-sparc64/head.h index 62fe9a08f..e3ff51686 100644 --- a/include/asm-sparc64/head.h +++ b/include/asm-sparc64/head.h @@ -1,10 +1,10 @@ -/* $Id: head.h,v 1.22 1997/06/02 06:33:40 davem Exp $ */ +/* $Id: head.h,v 1.27 1997/07/13 17:30:43 davem Exp $ */ #ifndef _SPARC64_HEAD_H #define _SPARC64_HEAD_H #include <asm/pstate.h> -#define KERNBASE 0xFFFFF80000000000 +#define KERNBASE 0x400000 #define BOOT_KERNEL b sparc64_boot; nop; nop; nop; nop; nop; nop; nop; /* We need a "cleaned" instruction... */ @@ -43,17 +43,6 @@ nop; \ nop; -/* Just for testing */ -#define PROM_TRAP \ - rd %pc, %g1; \ - sethi %uhi(KERNBASE), %g4; \ - sethi %hi(0xf0000000-0x8000), %g2; \ - sllx %g4, 32, %g4; \ - add %g1, %g2, %g1; \ - sub %g1, %g4, %g1; \ - jmpl %g1 + %g0, %g0; \ - nop; - #define TRAP_ARG(routine, arg) \ ba,pt %xcc, etrap; \ rd %pc, %g7; \ @@ -105,12 +94,12 @@ #define SUNOS_SYSCALL_TRAP SYSCALL_TRAP(linux_sparc_syscall, sunos_sys_table) #define LINUX_32BIT_SYSCALL_TRAP SYSCALL_TRAP(linux_sparc_syscall, sys_call_table32) #define LINUX_64BIT_SYSCALL_TRAP SYSCALL_TRAP(linux_sparc_syscall, sys_call_table64) +#define GETCC_TRAP TRAP(getcc) +#define SETCC_TRAP TRAP(setcc) /* FIXME: Write these actually */ #define NETBSD_SYSCALL_TRAP TRAP(netbsd_syscall) #define SOLARIS_SYSCALL_TRAP TRAP(solaris_syscall) #define BREAKPOINT_TRAP TRAP(breakpoint_trap) -#define GETCC_TRAP TRAP(getcc) -#define SETCC_TRAP TRAP(setcc) #define INDIRECT_SOLARIS_SYSCALL(tlvl) TRAP_ARG(indirect_syscall, tlvl) #define TRAP_IRQ(routine, level) \ @@ -126,7 +115,7 @@ /* On UP this is ok, and worth the effort, for SMP we need * a different mechanism and thus cannot do it all in trap table. -DaveM */ -#if 0 /* ndef __SMP__ */ +#ifndef __SMP__ #define TRAP_IVEC \ ldxa [%g2] ASI_UDB_INTR_R, %g3; \ and %g3, 0x7ff, %g3; \ @@ -207,16 +196,23 @@ #define SPILL_2_GENERIC(xxx) \ wr %g0, xxx, %asi; \ srl %sp, 0, %sp; \ - stda %l0, [%sp + 0x00] %asi; \ - stda %l2, [%sp + 0x08] %asi; \ - stda %l4, [%sp + 0x10] %asi; \ - stda %l6, [%sp + 0x18] %asi; \ - stda %i0, [%sp + 0x20] %asi; \ - stda %i2, [%sp + 0x28] %asi; \ - stda %i4, [%sp + 0x30] %asi; \ - stda %i6, [%sp + 0x38] %asi; \ + stwa %l0, [%sp + 0x00] %asi; \ + stwa %l1, [%sp + 0x04] %asi; \ + stwa %l2, [%sp + 0x08] %asi; \ + stwa %l3, [%sp + 0x0c] %asi; \ + stwa %l4, [%sp + 0x10] %asi; \ + stwa %l5, [%sp + 0x14] %asi; \ + stwa %l6, [%sp + 0x18] %asi; \ + stwa %l7, [%sp + 0x1c] %asi; \ + stwa %i0, [%sp + 0x20] %asi; \ + stwa %i1, [%sp + 0x24] %asi; \ + stwa %i2, [%sp + 0x28] %asi; \ + stwa %i3, [%sp + 0x2c] %asi; \ + stwa %i4, [%sp + 0x30] %asi; \ + stwa %i5, [%sp + 0x34] %asi; \ + stwa %i6, [%sp + 0x38] %asi; \ + stwa %i7, [%sp + 0x3c] %asi; \ saved; retry; nop; nop; nop; nop; \ - nop; nop; nop; nop; nop; nop; nop; nop; \ nop; nop; nop; nop; nop; nop; \ b,a,pt %xcc, spill_fixup_mna; \ b,a,pt %xcc, spill_fixup; @@ -287,16 +283,23 @@ #define FILL_2_GENERIC(xxx) \ wr %g0, xxx, %asi; \ srl %sp, 0, %sp; \ - ldda [%sp + 0x00] %asi, %l0; \ - ldda [%sp + 0x08] %asi, %l2; \ - ldda [%sp + 0x10] %asi, %l4; \ - ldda [%sp + 0x18] %asi, %l6; \ - ldda [%sp + 0x20] %asi, %i0; \ - ldda [%sp + 0x28] %asi, %i2; \ - ldda [%sp + 0x30] %asi, %i4; \ - ldda [%sp + 0x38] %asi, %i6; \ + lduwa [%sp + 0x00] %asi, %l0; \ + lduwa [%sp + 0x04] %asi, %l1; \ + lduwa [%sp + 0x08] %asi, %l2; \ + lduwa [%sp + 0x0c] %asi, %l3; \ + lduwa [%sp + 0x10] %asi, %l4; \ + lduwa [%sp + 0x14] %asi, %l5; \ + lduwa [%sp + 0x18] %asi, %l6; \ + lduwa [%sp + 0x1c] %asi, %l7; \ + lduwa [%sp + 0x20] %asi, %i0; \ + lduwa [%sp + 0x24] %asi, %i1; \ + lduwa [%sp + 0x28] %asi, %i2; \ + lduwa [%sp + 0x2c] %asi, %i3; \ + lduwa [%sp + 0x30] %asi, %i4; \ + lduwa [%sp + 0x34] %asi, %i5; \ + lduwa [%sp + 0x38] %asi, %i6; \ + lduwa [%sp + 0x3c] %asi, %i7; \ restored; retry; nop; nop; nop; nop; \ - nop; nop; nop; nop; nop; nop; nop; nop; \ nop; nop; nop; nop; nop; nop; \ b,a,pt %xcc, fill_fixup_mna; \ b,a,pt %xcc, fill_fixup; diff --git a/include/asm-sparc64/ioctls.h b/include/asm-sparc64/ioctls.h index 0432cb46f..1d6c1cace 100644 --- a/include/asm-sparc64/ioctls.h +++ b/include/asm-sparc64/ioctls.h @@ -1,14 +1,9 @@ -/* $Id: ioctls.h,v 1.2 1997/04/04 00:50:18 davem Exp $ */ +/* $Id: ioctls.h,v 1.4 1997/06/23 07:26:03 davem Exp $ */ #ifndef _ASM_SPARC64_IOCTLS_H #define _ASM_SPARC64_IOCTLS_H #include <asm/ioctl.h> -/* XXX 32-bit binary compatability issues, I am sure that - * XXX only IOCTL's which reference structures will be of - * XXX concern and these are easily fabricated using wrappers. - */ - /* Big T */ #define TCGETA _IOR('T', 1, struct termio) #define TCSETA _IOW('T', 2, struct termio) @@ -24,7 +19,7 @@ /* Note that all the ioctls that are not available in Linux have a * double underscore on the front to: a) avoid some programs to - * thing we support some ioctls under Linux (autoconfiguration stuff) + * think we support some ioctls under Linux (autoconfiguration stuff) */ /* Little t */ #define TIOCGETD _IOR('t', 0, int) @@ -69,8 +64,8 @@ /* 119 is the non-posix getpgrp tty ioctl */ #define __TIOCCDTR _IO('t', 120) /* SunOS Specific */ #define __TIOCSDTR _IO('t', 121) /* SunOS Specific */ -#define __TIOCCBRK _IO('t', 122) /* SunOS Specific */ -#define __TIOCSBRK _IO('t', 123) /* SunOS Specific */ +#define TIOCCBRK _IO('t', 122) +#define TIOCSBRK _IO('t', 123) #define __TIOCLGET _IOW('t', 124, int) /* SunOS Specific */ #define __TIOCLSET _IOW('t', 125, int) /* SunOS Specific */ #define __TIOCLBIC _IOW('t', 126, int) /* SunOS Specific */ diff --git a/include/asm-sparc64/mmu_context.h b/include/asm-sparc64/mmu_context.h index 9a5b10458..7e7aa0433 100644 --- a/include/asm-sparc64/mmu_context.h +++ b/include/asm-sparc64/mmu_context.h @@ -1,4 +1,4 @@ -/* $Id: mmu_context.h,v 1.10 1997/05/23 09:35:55 jj Exp $ */ +/* $Id: mmu_context.h,v 1.17 1997/07/13 19:13:39 davem Exp $ */ #ifndef __SPARC64_MMU_CONTEXT_H #define __SPARC64_MMU_CONTEXT_H @@ -11,11 +11,6 @@ #ifndef __ASSEMBLY__ -/* Initialize the context related info for a new mm_struct - * instance. - */ -#define init_new_context(mm) ((mm)->context = NO_CONTEXT) - #define destroy_context(mm) do { } while(0) extern unsigned long tlb_context_cache; @@ -24,61 +19,41 @@ extern unsigned long tlb_context_cache; #define CTX_VERSION_MASK ((~0UL) << CTX_VERSION_SHIFT) #define CTX_FIRST_VERSION ((1UL << CTX_VERSION_SHIFT) + 1UL) -extern __inline__ void get_new_mmu_context(struct mm_struct *mm, - unsigned long ctx) -{ - if((ctx & ~(CTX_VERSION_MASK)) == 0) { - unsigned long flags; - int entry; - - save_and_cli(flags); - __asm__ __volatile__("stxa %%g0, [%0] %1\n\t" - "stxa %%g0, [%0] %2" - : /* No outputs */ - : "r" (TLB_TAG_ACCESS), "i" (ASI_IMMU), - "i" (ASI_DMMU)); - for(entry = 0; entry < 62; entry++) { - spitfire_put_dtlb_data(entry, 0x0UL); - spitfire_put_itlb_data(entry, 0x0UL); - } - membar("#Sync"); - flushi(PAGE_OFFSET); - restore_flags(flags); +extern void get_new_mmu_context(struct mm_struct *mm, unsigned long ctx); - ctx = (ctx & CTX_VERSION_MASK) + CTX_FIRST_VERSION; - if(!ctx) - ctx = CTX_FIRST_VERSION; - } - tlb_context_cache = ctx + 1; - mm->context = ctx; -} +/* Initialize the context related info for a new mm_struct + * instance. + */ +#define init_new_context(mm) get_new_mmu_context((mm), tlb_context_cache) extern __inline__ void get_mmu_context(struct task_struct *tsk) { + register unsigned long paddr asm("o5"); struct mm_struct *mm = tsk->mm; - if(mm && - !(tsk->tss.flags & SPARC_FLAG_KTHREAD) && + flushw_user(); + if(!(tsk->tss.flags & SPARC_FLAG_KTHREAD) && !(tsk->flags & PF_EXITING)) { unsigned long ctx = tlb_context_cache; - register unsigned long paddr asm("o5"); - - flushw_user(); if((mm->context ^ ctx) & CTX_VERSION_MASK) get_new_mmu_context(mm, ctx); - tsk->tss.ctx = (mm->context & 0x1fff); - spitfire_set_secondary_context(tsk->tss.current_ds ? - mm->context : 0); - paddr = __pa(mm->pgd); - __asm__ __volatile__(" - rdpr %%pstate, %%o4 - wrpr %%o4, %1, %%pstate - mov %0, %%g7 - wrpr %%o4, 0x0, %%pstate - " : /* no outputs */ - : "r" (paddr), "i" (PSTATE_MG|PSTATE_IE) - : "o4"); - } + + /* Don't worry, set_fs() will restore it... */ + tsk->tss.ctx = (tsk->tss.current_ds ? + (mm->context & 0x1fff) : 0); + } else + tsk->tss.ctx = 0; + spitfire_set_secondary_context(tsk->tss.ctx); + __asm__ __volatile__("flush %g6"); + paddr = __pa(mm->pgd); + __asm__ __volatile__(" + rdpr %%pstate, %%o4 + wrpr %%o4, %1, %%pstate + mov %0, %%g7 + wrpr %%o4, 0x0, %%pstate + " : /* no outputs */ + : "r" (paddr), "i" (PSTATE_MG|PSTATE_IE) + : "o4"); } #endif /* !(__ASSEMBLY__) */ diff --git a/include/asm-sparc64/namei.h b/include/asm-sparc64/namei.h index af5afb721..e80c11979 100644 --- a/include/asm-sparc64/namei.h +++ b/include/asm-sparc64/namei.h @@ -1,4 +1,4 @@ -/* $Id: namei.h,v 1.4 1997/06/07 08:32:56 ecd Exp $ +/* $Id: namei.h,v 1.5 1997/07/17 02:24:28 davem Exp $ * linux/include/asm-sparc64/namei.h * * Routines to handle famous /usr/gnemul/s*. @@ -11,6 +11,7 @@ #define SPARC_BSD_EMUL "usr/gnemul/sunos/" #define SPARC_SOL_EMUL "usr/gnemul/solaris/" +#if 0 /* XXX FIXME */ extern int __namei(int, const char *, struct inode *, char *, struct inode **, struct inode **, struct qstr *, struct dentry **, int *); @@ -44,4 +45,6 @@ __prefix_namei(int retrieve_mode, const char * name, struct inode * base, return 0; } +#endif /* XXX FIXME */ + #endif /* __SPARC64_NAMEI_H */ diff --git a/include/asm-sparc64/page.h b/include/asm-sparc64/page.h index 71679e351..d39d3d494 100644 --- a/include/asm-sparc64/page.h +++ b/include/asm-sparc64/page.h @@ -1,4 +1,4 @@ -/* $Id: page.h,v 1.8 1997/03/26 12:24:21 davem Exp $ */ +/* $Id: page.h,v 1.14 1997/06/26 22:32:03 davem Exp $ */ #ifndef _SPARC64_PAGE_H #define _SPARC64_PAGE_H @@ -18,10 +18,15 @@ #ifndef __ASSEMBLY__ -#define clear_page(page) memset((void *)(page), 0, PAGE_SIZE) -#define copy_page(to,from) memcpy((void *)(to), (void *)(from), PAGE_SIZE) +#define clear_page(page) memset((void *)(page), 0, PAGE_SIZE) -#define STRICT_MM_TYPECHECKS +extern void copy_page(unsigned long to, unsigned long from); + +/* GROSS, defining this makes gcc pass these types as aggregates, + * and thus on the stack, turn this crap off... -DaveM + */ + +/* #define STRICT_MM_TYPECHECKS */ #ifdef STRICT_MM_TYPECHECKS /* These are used to make use of C type-checking.. */ @@ -89,7 +94,9 @@ typedef unsigned long iopgprot_t; #define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) #ifndef __ASSEMBLY__ -#define PAGE_OFFSET 0xFFFFF80000000000UL +/* Do prdele, look what happens to be in %g4... */ +register unsigned long page_offset asm("g4"); +#define PAGE_OFFSET page_offset #else #define PAGE_OFFSET 0xFFFFF80000000000 #endif diff --git a/include/asm-sparc64/pgtable.h b/include/asm-sparc64/pgtable.h index e56a4024d..5cbd9a3c5 100644 --- a/include/asm-sparc64/pgtable.h +++ b/include/asm-sparc64/pgtable.h @@ -1,4 +1,4 @@ -/* $Id: pgtable.h,v 1.34 1997/06/02 06:33:41 davem Exp $ +/* $Id: pgtable.h,v 1.49 1997/06/30 09:24:12 jj Exp $ * pgtable.h: SpitFire page table operations. * * Copyright 1996,1997 David S. Miller (davem@caip.rutgers.edu) @@ -51,7 +51,7 @@ #define PTRS_PER_PAGE (1UL << (PAGE_SHIFT-3)) /* NOTE: TLB miss handlers depend heavily upon where this is. */ -#define VMALLOC_START 0xFFFFFc0000000000UL +#define VMALLOC_START 0x0000000800000000UL #define VMALLOC_VMADDR(x) ((unsigned long)(x)) #endif /* !(__ASSEMBLY__) */ @@ -78,18 +78,17 @@ #define _PAGE_G 0x0000000000000001 /* Global */ /* Here are the SpitFire software bits we use in the TTE's. */ -#define _PAGE_PRESENT 0x0000000000001000 /* Present Page (ie. not swapped out) */ #define _PAGE_MODIFIED 0x0000000000000800 /* Modified Page (ie. dirty) */ #define _PAGE_ACCESSED 0x0000000000000400 /* Accessed Page (ie. referenced) */ #define _PAGE_READ 0x0000000000000200 /* Readable SW Bit */ #define _PAGE_WRITE 0x0000000000000100 /* Writable SW Bit */ -#define _PAGE_PRIV 0x0000000000000080 /* Software privilege bit */ +#define _PAGE_PRESENT 0x0000000000000080 /* Present Page (ie. not swapped out) */ #define _PAGE_CACHE (_PAGE_CP | _PAGE_CV) #define __DIRTY_BITS (_PAGE_MODIFIED | _PAGE_WRITE | _PAGE_W) #define __ACCESS_BITS (_PAGE_ACCESSED | _PAGE_READ | _PAGE_R) -#define __PRIV_BITS (_PAGE_P | _PAGE_PRIV) +#define __PRIV_BITS _PAGE_P #define PAGE_NONE __pgprot (_PAGE_PRESENT | _PAGE_VALID | _PAGE_CACHE | \ __PRIV_BITS | __ACCESS_BITS) @@ -112,7 +111,7 @@ #define _PAGE_CHG_MASK (_PFN_MASK | _PAGE_MODIFIED | _PAGE_ACCESSED | _PAGE_PRESENT) -#define pg_iobits (_PAGE_VALID | __PRIV_BITS | __ACCESS_BITS | _PAGE_E) +#define pg_iobits (_PAGE_VALID | _PAGE_PRESENT | __DIRTY_BITS | __ACCESS_BITS | _PAGE_E) #define __P000 PAGE_NONE #define __P001 PAGE_READONLY @@ -147,8 +146,7 @@ extern pte_t *__bad_pte(void); * hit for all __pa()/__va() operations. */ extern unsigned long phys_base; - -#define ZERO_PAGE (PAGE_OFFSET + phys_base) +#define ZERO_PAGE ((unsigned long)__va(phys_base)) /* This is for making TLB miss faster to process. */ extern unsigned long null_pmd_table; @@ -160,156 +158,47 @@ extern void *sparc_init_alloc(unsigned long *kbrk, unsigned long size); /* Cache and TLB flush operations. */ -extern __inline__ void flush_cache_all(void) -{ - unsigned long addr; - - flushw_all(); - for(addr = 0; addr < (PAGE_SIZE << 1); addr += 32) - spitfire_put_icache_tag(addr, 0x0UL); -} - -extern __inline__ void flush_cache_mm(struct mm_struct *mm) -{ - if(mm->context != NO_CONTEXT) { - unsigned long addr; - - flushw_user(); - for(addr = 0; addr < (PAGE_SIZE << 1); addr += 32) - spitfire_put_icache_tag(addr, 0x0UL); - } -} - -extern __inline__ void flush_cache_range(struct mm_struct *mm, unsigned long start, - unsigned long end) -{ - if(mm->context != NO_CONTEXT) { - unsigned long addr; +#define flush_cache_all() \ +do { unsigned long va; \ + flushw_all(); \ + for(va = 0; \ + va<(PAGE_SIZE<<1); \ + va += 32) \ +spitfire_put_icache_tag(va,0x0);\ +} while(0) - flushw_user(); - for(addr = 0; addr < (PAGE_SIZE << 1); addr += 32) - spitfire_put_icache_tag(addr, 0x0UL); - } -} - -extern __inline__ void flush_cache_page(struct vm_area_struct *vma, unsigned long page) -{ - struct mm_struct *mm = vma->vm_mm; - - if(mm->context != NO_CONTEXT) { - unsigned long addr; - - flushw_user(); - for(addr = 0; addr < (PAGE_SIZE << 1); addr += 32) - spitfire_put_icache_tag(addr, 0x0UL); - } -} +#define flush_cache_mm(mm) do { } while(0) +#define flush_cache_range(mm, start, end) do { } while(0) +#define flush_cache_page(vma, page) do { } while(0) /* This operation in unnecessary on the SpitFire since D-CACHE is write-through. */ #define flush_page_to_ram(page) do { } while (0) -extern __inline__ void flush_tlb_all(void) -{ - unsigned long flags; - int entry; - - /* Invalidate all non-locked TTE's in both the dtlb and itlb. */ - save_and_cli(flags); - __asm__ __volatile__("stxa %%g0, [%0] %1\n\t" - "stxa %%g0, [%0] %2" - : /* No outputs */ - : "r" (TLB_TAG_ACCESS), "i" (ASI_IMMU), "i" (ASI_DMMU)); - for(entry = 0; entry < 62; entry++) { - spitfire_put_dtlb_data(entry, 0x0UL); - spitfire_put_itlb_data(entry, 0x0UL); - } - membar("#Sync"); - flushi(PAGE_OFFSET); - restore_flags(flags); -} +extern void flush_tlb_all(void); +extern void __flush_tlb_mm(unsigned long context); extern __inline__ void flush_tlb_mm(struct mm_struct *mm) { - if(mm->context != NO_CONTEXT) { - __asm__ __volatile__(" - /* flush_tlb_mm() */ - rdpr %%pil, %%g1 - mov %1, %%g7 - wrpr %%g0, 15, %%pil - ldxa [%%g7] %2, %%g2 - cmp %%g2, %0 - be,pt %%icc, 1f - mov 0x50, %%g3 - stxa %0, [%%g7] %2 -1: - stxa %%g0, [%%g3] %3 - stxa %%g0, [%%g3] %4 - be,a,pt %%icc, 1f - nop - stxa %%g2, [%%g7] %2 -1: - flush %%g4 - wrpr %%g1, 0x0, %%pil -" : /* no outputs */ - : "r" (mm->context & 0x1fff), "i" (SECONDARY_CONTEXT), "i" (ASI_DMMU), - "i" (ASI_DMMU_DEMAP), "i" (ASI_IMMU_DEMAP) - : "g1", "g2", "g3", "g7", "cc"); - } + if(mm->context != NO_CONTEXT) + __flush_tlb_mm(mm->context & 0x1fff); } +extern void __flush_tlb_range(unsigned long context, unsigned long start, + unsigned long end); extern __inline__ void flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) { - if(mm->context != NO_CONTEXT) { - unsigned long old_ctx = spitfire_get_secondary_context(); - unsigned long new_ctx = (mm->context & 0x1fff); - unsigned long flags; - - start &= PAGE_MASK; - save_and_cli(flags); - if(new_ctx != old_ctx) - spitfire_set_secondary_context(mm->context); - while(start < end) { - spitfire_flush_dtlb_secondary_page(start); - spitfire_flush_itlb_secondary_page(start); - start += PAGE_SIZE; - } - if(new_ctx != old_ctx) - spitfire_set_secondary_context(old_ctx); - __asm__ __volatile__("flush %g4"); - restore_flags(flags); - } + if(mm->context != NO_CONTEXT) + __flush_tlb_range(mm->context & 0x1fff, start, end); } +extern void __flush_tlb_page(unsigned long context, unsigned long page); extern __inline__ void flush_tlb_page(struct vm_area_struct *vma, unsigned long page) { struct mm_struct *mm = vma->vm_mm; - if(mm->context != NO_CONTEXT) { - __asm__ __volatile__(" - /* flush_tlb_page() */ - rdpr %%pil, %%g1 - mov %1, %%g7 - wrpr %%g0, 15, %%pil - ldxa [%%g7] %2, %%g2 - cmp %%g2, %0 - be,pt %%icc, 1f - or %5, 0x10, %%g3 - stxa %0, [%%g7] %2 -1: - stxa %%g0, [%%g3] %3 - stxa %%g0, [%%g3] %4 - be,a,pt %%icc, 1f - nop - stxa %%g2, [%%g7] %2 -1: - flush %%g4 - wrpr %%g1, 0x0, %%pil -" : /* no outputs */ - : "r" (mm->context & 0x1fff), "i" (SECONDARY_CONTEXT), "i" (ASI_DMMU), - "i" (ASI_DMMU_DEMAP), "i" (ASI_IMMU_DEMAP), "r" (page & PAGE_MASK) - : "g1", "g2", "g3", "g7", "cc"); - } + if(mm->context != NO_CONTEXT) + __flush_tlb_page(mm->context & 0x1fff, page & PAGE_MASK); } extern inline pte_t mk_pte(unsigned long page, pgprot_t pgprot) @@ -394,24 +283,6 @@ extern inline pte_t pte_mkyoung(pte_t pte) return __pte(pte_val(pte) | (_PAGE_ACCESSED)); } -extern inline void SET_PAGE_DIR(struct task_struct *tsk, pgd_t *pgdir) -{ - register unsigned long paddr asm("o5"); - - paddr = __pa(pgdir); - - if(tsk == current) { - __asm__ __volatile__ (" - rdpr %%pstate, %%o4 - wrpr %%o4, %1, %%pstate - mov %0, %%g7 - wrpr %%o4, 0x0, %%pstate - " : /* No outputs */ - : "r" (paddr), "i" (PSTATE_MG|PSTATE_IE) - : "o4"); - } -} - /* to find an entry in a page-table-directory. */ extern inline pgd_t *pgd_offset(struct mm_struct *mm, unsigned long address) { return mm->pgd + ((address >> PGDIR_SHIFT) & (PTRS_PER_PAGE - 1)); } @@ -429,11 +300,16 @@ extern inline pte_t *pte_offset(pmd_t *dir, unsigned long address) extern __inline__ void __init_pmd(pmd_t *pmdp) { - extern void __bfill64(void *, unsigned long); + extern void __bfill64(void *, unsigned long *); - __bfill64((void *)pmdp, null_pte_table); + __bfill64((void *)pmdp, &null_pte_table); } +/* Turning this off makes things much faster, but eliminates some + * sanity checking as well. + */ +/* #define PGTABLE_SANITY_CHECKS */ + /* Allocate and free page tables. The xxx_kernel() versions are * used to allocate a kernel page table - this turns on supervisor * bits if any. @@ -456,11 +332,13 @@ extern inline pte_t * pte_alloc_kernel(pmd_t *pmd, unsigned long address) } free_page((unsigned long) page); } +#ifdef PGTABLE_SANITY_CHECKS if (pmd_bad(*pmd)) { printk("Bad pmd in pte_alloc_kernel: %08lx\n", pmd_val(*pmd)); pmd_set(pmd, BAD_PTE); return NULL; } +#endif return (pte_t *) pmd_page(*pmd) + address; } @@ -483,11 +361,13 @@ extern inline pmd_t * pmd_alloc_kernel(pgd_t *pgd, unsigned long address) } free_page((unsigned long) page); } +#ifdef PGTABLE_SANITY_CHECKS if (pgd_bad(*pgd)) { printk("Bad pgd in pmd_alloc_kernel: %08lx\n", pgd_val(*pgd)); pgd_set(pgd, BAD_PMD); return NULL; } +#endif return (pmd_t *) pgd_page(*pgd) + address; } @@ -509,11 +389,13 @@ extern inline pte_t * pte_alloc(pmd_t *pmd, unsigned long address) } free_page((unsigned long) page); } +#ifdef PGTABLE_SANITY_CHECKS if (pmd_bad(*pmd)) { printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd)); pmd_set(pmd, BAD_PTE); return NULL; } +#endif return (pte_t *) pmd_page(*pmd) + address; } @@ -536,11 +418,13 @@ extern inline pmd_t * pmd_alloc(pgd_t *pgd, unsigned long address) } free_page((unsigned long) page); } +#ifdef PGTABLE_SANITY_CHECKS if (pgd_bad(*pgd)) { printk("Bad pgd in pmd_alloc: %08lx\n", pgd_val(*pgd)); pgd_set(pgd, BAD_PMD); return NULL; } +#endif return (pmd_t *) pgd_page(*pgd) + address; } @@ -549,16 +433,33 @@ extern inline void pgd_free(pgd_t * pgd) extern inline pgd_t * pgd_alloc(void) { - extern void __bfill64(void *, unsigned long); + extern void __bfill64(void *, unsigned long *); pgd_t *pgd = (pgd_t *) __get_free_page(GFP_KERNEL); if (pgd) - __bfill64((void *)pgd, null_pmd_table); + __bfill64((void *)pgd, &null_pmd_table); return pgd; } extern pgd_t swapper_pg_dir[1024]; +extern inline void SET_PAGE_DIR(struct task_struct *tsk, pgd_t *pgdir) +{ + if(pgdir != swapper_pg_dir && tsk == current) { + register unsigned long paddr asm("o5"); + + paddr = __pa(pgdir); + __asm__ __volatile__ (" + rdpr %%pstate, %%o4 + wrpr %%o4, %1, %%pstate + mov %0, %%g7 + wrpr %%o4, 0x0, %%pstate + " : /* No outputs */ + : "r" (paddr), "i" (PSTATE_MG|PSTATE_IE) + : "o4"); + } +} + /* Routines for getting a dvma scsi buffer. */ struct mmu_sglist { char *addr; @@ -576,61 +477,15 @@ extern void mmu_get_scsi_sgl(struct mmu_sglist *, int, struct linux_sbus *sbus) #define mmu_lockarea(vaddr, len) (vaddr) #define mmu_unlockarea(vaddr, len) do { } while(0) +extern void fixup_dcache_alias(struct vm_area_struct *vma, unsigned long address, + pte_t pte); + extern inline void update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t pte) { /* Find and fix bad virutal cache aliases. */ - if((vma->vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED)) { - struct vm_area_struct *vmaring; - struct inode *inode; - unsigned long vaddr, offset, start; - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - int alias_found = 0; - - inode = vma->vm_inode; - if(!inode) - return; - - offset = (address & PAGE_MASK) - vma->vm_start; - vmaring = inode->i_mmap; - do { - vaddr = vmaring->vm_start + offset; - - /* This conditional is misleading... */ - if((vaddr ^ address) & PAGE_SIZE) { - alias_found++; - start = vmaring->vm_start; - while(start < vmaring->vm_end) { - pgdp = pgd_offset(vmaring->vm_mm, start); - if(!pgdp) goto next; - pmdp = pmd_offset(pgdp, start); - if(!pmdp) goto next; - ptep = pte_offset(pmdp, start); - if(!ptep) goto next; - - if(pte_val(*ptep) & _PAGE_PRESENT) { - flush_cache_page(vmaring, start); - *ptep = __pte(pte_val(*ptep) & - ~(_PAGE_CV)); - flush_tlb_page(vmaring, start); - } - next: - start += PAGE_SIZE; - } - } - } while((vmaring = vmaring->vm_next_share) != NULL); - - if(alias_found && (pte_val(pte) & _PAGE_CV)) { - pgdp = pgd_offset(vma->vm_mm, address); - pmdp = pmd_offset(pgdp, address); - ptep = pte_offset(pmdp, address); - flush_cache_page(vma, address); - *ptep = __pte(pte_val(*ptep) & ~(_PAGE_CV)); - flush_tlb_page(vma, address); - } - } + if((vma->vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED)) + fixup_dcache_alias(vma, address, pte); } /* Make a non-present pseudo-TTE. */ @@ -638,7 +493,7 @@ extern inline pte_t mk_swap_pte(unsigned long type, unsigned long offset) { pte_t pte; pte_val(pte) = (type<<PAGE_SHIFT)|(offset<<(PAGE_SHIFT+8)); return pte; } extern inline pte_t mk_pte_io(unsigned long page, pgprot_t prot, int space) -{ pte_t pte; pte_val(pte) = (page) | pgprot_val(prot); return pte; } +{ pte_t pte; pte_val(pte) = ((page) | pgprot_val(prot) | _PAGE_E) & ~(unsigned long)_PAGE_CACHE; return pte; } #define SWP_TYPE(entry) (((entry>>PAGE_SHIFT) & 0xff)) #define SWP_OFFSET(entry) ((entry) >> (PAGE_SHIFT+8)) @@ -650,17 +505,19 @@ sun4u_get_pte (unsigned long addr) pgd_t *pgdp; pmd_t *pmdp; pte_t *ptep; - - pgdp = pgd_offset (current->mm, addr); + + if (addr >= PAGE_OFFSET) + return addr & _PAGE_PADDR; + pgdp = pgd_offset_k (addr); pmdp = pmd_offset (pgdp, addr); ptep = pte_offset (pmdp, addr); return pte_val (*ptep) & _PAGE_PADDR; } -extern __inline__ unsigned int +extern __inline__ unsigned long __get_phys (unsigned long addr) { - return (sun4u_get_pte (addr) & 0x0fffffff); + return sun4u_get_pte (addr); } extern __inline__ int @@ -669,6 +526,9 @@ __get_iospace (unsigned long addr) return ((sun4u_get_pte (addr) & 0xf0000000) >> 28); } +extern void * module_map (unsigned long size); +extern void module_unmap (void *addr); + #endif /* !(__ASSEMBLY__) */ #endif /* !(_SPARC64_PGTABLE_H) */ diff --git a/include/asm-sparc64/processor.h b/include/asm-sparc64/processor.h index f58c9da70..019bbf600 100644 --- a/include/asm-sparc64/processor.h +++ b/include/asm-sparc64/processor.h @@ -1,4 +1,4 @@ -/* $Id: processor.h,v 1.27 1997/05/23 09:35:52 jj Exp $ +/* $Id: processor.h,v 1.32 1997/07/01 21:59:38 davem Exp $ * include/asm-sparc64/processor.h * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -32,34 +32,24 @@ /* The Sparc processor specific thread struct. */ struct thread_struct { - /* Floating point regs */ - /* Please check asm_offsets, so that not to much precious space - is wasted by this alignment and move the float_regs wherever - is better in this structure. Remember every byte of alignment - is multiplied by 512 to get the amount of wasted kernel memory. */ - unsigned int float_regs[64] __attribute__ ((aligned (64))); - unsigned long fsr; - - /* Context switch saved kernel state. */ - unsigned long ksp, kpc, wstate, cwp, ctx; +/*DC1*/ unsigned long ksp __attribute__ ((aligned(16))); + unsigned long kpc; +/*DC2*/ unsigned long wstate; + unsigned int cwp; + unsigned int ctx; + +/*DC3*/ unsigned int flags; + unsigned int new_signal; + unsigned long current_ds; +/*DC4*/ unsigned long w_saved; + struct pt_regs *kregs; - /* Storage for windows when user stack is bogus. */ struct reg_window reg_window[NSWINS] __attribute__ ((aligned (16))); unsigned long rwbuf_stkptrs[NSWINS] __attribute__ ((aligned (8))); - unsigned long w_saved; - /* Arch-specific task state flags, see below. */ - unsigned long flags; - - /* For signal handling */ unsigned long sig_address __attribute__ ((aligned (8))); unsigned long sig_desc; - struct sigstack sstk_info; - int current_ds, new_signal; - - struct pt_regs *kregs; - struct exec core_exec; /* just what it says. */ }; @@ -74,30 +64,18 @@ struct thread_struct { PAGE_SHARED , VM_READ | VM_WRITE | VM_EXEC, NULL, &init_mm.mmap } #define INIT_TSS { \ -/* FPU regs */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, \ -/* FPU status */ \ - 0, \ /* ksp, kpc, wstate, cwp, secctx */ \ 0, 0, 0, 0, 0, \ +/* flags, new_signal, current_ds, */ \ + SPARC_FLAG_KTHREAD, 0, USER_DS, \ +/* w_saved, kregs, */ \ + 0, 0, \ /* reg_window */ \ -{ { { 0, }, { 0, } }, }, \ + { { { 0, }, { 0, } }, }, \ /* rwbuf_stkptrs */ \ -{ 0, 0, 0, 0, 0, 0, 0, 0, }, \ -/* w_saved */ \ - 0, \ -/* flags */ \ - SPARC_FLAG_KTHREAD, \ -/* sig_address, sig_desc */ \ - 0, 0, \ -/* ex, sstk_info, current_ds, */ \ - { 0, 0, }, USER_DS, \ -/* new_signal, kregs */ \ - 0, 0, \ -/* core_exec */ \ -{ 0, }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, }, \ +/* sig_address, sig_desc, sstk_info, core_exec */ \ + 0, 0, { 0, 0, }, { 0, }, \ } #ifndef __ASSEMBLY__ @@ -111,11 +89,12 @@ extern __inline__ unsigned long thread_saved_pc(struct thread_struct *t) /* Do necessary setup to start up a newly executed thread. */ #define start_thread(regs, pc, sp) \ do { \ - regs->tstate = (regs->tstate & (TSTATE_CWP)) | TSTATE_IE; \ + regs->tstate = (regs->tstate & (TSTATE_CWP)) | (TSTATE_IE|TSTATE_PEF); \ regs->tpc = ((pc & (~3)) - 4); \ regs->tnpc = regs->tpc + 4; \ regs->y = 0; \ current->tss.flags &= ~SPARC_FLAG_32BIT; \ + current->tss.wstate = (1 << 3); \ __asm__ __volatile__( \ "stx %%g0, [%0 + %2 + 0x00]\n\t" \ "stx %%g0, [%0 + %2 + 0x08]\n\t" \ @@ -135,7 +114,7 @@ do { \ "stx %%g0, [%0 + %2 + 0x78]\n\t" \ "wrpr %%g0, (1 << 3), %%wstate\n\t" \ : \ - : "r" (regs), "r" (sp - REGWIN_SZ), \ + : "r" (regs), "r" (sp - REGWIN_SZ - STACK_BIAS), \ "i" ((const unsigned long)(&((struct pt_regs *)0)->u_regs[0]))); \ } while(0) @@ -146,11 +125,12 @@ do { \ pc &= 0x00000000ffffffffUL; \ sp &= 0x00000000ffffffffUL; \ \ - regs->tstate = (regs->tstate & (TSTATE_CWP)) | (TSTATE_IE | TSTATE_AM); \ + regs->tstate = (regs->tstate & (TSTATE_CWP))|(TSTATE_IE|TSTATE_AM|TSTATE_PEF); \ regs->tpc = ((pc & (~3)) - 4); \ regs->tnpc = regs->tpc + 4; \ regs->y = 0; \ current->tss.flags |= SPARC_FLAG_32BIT; \ + current->tss.wstate = (2 << 3); \ zero = 0; \ __asm__ __volatile__( \ "stx %%g0, [%0 + %2 + 0x00]\n\t" \ diff --git a/include/asm-sparc64/psrcompat.h b/include/asm-sparc64/psrcompat.h index b971514d6..22e9da3d6 100644 --- a/include/asm-sparc64/psrcompat.h +++ b/include/asm-sparc64/psrcompat.h @@ -1,4 +1,4 @@ -/* $Id: psrcompat.h,v 1.3 1997/06/05 06:22:54 davem Exp $ */ +/* $Id: psrcompat.h,v 1.4 1997/06/20 11:54:39 davem Exp $ */ #ifndef _SPARC64_PSRCOMPAT_H #define _SPARC64_PSRCOMPAT_H @@ -23,33 +23,19 @@ extern inline unsigned int tstate_to_psr(unsigned long tstate) { - unsigned int psr; unsigned long vers; - /* These fields are in the same place. */ - psr = (tstate & (TSTATE_CWP | TSTATE_PEF)); - - /* This is what the user would have always seen. */ - psr |= PSR_S; - - /* Slam in the 32-bit condition codes. */ - psr |= ((tstate & TSTATE_ICC) >> 12); - - /* This is completely arbitrary. */ __asm__ __volatile__("rdpr %%ver, %0" : "=r" (vers)); - psr |= ((vers << 8) >> 32) & PSR_IMPL; - psr |= ((vers << 24) >> 36) & PSR_VERS; - - return psr; + return ((tstate & TSTATE_CWP) | + PSR_S | + ((tstate & TSTATE_ICC) >> 12) | + (((vers << 8) >> 32) & PSR_IMPL) | + (((vers << 24) >> 36) & PSR_VERS)); } extern inline unsigned long psr_to_tstate_icc(unsigned int psr) { - unsigned long tstate; - - tstate = ((unsigned long)(psr & PSR_ICC)) << 12; - - return tstate; + return ((unsigned long)(psr & PSR_ICC)) << 12; } #endif /* !(_SPARC64_PSRCOMPAT_H) */ diff --git a/include/asm-sparc64/pstate.h b/include/asm-sparc64/pstate.h index 2233ee7f0..a1e1414d6 100644 --- a/include/asm-sparc64/pstate.h +++ b/include/asm-sparc64/pstate.h @@ -1,4 +1,4 @@ -/* $Id: pstate.h,v 1.4 1997/05/29 12:45:02 jj Exp $ */ +/* $Id: pstate.h,v 1.6 1997/06/25 07:39:45 jj Exp $ */ #ifndef _SPARC64_PSTATE_H #define _SPARC64_PSTATE_H @@ -14,6 +14,9 @@ #define PSTATE_CLE 0x0000000000000200 /* Current Little Endian. */ #define PSTATE_TLE 0x0000000000000100 /* Trap Little Endian. */ #define PSTATE_MM 0x00000000000000c0 /* Memory Model. */ +#define PSTATE_TSO 0x0000000000000000 /* MM: Total Store Order */ +#define PSTATE_PSO 0x0000000000000040 /* MM: Partial Store Order */ +#define PSTATE_RMO 0x0000000000000080 /* MM: Relaxed Memory Order */ #define PSTATE_RED 0x0000000000000020 /* Reset Error Debug State. */ #define PSTATE_PEF 0x0000000000000010 /* Floating Point Enable. */ #define PSTATE_AM 0x0000000000000008 /* Address Mask. */ @@ -47,6 +50,9 @@ #define TSTATE_CLE 0x0000000000020000 /* Current Little Endian. */ #define TSTATE_TLE 0x0000000000010000 /* Trap Little Endian. */ #define TSTATE_MM 0x000000000000c000 /* Memory Model. */ +#define TSTATE_TSO 0x0000000000000000 /* MM: Total Store Order */ +#define TSTATE_PSO 0x0000000000004000 /* MM: Partial Store Order */ +#define TSTATE_RMO 0x0000000000008000 /* MM: Relaxed Memory Order */ #define TSTATE_RED 0x0000000000002000 /* Reset Error Debug State. */ #define TSTATE_PEF 0x0000000000001000 /* Floating Point Enable. */ #define TSTATE_AM 0x0000000000000800 /* Address Mask. */ diff --git a/include/asm-sparc64/ptrace.h b/include/asm-sparc64/ptrace.h index 5da6f6dd1..a4784d41e 100644 --- a/include/asm-sparc64/ptrace.h +++ b/include/asm-sparc64/ptrace.h @@ -1,4 +1,4 @@ -/* $Id: ptrace.h,v 1.8 1997/05/27 19:30:27 jj Exp $ */ +/* $Id: ptrace.h,v 1.12 1997/06/24 16:30:35 davem Exp $ */ #ifndef _SPARC64_PTRACE_H #define _SPARC64_PTRACE_H @@ -15,7 +15,8 @@ struct pt_regs { unsigned long tstate; unsigned long tpc; unsigned long tnpc; - unsigned long y; + unsigned int y; + unsigned int fprs; }; struct pt_regs32 { @@ -137,6 +138,7 @@ extern void show_regs(struct pt_regs *); #define PT_V9_TPC 0x88 #define PT_V9_TNPC 0x90 #define PT_V9_Y 0x98 +#define PT_V9_FPRS 0x9c #define PT_TSTATE PT_V9_TSTATE #define PT_TPC PT_V9_TPC #define PT_TNPC PT_V9_TNPC @@ -265,6 +267,28 @@ extern void show_regs(struct pt_regs *); #define PTRACE_GETFPAREGS 20 #define PTRACE_SETFPAREGS 21 +/* There are for debugging 64-bit processes, either from a 32 or 64 bit + * parent. Thus their compliments are for debugging 32-bit processes only. + */ + +#define PTRACE_GETREGS64 22 +#define PTRACE_SETREGS64 23 +/* PTRACE_SYSCALL is 24 */ +#define PTRACE_GETFPREGS64 25 +#define PTRACE_SETFPREGS64 26 + #define PTRACE_GETUCODE 29 /* stupid bsd-ism */ +/* These are for 32-bit processes debugging 64-bit ones. + * Here addr and addr2 are passed in %g2 and %g3 respectively. + */ +#define PTRACE_PEEKTEXT64 (30 + PTRACE_PEEKTEXT) +#define PTRACE_POKETEXT64 (30 + PTRACE_POKETEXT) +#define PTRACE_PEEKDATA64 (30 + PTRACE_PEEKDATA) +#define PTRACE_POKEDATA64 (30 + PTRACE_POKEDATA) +#define PTRACE_READDATA64 (30 + PTRACE_READDATA) +#define PTRACE_WRITEDATA64 (30 + PTRACE_WRITEDATA) +#define PTRACE_READTEXT64 (30 + PTRACE_READTEXT) +#define PTRACE_WRITETEXT64 (30 + PTRACE_WRITETEXT) + #endif /* !(_SPARC64_PTRACE_H) */ diff --git a/include/asm-sparc64/reg.h b/include/asm-sparc64/reg.h index 716b8f8c6..ea3fc6e9c 100644 --- a/include/asm-sparc64/reg.h +++ b/include/asm-sparc64/reg.h @@ -1,4 +1,4 @@ -/* $Id: reg.h,v 1.1 1996/12/26 14:22:34 davem Exp $ +/* $Id: reg.h,v 1.2 1997/06/24 23:19:55 davem Exp $ * linux/asm-sparc64/reg.h * Layout of the registers as expected by gdb on the Sparc * we should replace the user.h definitions with those in @@ -76,4 +76,33 @@ struct fpu { struct fp_status f_fpstatus; }; +struct regs64 { + unsigned long r_g1; + unsigned long r_g2; + unsigned long r_g3; + unsigned long r_g4; + unsigned long r_g5; + unsigned long r_g6; + unsigned long r_g7; + unsigned long r_o0; + unsigned long r_o1; + unsigned long r_o2; + unsigned long r_o3; + unsigned long r_o4; + unsigned long r_o5; + unsigned long r_o6; + unsigned long r_o7; + unsigned long tstate; + unsigned long tpc; + unsigned long tnpc; + unsigned int y; + unsigned int fprs; +}; + +struct fp_status64 { + unsigned long regs[32]; + unsigned long fsr; +}; + + #endif /* __SPARC64_REG_H */ diff --git a/include/asm-sparc64/resource.h b/include/asm-sparc64/resource.h index 5e7a7f8c1..b3aedd4ee 100644 --- a/include/asm-sparc64/resource.h +++ b/include/asm-sparc64/resource.h @@ -1,4 +1,4 @@ -/* $Id: resource.h,v 1.2 1997/04/04 00:50:27 davem Exp $ +/* $Id: resource.h,v 1.3 1997/06/14 17:35:09 davem Exp $ * resource.h: Resource definitions. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) @@ -25,7 +25,6 @@ #define RLIM_NLIMITS 10 #ifdef __KERNEL__ -/* XXX 32-bit binary compatability... */ #define INIT_RLIMITS \ { \ {LONG_MAX, LONG_MAX}, {LONG_MAX, LONG_MAX}, \ diff --git a/include/asm-sparc64/sigcontext.h b/include/asm-sparc64/sigcontext.h index 9d35493d9..3fba2f834 100644 --- a/include/asm-sparc64/sigcontext.h +++ b/include/asm-sparc64/sigcontext.h @@ -1,14 +1,9 @@ -/* $Id: sigcontext.h,v 1.4 1997/04/04 00:50:28 davem Exp $ */ +/* $Id: sigcontext.h,v 1.8 1997/06/20 11:54:41 davem Exp $ */ #ifndef __SPARC64_SIGCONTEXT_H #define __SPARC64_SIGCONTEXT_H #include <asm/ptrace.h> -/* XXX This gets exported to userland as well as kernel, it is probably - * XXX riddled with many hard to find 32-bit binary compatability issues. - * XXX Signals and this file need to be investigated heavily. -DaveM - */ - #define SUNOS_MAXWIN 31 #ifndef __ASSEMBLY__ @@ -47,12 +42,12 @@ struct sigcontext32 { struct sigcontext { int sigc_onstack; /* state to restore */ int sigc_mask; /* sigmask to restore */ - int sigc_sp; /* stack pointer */ - int sigc_pc; /* program counter */ - int sigc_npc; /* next program counter */ - int sigc_psr; /* for condition codes etc */ - int sigc_g1; /* User uses these two registers */ - int sigc_o0; /* within the trampoline code. */ + unsigned long sigc_sp; /* stack pointer */ + unsigned long sigc_pc; /* program counter */ + unsigned long sigc_npc; /* next program counter */ + unsigned long sigc_psr; /* for condition codes etc */ + unsigned long sigc_g1; /* User uses these two registers */ + unsigned long sigc_o0; /* within the trampoline code. */ /* Now comes information regarding the users window set * at the time of the signal. @@ -72,17 +67,6 @@ typedef struct { } __siginfo32_t; typedef struct { - unsigned int si_float_regs [32]; - unsigned int si_fsr; - unsigned int si_fpqdepth; - struct { - unsigned int *insn_addr; - unsigned int insn; - } si_fpqueue [16]; -} __siginfo_fpu32_t; - - -typedef struct { struct pt_regs si_regs; int si_mask; } __siginfo_t; @@ -90,6 +74,7 @@ typedef struct { typedef struct { unsigned int si_float_regs [64]; unsigned long si_fsr; + unsigned long si_gsr; unsigned int si_fpqdepth; struct { unsigned int *insn_addr; diff --git a/include/asm-sparc64/softirq.h b/include/asm-sparc64/softirq.h index fa32f67e5..8386e4a15 100644 --- a/include/asm-sparc64/softirq.h +++ b/include/asm-sparc64/softirq.h @@ -43,10 +43,12 @@ do { int ent = nr; \ do { int ent = nr; \ bh_mask &= ~(1 << ent); \ bh_mask_count[ent]++; \ + barrier(); \ } while(0) #define enable_bh(nr) \ do { int ent = nr; \ + barrier(); \ if (!--bh_mask_count[ent]) \ bh_mask |= 1 << ent; \ } while(0) diff --git a/include/asm-sparc64/spinlock.h b/include/asm-sparc64/spinlock.h index ec1ad2ea0..cefd43309 100644 --- a/include/asm-sparc64/spinlock.h +++ b/include/asm-sparc64/spinlock.h @@ -53,6 +53,11 @@ typedef struct { } rwlock_t; #else /* !(__SMP__) */ +/* All of these locking primitives are expected to work properly + * even in an RMO memory model, which currently is what the kernel + * runs in. + */ + typedef unsigned char spinlock_t; #define SPIN_LOCK_UNLOCKED 0 #define spin_lock_init(lock) (*(lock) = 0) @@ -64,6 +69,7 @@ extern __inline__ void spin_lock(spinlock_t *lock) 1: ldstub [%0], %%g2 brnz,a,pn %%g2, 2f ldub [%0], %%g2 + membar #LoadLoad | #LoadStore .text 2 2: brnz,a,pt 2b ldub [%0], %%g2 @@ -77,7 +83,8 @@ extern __inline__ void spin_lock(spinlock_t *lock) extern __inline__ int spin_trylock(spinlock_t *lock) { unsigned int result; - __asm__ __volatile__("ldstub [%1], %0" + __asm__ __volatile__("ldstub [%1], %0\n\t" + "membar #LoadLoad | #LoadStore" : "=r" (result) : "r" (lock) : "memory"); @@ -86,7 +93,11 @@ extern __inline__ int spin_trylock(spinlock_t *lock) extern __inline__ void spin_unlock(spinlock_t *lock) { - __asm__ __volatile__("stb %%g0, [%0]" : : "r" (lock) : "memory"); + __asm__ __volatile__("membar #StoreStore | #LoadStore\n\t" + "stb %%g0, [%0]" + : /* No outputs */ + : "r" (lock) + : "memory"); } extern __inline__ void spin_lock_irq(spinlock_t *lock) @@ -96,6 +107,7 @@ extern __inline__ void spin_lock_irq(spinlock_t *lock) ldstub [%0], %%g2 brnz,a,pn %%g2, 2f ldub [%0], %%g2 + membar #LoadLoad | #LoadStore .text 2 2: brnz,a,pt 2b ldub [%0], %%g2 @@ -109,6 +121,7 @@ extern __inline__ void spin_lock_irq(spinlock_t *lock) extern __inline__ void spin_unlock_irq(spinlock_t *lock) { __asm__ __volatile__(" + membar #StoreStore | #LoadStore stb %%g0, [%0] wrpr %%g0, 0x0, %%pil " : /* no outputs */ @@ -116,28 +129,30 @@ extern __inline__ void spin_unlock_irq(spinlock_t *lock) : "memory"); } -#define spin_lock_irqsave(lock, flags) \ -do { register spinlock_t *lp asm("g1"); \ - lp = lock; \ - __asm__ __volatile__( \ - " rdpr %%pil, %0\n\t" \ - " wrpr %%g0, 15, %%pil\n\t" \ - "1: ldstub [%1], %%g2\n\t" \ - " brnz,a,pnt %%g2, 2f\n\t" \ - " ldub [%1], %%g2\n\t" \ - " .text 2\n\t" \ - "2: brnz,a,pt %%g2, 2b\n\t" \ - " ldub [%1], %%g2\n\t" \ - " b,a,pt %%xcc, 1b\n\t" \ - " .previous\n" \ - : "=r" (flags) \ - : "r" (lp) \ - : "g2", "memory"); \ +#define spin_lock_irqsave(lock, flags) \ +do { register spinlock_t *lp asm("g1"); \ + lp = lock; \ + __asm__ __volatile__( \ + " rdpr %%pil, %0\n\t" \ + " wrpr %%g0, 15, %%pil\n\t" \ + "1: ldstub [%1], %%g2\n\t" \ + " brnz,a,pnt %%g2, 2f\n\t" \ + " ldub [%1], %%g2\n\t" \ + " membar #LoadLoad | #LoadStore\n\t" \ + " .text 2\n\t" \ + "2: brnz,a,pt %%g2, 2b\n\t" \ + " ldub [%1], %%g2\n\t" \ + " b,a,pt %%xcc, 1b\n\t" \ + " .previous\n" \ + : "=r" (flags) \ + : "r" (lp) \ + : "g2", "memory"); \ } while(0) extern __inline__ void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags) { __asm__ __volatile__(" + membar #StoreStore | #LoadStore stb %%g0, [%0] wrpr %1, 0x0, %%pil " : /* no outputs */ @@ -161,6 +176,7 @@ extern __inline__ void read_lock(rwlock_t *rw) cmp %%g2, %%g3 bne,a,pn %%xcc, 1b ldx [%0],%%g2 + membar #LoadLoad | #LoadStore .text 2 2: ldx [%0], %%g2 3: brlz,pt %%g2, 3b @@ -169,12 +185,13 @@ extern __inline__ void read_lock(rwlock_t *rw) .previous " : /* no outputs */ : "r" (rw) - : "g2", "g3", "memory"); + : "g2", "g3", "cc", "memory"); } extern __inline__ void read_unlock(rwlock_t *rw) { __asm__ __volatile__(" + membar #StoreStore | #LoadStore ldx [%0], %%g2 1: sub %%g2, 1, %%g3 @@ -184,7 +201,7 @@ extern __inline__ void read_unlock(rwlock_t *rw) ldx [%0], %%g2 " : /* no outputs */ : "r" (rw) - : "g2", "g3", "memory"); + : "g2", "g3", "cc", "memory"); } extern __inline__ void write_lock(rwlock_t *rw) @@ -203,6 +220,7 @@ extern __inline__ void write_lock(rwlock_t *rw) andncc %%g3, %%g5, %%g0 bne,a,pn %%xcc, 3f ldx [%0], %%g2 + membar #LoadLoad | #LoadStore .text 2 3: andn %%g2, %%g5, %%g3 @@ -210,6 +228,7 @@ extern __inline__ void write_lock(rwlock_t *rw) cmp %%g2, %%g3 bne,a,pn %%xcc, 3b ldx [%0], %%g2 + membar #LoadLoad | #LoadStore 5: ldx [%0], %%g2 6: brlz,pt %%g2, 6b ldx [%0], %%g2 @@ -222,6 +241,7 @@ extern __inline__ void write_lock(rwlock_t *rw) extern __inline__ void write_unlock(rwlock_t *rw) { __asm__ __volatile__(" + membar #StoreStore | #LoadStore sethi %%uhi(0x8000000000000000), %%g5 ldx [%0], %%g2 sllx %%g5, 32, %%g5 diff --git a/include/asm-sparc64/string.h b/include/asm-sparc64/string.h index b420d80bb..45b166c91 100644 --- a/include/asm-sparc64/string.h +++ b/include/asm-sparc64/string.h @@ -1,4 +1,4 @@ -/* $Id: string.h,v 1.5 1997/05/18 04:16:57 davem Exp $ +/* $Id: string.h,v 1.7 1997/07/13 18:23:44 davem Exp $ * string.h: External definitions for optimized assembly string * routines for the Linux Kernel. * @@ -13,8 +13,14 @@ #ifdef __KERNEL__ +#include <asm/asi.h> + extern void __memmove(void *,const void *,__kernel_size_t); extern __kernel_size_t __memcpy(void *,const void *,__kernel_size_t); +extern __kernel_size_t __memcpy_short(void *,const void *,__kernel_size_t,long,long); +extern __kernel_size_t __memcpy_entry(void *,const void *,__kernel_size_t,long,long); +extern __kernel_size_t __memcpy_16plus(void *,const void *,__kernel_size_t,long,long); +extern __kernel_size_t __memcpy_384plus(void *,const void *,__kernel_size_t,long,long); extern __kernel_size_t __memset(void *,int,__kernel_size_t); #ifndef EXPORT_SYMTAB @@ -35,24 +41,11 @@ extern __kernel_size_t __memset(void *,int,__kernel_size_t); extern inline void *__constant_memcpy(void *to, const void *from, __kernel_size_t n) { - extern void __copy_1page(void *, const void *); - if(n) { if(n <= 32) { __builtin_memcpy(to, from, n); } else { -#if 0 - switch(n) { - case 8192: - __copy_1page(to, from); - break; - default: -#endif - __memcpy(to, from, n); -#if 0 - break; - } -#endif + __memcpy(to, from, n); } } return to; @@ -74,15 +67,13 @@ extern inline void *__nonconstant_memcpy(void *to, const void *from, __kernel_si extern inline void *__constant_c_and_count_memset(void *s, char c, __kernel_size_t count) { - extern void *bzero_1page(void *); + extern void *__bzero_1page(void *); extern __kernel_size_t __bzero(void *, __kernel_size_t); if(!c) { -#if 0 - if(count == 8192) - bzero_1page(s); + if (count == 8192) + __bzero_1page(s); else -#endif __bzero(s, count); } else { __memset(s, c, count); diff --git a/include/asm-sparc64/system.h b/include/asm-sparc64/system.h index d0d88fa5c..6e7c42e55 100644 --- a/include/asm-sparc64/system.h +++ b/include/asm-sparc64/system.h @@ -1,4 +1,4 @@ -/* $Id: system.h,v 1.22 1997/06/01 10:27:28 davem Exp $ */ +/* $Id: system.h,v 1.26 1997/06/28 10:04:03 davem Exp $ */ #ifndef __SPARC64_SYSTEM_H #define __SPARC64_SYSTEM_H @@ -95,45 +95,15 @@ extern __inline__ void flushw_user(void) { __asm__ __volatile__(" rdpr %%otherwin, %%g1 - brz,pt %%g1, 2f + brz,pt %%g1, 1f + mov %%o7, %%g3 + call __flushw_user clr %%g2 -1: - save %%sp, %0, %%sp - rdpr %%otherwin, %%g1 - brnz,pt %%g1, 1b - add %%g2, 1, %%g2 -1: - subcc %%g2, 1, %%g2 - bne,pt %%xcc, 1b - restore %%g0, %%g0, %%g0 -2: - " : : "i" (-REGWIN_SZ) - : "g1", "g2", "cc"); +1:" : : : "g1", "g2", "g3"); } #define flush_user_windows flushw_user -#ifdef __SMP__ - -#include <asm/fpumacro.h> - -#define SWITCH_ENTER(prev) \ - if((prev)->flags & PF_USEDFPU) { \ - fprs_write(FPRS_FEF); \ - fpsave((unsigned long *) &(prev)->tss.float_regs[0], \ - &(prev)->tss.fsr); \ - (prev)->flags &= ~PF_USEDFPU; \ - (prev)->tss.kregs->tstate &= ~TSTATE_PEF; \ - } - -#define SWITCH_DO_LAZY_FPU(next) -#else -#define SWITCH_ENTER(prev) -#define SWITCH_DO_LAZY_FPU(next) \ - if(last_task_used_math != (next)) \ - (next)->tss.kregs->tstate &= ~TSTATE_PEF -#endif - /* See what happens when you design the chip correctly? * NOTE NOTE NOTE this is extremely non-trivial what I * am doing here. GCC needs only one register to stuff @@ -146,13 +116,13 @@ extern __inline__ void flushw_user(void) do { \ __label__ switch_continue; \ register unsigned long task_pc asm("o7"); \ - SWITCH_ENTER(prev) \ - SWITCH_DO_LAZY_FPU(next); \ + (prev)->tss.kregs->fprs = 0; \ task_pc = ((unsigned long) &&switch_continue) - 0x8; \ __asm__ __volatile__( \ "rdpr %%pstate, %%g2\n\t" \ - "wrpr %%g2, 0x2, %%pstate\n\t" \ + "wrpr %%g2, 0x3, %%pstate\n\t" \ "flushw\n\t" \ +/*XXX*/ "wr %%g0, 0, %%fprs\n\t" \ "stx %%i6, [%%sp + 2047 + 0x70]\n\t" \ "stx %%i7, [%%sp + 2047 + 0x78]\n\t" \ "rdpr %%wstate, %%o5\n\t" \ @@ -160,19 +130,20 @@ do { \ "stx %%o5, [%%g6 + %2]\n\t" \ "rdpr %%cwp, %%o5\n\t" \ "stx %%o7, [%%g6 + %4]\n\t" \ - "stx %%o5, [%%g6 + %5]\n\t" \ + "st %%o5, [%%g6 + %5]\n\t" \ "mov %0, %%g6\n\t" \ - "ldx [%0 + %5], %%g1\n\t" \ - "wr %0, 0x0, %%pic\n\t" \ + "ld [%0 + %5], %%g1\n\t" \ "wrpr %%g1, %%cwp\n\t" \ "ldx [%%g6 + %2], %%o5\n\t" \ "ldx [%%g6 + %3], %%o6\n\t" \ "ldx [%%g6 + %4], %%o7\n\t" \ + "mov %%g6, %0\n\t" \ "wrpr %%o5, 0x0, %%wstate\n\t" \ "ldx [%%sp + 2047 + 0x70], %%i6\n\t" \ "ldx [%%sp + 2047 + 0x78], %%i7\n\t" \ + "wrpr %%g0, 0x96, %%pstate\n\t" \ "jmpl %%o7 + 0x8, %%g0\n\t" \ - " wrpr %%g2, 0x0, %%pstate\n\t" \ + " mov %0, %%g6\n\t" \ : /* No outputs */ \ : "r" (next), "r" (task_pc), \ "i" ((const unsigned long)(&((struct task_struct *)0)->tss.wstate)), \ @@ -200,15 +171,15 @@ extern __inline__ unsigned long xchg_u64(__volatile__ unsigned long *m, { unsigned long temp; __asm__ __volatile__(" - ldx [%3], %1 -1: + mov %0, %%g1 +1: ldx [%3], %1 casx [%3], %1, %0 cmp %1, %0 bne,a,pn %%xcc, 1b - ldx [%3], %1 + mov %%g1, %0 " : "=&r" (val), "=&r" (temp) : "0" (val), "r" (m) - : "cc"); + : "g1", "cc"); return val; } diff --git a/include/asm-sparc64/uaccess.h b/include/asm-sparc64/uaccess.h index 40ad3ee21..c0668e3f2 100644 --- a/include/asm-sparc64/uaccess.h +++ b/include/asm-sparc64/uaccess.h @@ -1,4 +1,4 @@ -/* $Id: uaccess.h,v 1.13 1997/05/29 12:45:04 jj Exp $ */ +/* $Id: uaccess.h,v 1.20 1997/07/13 18:23:45 davem Exp $ */ #ifndef _ASM_UACCESS_H #define _ASM_UACCESS_H @@ -22,26 +22,26 @@ * * "For historical reasons, these macros are grossly misnamed." -Linus */ -#define KERNEL_DS 0 -#define USER_DS -1 +#define KERNEL_DS 0x00 +#define USER_DS 0x2B /* har har har */ #define VERIFY_READ 0 #define VERIFY_WRITE 1 #define get_fs() (current->tss.current_ds) #define get_ds() (KERNEL_DS) -extern __inline__ void set_fs(int val) -{ - if (val != current->tss.current_ds) { - if (val == KERNEL_DS) { - flushw_user (); - spitfire_set_secondary_context (0); - } else { - spitfire_set_secondary_context (current->mm->context); - } - current->tss.current_ds = val; - } -} +#define set_fs(val) \ +do { \ + current->tss.current_ds = (val); \ + if ((val) == KERNEL_DS) { \ + flushw_user (); \ + current->tss.ctx = 0; \ + } else { \ + current->tss.ctx = (current->mm->context & 0x1fff); \ + } \ + spitfire_set_secondary_context(current->tss.ctx); \ + __asm__ __volatile__("flush %g6"); \ +} while(0) #define __user_ok(addr,size) 1 #define __kernel_ok (get_fs() == KERNEL_DS) @@ -255,8 +255,44 @@ __asm__ __volatile__( \ extern int __get_user_bad(void); -extern __kernel_size_t __copy_to_user(void *to, void *from, __kernel_size_t size); -extern __kernel_size_t __copy_from_user(void *to, void *from, __kernel_size_t size); +extern __kernel_size_t __memcpy_short(void *to, const void *from, + __kernel_size_t size, + long asi_src, long asi_dst); + +extern __kernel_size_t __memcpy_entry(void *to, const void *from, + __kernel_size_t size, + long asi_src, long asi_dst); + +extern __kernel_size_t __memcpy_16plus(void *to, const void *from, + __kernel_size_t size, + long asi_src, long asi_dst); + +extern __kernel_size_t __memcpy_386plus(void *to, const void *from, + __kernel_size_t size, + long asi_src, long asi_dst); + +extern __kernel_size_t __copy_from_user(void *to, const void *from, + __kernel_size_t size); + +extern __kernel_size_t __copy_to_user(void *to, const void *from, + __kernel_size_t size); + +extern __kernel_size_t __copy_in_user(void *to, const void *from, + __kernel_size_t size); + +#define copy_from_user(to,from,n) \ + __copy_from_user((void *)(to), \ + (void *)(from), (__kernel_size_t)(n)) + +#define copy_from_user_ret(to,from,n,retval) ({ \ +if (copy_from_user(to,from,n)) \ + return retval; \ +}) + +#define __copy_from_user_ret(to,from,n,retval) ({ \ +if (__copy_from_user(to,from,n)) \ + return retval; \ +}) #define copy_to_user(to,from,n) \ __copy_to_user((void *)(to), \ @@ -272,37 +308,27 @@ if (__copy_to_user(to,from,n)) \ return retval; \ }) -#define copy_from_user(to,from,n) \ - __copy_from_user((void *)(to), \ - (void *)(from), (__kernel_size_t)(n)) +#define copy_in_user(to,from,n) \ + __copy_in_user((void *)(to), \ + (void *) (from), (__kernel_size_t)(n)) -#define copy_from_user_ret(to,from,n,retval) ({ \ -if (copy_from_user(to,from,n)) \ +#define copy_in_user_ret(to,from,n,retval) ({ \ +if (copy_in_user(to,from,n)) \ return retval; \ }) -#define __copy_from_user_ret(to,from,n,retval) ({ \ -if (__copy_from_user(to,from,n)) \ +#define __copy_in_user_ret(to,from,n,retval) ({ \ +if (__copy_in_user(to,from,n)) \ return retval; \ }) extern __inline__ __kernel_size_t __clear_user(void *addr, __kernel_size_t size) { - __kernel_size_t ret; - __asm__ __volatile__ (" - .section __ex_table,#alloc - .align 8 - .xword 1f,3 - .previous -1: - wr %%g0, %3, %%asi - mov %2, %%o1 - call __bzero_noasi - mov %1, %%o0 - mov %%o0, %0 - " : "=r" (ret) : "r" (addr), "r" (size), "i" (ASI_S) : - "cc", "o0", "o1", "o2", "o3", "o4", "o5", "o7", "g1", "g2", "g3", "g5", "g7"); - return ret; + extern __kernel_size_t __bzero_noasi(void *addr, __kernel_size_t size); + + + __asm__ __volatile__ ("wr %%g0, %0, %%asi" : : "i" (ASI_S)); + return __bzero_noasi(addr, size); } #define clear_user(addr,n) \ diff --git a/include/asm-sparc64/uctx.h b/include/asm-sparc64/uctx.h new file mode 100644 index 000000000..1899ff971 --- /dev/null +++ b/include/asm-sparc64/uctx.h @@ -0,0 +1,71 @@ +/* $Id: uctx.h,v 1.1 1997/07/18 06:29:24 ralf Exp $ + * uctx.h: Sparc64 {set,get}context() register state layouts. + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + */ + +#ifndef __SPARC64_UCTX_H +#define __SPARC64_UCTX_H + +#define MC_TSTATE 0 +#define MC_PC 1 +#define MC_NPC 2 +#define MC_Y 3 +#define MC_G1 4 +#define MC_G2 5 +#define MC_G3 6 +#define MC_G4 7 +#define MC_G5 8 +#define MC_G6 9 +#define MC_G7 10 +#define MC_O0 11 +#define MC_O1 12 +#define MC_O2 13 +#define MC_O3 14 +#define MC_O4 15 +#define MC_O5 16 +#define MC_O6 17 +#define MC_O7 18 +#define MC_NGREG 19 + +typedef unsigned long mc_greg_t; +typedef mc_greg_t mc_gregset_t[MC_NGREG]; + +#define MC_MAXFPQ 16 +struct mc_fq { + unsigned long *mcfq_addr; + unsigned int mcfq_insn; +}; + +struct mc_fpu { + union { + unsigned int sregs[32]; + unsigned long dregs[32]; + long double qregs[16]; + } mcfpu_fregs; + unsigned long mcfpu_fsr; + unsigned long mcfpu_fprs; + unsigned long mcfpu_gsr; + struct mc_fq *mcfpu_fq; + unsigned char mcfpu_qcnt; + unsigned char mcfpu_qentsz; + unsigned char mcfpu_enab; +}; +typedef struct mc_fpu mc_fpu_t; + +typedef struct { + mc_gregset_t mc_gregs; + mc_greg_t mc_fp; + mc_greg_t mc_i7; + mc_fpu_t mc_fpregs; +} mcontext_t; + +struct ucontext { + struct ucontext *uc_link; + unsigned long uc_flags; + sigset_t uc_sigmask; + mcontext_t uc_mcontext; +}; +typedef struct ucontext ucontext_t; + +#endif /* __SPARC64_UCTX_H */ diff --git a/include/asm-sparc64/unistd.h b/include/asm-sparc64/unistd.h index cb17f1888..27afe645e 100644 --- a/include/asm-sparc64/unistd.h +++ b/include/asm-sparc64/unistd.h @@ -1,4 +1,4 @@ -/* $Id: unistd.h,v 1.5 1997/05/21 10:21:57 jj Exp $ */ +/* $Id: unistd.h,v 1.7 1997/06/16 05:37:44 davem Exp $ */ #ifndef _SPARC64_UNISTD_H #define _SPARC64_UNISTD_H @@ -113,35 +113,35 @@ #define __NR_setdopt 94 /* SunOS Specific */ #define __NR_fsync 95 /* Common */ #define __NR_setpriority 96 /* Common */ -#define __NR_socket 97 /* SunOS Specific */ -#define __NR_connect 98 /* SunOS Specific */ -#define __NR_accept 99 /* SunOS Specific */ +#define __NR_socket 97 /* Common */ +#define __NR_connect 98 /* Common */ +#define __NR_accept 99 /* Common */ #define __NR_getpriority 100 /* Common */ -#define __NR_send 101 /* SunOS Specific */ -#define __NR_recv 102 /* SunOS Specific */ +#define __NR_send 101 /* Common */ +#define __NR_recv 102 /* Common */ /* #define __NR_ni_syscall 103 ENOSYS under SunOS */ -#define __NR_bind 104 /* SunOS Specific */ -#define __NR_setsockopt 105 /* SunOS Specific */ -#define __NR_listen 106 /* SunOS Specific */ +#define __NR_bind 104 /* Common */ +#define __NR_setsockopt 105 /* Common */ +#define __NR_listen 106 /* Common */ /* #define __NR_ni_syscall 107 ENOSYS under SunOS */ #define __NR_sigvec 108 /* SunOS Specific */ #define __NR_sigblock 109 /* SunOS Specific */ #define __NR_sigsetmask 110 /* SunOS Specific */ #define __NR_sigpause 111 /* SunOS Specific */ #define __NR_sigstack 112 /* SunOS Specific */ -#define __NR_recvmsg 113 /* SunOS Specific */ -#define __NR_sendmsg 114 /* SunOS Specific */ +#define __NR_recvmsg 113 /* Common */ +#define __NR_sendmsg 114 /* Common */ #define __NR_vtrace 115 /* SunOS Specific */ #define __NR_gettimeofday 116 /* Common */ #define __NR_getrusage 117 /* Common */ -#define __NR_getsockopt 118 /* SunOS Specific */ +#define __NR_getsockopt 118 /* Common */ /* #define __NR_ni_syscall 119 ENOSYS under SunOS */ #define __NR_readv 120 /* Common */ #define __NR_writev 121 /* Common */ #define __NR_settimeofday 122 /* Common */ #define __NR_fchown 123 /* Common */ #define __NR_fchmod 124 /* Common */ -#define __NR_recvfrom 125 /* SunOS Specific */ +#define __NR_recvfrom 125 /* Common */ #define __NR_setreuid 126 /* Common */ #define __NR_setregid 127 /* Common */ #define __NR_rename 128 /* Common */ @@ -149,15 +149,15 @@ #define __NR_ftruncate 130 /* Common */ #define __NR_flock 131 /* Common */ /* #define __NR_ni_syscall 132 ENOSYS under SunOS */ -#define __NR_sendto 133 /* SunOS Specific */ -#define __NR_shutdown 134 /* SunOS Specific */ -#define __NR_socketpair 135 /* SunOS Specific */ +#define __NR_sendto 133 /* Common */ +#define __NR_shutdown 134 /* Common */ +#define __NR_socketpair 135 /* Common */ #define __NR_mkdir 136 /* Common */ #define __NR_rmdir 137 /* Common */ #define __NR_utimes 138 /* SunOS Specific */ /* #define __NR_ni_syscall 139 ENOSYS under SunOS */ #define __NR_adjtime 140 /* SunOS Specific */ -#define __NR_getpeername 141 /* SunOS Specific */ +#define __NR_getpeername 141 /* Common */ #define __NR_gethostid 142 /* SunOS Specific */ /* #define __NR_ni_syscall 143 ENOSYS under SunOS */ #define __NR_getrlimit 144 /* Common */ @@ -166,7 +166,7 @@ /* #define __NR_ni_syscall 147 ENOSYS under SunOS */ /* #define __NR_ni_syscall 148 ENOSYS under SunOS */ /* #define __NR_ni_syscall 149 ENOSYS under SunOS */ -#define __NR_getsockname 150 /* SunOS Specific */ +#define __NR_getsockname 150 /* Common */ #define __NR_getmsg 151 /* SunOS Specific */ #define __NR_putmsg 152 /* SunOS Specific */ #define __NR_poll 153 /* SunOS Specific */ @@ -467,6 +467,7 @@ static __inline__ pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned lo #endif /* __KERNEL_SYSCALLS__ */ +#ifdef __KERNEL__ /* sysconf options, for SunOS compatibility */ #define _SC_ARG_MAX 1 #define _SC_CHILD_MAX 2 @@ -476,5 +477,6 @@ static __inline__ pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned lo #define _SC_JOB_CONTROL 6 #define _SC_SAVED_IDS 7 #define _SC_VERSION 8 +#endif #endif /* _SPARC64_UNISTD_H */ diff --git a/include/asm-sparc64/vaddrs.h b/include/asm-sparc64/vaddrs.h index cd82abb06..b88085668 100644 --- a/include/asm-sparc64/vaddrs.h +++ b/include/asm-sparc64/vaddrs.h @@ -1,4 +1,4 @@ -/* $Id: vaddrs.h,v 1.6 1997/04/04 00:50:31 davem Exp $ */ +/* $Id: vaddrs.h,v 1.8 1997/06/27 14:55:13 jj Exp $ */ #ifndef _SPARC64_VADDRS_H #define _SPARC64_VADDRS_H @@ -14,12 +14,15 @@ * mappings for devices and is the speedup improvements of not loading * a pointer and then the value in the assembly code */ -#define IOBASE_VADDR 0xfffffd0000000000ULL /* Base for mapping pages */ -#define IOBASE_LEN 0x0000008000000000ULL /* Length of the IO area */ -#define IOBASE_END 0xfffffd8000000000ULL -#define DVMA_VADDR 0xfffffd8000000000ULL /* Base area of the DVMA on suns */ -#define DVMA_LEN 0x0000004000000000ULL /* Size of the DVMA address space */ -#define DVMA_END 0xfffffdc000000000ULL +#define IOBASE_VADDR 0x0000006000000000ULL /* Base for mapping pages */ +#define IOBASE_LEN 0x0000001000000000ULL /* Length of the IO area */ +#define IOBASE_END 0x0000007000000000ULL +#define DVMA_VADDR 0x0000007000000000ULL /* Base area of the DVMA on suns */ +#define DVMA_LEN 0x0000001000000000ULL /* Size of the DVMA address space */ +#define DVMA_END 0x0000008000000000ULL +#define MODULES_VADDR 0x0000000001000000ULL /* Where to map modules */ +#define MODULES_LEN 0x000000007f000000ULL +#define MODULES_END 0x0000000080000000ULL #endif /* !(_SPARC_VADDRS_H) */ diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h index eb91dd0fa..adf4278e6 100644 --- a/include/linux/binfmts.h +++ b/include/linux/binfmts.h @@ -19,12 +19,11 @@ struct linux_binprm{ unsigned long p; int sh_bang; int java; /* Java binary, prevent recursive invocation */ - struct inode * inode; + struct dentry * dentry; int e_uid, e_gid; int argc, envc; char * filename; /* Name of binary */ unsigned long loader, exec; - int dont_iput; /* binfmt handler has put inode */ }; /* @@ -42,15 +41,16 @@ struct linux_binfmt { extern int register_binfmt(struct linux_binfmt *); extern int unregister_binfmt(struct linux_binfmt *); -extern int read_exec(struct inode *inode, unsigned long offset, +extern int read_exec(struct dentry *, unsigned long offset, char * addr, unsigned long count, int to_kmem); -extern int open_inode(struct inode * inode, int mode); +extern int open_dentry(struct dentry *, int mode); extern int init_elf_binfmt(void); extern int init_elf32_binfmt(void); extern int init_irix_binfmt(void); extern int init_aout_binfmt(void); +extern int init_aout32_binfmt(void); extern int init_script_binfmt(void); extern int init_java_binfmt(void); extern int init_em86_binfmt(void); diff --git a/include/linux/console_struct.h b/include/linux/console_struct.h index e9ef418f7..99ed0e347 100644 --- a/include/linux/console_struct.h +++ b/include/linux/console_struct.h @@ -4,6 +4,8 @@ * Data structure and defines shared between console.c, vga.c and tga.c */ +#include <linux/config.h> + #define NPAR 16 struct vc_data { @@ -17,12 +19,17 @@ struct vc_data { unsigned char vc_halfcolor; /* Colour for half intensity mode */ unsigned long vc_origin; /* Used for EGA/VGA fast scroll */ unsigned long vc_scr_end; /* Used for EGA/VGA fast scroll */ - unsigned long vc_pos; unsigned long vc_x,vc_y; unsigned long vc_top,vc_bottom; unsigned long vc_state; unsigned long vc_npar,vc_par[NPAR]; +#ifdef CONFIG_FB_CONSOLE + unsigned short *vc_video_mem_start; /* Start of video RAM */ + unsigned short *vc_pos; +#else + unsigned long vc_pos; unsigned long vc_video_mem_start; /* Start of video RAM */ +#endif unsigned long vc_video_mem_end; /* End of video RAM (sort of) */ unsigned long vc_saved_x; unsigned long vc_saved_y; diff --git a/include/linux/cyclades.h b/include/linux/cyclades.h index 9386c17f0..0b22c2e30 100644 --- a/include/linux/cyclades.h +++ b/include/linux/cyclades.h @@ -1,4 +1,4 @@ -/* $Revision: 1.7 $$Date: 1997/03/26 10:30:00 $ +/* $Revision: 2.0 $$Date: 1997/06/30 10:30:00 $ * linux/include/linux/cyclades.h * * This file is maintained by Marcio Saito <marcio@cyclades.com> and @@ -6,6 +6,9 @@ * * This file contains the general definitions for the cyclades.c driver *$Log: cyclades.h,v $ + *Revision 1.1.1.1 1997/06/01 03:17:04 ralf + *Initial import of Linux/MIPS pre-2.1.40. + * *Revision 1.7 1997/03/26 10:30:00 daniel *new entries at the end of cyclades_port struct to reallocate *variables illegally allocated within card memory. @@ -86,6 +89,8 @@ typedef unsigned char ucchar; /* 8 bits, unsigned */ */ #define DP_WINDOW_SIZE (0x00080000) /* window size 512 Kb */ +#define ZE_DP_WINDOW_SIZE (0x00100000) /* window size 1 Mb (Ze and + 8Zo V.2 */ #define CTRL_WINDOW_SIZE (0x00000100) /* runtime regs 256 bytes */ /* @@ -183,6 +188,7 @@ struct RUNTIME_9060 { #define ID_ADDRESS 0x00000180L /* signature/pointer address */ #define ZFIRM_ID 0x5557465AL /* ZFIRM/U signature */ +#define ZFIRM_HLT 0x59505B5CL /* ZFIRM needs external power supply */ struct FIRM_ID { uclong signature; /* ZFIRM/U signature */ uclong zfwctrl_addr; /* pointer to ZFW_CTRL structure */ @@ -238,7 +244,10 @@ struct FIRM_ID { #define C_IN_RXBRK 0x00001000 /* Break received */ #define C_IN_PR_ERROR 0x00002000 /* parity error */ #define C_IN_FR_ERROR 0x00004000 /* frame error */ - +#define C_IN_OVR_ERROR 0x00008000 /* overrun error */ +#define C_IN_RXOFL 0x00010000 /* RX buffer overflow */ +#define C_IN_IOCTLW 0x00020000 /* I/O control w/ wait */ + /* flow control */ #define C_FL_OXX 0x00000001 /* output Xon/Xoff flow control */ @@ -294,6 +303,8 @@ struct FIRM_ID { #define C_CM_RXBRK 0x84 /* Break received */ #define C_CM_PR_ERROR 0x85 /* Parity error */ #define C_CM_FR_ERROR 0x86 /* Frame error */ +#define C_CM_OVR_ERROR 0x87 /* Overrun error */ +#define C_CM_RXOFL 0x88 /* RX buffer overflow */ #define C_CM_CMDERROR 0x90 /* command error */ #define C_CM_FATAL 0x91 /* fatal error */ #define C_CM_HW_RESET 0x92 /* reset board */ @@ -468,9 +479,10 @@ struct cyclades_port { #define CyMaxChipsPerCard 8 -#define CyPCI_Ywin 0x4000 -#define CyPCI_Zctl 0x100 -#define CyPCI_Zwin 0x80000 +#define CyPCI_Ywin 0x4000 +#define CyPCI_Zctl 0x100 +#define CyPCI_Zwin 0x80000 +#define CyPCI_Ze_win (2 * CyPCI_Zwin) /**** CD1400 registers ****/ diff --git a/include/linux/dcache.h b/include/linux/dcache.h new file mode 100644 index 000000000..1ca8af0d7 --- /dev/null +++ b/include/linux/dcache.h @@ -0,0 +1,122 @@ +#ifndef __LINUX_DCACHE_H +#define __LINUX_DCACHE_H + +/* + * linux/include/linux/dcache.h + * + * Directory cache data structures + */ + +#define D_MAXLEN 1024 + +#define IS_ROOT(x) ((x) == (x)->d_parent) + +/* + * "quick string" -- eases parameter passing, but more importantly + * saves "metadata" about the string (ie length and the hash). + */ +struct qstr { + const unsigned char * name; + unsigned int len, hash; +}; + +/* Name hashing routines. Initial hash value */ +#define init_name_hash() 0 + +/* partial hash update function. Assume roughly 4 bits per character */ +static inline unsigned long partial_name_hash(unsigned char c, unsigned long prevhash) +{ + prevhash = (prevhash << 4) | (prevhash >> (8*sizeof(unsigned long)-4)); + return prevhash ^ c; +} + +/* Finally: cut down the number of bits to a int value (and try to avoid losing bits) */ +static inline unsigned long end_name_hash(unsigned long hash) +{ + if (sizeof(hash) > sizeof(unsigned int)) + hash += hash >> 4*sizeof(hash); + return (unsigned int) hash; +} + +struct dentry { + int d_count; + unsigned int d_flags; + struct inode * d_inode; /* Where the name belongs to - NULL is negative */ + struct dentry * d_parent; /* parent directory */ + struct dentry * d_mounts; /* mount information */ + struct dentry * d_covers; + struct list_head d_hash; /* lookup hash list */ + struct list_head d_alias; /* inode alias list */ + struct list_head d_lru; /* d_count = 0 LRU list */ + struct qstr d_name; + struct dentry * (*d_revalidate)(struct dentry *); +}; + +/* + * d_drop() unhashes the entry from the parent + * dentry hashes, so that it won't be found through + * a VFS lookup any more. Note that this is different + * from deleting the dentry - d_delete will try to + * mark the dentry negative if possible, giving a + * successful _negative_ lookup, while d_drop will + * just make the cache lookup fail. + * + * d_drop() is used mainly for stuff that wants + * to invalidate a dentry for some reason (NFS + * timeouts or autofs deletes). + */ +static inline void d_drop(struct dentry * dentry) +{ + list_del(&dentry->d_hash); + INIT_LIST_HEAD(&dentry->d_hash); +} + +/* + * These are the low-level FS interfaces to the dcache.. + */ +extern void d_instantiate(struct dentry *, struct inode *); +extern void d_delete(struct dentry *); + + +/* allocate/de-allocate */ +extern void d_free(struct dentry *); +extern struct dentry * d_alloc(struct dentry * parent, const struct qstr *name); +extern void shrink_dcache(void); + +/* only used at mount-time */ +extern struct dentry * d_alloc_root(struct inode * root_inode, struct dentry * old_root); + +/* + * This adds the entry to the hash queues and initializes "d_inode". + * The entry was actually filled in earlier during "d_alloc()" + */ +extern void d_add(struct dentry * entry, struct inode * inode); + +/* used for rename() and baskets */ +extern void d_move(struct dentry * entry, struct dentry * newparent, struct qstr * newname); + +/* appendix may either be NULL or be used for transname suffixes */ +extern struct dentry * d_lookup(struct dentry * dir, struct qstr * name); + +/* write full pathname into buffer and return length */ +extern int d_path(struct dentry * entry, struct dentry * chroot, char * buf); + +/* Allocation counts.. */ +static inline struct dentry * dget(struct dentry *dentry) +{ + if (dentry) + dentry->d_count++; + return dentry; +} + +extern void dput(struct dentry *); + +/* + * This is ugly. The inode:dentry relationship is a 1:n + * relationship, so we have to return one (random) dentry + * from the alias list. We select the first one.. + */ +#define i_dentry(inode) \ + list_entry((inode)->i_dentry.next, struct dentry, d_alias) + +#endif /* __LINUX_DCACHE_H */ diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h index 9c66cfc29..2831b1850 100644 --- a/include/linux/ext2_fs.h +++ b/include/linux/ext2_fs.h @@ -483,6 +483,7 @@ extern int ext2_getcluster (struct inode * inode, long block); extern void ext2_read_inode (struct inode *); extern void ext2_write_inode (struct inode *); extern void ext2_put_inode (struct inode *); +extern void ext2_delete_inode (struct inode *); extern int ext2_sync_inode (struct inode *); extern void ext2_discard_prealloc (struct inode *); @@ -492,17 +493,15 @@ extern int ext2_ioctl (struct inode *, struct file *, unsigned int, /* namei.c */ extern void ext2_release (struct inode *, struct file *); -extern int ext2_lookup (struct inode *,const char *, int, struct inode **); -extern int ext2_create (struct inode *,const char *, int, int, - struct inode **); -extern int ext2_mkdir (struct inode *, const char *, int, int); -extern int ext2_rmdir (struct inode *, const char *, int); -extern int ext2_unlink (struct inode *, const char *, int); -extern int ext2_symlink (struct inode *, const char *, int, const char *); -extern int ext2_link (struct inode *, struct inode *, const char *, int); -extern int ext2_mknod (struct inode *, const char *, int, int, int); -extern int ext2_rename (struct inode *, const char *, int, - struct inode *, const char *, int); +extern int ext2_lookup (struct inode *, struct dentry *); +extern int ext2_create (struct inode *,struct dentry *,int); +extern int ext2_mkdir (struct inode *,struct dentry *,int); +extern int ext2_rmdir (struct inode *,struct dentry *); +extern int ext2_unlink (struct inode *,struct dentry *); +extern int ext2_symlink (struct inode *,struct dentry *,const char *); +extern int ext2_link (struct inode *, struct inode *, struct dentry *); +extern int ext2_mknod (struct inode *, struct dentry *, int, int); +extern int ext2_rename (struct inode *, struct dentry *,struct inode *, struct dentry *); /* super.c */ extern void ext2_error (struct super_block *, const char *, const char *, ...) @@ -517,7 +516,7 @@ extern void ext2_write_super (struct super_block *); extern int ext2_remount (struct super_block *, int *, char *); extern struct super_block * ext2_read_super (struct super_block *,void *,int); extern int init_ext2_fs(void); -extern void ext2_statfs (struct super_block *, struct statfs *, int); +extern int ext2_statfs (struct super_block *, struct statfs *, int); /* truncate.c */ extern void ext2_truncate (struct inode *); diff --git a/include/linux/file.h b/include/linux/file.h index 0cb531c0c..b8cc01f0a 100644 --- a/include/linux/file.h +++ b/include/linux/file.h @@ -12,7 +12,7 @@ extern inline struct file * fget(unsigned long fd) return file; } -extern int __fput(struct file *, struct inode *); +extern int __fput(struct file *); extern void insert_file_free(struct file *file); /* It does not matter which list it is on. */ @@ -23,13 +23,13 @@ extern inline void remove_filp(struct file *file) *file->f_pprev = file->f_next; } -extern inline int fput(struct file *file, struct inode *inode) +extern inline int fput(struct file *file) { int count = file->f_count-1; int error = 0; if (!count) { - error = __fput(file, inode); + error = __fput(file); file->f_count = 0; remove_filp(file); insert_file_free(file); diff --git a/include/linux/fs.h b/include/linux/fs.h index 76fa53a7e..b459e966c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -15,12 +15,13 @@ #include <linux/net.h> #include <linux/kdev_t.h> #include <linux/ioctl.h> +#include <linux/list.h> +#include <linux/dcache.h> + #include <asm/atomic.h> +#include <asm/bitops.h> + -/* Prefixes for routines (having no effect), but indicate what - * the routine may do. This can greatly ease reasoning about routines... - */ -#define blocking /*routine may schedule()*/ /* * It's silly to have NR_OPEN bigger than NR_FILE, but I'll fix @@ -74,9 +75,6 @@ extern int max_files, nr_files; */ #define FS_IBASKET 8 /* FS does callback to free_ibasket() if space gets low. */ -/* public flags for i_status */ -#define ST_MODIFIED 1024 - /* * These are the fs-independent mount-flags: up to 16 flags are supported */ @@ -104,13 +102,6 @@ extern int max_files, nr_files; #define MS_MGC_MSK 0xffff0000 /* magic flag number mask */ /* - * Public flags for namei() - */ -#define NAM_PLAIN 0 /* Retrieve last component of pathname as is. */ -#define NAM_FOLLOW_LINK 2 /* If last component of path is a symlink, follow it */ -#define NAM_FOLLOW_TRAILSLASH 4 /* Follow last symlink only if trailed by slash. */ - -/* * Note that read-only etc flags are inode-specific: setting some file-system * flags just means all the inodes inherit those flags by default. It might be * possible to override it selectively if you really wanted to with some @@ -129,7 +120,12 @@ extern int max_files, nr_files; #define IS_APPEND(inode) ((inode)->i_flags & S_APPEND) #define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE) #define IS_NOATIME(inode) ((inode)->i_flags & MS_NOATIME) -#define DO_UPDATE_ATIME(inode) (!IS_NOATIME(inode) && !IS_RDONLY(inode)) + +#define UPDATE_ATIME(inode) \ + if (!IS_NOATIME(inode) && !IS_RDONLY(inode)) { \ + inode->i_atime = CURRENT_TIME; \ + mark_inode_dirty(inode); \ + } /* the read-only stuff doesn't really belong here, but any other place is probably as bad and I don't want to create yet another include file. */ @@ -155,7 +151,7 @@ extern int max_files, nr_files; extern void buffer_init(void); extern void inode_init(void); extern void file_table_init(void); -extern unsigned long name_cache_init(unsigned long start, unsigned long end); +extern void dcache_init(void); typedef char buffer_block[BLOCK_SIZE]; @@ -183,18 +179,16 @@ typedef char buffer_block[BLOCK_SIZE]; */ struct buffer_head { /* First cache line: */ + struct buffer_head * b_next; /* Hash queue list */ unsigned long b_blocknr; /* block number */ + unsigned long b_size; /* block size */ kdev_t b_dev; /* device (B_FREE = free) */ kdev_t b_rdev; /* Real device */ unsigned long b_rsector; /* Real buffer location on disk */ - struct buffer_head * b_next; /* Hash queue list */ struct buffer_head * b_this_page; /* circular list of buffers in one page */ - - /* Second cache line: */ unsigned long b_state; /* buffer state bitmap (see above) */ struct buffer_head * b_next_free; unsigned int b_count; /* users using this block */ - unsigned long b_size; /* block size */ /* Non-performance-critical data follows. */ char * b_data; /* pointer to data block (1024 bytes) */ @@ -305,14 +299,12 @@ struct iattr { #include <linux/quota.h> struct inode { - struct inode *i_hash_next; - struct inode *i_hash_prev; - struct inode *i_next; - struct inode *i_prev; + struct list_head i_hash; + struct list_head i_list; unsigned long i_ino; kdev_t i_dev; - atomic_t i_count; + unsigned short i_count; umode_t i_mode; nlink_t i_nlink; uid_t i_uid; @@ -335,28 +327,14 @@ struct inode { struct page *i_pages; struct dquot *i_dquot[MAXQUOTAS]; - struct inode *i_lru_next; - struct inode *i_lru_prev; + struct list_head i_dentry; - struct inode *i_basket_next; - struct inode *i_basket_prev; - struct dentry *i_dentry; + unsigned long i_state; - short i_ddir_count; - short i_dent_count; - unsigned short i_status; - unsigned short i_reuse_count; - - struct inode *i_mount; unsigned int i_flags; - unsigned char i_lock; - unsigned char i_dirt; unsigned char i_pipe; unsigned char i_sock; - unsigned char i_level; - unsigned short i_fill; - int i_writecount; unsigned int i_attr_flags; union { @@ -377,9 +355,21 @@ struct inode { } u; }; +/* Inode state bits.. */ +#define I_DIRTY 0 +#define I_LOCK 1 +#define I_FREEING 2 + +extern void __mark_inode_dirty(struct inode *); +static inline void mark_inode_dirty(struct inode *inode) +{ + if (!test_and_set_bit(I_DIRTY, &inode->i_state)) + __mark_inode_dirty(inode); +} + struct file { struct file *f_next, **f_pprev; - struct inode *f_inode; + struct dentry *f_dentry; struct file_operations *f_op; mode_t f_mode; loff_t f_pos; @@ -395,6 +385,8 @@ struct file { void *private_data; }; +extern int init_private_file(struct file *, struct dentry *, int); + #define FL_POSIX 1 #define FL_FLOCK 2 #define FL_BROKEN 4 /* broken flock() emulation */ @@ -503,8 +495,7 @@ struct super_block { unsigned long s_flags; unsigned long s_magic; unsigned long s_time; - struct inode *s_covered; - struct inode *s_mounted; + struct dentry *s_root; struct wait_queue *s_wait; struct inode *s_ibasket; @@ -553,16 +544,17 @@ struct file_operations { struct inode_operations { struct file_operations * default_file_ops; - int (*create) (struct inode *,const char *,int,int,struct inode **); - int (*lookup) (struct inode *,const char *,int,struct inode **); - int (*link) (struct inode *,struct inode *,const char *,int); - int (*unlink) (struct inode *,const char *,int); - int (*symlink) (struct inode *,const char *,int,const char *); - int (*mkdir) (struct inode *,const char *,int,int); - int (*rmdir) (struct inode *,const char *,int); - int (*mknod) (struct inode *,const char *,int,int,int); - int (*rename) (struct inode *,const char *,int,struct inode *,const char *,int); + int (*create) (struct inode *,struct dentry *,int); + int (*lookup) (struct inode *,struct dentry *); + int (*link) (struct inode *,struct inode *,struct dentry *); + int (*unlink) (struct inode *,struct dentry *); + int (*symlink) (struct inode *,struct dentry *,const char *); + int (*mkdir) (struct inode *,struct dentry *,int); + int (*rmdir) (struct inode *,struct dentry *); + int (*mknod) (struct inode *,struct dentry *,int,int); + int (*rename) (struct inode *,struct dentry *,struct inode *,struct dentry *); int (*readlink) (struct inode *,char *,int); + struct dentry * (*follow_link) (struct inode *, struct dentry *); int (*readpage) (struct inode *, struct page *); int (*writepage) (struct inode *, struct page *); int (*bmap) (struct inode *,int); @@ -576,12 +568,13 @@ struct inode_operations { struct super_operations { void (*read_inode) (struct inode *); - int (*notify_change) (struct inode *, struct iattr *); void (*write_inode) (struct inode *); void (*put_inode) (struct inode *); + void (*delete_inode) (struct inode *); + int (*notify_change) (struct inode *, struct iattr *); void (*put_super) (struct super_block *); void (*write_super) (struct super_block *); - void (*statfs) (struct super_block *, struct statfs *, int); + int (*statfs) (struct super_block *, struct statfs *, int); int (*remount_fs) (struct super_block *, int *, char *); }; @@ -610,7 +603,7 @@ asmlinkage int sys_close(unsigned int); /* yes, it's really unsigned */ extern void kill_fasync(struct fasync_struct *fa, int sig); -extern int getname(const char * filename, char **result); +extern char * getname(const char * filename); extern void putname(char * name); extern int do_truncate(struct inode *, unsigned long); extern int register_blkdev(unsigned int, const char *, struct file_operations *); @@ -640,8 +633,8 @@ extern struct file_operations rdwr_pipe_fops; extern struct file_system_type *get_fs_type(const char *name); extern int fs_may_mount(kdev_t dev); -extern int fs_may_umount(kdev_t dev, struct inode * mount_root); -extern int fs_may_remount_ro(kdev_t dev); +extern int fs_may_umount(struct super_block *, struct dentry * root); +extern int fs_may_remount_ro(struct super_block *); extern struct file *inuse_filps; extern struct super_block super_blocks[NR_SUPER]; @@ -689,15 +682,31 @@ extern int fsync_dev(kdev_t dev); extern void sync_supers(kdev_t dev); extern int bmap(struct inode * inode,int block); extern int notify_change(struct inode *, struct iattr *); -extern int namei(int retr_mode, const char *pathname, struct inode **res_inode); extern int permission(struct inode * inode,int mask); extern int get_write_access(struct inode *inode); extern void put_write_access(struct inode *inode); -extern int open_namei(const char * pathname, int flag, int mode, - struct inode ** res_inode, struct inode * base); -extern int do_mknod(const char * filename, int mode, dev_t dev); +extern struct dentry * open_namei(const char * pathname, int flag, int mode); +extern struct dentry * do_mknod(const char * filename, int mode, dev_t dev); extern int do_pipe(int *); +/* + * Kernel pointers have redundant information, so we can use a + * scheme where we can return either an error code or a dentry + * pointer with the same return value. + * + * This should be a per-architecture thing, to allow different + * error and pointer decisions. + */ +#define ERR_PTR(err) ((void *)((long)(err))) +#define PTR_ERR(ptr) ((long)(ptr)) +#define IS_ERR(ptr) ((unsigned long)(ptr) > (unsigned long)(-1000)) + +extern struct dentry * lookup_dentry(const char *, struct dentry *, int); +extern struct dentry * __namei(const char *, int); + +#define namei(pathname) __namei(pathname, 1) +#define lnamei(pathname) __namei(pathname, 0) + #include <asm/semaphore.h> /* Intended for short locks of the global data structures in inode.c. @@ -726,87 +735,19 @@ extern inline void vfs_unlock(void) /* Not to be used by ordinary vfs users */ extern void _get_inode(struct inode * inode); -extern blocking void __iput(struct inode * inode); - -/* This must not be called if the inode is not in use (i.e. given - * back with iput(). The atomic inc assumes that the inode is - * already in use, and just has to be incremented higher. - * Please do not directly manipulate i_count any more. - * Use iget, iinc and iput. - * You may test i_count for zero if you are aware that it - * might change under you. - */ -extern inline void iinc(struct inode * inode) -{ - atomic_inc(&inode->i_count); -} - -/* The same, but the inode may not be in use. This must be called - * with vfslock() held, and be asure that the inode argument is - * valid (i.e. not out of cache). So the vfs_lock() must span the - * retrieval method of the inode. - */ -extern inline void iinc_zero(struct inode * inode) -{ - if(!atomic_read(&inode->i_count)) { - atomic_inc(&inode->i_count); - _get_inode(inode); - } else - atomic_inc(&inode->i_count); -} - -extern blocking void _iput(struct inode * inode); -extern inline blocking void iput(struct inode * inode) -{ - if(inode) { - extern void wake_up_interruptible(struct wait_queue **q); +extern void iput(struct inode * inode); - if(inode->i_pipe) - wake_up_interruptible(&inode->u.pipe_i.wait); +extern struct inode * iget(struct super_block * sb, unsigned long nr); +extern void clear_inode(struct inode * inode); +extern struct inode * get_empty_inode(void); - /* It does not matter if somebody re-increments it in between, - * only the _last_ user needs to call _iput(). - */ - if(atomic_dec_and_test(&inode->i_count) && inode->i_ddir_count <= 0) - _iput(inode); - } -} - -extern blocking struct inode * __iget(struct super_block * sb, unsigned long nr, int crsmnt); -extern blocking void _clear_inode(struct inode * inode, int external, int verbose); -extern blocking inline void clear_inode(struct inode * inode) -{ - vfs_lock(); - _clear_inode(inode, 1, 1); - vfs_unlock(); -} -extern blocking struct inode * _get_empty_inode(void); -extern inline blocking struct inode * get_empty_inode(void) -{ - struct inode * inode; - vfs_lock(); - inode = _get_empty_inode(); - vfs_unlock(); - return inode; -} /* Please prefer to use this function in future, instead of using * a get_empty_inode()/insert_inode_hash() combination. * It allows for better checking and less race conditions. */ -blocking struct inode * get_empty_inode_hashed(dev_t i_dev, unsigned long i_ino); - -extern inline blocking int free_ibasket(struct super_block * sb) -{ - extern blocking int _free_ibasket(struct super_block * sb); - int res; - vfs_lock(); - res = _free_ibasket(sb); - vfs_unlock(); - return res; -} +extern struct inode * get_empty_inode_hashed(dev_t i_dev, unsigned long i_ino); extern void insert_inode_hash(struct inode *); -extern blocking struct inode * get_pipe_inode(void); extern int get_unused_fd(void); extern void put_unused_fd(int); extern struct file * get_empty_filp(void); @@ -872,12 +813,6 @@ extern int dcache_lookup(struct inode *, const char *, int, unsigned long *); extern int inode_change_ok(struct inode *, struct iattr *); extern void inode_setattr(struct inode *, struct iattr *); -extern inline blocking -struct inode * iget(struct super_block * sb, unsigned long nr) -{ - return __iget(sb, nr, 1); -} - /* kludge to get SCSI modules working */ #include <linux/minix_fs.h> #include <linux/minix_fs_sb.h> diff --git a/include/linux/ghash.h b/include/linux/ghash.h new file mode 100644 index 000000000..278f6c2f6 --- /dev/null +++ b/include/linux/ghash.h @@ -0,0 +1,218 @@ +/* + * include/linux/ghash.h -- generic hashing with fuzzy retrieval + * + * (C) 1997 Thomas Schoebel-Theuer + * + * The algorithms implemented here seem to be a completely new invention, + * and I'll publish the fundamentals in a paper. + */ + +#ifndef _GHASH_H +#define _GHASH_H +/* HASHSIZE _must_ be a power of two!!! */ + + +#define DEF_HASH_FUZZY_STRUCTS(NAME,HASHSIZE,TYPE) \ +\ +struct NAME##_table {\ + TYPE * hashtable[HASHSIZE];\ + TYPE * sorted_list;\ + int nr_entries;\ +};\ +\ +struct NAME##_ptrs {\ + TYPE * next_hash;\ + TYPE * prev_hash;\ + TYPE * next_sorted;\ + TYPE * prev_sorted;\ +}; + +#define DEF_HASH_FUZZY(LINKAGE,NAME,HASHSIZE,TYPE,PTRS,KEYTYPE,KEY,KEYCMP,KEYEQ,HASHFN)\ +\ +LINKAGE void insert_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\ +{\ + int ix = HASHFN(elem->KEY);\ + TYPE ** base = &tbl->hashtable[ix];\ + TYPE * ptr = *base;\ + TYPE * prev = NULL;\ +\ + tbl->nr_entries++;\ + while(ptr && KEYCMP(ptr->KEY, elem->KEY)) {\ + base = &ptr->PTRS.next_hash;\ + prev = ptr;\ + ptr = *base;\ + }\ + elem->PTRS.next_hash = ptr;\ + elem->PTRS.prev_hash = prev;\ + if(ptr) {\ + ptr->PTRS.prev_hash = elem;\ + }\ + *base = elem;\ +\ + ptr = prev;\ + if(!ptr) {\ + ptr = tbl->sorted_list;\ + prev = NULL;\ + } else {\ + prev = ptr->PTRS.prev_sorted;\ + }\ + while(ptr) {\ + TYPE * next = ptr->PTRS.next_hash;\ + if(next && KEYCMP(next->KEY, elem->KEY)) {\ + prev = ptr;\ + ptr = next;\ + } else if(KEYCMP(ptr->KEY, elem->KEY)) {\ + prev = ptr;\ + ptr = ptr->PTRS.next_sorted;\ + } else\ + break;\ + }\ + elem->PTRS.next_sorted = ptr;\ + elem->PTRS.prev_sorted = prev;\ + if(ptr) {\ + ptr->PTRS.prev_sorted = elem;\ + }\ + if(prev) {\ + prev->PTRS.next_sorted = elem;\ + } else {\ + tbl->sorted_list = elem;\ + }\ +}\ +\ +LINKAGE void remove_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\ +{\ + TYPE * next = elem->PTRS.next_hash;\ + TYPE * prev = elem->PTRS.prev_hash;\ +\ + tbl->nr_entries--;\ + if(next)\ + next->PTRS.prev_hash = prev;\ + if(prev)\ + prev->PTRS.next_hash = next;\ + else {\ + int ix = HASHFN(elem->KEY);\ + tbl->hashtable[ix] = next;\ + }\ +\ + next = elem->PTRS.next_sorted;\ + prev = elem->PTRS.prev_sorted;\ + if(next)\ + next->PTRS.prev_sorted = prev;\ + if(prev)\ + prev->PTRS.next_sorted = next;\ + else\ + tbl->sorted_list = next;\ +}\ +\ +LINKAGE TYPE * find_##NAME##_hash(struct NAME##_table * tbl, KEYTYPE pos)\ +{\ + int ix = hashfn(pos);\ + TYPE * ptr = tbl->hashtable[ix];\ + while(ptr && KEYCMP(ptr->KEY, pos))\ + ptr = ptr->PTRS.next_hash;\ + if(ptr && !KEYEQ(ptr->KEY, pos))\ + ptr = NULL;\ + return ptr;\ +}\ +\ +LINKAGE TYPE * find_##NAME##_hash_fuzzy(struct NAME##_table * tbl, KEYTYPE pos)\ +{\ + int ix;\ + int offset;\ + TYPE * ptr;\ + TYPE * next;\ +\ + ptr = tbl->sorted_list;\ + if(!ptr || KEYCMP(pos, ptr->KEY))\ + return NULL;\ + ix = HASHFN(pos);\ + offset = HASHSIZE;\ + do {\ + offset >>= 1;\ + next = tbl->hashtable[(ix+offset) & ((HASHSIZE)-1)];\ + if(next && (KEYCMP(next->KEY, pos) || KEYEQ(next->KEY, pos))\ + && KEYCMP(ptr->KEY, next->KEY))\ + ptr = next;\ + } while(offset);\ +\ + for(;;) {\ + next = ptr->PTRS.next_hash;\ + if(next) {\ + if(KEYCMP(next->KEY, pos)) {\ + ptr = next;\ + continue;\ + }\ + }\ + next = ptr->PTRS.next_sorted;\ + if(next && KEYCMP(next->KEY, pos)) {\ + ptr = next;\ + continue;\ + }\ + return ptr;\ + }\ + return NULL;\ +} + +#define DEF_HASH_STRUCTS(NAME,HASHSIZE,TYPE) \ +\ +struct NAME##_table {\ + TYPE * hashtable[HASHSIZE];\ + int nr_entries;\ +};\ +\ +struct NAME##_ptrs {\ + TYPE * next_hash;\ + TYPE * prev_hash;\ +}; + +#define DEF_HASH(LINKAGE,NAME,HASHSIZE,TYPE,PTRS,KEYTYPE,KEY,KEYCMP,KEYEQ,HASHFN)\ +\ +LINKAGE void insert_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\ +{\ + int ix = HASHFN(elem->KEY);\ + TYPE ** base = &tbl->hashtable[ix];\ + TYPE * ptr = *base;\ + TYPE * prev = NULL;\ +\ + tbl->nr_entries++;\ + while(ptr && KEYCMP(ptr->KEY, elem->KEY)) {\ + base = &ptr->PTRS.next_hash;\ + prev = ptr;\ + ptr = *base;\ + }\ + elem->PTRS.next_hash = ptr;\ + elem->PTRS.prev_hash = prev;\ + if(ptr) {\ + ptr->PTRS.prev_hash = elem;\ + }\ + *base = elem;\ +}\ +\ +LINKAGE void remove_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\ +{\ + TYPE * next = elem->PTRS.next_hash;\ + TYPE * prev = elem->PTRS.prev_hash;\ +\ + tbl->nr_entries--;\ + if(next)\ + next->PTRS.prev_hash = prev;\ + if(prev)\ + prev->PTRS.next_hash = next;\ + else {\ + int ix = HASHFN(elem->KEY);\ + tbl->hashtable[ix] = next;\ + }\ +}\ +\ +LINKAGE TYPE * find_##NAME##_hash(struct NAME##_table * tbl, KEYTYPE pos)\ +{\ + int ix = hashfn(pos);\ + TYPE * ptr = tbl->hashtable[ix];\ + while(ptr && KEYCMP(ptr->KEY, pos))\ + ptr = ptr->PTRS.next_hash;\ + if(ptr && !KEYEQ(ptr->KEY, pos))\ + ptr = NULL;\ + return ptr;\ +} + +#endif diff --git a/include/linux/if_arp.h b/include/linux/if_arp.h index 5f18a049a..c93764cb7 100644 --- a/include/linux/if_arp.h +++ b/include/linux/if_arp.h @@ -47,6 +47,8 @@ #define ARPHRD_ROSE 270 #define ARPHRD_X25 271 /* CCITT X.25 */ #define ARPHRD_PPP 512 +#define ARPHRD_HDLC 513 /* (Cisco) HDLC */ +#define ARPHRD_LAPB 516 /* LAPB */ #define ARPHRD_TUNNEL 768 /* IPIP tunnel */ #define ARPHRD_TUNNEL6 769 /* IPIP6 tunnel */ diff --git a/drivers/net/shaper.h b/include/linux/if_shaper.h index abb6198af..abb6198af 100644 --- a/drivers/net/shaper.h +++ b/include/linux/if_shaper.h diff --git a/include/linux/iso_fs.h b/include/linux/iso_fs.h index 03239cfc3..011edfe6f 100644 --- a/include/linux/iso_fs.h +++ b/include/linux/iso_fs.h @@ -152,8 +152,7 @@ extern int find_rock_ridge_relocation(struct iso_directory_record *, struct inod extern int isofs_open(struct inode * inode, struct file * filp); extern void isofs_release(struct inode * inode, struct file * filp); -extern int isofs_lookup(struct inode * dir,const char * name, int len, - struct inode ** result); +extern int isofs_lookup(struct inode * dir, struct dentry *); extern unsigned long isofs_count_free_inodes(struct super_block *sb); extern int isofs_new_block(int dev); extern int isofs_free_block(int dev, int block); @@ -164,7 +163,7 @@ extern struct super_block *isofs_read_super(struct super_block *,void *,int); extern int init_iso9660_fs(void); extern void isofs_read_inode(struct inode *); extern void isofs_put_inode(struct inode *); -extern void isofs_statfs(struct super_block *, struct statfs *, int); +extern int isofs_statfs(struct super_block *, struct statfs *, int); extern int isofs_lseek(struct inode *, struct file *, off_t, int); extern int isofs_read(struct inode *, struct file *, char *, int); diff --git a/include/linux/iso_fs_i.h b/include/linux/iso_fs_i.h index d8065500f..a1343a636 100644 --- a/include/linux/iso_fs_i.h +++ b/include/linux/iso_fs_i.h @@ -6,7 +6,6 @@ */ struct iso_inode_info { unsigned int i_first_extent; - unsigned int i_backlink; unsigned char i_file_format; }; diff --git a/include/linux/joystick.h b/include/linux/joystick.h new file mode 100644 index 000000000..a9a56273d --- /dev/null +++ b/include/linux/joystick.h @@ -0,0 +1,61 @@ +#ifndef _LINUX_JOYSTICK_H +#define _LINUX_JOYSTICK_H + +#define JS_RETURN sizeof(struct js_status) /*number of bytes returned by js_read*/ +#define JS_TRUE 1 +#define JS_FALSE 0 +#define JS_PORT 0x201 /*io port for joystick operations*/ +#define JS_DEF_TIMEOUT 0x1300 /*default timeout value for js_read()*/ +#define JS_DEF_CORR 0 /*default correction factor*/ +#define JS_DEF_TIMELIMIT 10L /*default data valid time =10 jiffies == 100ms*/ +#define JS_X_0 0x01 /*bit mask for x-axis js0*/ +#define JS_Y_0 0x02 /*bit mask for y-axis js0*/ +#define JS_X_1 0x04 /*bit mask for x-axis js1*/ +#define JS_Y_1 0x08 /*bit mask for y-axis js1*/ +#define JS_MAX 2 /*Max number of joysticks*/ +#define PIT_MODE 0x43 /*io port for timer 0*/ +#define PIT_COUNTER_0 0x40 /*io port for timer 0*/ +#define JSIOCSCAL 0x01 /*ioctl cmd to set joystick correction factor*/ +#define JSIOCGCAL 0x02 /*ioctl cmd to get joystick correction factor*/ +#define JSIOCSTIMEOUT 0x03 /*ioctl cmd to set maximum number of iterations + to wait for a timeout*/ +#define JSIOCGTIMEOUT 0x04 /*as above, to get*/ +#define JSIOCSTIMELIMIT 0x05 /*set data retention time*/ +#define JSIOCGTIMELIMIT 0x06 /*get data retention time*/ +#define JSIOCGCONFIG 0x07 /*get the whole js_data[minor] struct*/ +#define JSIOCSCONFIG 0x08 /*set the whole js_data[minor] struct + except js_busy!*/ + +/* + * This union is used for the ioctl to set the scaling factor and to + * return the current values for a joystick. 'buttons' is ignored on + * the ioctl call + */ + +struct js_status +{ + int buttons; + int x; + int y; +}; + +/* + * This struct is used for misc data about the joystick + */ + +struct js_config +{ + int js_timeout; /*timeout*/ + int busy; /*joystick is in use*/ + long js_expiretime; /*Time when stick after which stick must be re-read*/ + long js_timelimit; /*Max time before data is invalid*/ + struct js_status js_save; /*last read data*/ + struct js_status js_corr; /*correction factor*/ +}; + +#define LATCH (1193180L/HZ) /*initial timer 0 value*/ +#define DELTA_TIME(X,Y) ((X)-(Y)+(((X)>=(Y))?0:LATCH)) + +extern int joystick_init(void); + +#endif /* _LINUX_JOYSTICK_H */ diff --git a/include/linux/list.h b/include/linux/list.h new file mode 100644 index 000000000..84a0ecef6 --- /dev/null +++ b/include/linux/list.h @@ -0,0 +1,63 @@ +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +/* + * Simple doubly linked list implementation. + */ +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD(name) \ + struct list_head name = { &name, &name } + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +static inline void list_add(struct list_head *new, struct list_head *head) +{ + struct list_head *next = head->next; + next->prev = new; + new->next = next; + new->prev = head; + head->next = new; +} + +static inline void list_del(struct list_head *entry) +{ + struct list_head *next, *prev; + next = entry->next; + prev = entry->prev; + next->prev = prev; + prev->next = next; +} + +static inline int list_empty(struct list_head *head) +{ + return head->next == head; +} + +/* + * Splice in "list" into "head" + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + struct list_head *first = list->next; + + if (first != list) { + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; + } +} + +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +#endif diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 72035cc5c..5c1d0e4cc 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -166,7 +166,7 @@ void nlmsvc_free_host_resources(struct nlm_host *); extern __inline__ struct inode * nlmsvc_file_inode(struct nlm_file *file) { - return file->f_file.f_inode; + return file->f_file.f_dentry->d_inode; } /* diff --git a/include/linux/minix_fs.h b/include/linux/minix_fs.h index 069686fff..8c75cc3c5 100644 --- a/include/linux/minix_fs.h +++ b/include/linux/minix_fs.h @@ -88,19 +88,17 @@ struct minix_dir_entry { #ifdef __KERNEL__ -extern int minix_lookup(struct inode * dir,const char * name, int len, - struct inode ** result); -extern int minix_create(struct inode * dir,const char * name, int len, int mode, - struct inode ** result); -extern int minix_mkdir(struct inode * dir, const char * name, int len, int mode); -extern int minix_rmdir(struct inode * dir, const char * name, int len); -extern int minix_unlink(struct inode * dir, const char * name, int len); -extern int minix_symlink(struct inode * inode, const char * name, int len, +extern int minix_lookup(struct inode * dir, struct dentry *dentry); +extern int minix_create(struct inode * dir, struct dentry *dentry, int mode); +extern int minix_mkdir(struct inode * dir, struct dentry *dentry, int mode); +extern int minix_rmdir(struct inode * dir, struct dentry *dentry); +extern int minix_unlink(struct inode * dir, struct dentry *dentry); +extern int minix_symlink(struct inode * inode, struct dentry *dentry, const char * symname); -extern int minix_link(struct inode * oldinode, struct inode * dir, const char * name, int len); -extern int minix_mknod(struct inode * dir, const char * name, int len, int mode, int rdev); -extern int minix_rename(struct inode * old_dir, const char * old_name, int old_len, - struct inode * new_dir, const char * new_name, int new_len); +extern int minix_link(struct inode * oldinode, struct inode * dir, struct dentry *dentry); +extern int minix_mknod(struct inode * dir, struct dentry *dentry, int mode, int rdev); +extern int minix_rename(struct inode * old_dir, struct dentry *old_dentry, + struct inode * new_dir, struct dentry *new_dentry); extern struct inode * minix_new_inode(const struct inode * dir); extern void minix_free_inode(struct inode * inode); extern unsigned long minix_count_free_inodes(struct super_block *sb); @@ -121,8 +119,7 @@ extern void minix_write_super(struct super_block *); extern int minix_remount (struct super_block * sb, int * flags, char * data); extern void minix_read_inode(struct inode *); extern void minix_write_inode(struct inode *); -extern void minix_put_inode(struct inode *); -extern void minix_statfs(struct super_block *, struct statfs *, int); +extern int minix_statfs(struct super_block *, struct statfs *, int); extern int minix_sync_inode(struct inode *); extern int minix_sync_file(struct inode *, struct file *); diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h index 471caa0b7..8f4bde9ab 100644 --- a/include/linux/miscdevice.h +++ b/include/linux/miscdevice.h @@ -8,6 +8,7 @@ #define AMIGAMOUSE_MINOR 4 #define ATARIMOUSE_MINOR 5 #define SUN_MOUSE_MINOR 6 +#define PC110PAD_MINOR 9 #define RTC_MINOR 135 #define SUN_OPENPROM_MINOR 139 #define MISC_DYNAMIC_MINOR 255 diff --git a/include/linux/mm.h b/include/linux/mm.h index c8b9046a7..bfde668c7 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -48,7 +48,7 @@ struct vm_area_struct { struct vm_operations_struct * vm_ops; unsigned long vm_offset; - struct inode * vm_inode; + struct dentry * vm_dentry; unsigned long vm_pte; /* shared mem */ }; @@ -118,7 +118,7 @@ typedef struct page { unsigned long offset; struct page *next_hash; atomic_t count; - unsigned flags; /* atomic flags, some possibly updated asynchronously */ + unsigned long flags; /* atomic flags, some possibly updated asynchronously */ unsigned dirty:16, age:8; struct wait_queue *wait; diff --git a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h index 36ed2a890..0badd9ebe 100644 --- a/include/linux/msdos_fs.h +++ b/include/linux/msdos_fs.h @@ -215,7 +215,7 @@ extern void fat_put_super(struct super_block *sb); extern void fat_read_inode(struct inode *inode, struct inode_operations *dir_ops); extern struct super_block *fat_read_super(struct super_block *s, void *data, int silent); extern void msdos_put_super(struct super_block *sb); -extern void fat_statfs(struct super_block *sb,struct statfs *buf, int); +extern int fat_statfs(struct super_block *sb,struct statfs *buf, int); extern void fat_write_inode(struct inode *inode); /* dir.c */ diff --git a/include/linux/net.h b/include/linux/net.h index bd30d7804..82a4b7570 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -136,5 +136,8 @@ extern int sock_sendmsg(struct socket *, struct msghdr *m, int len); extern int sock_recvmsg(struct socket *, struct msghdr *m, int len, int flags); extern int sock_readv_writev(int type, struct inode * inode, struct file * file, const struct iovec * iov, long count, long size); + +int net_ratelimit(void); + #endif /* __KERNEL__ */ #endif /* _LINUX_NET_H */ diff --git a/include/linux/nfsd/nfsfh.h b/include/linux/nfsd/nfsfh.h index 489762a36..75c2c91b2 100644 --- a/include/linux/nfsd/nfsfh.h +++ b/include/linux/nfsd/nfsfh.h @@ -57,7 +57,7 @@ struct knfs_fh { typedef struct svc_fh { struct knfs_fh fh_handle; /* FH data */ struct svc_export * fh_export; /* export pointer */ - struct inode * fh_inode; /* inode */ + struct dentry * fh_dentry; /* file */ size_t fh_pre_size; /* size before operation */ time_t fh_pre_mtime; /* mtime before oper */ time_t fh_pre_ctime; /* ctime before oper */ @@ -98,7 +98,7 @@ fh_init(struct svc_fh *fhp) static inline void fh_lock(struct svc_fh *fhp) { - struct inode *inode = fhp->fh_inode; + struct inode *inode = fhp->fh_dentry->d_inode; /* dfprintk(FILEOP, "nfsd: fh_lock(%x/%ld) locked = %d\n", @@ -118,7 +118,7 @@ fh_lock(struct svc_fh *fhp) static inline void fh_unlock(struct svc_fh *fhp) { - struct inode *inode = fhp->fh_inode; + struct inode *inode = fhp->fh_dentry->d_inode; if (fhp->fh_locked) { if (!fhp->fh_post_version) @@ -135,9 +135,9 @@ fh_unlock(struct svc_fh *fhp) static inline void fh_put(struct svc_fh *fhp) { - if (fhp->fh_inode) { + if (fhp->fh_dentry) { fh_unlock(fhp); - iput(fhp->fh_inode); + dput(fhp->fh_dentry); } } #else @@ -146,19 +146,19 @@ fh_put(struct svc_fh *fhp) static inline void __fh_put(struct svc_fh *fhp, char *file, int line) { - struct inode *inode; + struct dentry *dentry; - if (!(inode = fhp->fh_inode)) + if (!(dentry = fhp->fh_dentry)) return; - if (!atomic_read(&inode->i_count)) { - printk("nfsd: trying to free free inode in %s:%d\n" - " dev %04x ino %ld, mode %07o\n", - file, line, inode->i_dev, - inode->i_ino, inode->i_mode); + if (!dentry->d_count) { + printk("nfsd: trying to free free dentry in %s:%d\n" + " file %s/%s\n", + file, line, + dentry->d_parent->d_name.name, dentry->d_name.name); } else { fh_unlock(fhp); - iput(inode); + dput(dentry); } } #endif diff --git a/include/linux/omirr.h b/include/linux/omirr.h deleted file mode 100644 index 379867423..000000000 --- a/include/linux/omirr.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * fs/proc/omirr.c - online mirror support - * - * (C) 1997 Thomas Schoebel-Theuer - */ - -#ifndef OMIRR_H -#define OMIRR_H -#include <linux/fs.h> -#include <linux/dalloc.h> - -extern int omirr_print(struct dentry * ent1, struct dentry * ent2, - struct qstr * suffix, const char * fmt, ...); - -extern int omirr_printall(struct inode * inode, const char * fmt, ...); - -#endif diff --git a/include/linux/pci.h b/include/linux/pci.h index b13929d6a..138e67bfe 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -497,7 +497,7 @@ #define PCI_DEVICE_ID_VIA_82C586_1 0x0571 #define PCI_DEVICE_ID_VIA_82C576 0x0576 #define PCI_DEVICE_ID_VIA_82C585 0x0585 -#define PCI_DEVICE_ID_VIA_82C586_0 0x0586 +#define PCI_DEVICE_ID_VIA_82C586 0x0586 #define PCI_DEVICE_ID_VIA_82C416 0x1571 #define PCI_VENDOR_ID_VORTEX 0x1119 diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index 21b39d628..3a847d72c 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -5,7 +5,6 @@ struct pipe_inode_info { struct wait_queue * wait; char * base; unsigned int start; - unsigned int len; unsigned int lock; unsigned int rd_openers; unsigned int wr_openers; @@ -16,7 +15,7 @@ struct pipe_inode_info { #define PIPE_WAIT(inode) ((inode).u.pipe_i.wait) #define PIPE_BASE(inode) ((inode).u.pipe_i.base) #define PIPE_START(inode) ((inode).u.pipe_i.start) -#define PIPE_LEN(inode) ((inode).u.pipe_i.len) +#define PIPE_LEN(inode) ((inode).i_size) #define PIPE_RD_OPENERS(inode) ((inode).u.pipe_i.rd_openers) #define PIPE_WR_OPENERS(inode) ((inode).u.pipe_i.wr_openers) #define PIPE_READERS(inode) ((inode).u.pipe_i.readers) diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index b0e33a0c6..80cdf71a7 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -310,10 +310,11 @@ static inline int proc_scsi_unregister(struct proc_dir_entry *driver, int x) extern struct super_block *proc_read_super(struct super_block *,void *,int); extern int init_proc_fs(void); extern struct inode * proc_get_inode(struct super_block *, int, struct proc_dir_entry *); -extern void proc_statfs(struct super_block *, struct statfs *, int); +extern int proc_statfs(struct super_block *, struct statfs *, int); extern void proc_read_inode(struct inode *); extern void proc_write_inode(struct inode *); -extern int proc_match(int, const char *, struct proc_dir_entry *); + +extern int proc_match(int, const char *,struct proc_dir_entry *); /* * These are generic /proc routines that use the internal @@ -323,7 +324,7 @@ extern int proc_match(int, const char *, struct proc_dir_entry *); * of the /proc/<pid> subdirectories. */ extern int proc_readdir(struct inode *, struct file *, void *, filldir_t); -extern int proc_lookup(struct inode *, const char *, int, struct inode **); +extern int proc_lookup(struct inode *, struct dentry *); struct openpromfs_dev { struct openpromfs_dev *next; @@ -335,7 +336,7 @@ struct openpromfs_dev { }; extern struct inode_operations * proc_openprom_register(int (*readdir)(struct inode *, struct file *, void *, filldir_t), - int (*lookup)(struct inode *, const char *, int, struct inode **), + int (*lookup)(struct inode *, struct qstr *, struct inode **), void (*use)(struct inode *, int), struct openpromfs_dev ***); extern void proc_openprom_deregister(void); @@ -363,9 +364,6 @@ extern struct inode_operations proc_ringbuf_inode_operations; #endif extern struct inode_operations proc_omirr_inode_operations; -/* Not sure whether this belongs here */ -int proc_arbitrary_lookup(struct inode * dir, const char * name, - int len, struct inode ** result); #endif /* diff --git a/include/linux/random.h b/include/linux/random.h index b2706ce03..7be5e9898 100644 --- a/include/linux/random.h +++ b/include/linux/random.h @@ -55,6 +55,8 @@ extern void get_random_bytes(void *buf, int nbytes); extern __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr, __u16 sport, __u16 dport); +extern __u32 secure_tcp_syn_cookie(__u32 saddr, __u32 daddr, + __u16 sport, __u16 dport, __u32 sseq, __u32 count); #ifndef MODULE extern struct file_operations random_fops, urandom_fops; diff --git a/include/linux/rose.h b/include/linux/rose.h index 2ca68dbaa..cd82c95c3 100644 --- a/include/linux/rose.h +++ b/include/linux/rose.h @@ -8,8 +8,9 @@ #define ROSE_KERNEL_H #define PF_ROSE AF_ROSE -#define ROSE_MTU 128 +#define ROSE_MTU 251 +#define ROSE_DEFER 1 #define ROSE_T1 2 #define ROSE_T2 3 #define ROSE_T3 4 @@ -17,7 +18,22 @@ #define ROSE_QBITINCL 6 #define ROSE_HOLDBACK 7 +#define SIOCRSGCAUSE (SIOCPROTOPRIVATE+0) +#define SIOCRSSCAUSE (SIOCPROTOPRIVATE+1) #define SIOCRSL2CALL (SIOCPROTOPRIVATE+2) +#define SIOCRSACCEPT (SIOCPROTOPRIVATE+3) +#define SIOCRSCLRRT (SIOCPROTOPRIVATE+4) + +#define ROSE_DTE_ORIGINATED 0x00 +#define ROSE_NUMBER_BUSY 0x01 +#define ROSE_INVALID_FACILITY 0x03 +#define ROSE_NETWORK_CONGESTION 0x05 +#define ROSE_OUT_OF_ORDER 0x09 +#define ROSE_ACCESS_BARRED 0x0B +#define ROSE_NOT_OBTAINABLE 0x0D +#define ROSE_REMOTE_PROCEDURE 0x11 +#define ROSE_LOCAL_PROCEDURE 0x13 +#define ROSE_SHIP_ABSENT 0x39 typedef struct { char rose_addr[5]; @@ -40,4 +56,9 @@ struct rose_route_struct { ax25_address digipeaters[AX25_MAX_DIGIS]; }; +struct rose_cause_struct { + unsigned char cause; + unsigned char diagnostic; +}; + #endif diff --git a/include/linux/sched.h b/include/linux/sched.h index 189194a49..361498ead 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -130,7 +130,7 @@ struct files_struct { struct fs_struct { int count; int umask; - struct inode * root, * pwd; + struct dentry * root, * pwd; }; #define INIT_FS { \ @@ -519,12 +519,10 @@ extern inline void __remove_wait_queue(struct wait_queue ** p, struct wait_queue { struct wait_queue * next = wait->next; struct wait_queue * head = next; + struct wait_queue * tmp; - for (;;) { - struct wait_queue * nextlist = head->next; - if (nextlist == wait) - break; - head = nextlist; + while ((tmp = head->next) != wait) { + head = tmp; } head->next = next; } diff --git a/include/linux/sdla_fr.h b/include/linux/sdla_fr.h index bcca8d58d..c66ee0ec3 100644 --- a/include/linux/sdla_fr.h +++ b/include/linux/sdla_fr.h @@ -281,7 +281,7 @@ typedef struct fr_dlc_conf } fr_dlc_conf_t; /*---------------------------------------------------------------------------- - * S502 Interrupt mode control block. + * S502 interrupt mode control block. * This structure is passed to the FR_SET_INTR_FLAGS and returned by the * FR_READ_INTR_FLAGS commands. */ @@ -292,7 +292,7 @@ typedef struct fr502_intr_ctl } fr502_intr_ctl_t; /*---------------------------------------------------------------------------- - * S508 Interrupt mode control block. + * S508 interrupt mode control block. * This structure is passed to the FR_SET_INTR_FLAGS and returned by the * FR_READ_INTR_FLAGS commands. */ @@ -306,10 +306,10 @@ typedef struct fr508_intr_ctl } fr508_intr_ctl_t; /*---------------------------------------------------------------------------- - * Channel Status. + * Channel status. * This structure is returned by the FR_READ_STATUS command. */ -typedef struct frDLCStatus +typedef struct fr_dlc_Status { unsigned char status PACKED; /* 00h: link/DLCI status */ struct @@ -317,7 +317,7 @@ typedef struct frDLCStatus unsigned short dlci PACKED; /* 01h: DLCI number */ unsigned char status PACKED; /* 03h: DLCI status */ } circuit[1] PACKED; -} frDLCStatus_t; +} fr_dlc_status_t; /* 'status' defines */ #define FR_LINK_INOPER 0x00 /* for global status (DLCI == 0) */ @@ -333,7 +333,7 @@ typedef struct frDLCStatus * This structure is returned by the FR_READ_STATISTICS command when * dcli == 0. */ -typedef struct frLinkStat +typedef struct fr_link_stat { unsigned short rx_too_long PACKED; /* 00h: */ unsigned short rx_dropped PACKED; /* 02h: */ @@ -363,14 +363,14 @@ typedef struct frLinkStat unsigned short current_T392 PACKED; /* 32h: */ unsigned short current_N392 PACKED; /* 34h: */ unsigned short current_N393 PACKED; /* 36h: */ -} frLinkStat_t; +} fr_link_stat_t; /*---------------------------------------------------------------------------- - * DLCI Statistics. + * DLCI statistics. * This structure is returned by the FR_READ_STATISTICS command when * dlci != 0. */ -typedef struct frDLCIStat +typedef struct fr_dlci_stat { unsigned long tx_frames PACKED; /* 00h: */ unsigned long tx_bytes PACKED; /* 04h: */ @@ -384,13 +384,13 @@ typedef struct frDLCIStat unsigned long tx_calc_timer PACKED; /* 24h: */ unsigned long rx_throughput PACKED; /* 28h: */ unsigned long rx_calc_timer PACKED; /* 2Ch: */ -} frDLCIStat_t; +} fr_dlci_stat_t; /*---------------------------------------------------------------------------- - * Communications Error Statistics. + * Communications error statistics. * This structure is returned by the FR_READ_ERROR_STATS command. */ -typedef struct frCommStat +typedef struct fr_comm_stat { unsigned char rx_overruns PACKED; /* 00h: */ unsigned char rx_bad_crc PACKED; /* 01h: */ @@ -401,7 +401,7 @@ typedef struct frCommStat unsigned char tx_missed_undr PACKED; /* 06h: */ unsigned char dcd_dropped PACKED; /* 07h: */ unsigned char cts_dropped PACKED; /* 08h: */ -} frCommStat_t; +} fr_comm_stat_t; /*---------------------------------------------------------------------------- * Defines for the FR_ISSUE_IS_FRAME command. diff --git a/include/linux/sdla_x25.h b/include/linux/sdla_x25.h index 424e9fe6a..0cff58224 100644 --- a/include/linux/sdla_x25.h +++ b/include/linux/sdla_x25.h @@ -480,7 +480,7 @@ typedef struct X25Stats unsigned short rxRR PACKED; /* 3Ah: RR */ unsigned short txRNR PACKED; /* 3Ch: RNR */ unsigned short rxRNR PACKED; /* 3Eh: RNR */ -} X25Stats; +} TX25Stats; /*---------------------------------------------------------------------------- * X25_READ_HISTORY_TABLE Command. diff --git a/include/linux/selection.h b/include/linux/selection.h index d6f1248f0..cad2a29b0 100644 --- a/include/linux/selection.h +++ b/include/linux/selection.h @@ -23,11 +23,11 @@ extern unsigned long get_video_size_row(unsigned int console); #define get_video_num_columns(dummy) video_num_columns #define get_video_num_lines(dummy) video_num_lines #define get_video_size_row(dummy) video_size_row -#endif - extern unsigned long video_num_columns; extern unsigned long video_num_lines; extern unsigned long video_size_row; +#endif + extern unsigned char video_type; extern unsigned long video_mem_base; extern unsigned long video_mem_term; @@ -73,6 +73,8 @@ extern void putconsxy(int currcons, char *p); /* how to access screen memory */ +#include <linux/config.h> + #if defined(CONFIG_TGA_CONSOLE) extern int tga_blitc(unsigned int, unsigned long); diff --git a/include/linux/simp.h b/include/linux/simp.h new file mode 100644 index 000000000..bf838f815 --- /dev/null +++ b/include/linux/simp.h @@ -0,0 +1,39 @@ +/* + * include/linux/simp.h -- simple allocator for cached objects + * + * This is meant as a faster and simpler (not full-featured) replacement + * for SLAB, thus the name "simp" :-) + * + * (C) 1997 Thomas Schoebel-Theuer + */ + +#ifndef SIMP_H +#define SIMP_H + +/* used for constructors / destructors */ +typedef void (*structor)(void *); + +/* create an object cache */ +/* positive clearable_offset means the next two pointers at that offset + * can be internally used for freelist pointers when the object is + * deallocated / not in use; + * if there is no space inside the element that can be reused for + * this purpose, supply -1. Using positive offsets is essential for + * saving space with very small-sized objects. + * + * Note for big-sized objects in the range of whole pages, use + * the fast Linux page allocator instead, directly. + */ +extern struct simp * simp_create(char * name, long size, + structor first_ctor, + structor again_ctor, + structor dtor); + +/* alloc / dealloc routines */ +extern void * simp_alloc(struct simp * simp); +extern void simp_free(void * objp); + +/* garbage collection */ +extern long simp_garbage(void); + +#endif diff --git a/include/linux/slab.h b/include/linux/slab.h index 08be13221..decb1e747 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -32,7 +32,7 @@ typedef struct kmem_cache_s kmem_cache_t; #define SLAB_DEBUG_FREE 0x00000100UL /* Peform (expensive) checks on free */ #define SLAB_DEBUG_INITIAL 0x00000200UL /* Call constructor (as verifier) */ #define SLAB_RED_ZONE 0x00000400UL /* Red zone objs in a cache */ -#define SLAB_POISION 0x00000800UL /* Poision objects */ +#define SLAB_POISON 0x00000800UL /* Poison objects */ #define SLAB_NO_REAP 0x00001000UL /* never reap from the cache */ #define SLAB_HWCACHE_ALIGN 0x00002000UL /* align objs on a h/w cache lines */ #if 0 @@ -56,8 +56,8 @@ extern void *kmem_cache_alloc(kmem_cache_t *, int); extern void kmem_cache_free(kmem_cache_t *, void *); extern void *kmalloc(size_t, int); -extern void kfree(void *); -extern void kfree_s(void *, size_t); +extern void kfree(const void *); +extern void kfree_s(const void *, size_t); extern int kmem_cache_reap(int, int, int); extern int get_slabinfo(char *); diff --git a/include/linux/swap.h b/include/linux/swap.h index 56fef7a21..7faaccf75 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -19,8 +19,7 @@ struct swap_info_struct { unsigned int flags; kdev_t swap_device; - char *swap_filename; - struct inode * swap_file; + struct dentry * swap_file; unsigned char * swap_map; unsigned char * swap_lockmap; unsigned int lowest_bit; diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 2ec41aa05..75fdf6c3d 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -173,6 +173,7 @@ enum NET_IPV4_IGMP_AGE_THRESHOLD, NET_TCP_SYNCOOKIES, NET_TCP_ALWAYS_SYNCOOKIE, + NET_TCP_STDURG, }; diff --git a/include/linux/sysv_fs.h b/include/linux/sysv_fs.h index e68f5d5f1..53ead0c99 100644 --- a/include/linux/sysv_fs.h +++ b/include/linux/sysv_fs.h @@ -372,8 +372,8 @@ extern int sysv_symlink(struct inode * inode, const char * name, int len, const char * symname); extern int sysv_link(struct inode * oldinode, struct inode * dir, const char * name, int len); extern int sysv_mknod(struct inode * dir, const char * name, int len, int mode, int rdev); -extern int sysv_rename(struct inode * old_dir, const char * old_name, int old_len, - struct inode * new_dir, const char * new_name, int new_len); +extern int sysv_rename(struct inode * old_dir, struct dentry * old_dentry, + struct inode * new_dir, struct dentry * new_dentry); extern struct inode * sysv_new_inode(const struct inode * dir); extern void sysv_free_inode(struct inode * inode); extern unsigned long sysv_count_free_inodes(struct super_block *sb); @@ -396,7 +396,7 @@ extern void sysv_read_inode(struct inode *); extern int sysv_notify_change(struct inode *, struct iattr *); extern void sysv_write_inode(struct inode *); extern void sysv_put_inode(struct inode *); -extern void sysv_statfs(struct super_block *, struct statfs *, int); +extern int sysv_statfs(struct super_block *, struct statfs *, int); extern int sysv_sync_inode(struct inode *); extern int sysv_sync_file(struct inode *, struct file *); extern int sysv_mmap(struct inode *, struct file *, struct vm_area_struct *); diff --git a/include/linux/tqueue.h b/include/linux/tqueue.h index de88d20d1..a2e1cb369 100644 --- a/include/linux/tqueue.h +++ b/include/linux/tqueue.h @@ -42,7 +42,7 @@ struct tq_struct { struct tq_struct *next; /* linked list of active bh's */ - int sync; /* must be initialized to zero */ + unsigned long sync; /* must be initialized to zero */ void (*routine)(void *); /* function to call */ void *data; /* argument to function */ }; diff --git a/include/linux/tty.h b/include/linux/tty.h index 109955a8d..589fc6b88 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -96,13 +96,19 @@ extern struct screen_info screen_info; struct tty_flip_buffer { struct tq_struct tqueue; - unsigned char char_buf[2*TTY_FLIPBUF_SIZE]; - char flag_buf[2*TTY_FLIPBUF_SIZE]; + struct semaphore pty_sem; char *char_buf_ptr; unsigned char *flag_buf_ptr; int count; int buf_num; + unsigned char char_buf[2*TTY_FLIPBUF_SIZE]; + char flag_buf[2*TTY_FLIPBUF_SIZE]; + unsigned char slop[4]; /* N.B. bug overwrites buffer by 1 */ }; +/* + * The pty uses char_buf and flag_buf as a contiguous buffer + */ +#define PTY_BUF_SIZE 4*TTY_FLIPBUF_SIZE /* * When a break, frame error, or parity error happens, these codes are @@ -204,7 +210,7 @@ struct tty_flip_buffer { * most often used by a windowing system, which will set the correct * size each time the window is created or resized anyway. * IMPORTANT: since this structure is dynamically allocated, it must - * be no larger than 4096 bytes. Changing TTY_BUF_SIZE will change + * be no larger than 4096 bytes. Changing TTY_FLIPBUF_SIZE will change * the size of this structure, and it needs to be done with care. * - TYT, 9/14/92 */ diff --git a/include/linux/ufs_fs.h b/include/linux/ufs_fs.h index af856645d..3afea01ad 100644 --- a/include/linux/ufs_fs.h +++ b/include/linux/ufs_fs.h @@ -6,7 +6,7 @@ * Laboratory for Computer Science Research Computing Facility * Rutgers, The State University of New Jersey * - * $Id: ufs_fs.h,v 1.7 1996/08/13 19:27:59 ecd Exp $ + * $Id: ufs_fs.h,v 1.1.1.1 1997/06/01 03:17:06 ralf Exp $ * */ @@ -225,7 +225,7 @@ extern void ufs_put_inode(struct inode * inode); extern void ufs_print_inode (struct inode *); /* ufs_namei.c */ -extern int ufs_lookup (struct inode *, const char *, int, struct inode **); +extern int ufs_lookup (struct inode *, struct qstr *, struct inode **); /* ufs_super.c */ extern void ufs_warning (struct super_block *, const char *, const char *, ...) diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h index fcc6ad09f..40072ab47 100644 --- a/include/linux/vmalloc.h +++ b/include/linux/vmalloc.h @@ -17,6 +17,8 @@ struct vm_struct * get_vm_area(unsigned long size); void vfree(void * addr); void * vmalloc(unsigned long size); int vread(char *buf, char *addr, int count); +void vmfree_area_pages(unsigned long address, unsigned long size); +int vmalloc_area_pages(unsigned long address, unsigned long size); extern inline void set_pgdir(unsigned long address, pgd_t entry) { @@ -34,3 +36,4 @@ extern inline void set_pgdir(unsigned long address, pgd_t entry) } #endif + diff --git a/include/linux/wanpipe.h b/include/linux/wanpipe.h index f36642161..922a76961 100644 --- a/include/linux/wanpipe.h +++ b/include/linux/wanpipe.h @@ -11,6 +11,8 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * ============================================================================ +* Jan 15, 1997 Gene Kozin Version 3.1.0 +* o added UDP management stuff * Jan 02, 1997 Gene Kozin Version 3.0.0 *****************************************************************************/ #ifndef _WANPIPE_H @@ -19,6 +21,11 @@ #include <linux/wanrouter.h> /* Defines */ + +#ifndef PACKED +#define PACKED __attribute__((packed)) +#endif + #define WANPIPE_MAGIC 0x414C4453L /* signatire: 'SDLA' reversed */ /* IOCTL numbers (up to 16) */ @@ -44,6 +51,22 @@ typedef struct sdla_exec /* WANPIPE_EXEC */ void* data; /* -> data buffer */ } sdla_exec_t; +/* UDP management stuff */ + +typedef struct wum_header +{ + unsigned char signature[8]; /* 00h: signature */ + unsigned char type; /* 08h: request/reply */ + unsigned char command; /* 09h: commnand */ + unsigned char reserved[6]; /* 0Ah: reserved */ +} wum_header_t; + +#define WUM_SIGNATURE_L 0x50495046 +#define WUM_SIGNATURE_H 0x444E3845 + +#define WUM_KILL 0x50 +#define WUM_EXEC 0x51 + #ifdef __KERNEL__ /****** Kernel Interface ****************************************************/ @@ -77,6 +100,7 @@ typedef struct sdla wan_device_t wandev; /* WAN device data space */ unsigned open_cnt; /* number of open interfaces */ unsigned long state_tick; /* link state timestamp */ +/* unsigned tx_int_enabled; */ /* tranmit interrupt enabled or not */ char in_isr; /* interrupt-in-service flag */ void* mbox; /* -> mailbox */ void* rxmb; /* -> receive mailbox */ diff --git a/include/linux/wanrouter.h b/include/linux/wanrouter.h index 444608afb..accb9c9ee 100644 --- a/include/linux/wanrouter.h +++ b/include/linux/wanrouter.h @@ -1,5 +1,5 @@ /***************************************************************************** -* wanrouter.h Definitions for the WAN Multiprotocol Router Module. +* router.h Definitions for the WAN Multiprotocol Router Module. * This module provides API and common services for WAN Link * Drivers and is completely hardware-independent. * @@ -12,6 +12,10 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * ============================================================================ +* May 29, 1997 Jaspreet Singh Added 'tx_int_enabled' tp 'wan_device_t' +* May 21, 1997 Jaspreet Singh Added 'udp_port' to 'wan_device_t' +* Apr 25, 1997 Farhan Thawar Added 'udp_port' to 'wandev_conf_t' +* Jan 16, 1997 Gene Kozin router_devlist made public * Jan 02, 1997 Gene Kozin Initial version (based on wanpipe.h). *****************************************************************************/ #ifndef _ROUTER_H @@ -128,7 +132,8 @@ typedef struct wandev_conf int dma; /* DMA request level */ unsigned bps; /* data transfer rate */ unsigned mtu; /* maximum transmit unit size */ - char interface; /* RS-232/V.35, etc. */ + unsigned udp_port; /* UDP port for management */ + char interface; /* RS-232/V.35, etc. */ char clocking; /* external/internal */ char line_coding; /* NRZ/NRZI/FM0/FM1, etc. */ char station; /* DTE/DCE, primary/secondary, etc. */ @@ -283,6 +288,8 @@ typedef struct wan_device int dma; /* DMA request level */ unsigned bps; /* data transfer rate */ unsigned mtu; /* max physical transmit unit size */ + unsigned udp_port; /* UDP port for management */ + unsigned tx_int_enabled; /* Transmit Interrupt enabled or not */ char interface; /* RS-232/V.35, etc. */ char clocking; /* external/internal */ char line_coding; /* NRZ/NRZI/FM0/FM1, etc. */ @@ -311,22 +318,24 @@ typedef struct wan_device struct proc_dir_entry dent; /* proc filesystem entry */ } wan_device_t; -/* Init Point */ -extern void wanrouter_init(void); - /* Public functions available for device drivers */ -extern int register_wan_device (wan_device_t* wandev); -extern int unregister_wan_device (char* name); -unsigned short wanrouter_type_trans (struct sk_buff* skb, struct device* dev); -int wanrouter_encapsulate (struct sk_buff* skb, struct device* dev); +extern int register_wandev (wan_device_t* wandev); +extern int unregister_wandev (char* name); +unsigned short wan_type_trans (struct sk_buff* skb, struct device* dev); +int wan_encapsulate (struct sk_buff* skb, struct device* dev); /* Proc interface functions. These must not be called by the drivers! */ extern int wanrouter_proc_init (void); extern void wanrouter_proc_cleanup (void); extern int wanrouter_proc_add (wan_device_t* wandev); extern int wanrouter_proc_delete (wan_device_t* wandev); -extern int wanrouter_ioctl(struct inode* inode, struct file* file, - unsigned int cmd, unsigned long arg); +extern int wanrouter_ioctl( + struct inode* inode, struct file* file, + unsigned int cmd, unsigned long arg) +; + +/* Public Data */ +extern wan_device_t* router_devlist; /* list of registered devices */ #endif /* __KERNEL__ */ #endif /* _ROUTER_H */ diff --git a/include/linux/wrapper.h b/include/linux/wrapper.h index 750b97084..2d6d4719f 100644 --- a/include/linux/wrapper.h +++ b/include/linux/wrapper.h @@ -20,9 +20,9 @@ #define module_unregister_blkdev unregister_blkdev #define inode_get_rdev(i) i->i_rdev -#define inode_get_count(i) atomic_read(&((i)->i_count)) -#define inode_inc_count(i) atomic_inc(&((i)->i_count)) -#define inode_dec_count(i) atomic_dec(&((i)->i_count)) +#define inode_get_count(i) i->i_count +#define inode_inc_count(i) i->i_count++ +#define inode_dec_count(i) i->i_count-- #define file_get_flags(f) f->f_flags diff --git a/include/linux/x25.h b/include/linux/x25.h index 6af8e9a5b..0435e9701 100644 --- a/include/linux/x25.h +++ b/include/linux/x25.h @@ -13,6 +13,7 @@ #define SIOCX25SFACILITIES (SIOCPROTOPRIVATE + 3) #define SIOCX25GCALLUSERDATA (SIOCPROTOPRIVATE + 4) #define SIOCX25SCALLUSERDATA (SIOCPROTOPRIVATE + 5) +#define SIOCX25GCAUSEDIAG (SIOCPROTOPRIVATE + 6) /* * Values for {get,set}sockopt. @@ -33,42 +34,11 @@ #define X25_PS4096 12 /* - * X.25 Reset error and diagnostic codes. - */ -#define X25_ERR_RESET 100 /* Call Reset */ -#define X25_ERR_ROUT 101 /* Out of Order */ -#define X25_ERR_RRPE 102 /* Remote Procedure Error */ -#define X25_ERR_RLPE 103 /* Local Procedure Error */ -#define X25_ERR_RNCG 104 /* Network Congestion */ -#define X25_ERR_RRDO 105 /* Remote DTE Operational */ -#define X25_ERR_RNOP 106 /* Network Operational */ -#define X25_ERR_RINV 107 /* Invalid Call */ -#define X25_ERR_RNOO 108 /* Network Out of Order */ - -/* - * X.25 Clear error and diagnostic codes. - */ -#define X25_ERR_CLEAR 110 /* Call Cleared */ -#define X25_ERR_CBUSY 111 /* Number Busy */ -#define X25_ERR_COUT 112 /* Out of Order */ -#define X25_ERR_CRPE 113 /* Remote Procedure Error */ -#define X25_ERR_CRRC 114 /* Collect Call Refused */ -#define X25_ERR_CINV 115 /* Invalid Call */ -#define X25_ERR_CNFS 116 /* Invalid Fast Select */ -#define X25_ERR_CSA 117 /* Ship Absent */ -#define X25_ERR_CIFR 118 /* Invalid Facility Request */ -#define X25_ERR_CAB 119 /* Access Barred */ -#define X25_ERR_CLPE 120 /* Local Procedure Error */ -#define X25_ERR_CNCG 121 /* Network Congestion */ -#define X25_ERR_CNOB 122 /* Not Obtainable */ -#define X25_ERR_CROO 123 /* RPOA Out of Order */ - -/* * An X.121 address, it is held as ASCII text, null terminated, up to 15 * digits and a null terminator. */ typedef struct { - char x25_addr[16]; + char x25_addr[16]; } x25_address; /* @@ -114,4 +84,12 @@ struct x25_calluserdata { unsigned char cuddata[128]; }; +/* + * Call clearing Cause and Diagnostic structure. + */ +struct x25_causediag { + unsigned char cause; + unsigned char diagnostic; +}; + #endif diff --git a/include/net/ax25.h b/include/net/ax25.h index 9e6f0df11..fd25e9f7f 100644 --- a/include/net/ax25.h +++ b/include/net/ax25.h @@ -9,10 +9,8 @@ #include <linux/config.h> #include <linux/ax25.h> -#define AX25_SLOWHZ 10 /* Run timing at 1/10 second - gives us better resolution for 56kbit links */ - -#define AX25_T1CLAMPLO (1 * AX25_SLOWHZ) /* If defined, clamp at 1 second **/ -#define AX25_T1CLAMPHI (30 * AX25_SLOWHZ) /* If defined, clamp at 30 seconds **/ +#define AX25_T1CLAMPLO 1 +#define AX25_T1CLAMPHI (30 * HZ) #define AX25_BPQ_HEADER_LEN 16 #define AX25_KISS_HEADER_LEN 1 @@ -125,14 +123,14 @@ enum { #define AX25_DEF_CONMODE 2 /* Connected mode allowed */ #define AX25_DEF_WINDOW 2 /* Window=2 */ #define AX25_DEF_EWINDOW 32 /* Module-128 Window=32 */ -#define AX25_DEF_T1 (10 * AX25_SLOWHZ) /* T1=10s */ -#define AX25_DEF_T2 (3 * AX25_SLOWHZ) /* T2=3s */ -#define AX25_DEF_T3 (300 * AX25_SLOWHZ) /* T3=300s */ +#define AX25_DEF_T1 (10 * HZ) /* T1=10s */ +#define AX25_DEF_T2 (3 * HZ) /* T2=3s */ +#define AX25_DEF_T3 (300 * HZ) /* T3=300s */ #define AX25_DEF_N2 10 /* N2=10 */ -#define AX25_DEF_IDLE (0 * 60 * AX25_SLOWHZ) /* Idle=None */ +#define AX25_DEF_IDLE (0 * 60 * HZ) /* Idle=None */ #define AX25_DEF_PACLEN 256 /* Paclen=256 */ #define AX25_DEF_PROTOCOL AX25_PROTO_STD_SIMPLEX /* Standard AX.25 */ -#define AX25_DEF_DS_TIMEOUT (3 * 60 * AX25_SLOWHZ) /* DAMA timeout 3 minutes */ +#define AX25_DEF_DS_TIMEOUT (3 * 60 * HZ) /* DAMA timeout 3 minutes */ typedef struct ax25_uid_assoc { struct ax25_uid_assoc *next; @@ -186,8 +184,8 @@ typedef struct ax25_cb { unsigned short vs, vr, va; unsigned char condition, backoff; unsigned char n2, n2count; - unsigned short t1, t2, t3, idle, rtt; - unsigned short t1timer, t2timer, t3timer, idletimer; + struct timer_list t1timer, t2timer, t3timer, idletimer; + unsigned long t1, t2, t3, idle, rtt; unsigned short paclen, fragno, fraglen; struct sk_buff_head write_queue; struct sk_buff_head reseq_queue; @@ -251,20 +249,22 @@ extern void ax25_ds_set_timer(ax25_dev *); extern void ax25_ds_del_timer(ax25_dev *); extern void ax25_ds_timer(ax25_cb *); extern void ax25_ds_t1_timeout(ax25_cb *); +extern void ax25_ds_heartbeat_expiry(ax25_cb *); +extern void ax25_ds_t3timer_expiry(ax25_cb *); +extern void ax25_ds_idletimer_expiry(ax25_cb *); #include <net/ax25call.h> /* ax25_iface.c */ extern int ax25_protocol_register(unsigned int, int (*)(struct sk_buff *, ax25_cb *)); extern void ax25_protocol_release(unsigned int); -extern int ax25_linkfail_register(void (*)(ax25_address *, struct device *)); -extern void ax25_linkfail_release(void (*)(ax25_address *, struct device *)); +extern int ax25_linkfail_register(void (*)(ax25_cb *, int)); +extern void ax25_linkfail_release(void (*)(ax25_cb *, int)); extern int ax25_listen_register(ax25_address *, struct device *); extern void ax25_listen_release(ax25_address *, struct device *); extern int (*ax25_protocol_function(unsigned int))(struct sk_buff *, ax25_cb *); extern int ax25_listen_mine(ax25_address *, struct device *); -extern void ax25_link_failed(ax25_address *, struct device *); -extern int ax25_link_up(ax25_address *, ax25_address *, ax25_digi *, struct device *); +extern void ax25_link_failed(ax25_cb *, int); extern int ax25_protocol_is_registered(unsigned int); /* ax25_in.c */ @@ -276,7 +276,7 @@ extern int ax25_encapsulate(struct sk_buff *, struct device *, unsigned short, extern int ax25_rebuild_header(struct sk_buff *); /* ax25_out.c */ -extern int ax25_send_frame(struct sk_buff *, int, ax25_address *, ax25_address *, ax25_digi *, struct device *); +extern ax25_cb *ax25_send_frame(struct sk_buff *, int, ax25_address *, ax25_address *, ax25_digi *, struct device *); extern void ax25_output(ax25_cb *, int, struct sk_buff *); extern void ax25_kick(ax25_cb *); extern void ax25_transmit_buffer(ax25_cb *, struct sk_buff *, int); @@ -288,9 +288,8 @@ extern void ax25_rt_device_down(struct device *); extern int ax25_rt_ioctl(unsigned int, void *); extern int ax25_rt_get_info(char *, char **, off_t, int, int); extern int ax25_rt_autobind(ax25_cb *, ax25_address *); -extern void ax25_rt_build_path(ax25_cb *, ax25_address *, struct device *); -extern struct sk_buff *ax25_dg_build_path(struct sk_buff *, ax25_address *, struct device *); -extern char ax25_ip_mode_get(ax25_address *, struct device *); +extern ax25_route *ax25_rt_find_route(ax25_address *, struct device *); +extern struct sk_buff *ax25_rt_build_path(struct sk_buff *, ax25_address *, ax25_address *, ax25_digi *); extern void ax25_rt_free(void); /* ax25_std_in.c */ @@ -304,7 +303,11 @@ extern void ax25_std_enquiry_response(ax25_cb *); extern void ax25_std_timeout_response(ax25_cb *); /* ax25_std_timer.c */ -extern void ax25_std_timer(ax25_cb *); +extern void ax25_std_heartbeat_expiry(ax25_cb *); +extern void ax25_std_t1timer_expiry(ax25_cb *); +extern void ax25_std_t2timer_expiry(ax25_cb *); +extern void ax25_std_t3timer_expiry(ax25_cb *); +extern void ax25_std_idletimer_expiry(ax25_cb *); /* ax25_subr.c */ extern void ax25_clear_queues(ax25_cb *); @@ -314,11 +317,23 @@ extern int ax25_validate_nr(ax25_cb *, unsigned short); extern int ax25_decode(ax25_cb *, struct sk_buff *, int *, int *, int *); extern void ax25_send_control(ax25_cb *, int, int, int); extern void ax25_return_dm(struct device *, ax25_address *, ax25_address *, ax25_digi *); -extern unsigned short ax25_calculate_t1(ax25_cb *); +extern void ax25_calculate_t1(ax25_cb *); extern void ax25_calculate_rtt(ax25_cb *); +extern void ax25_disconnect(ax25_cb *, int); /* ax25_timer.c */ -extern void ax25_set_timer(ax25_cb *); +extern void ax25_start_heartbeat(ax25_cb *); +extern void ax25_start_t1timer(ax25_cb *); +extern void ax25_start_t2timer(ax25_cb *); +extern void ax25_start_t3timer(ax25_cb *); +extern void ax25_start_idletimer(ax25_cb *); +extern void ax25_stop_heartbeat(ax25_cb *); +extern void ax25_stop_t1timer(ax25_cb *); +extern void ax25_stop_t2timer(ax25_cb *); +extern void ax25_stop_t3timer(ax25_cb *); +extern void ax25_stop_idletimer(ax25_cb *); +extern int ax25_t1timer_running(ax25_cb *); +extern unsigned long ax25_display_timer(struct timer_list *); /* ax25_uid.c */ extern int ax25_uid_policy; diff --git a/include/net/lapb.h b/include/net/lapb.h index f06583cee..7cc9b3452 100644 --- a/include/net/lapb.h +++ b/include/net/lapb.h @@ -2,8 +2,6 @@ #define _LAPB_H #include <linux/lapb.h> -#define LAPB_SLOWHZ 10 /* Run timing at 1/10 second */ - #define LAPB_HEADER_LEN 20 /* LAPB over Ethernet + a bit more */ #define LAPB_ACK_PENDING_CONDITION 0x01 @@ -58,10 +56,10 @@ enum { }; #define LAPB_DEFAULT_MODE (LAPB_STANDARD | LAPB_SLP | LAPB_DTE) -#define LAPB_DEFAULT_WINDOW 7 /* Window=7 */ -#define LAPB_DEFAULT_T1 (5 * LAPB_SLOWHZ) /* T1=5s */ -#define LAPB_DEFAULT_T2 (1 * LAPB_SLOWHZ) /* T2=1s */ -#define LAPB_DEFAULT_N2 20 /* N2=20 */ +#define LAPB_DEFAULT_WINDOW 7 /* Window=7 */ +#define LAPB_DEFAULT_T1 (5 * HZ) /* T1=5s */ +#define LAPB_DEFAULT_T2 (1 * HZ) /* T2=1s */ +#define LAPB_DEFAULT_N2 20 /* N2=20 */ #define LAPB_SMODULUS 8 #define LAPB_EMODULUS 128 @@ -91,14 +89,12 @@ typedef struct lapb_cb { unsigned char condition; unsigned short n2, n2count; unsigned short t1, t2; - unsigned short t1timer, t2timer; + struct timer_list t1timer, t2timer; /* Internal control information */ - struct sk_buff_head input_queue; struct sk_buff_head write_queue; struct sk_buff_head ack_queue; unsigned char window; - struct timer_list timer; struct lapb_register_struct callbacks; /* FRMR control information */ @@ -136,7 +132,11 @@ extern void lapb_send_control(lapb_cb *, int, int, int); extern void lapb_transmit_frmr(lapb_cb *); /* lapb_timer.c */ -extern void lapb_set_timer(lapb_cb *); +extern void lapb_start_t1timer(lapb_cb *); +extern void lapb_start_t2timer(lapb_cb *); +extern void lapb_stop_t1timer(lapb_cb *); +extern void lapb_stop_t2timer(lapb_cb *); +extern int lapb_t1timer_running(lapb_cb *); /* * Debug levels. diff --git a/include/net/netrom.h b/include/net/netrom.h index 8a255660a..cc9fc842c 100644 --- a/include/net/netrom.h +++ b/include/net/netrom.h @@ -8,8 +8,6 @@ #define _NETROM_H #include <linux/netrom.h> -#define NR_SLOWHZ 10 /* Run timing at 1/10 second */ - #define NR_NETWORK_LEN 15 #define NR_TRANSPORT_LEN 5 @@ -40,17 +38,17 @@ enum { #define NR_COND_PEER_RX_BUSY 0x04 #define NR_COND_OWN_RX_BUSY 0x08 -#define NR_DEFAULT_T1 (120 * NR_SLOWHZ) /* Outstanding frames - 120 seconds */ -#define NR_DEFAULT_T2 (5 * NR_SLOWHZ) /* Response delay - 5 seconds */ -#define NR_DEFAULT_N2 3 /* Number of Retries - 3 */ -#define NR_DEFAULT_T4 (180 * NR_SLOWHZ) /* Busy Delay - 180 seconds */ -#define NR_DEFAULT_IDLE (20* 60 * NR_SLOWHZ) /* No Activuty Timeout - 900 seconds*/ -#define NR_DEFAULT_WINDOW 4 /* Default Window Size - 4 */ -#define NR_DEFAULT_OBS 6 /* Default Obsolescence Count - 6 */ -#define NR_DEFAULT_QUAL 10 /* Default Neighbour Quality - 10 */ -#define NR_DEFAULT_TTL 16 /* Default Time To Live - 16 */ -#define NR_DEFAULT_ROUTING 1 /* Is routing enabled ? */ -#define NR_DEFAULT_FAILS 2 /* Link fails until route fails */ +#define NR_DEFAULT_T1 (120 * HZ) /* Outstanding frames - 120 seconds */ +#define NR_DEFAULT_T2 (5 * HZ) /* Response delay - 5 seconds */ +#define NR_DEFAULT_N2 3 /* Number of Retries - 3 */ +#define NR_DEFAULT_T4 (180 * HZ) /* Busy Delay - 180 seconds */ +#define NR_DEFAULT_IDLE (0 * 60 * HZ) /* No Activity Timeout - none */ +#define NR_DEFAULT_WINDOW 4 /* Default Window Size - 4 */ +#define NR_DEFAULT_OBS 6 /* Default Obsolescence Count - 6 */ +#define NR_DEFAULT_QUAL 10 /* Default Neighbour Quality - 10 */ +#define NR_DEFAULT_TTL 16 /* Default Time To Live - 16 */ +#define NR_DEFAULT_ROUTING 1 /* Is routing enabled ? */ +#define NR_DEFAULT_FAILS 2 /* Link fails until route fails */ #define NR_MODULUS 256 #define NR_MAX_WINDOW_SIZE 127 /* Maximum Window Allowable - 127 */ @@ -64,9 +62,12 @@ typedef struct { unsigned char state, condition, bpqext, window; unsigned short vs, vr, va, vl; unsigned char n2, n2count; - unsigned short t1, t2, t4, idle; - unsigned short t1timer, t2timer, t4timer, idletimer; + unsigned long t1, t2, t4, idle; unsigned short fraglen; + struct timer_list t1timer; + struct timer_list t2timer; + struct timer_list t4timer; + struct timer_list idletimer; struct sk_buff_head ack_queue; struct sk_buff_head reseq_queue; struct sk_buff_head frag_queue; @@ -77,6 +78,7 @@ struct nr_neigh { struct nr_neigh *next; ax25_address callsign; ax25_digi *digipeat; + ax25_cb *ax25; struct device *dev; unsigned char quality; unsigned char locked; @@ -138,7 +140,7 @@ extern void nr_rt_device_down(struct device *); extern struct device *nr_dev_first(void); extern struct device *nr_dev_get(ax25_address *); extern int nr_rt_ioctl(unsigned int, void *); -extern void nr_link_failed(ax25_address *, struct device *); +extern void nr_link_failed(ax25_cb *, int); extern int nr_route_frame(struct sk_buff *, ax25_cb *); extern int nr_nodes_get_info(char *, char **, off_t, int, int); extern int nr_neigh_get_info(char *, char **, off_t, int, int); @@ -152,9 +154,20 @@ extern int nr_validate_nr(struct sock *, unsigned short); extern int nr_in_rx_window(struct sock *, unsigned short); extern void nr_write_internal(struct sock *, int); extern void nr_transmit_dm(struct sk_buff *); +extern void nr_disconnect(struct sock *, int); /* nr_timer.c */ -extern void nr_set_timer(struct sock *); +extern void nr_start_heartbeat(struct sock *); +extern void nr_start_t1timer(struct sock *); +extern void nr_start_t2timer(struct sock *); +extern void nr_start_t4timer(struct sock *); +extern void nr_start_idletimer(struct sock *); +extern void nr_stop_heartbeat(struct sock *); +extern void nr_stop_t1timer(struct sock *); +extern void nr_stop_t2timer(struct sock *); +extern void nr_stop_t4timer(struct sock *); +extern void nr_stop_idletimer(struct sock *); +extern int nr_t1timer_running(struct sock *); /* sysctl_net_netrom.c */ extern void nr_register_sysctl(void); diff --git a/include/net/rose.h b/include/net/rose.h index 1e60cfa8f..86f6a6721 100644 --- a/include/net/rose.h +++ b/include/net/rose.h @@ -8,8 +8,6 @@ #define _ROSE_H #include <linux/rose.h> -#define ROSE_SLOWHZ 10 /* Run timing at 1/10 second */ - #define ROSE_ADDR_LEN 5 #define ROSE_MIN_LEN 3 @@ -45,22 +43,23 @@ enum { ROSE_STATE_1, /* Awaiting Call Accepted */ ROSE_STATE_2, /* Awaiting Clear Confirmation */ ROSE_STATE_3, /* Data Transfer */ - ROSE_STATE_4 /* Awaiting Reset Confirmation */ + ROSE_STATE_4, /* Awaiting Reset Confirmation */ + ROSE_STATE_5 /* Deferred Call Acceptance */ }; -#define ROSE_DEFAULT_T0 (180 * ROSE_SLOWHZ) /* Default T10 T20 value */ -#define ROSE_DEFAULT_T1 (200 * ROSE_SLOWHZ) /* Default T11 T21 value */ -#define ROSE_DEFAULT_T2 (180 * ROSE_SLOWHZ) /* Default T12 T22 value */ -#define ROSE_DEFAULT_T3 (180 * ROSE_SLOWHZ) /* Default T13 T23 value */ -#define ROSE_DEFAULT_HB (5 * ROSE_SLOWHZ) /* Default Holdback value */ -#define ROSE_DEFAULT_IDLE (20 * 60 * ROSE_SLOWHZ) /* Default No Activity value */ -#define ROSE_DEFAULT_ROUTING 1 /* Default routing flag */ -#define ROSE_DEFAULT_FAIL_TIMEOUT (120 * ROSE_SLOWHZ) /* Time until link considered usable */ -#define ROSE_DEFAULT_MAXVC 50 /* Maximum number of VCs per neighbour */ -#define ROSE_DEFAULT_WINDOW_SIZE 3 /* Default window size */ +#define ROSE_DEFAULT_T0 (180 * HZ) /* Default T10 T20 value */ +#define ROSE_DEFAULT_T1 (200 * HZ) /* Default T11 T21 value */ +#define ROSE_DEFAULT_T2 (180 * HZ) /* Default T12 T22 value */ +#define ROSE_DEFAULT_T3 (180 * HZ) /* Default T13 T23 value */ +#define ROSE_DEFAULT_HB (5 * HZ) /* Default Holdback value */ +#define ROSE_DEFAULT_IDLE (0 * 60 * HZ) /* No Activity Timeout - none */ +#define ROSE_DEFAULT_ROUTING 1 /* Default routing flag */ +#define ROSE_DEFAULT_FAIL_TIMEOUT (120 * HZ) /* Time until link considered usable */ +#define ROSE_DEFAULT_MAXVC 50 /* Maximum number of VCs per neighbour */ +#define ROSE_DEFAULT_WINDOW_SIZE 3 /* Default window size */ #define ROSE_MODULUS 8 -#define ROSE_MAX_PACKET_SIZE 256 /* Maximum packet size */ +#define ROSE_MAX_PACKET_SIZE 251 /* Maximum packet size */ #define ROSE_COND_ACK_PENDING 0x01 #define ROSE_COND_PEER_RX_BUSY 0x02 @@ -81,14 +80,16 @@ struct rose_neigh { struct rose_neigh *next; ax25_address callsign; ax25_digi *digipeat; + ax25_cb *ax25; struct device *dev; unsigned short count; + unsigned short use; unsigned int number; char restarted; char dce_mode; struct sk_buff_head queue; - unsigned short t0timer, ftimer; - struct timer_list timer; + struct timer_list t0timer; + struct timer_list ftimer; }; struct rose_node { @@ -124,11 +125,13 @@ typedef struct { struct rose_neigh *neighbour; struct device *device; unsigned int lci, rand; - unsigned char state, condition, qbitincl; + unsigned char state, condition, qbitincl, defer; + unsigned char cause, diagnostic; unsigned short vs, vr, va, vl; - unsigned short timer; - unsigned short t1, t2, t3, hb, idle; + unsigned long t1, t2, t3, hb, idle; unsigned short fraglen; + struct timer_list timer; + struct timer_list idletimer; struct sk_buff_head frag_queue; struct sock *sk; /* Backlink to socket */ } rose_cb; @@ -164,12 +167,17 @@ extern int rose_init(struct device *); extern int rose_process_rx_frame(struct sock *, struct sk_buff *); /* rose_link.c */ -extern void rose_link_set_timer(struct rose_neigh *); +extern void rose_start_ftimer(struct rose_neigh *); +extern void rose_start_t0timer(struct rose_neigh *); +extern void rose_stop_ftimer(struct rose_neigh *); +extern void rose_stop_t0timer(struct rose_neigh *); +extern int rose_ftimer_running(struct rose_neigh *); +extern int rose_t0timer_running(struct rose_neigh *); extern void rose_link_rx_restart(struct sk_buff *, struct rose_neigh *, unsigned short); extern void rose_transmit_restart_request(struct rose_neigh *); extern void rose_transmit_restart_confirmation(struct rose_neigh *); extern void rose_transmit_diagnostic(struct rose_neigh *, unsigned char); -extern void rose_transmit_clear_request(struct rose_neigh *, unsigned int, unsigned char); +extern void rose_transmit_clear_request(struct rose_neigh *, unsigned int, unsigned char, unsigned char); extern void rose_transmit_link(struct sk_buff *, struct rose_neigh *); /* rose_out.c */ @@ -185,9 +193,9 @@ extern struct device *rose_dev_first(void); extern struct device *rose_dev_get(rose_address *); extern struct rose_route *rose_route_free_lci(unsigned int, struct rose_neigh *); extern struct device *rose_ax25_dev_get(char *); -extern struct rose_neigh *rose_get_neigh(rose_address *); +extern struct rose_neigh *rose_get_neigh(rose_address *, unsigned char *, unsigned char *); extern int rose_rt_ioctl(unsigned int, void *); -extern void rose_link_failed(ax25_address *, struct device *); +extern void rose_link_failed(ax25_cb *, int); extern int rose_route_frame(struct sk_buff *, ax25_cb *); extern int rose_nodes_get_info(char *, char **, off_t, int, int); extern int rose_neigh_get_info(char *, char **, off_t, int, int); @@ -201,9 +209,18 @@ extern void rose_write_internal(struct sock *, int); extern int rose_decode(struct sk_buff *, int *, int *, int *, int *, int *); extern int rose_parse_facilities(struct sk_buff *, struct rose_facilities *); extern int rose_create_facilities(unsigned char *, rose_cb *); +extern void rose_disconnect(struct sock *, int, int, int); /* rose_timer.c */ -extern void rose_set_timer(struct sock *); +extern void rose_start_heartbeat(struct sock *); +extern void rose_start_t1timer(struct sock *); +extern void rose_start_t2timer(struct sock *); +extern void rose_start_t3timer(struct sock *); +extern void rose_start_hbtimer(struct sock *); +extern void rose_start_idletimer(struct sock *); +extern void rose_stop_heartbeat(struct sock *); +extern void rose_stop_timer(struct sock *); +extern void rose_stop_idletimer(struct sock *); /* sysctl_net_rose.c */ extern void rose_register_sysctl(void); diff --git a/include/net/tcp.h b/include/net/tcp.h index cedc7f3b1..d0f812e4d 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -281,15 +281,17 @@ struct tcp_func { int (*conn_request) (struct sock *sk, struct sk_buff *skb, - void *opt, - __u32 isn); + void *opt, __u32 isn); struct sock * (*syn_recv_sock) (struct sock *sk, struct sk_buff *skb, - struct open_request *req); + struct open_request *req, + struct dst_entry *dst); +#if 0 __u32 (*init_sequence) (struct sock *sk, struct sk_buff *skb); +#endif struct sock * (*get_sock) (struct sk_buff *skb, struct tcphdr *th); @@ -385,7 +387,8 @@ extern int tcp_recvmsg(struct sock *sk, int len, int nonblock, int flags, int *addr_len); -extern void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp); +extern void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp, + int no_fancy); /* * TCP v4 functions exported for the inet6 API @@ -407,7 +410,8 @@ extern int tcp_v4_conn_request(struct sock *sk, extern struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, - struct open_request *req); + struct open_request *req, + struct dst_entry *dst); extern int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb); @@ -417,6 +421,12 @@ extern int tcp_v4_connect(struct sock *sk, int addr_len); +/* From syncookies.c */ +extern struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, + struct ip_options *opt); +extern __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, + __u16 *mss); + extern void tcp_read_wakeup(struct sock *); extern void tcp_write_xmit(struct sock *); extern void tcp_time_wait(struct sock *); @@ -522,7 +532,6 @@ static __inline__ u16 tcp_v4_check(struct tcphdr *th, int len, return csum_tcpudp_magic(saddr,daddr,len,IPPROTO_TCP,base); } - #undef STATE_TRACE #ifdef STATE_TRACE diff --git a/include/net/x25.h b/include/net/x25.h index cb1ae8bee..7b58ad4e3 100644 --- a/include/net/x25.h +++ b/include/net/x25.h @@ -8,8 +8,6 @@ #define _X25_H #include <linux/x25.h> -#define X25_SLOWHZ 1 /* Run timing at 1 Hz */ - #define X25_ADDR_LEN 16 #define X25_MAX_L2_LEN 18 /* 802.2 LLC */ @@ -67,11 +65,11 @@ enum { X25_LINK_STATE_3 }; -#define X25_DEFAULT_T20 (180 * X25_SLOWHZ) /* Default T20 value */ -#define X25_DEFAULT_T21 (200 * X25_SLOWHZ) /* Default T21 value */ -#define X25_DEFAULT_T22 (180 * X25_SLOWHZ) /* Default T22 value */ -#define X25_DEFAULT_T23 (180 * X25_SLOWHZ) /* Default T23 value */ -#define X25_DEFAULT_T2 (3 * X25_SLOWHZ) /* Default ack holdback value */ +#define X25_DEFAULT_T20 (180 * HZ) /* Default T20 value */ +#define X25_DEFAULT_T21 (200 * HZ) /* Default T21 value */ +#define X25_DEFAULT_T22 (180 * HZ) /* Default T22 value */ +#define X25_DEFAULT_T23 (180 * HZ) /* Default T23 value */ +#define X25_DEFAULT_T2 (3 * HZ) /* Default ack holdback value */ #define X25_DEFAULT_WINDOW_SIZE 2 /* Default Window Size */ #define X25_DEFAULT_PACKET_SIZE X25_PS128 /* Default Packet Size */ @@ -113,8 +111,8 @@ struct x25_neigh { unsigned int state; unsigned int extended; struct sk_buff_head queue; - unsigned short t20, t20timer; - struct timer_list timer; + unsigned long t20; + struct timer_list t20timer; }; typedef struct { @@ -123,13 +121,14 @@ typedef struct { unsigned int lci; unsigned char state, condition, qbitincl, intflag; unsigned short vs, vr, va, vl; - unsigned short timer; - unsigned short t2, t21, t22, t23; + unsigned long t2, t21, t22, t23; unsigned short fraglen; struct sk_buff_head fragment_queue; struct sk_buff_head interrupt_in_queue; struct sk_buff_head interrupt_out_queue; struct sock *sk; /* Backlink to socket */ + struct timer_list timer; + struct x25_causediag causediag; struct x25_facilities facilities; struct x25_calluserdata calluserdata; } x25_cb; @@ -143,8 +142,8 @@ extern int sysctl_x25_ack_holdback_timeout; extern int x25_addr_ntoa(unsigned char *, x25_address *, x25_address *); extern int x25_addr_aton(unsigned char *, x25_address *, x25_address *); -extern unsigned int x25_new_lci(void); -extern struct sock *x25_find_socket(unsigned int); +extern unsigned int x25_new_lci(struct x25_neigh *); +extern struct sock *x25_find_socket(unsigned int, struct x25_neigh *); extern void x25_destroy_socket(struct sock *); extern int x25_rx_call_request(struct sk_buff *, struct x25_neigh *, unsigned int); @@ -199,9 +198,17 @@ extern void x25_clear_queues(struct sock *); extern int x25_validate_nr(struct sock *, unsigned short); extern void x25_write_internal(struct sock *, int); extern int x25_decode(struct sock *, struct sk_buff *, int *, int *, int *, int *, int *); +extern void x25_disconnect(struct sock *, int, unsigned char, unsigned char); /* x25_timer.c */ -extern void x25_set_timer(struct sock *); +extern void x25_start_heartbeat(struct sock *); +extern void x25_start_t2timer(struct sock *); +extern void x25_start_t21timer(struct sock *); +extern void x25_start_t22timer(struct sock *); +extern void x25_start_t23timer(struct sock *); +extern void x25_stop_heartbeat(struct sock *); +extern void x25_stop_timer(struct sock *); +extern unsigned long x25_display_timer(struct sock *); /* sysctl_net_x25.c */ extern void x25_register_sysctl(void); diff --git a/init/main.c b/init/main.c index a0d15926e..5366c2e06 100644 --- a/init/main.c +++ b/init/main.c @@ -32,7 +32,6 @@ #include <linux/slab.h> #include <linux/major.h> #include <linux/blk.h> -#include <linux/nametrans.h> #include <linux/init.h> #ifdef CONFIG_ROOT_NFS #include <linux/nfs_fs.h> @@ -570,12 +569,6 @@ __initfunc(static int checksetup(char *line)) return 1; } #endif -#ifdef CONFIG_TRANS_NAMES - if(!strncmp(line,"nametrans=",10)) { - nametrans_setup(line+10); - return 1; - } -#endif while (bootsetups[i].str) { int n = strlen(bootsetups[i].str); if (!strncmp(line,bootsetups[i].str,n)) { @@ -769,6 +762,12 @@ __initfunc(static void parse_options(char *line)) if (!strncmp(line,"init=",5)) { line += 5; execute_command = line; + /* In case LILO is going to boot us with default command line, + * it prepends "auto" before the whole cmdline which makes + * the shell think it should execute a script with such name. + * So we ignore all arguments entered _before_ init=... [MJ] + */ + args = 0; continue; } if (checksetup(line)) @@ -901,7 +900,6 @@ __initfunc(asmlinkage void start_kernel(void)) memory_start = kmem_cache_init(memory_start, memory_end); sti(); calibrate_delay(); - memory_start = name_cache_init(memory_start,memory_end); #ifdef CONFIG_BLK_DEV_INITRD if (initrd_start && !initrd_below_start_ok && initrd_start < memory_start) { printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - " @@ -916,6 +914,7 @@ __initfunc(asmlinkage void start_kernel(void)) #endif uidcache_init(); filescache_init(); + dcache_init(); vma_init(); buffer_init(); inode_init(); @@ -555,7 +555,7 @@ asmlinkage int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr) shmd->vm_flags = VM_SHM | VM_MAYSHARE | VM_SHARED | VM_MAYREAD | VM_MAYEXEC | VM_READ | VM_EXEC | ((shmflg & SHM_RDONLY) ? 0 : VM_MAYWRITE | VM_WRITE); - shmd->vm_inode = NULL; + shmd->vm_dentry = NULL; shmd->vm_offset = 0; shmd->vm_ops = &shm_vm_ops; diff --git a/kernel/exit.c b/kernel/exit.c index f6e8fb9b1..9a725e9c8 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -369,8 +369,11 @@ static inline void close_files(struct files_struct * files) break; while (set) { if (set & 1) { - close_fp(files->fd[i]); - files->fd[i] = NULL; + struct file * file = files->fd[i]; + if (file) { + files->fd[i] = NULL; + close_fp(file); + } } i++; set >>= 1; @@ -405,8 +408,8 @@ static inline void __exit_fs(struct task_struct *tsk) if (fs) { tsk->fs = NULL; if (!--fs->count) { - iput(fs->root); - iput(fs->pwd); + dput(fs->root); + dput(fs->pwd); kfree(fs); } } diff --git a/kernel/fork.c b/kernel/fork.c index c3bcf7cca..900d6015c 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -211,7 +211,7 @@ static inline int dup_mmap(struct mm_struct * mm) flush_cache_mm(current->mm); pprev = &mm->mmap; for (mpnt = current->mm->mmap ; mpnt ; mpnt = mpnt->vm_next) { - struct inode *inode; + struct dentry *dentry; tmp = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); if (!tmp) { @@ -223,11 +223,11 @@ static inline int dup_mmap(struct mm_struct * mm) tmp->vm_flags &= ~VM_LOCKED; tmp->vm_mm = mm; tmp->vm_next = NULL; - inode = tmp->vm_inode; - if (inode) { - atomic_inc(&inode->i_count); + dentry = tmp->vm_dentry; + if (dentry) { + dentry->d_count++; if (tmp->vm_flags & VM_DENYWRITE) - inode->i_writecount--; + dentry->d_inode->i_writecount--; /* insert tmp into the share list, just after mpnt */ if((tmp->vm_next_share = mpnt->vm_next_share) != NULL) @@ -302,10 +302,8 @@ static inline int copy_fs(unsigned long clone_flags, struct task_struct * tsk) return -1; tsk->fs->count = 1; tsk->fs->umask = current->fs->umask; - if ((tsk->fs->root = current->fs->root)) - atomic_inc(&tsk->fs->root->i_count); - if ((tsk->fs->pwd = current->fs->pwd)) - atomic_inc(&tsk->fs->pwd->i_count); + tsk->fs->root = dget(current->fs->root); + tsk->fs->pwd = dget(current->fs->pwd); return 0; } diff --git a/kernel/ksyms.c b/kernel/ksyms.c index 8e5607e1e..fff97a75a 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -141,12 +141,19 @@ EXPORT_SYMBOL(update_vm_cache); EXPORT_SYMBOL(getname); EXPORT_SYMBOL(putname); EXPORT_SYMBOL(__fput); -EXPORT_SYMBOL(__iget); -EXPORT_SYMBOL(_iput); -EXPORT_SYMBOL(namei); +EXPORT_SYMBOL(iget); +EXPORT_SYMBOL(iput); +EXPORT_SYMBOL(__namei); +EXPORT_SYMBOL(lookup_dentry); EXPORT_SYMBOL(open_namei); EXPORT_SYMBOL(sys_close); EXPORT_SYMBOL(close_fp); +EXPORT_SYMBOL(d_alloc_root); +EXPORT_SYMBOL(d_delete); +EXPORT_SYMBOL(d_add); +EXPORT_SYMBOL(d_move); +EXPORT_SYMBOL(d_instantiate); +EXPORT_SYMBOL(__mark_inode_dirty); EXPORT_SYMBOL(insert_file_free); EXPORT_SYMBOL(check_disk_change); EXPORT_SYMBOL(invalidate_buffers); @@ -177,6 +184,7 @@ EXPORT_SYMBOL(posix_lock_file); EXPORT_SYMBOL(posix_test_lock); EXPORT_SYMBOL(posix_block_lock); EXPORT_SYMBOL(posix_unblock_lock); +EXPORT_SYMBOL(dput); #if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE) EXPORT_SYMBOL(do_nfsservctl); @@ -329,7 +337,7 @@ EXPORT_SYMBOL(setup_arg_pages); EXPORT_SYMBOL(copy_strings); EXPORT_SYMBOL(do_execve); EXPORT_SYMBOL(flush_old_exec); -EXPORT_SYMBOL(open_inode); +EXPORT_SYMBOL(open_dentry); EXPORT_SYMBOL(read_exec); /* Miscellaneous access points */ @@ -341,7 +349,7 @@ EXPORT_SYMBOL(set_writetime); EXPORT_SYMBOL(sys_tz); EXPORT_SYMBOL(__wait_on_super); EXPORT_SYMBOL(file_fsync); -EXPORT_SYMBOL(_clear_inode); +EXPORT_SYMBOL(clear_inode); EXPORT_SYMBOL(refile_buffer); EXPORT_SYMBOL(nr_async_pages); EXPORT_SYMBOL(___strtok); @@ -352,7 +360,7 @@ EXPORT_SYMBOL(chrdev_inode_operations); EXPORT_SYMBOL(blkdev_inode_operations); EXPORT_SYMBOL(read_ahead); EXPORT_SYMBOL(get_hash_table); -EXPORT_SYMBOL(_get_empty_inode); +EXPORT_SYMBOL(get_empty_inode); EXPORT_SYMBOL(insert_inode_hash); EXPORT_SYMBOL(event); EXPORT_SYMBOL(__down); diff --git a/kernel/module.c b/kernel/module.c index 885539b5c..c584eb3ae 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -79,7 +79,8 @@ get_mod_name(const char *user_name, char **buf) unsigned long page; long retval; - if ((unsigned long)user_name >= TASK_SIZE) + if ((unsigned long)user_name >= TASK_SIZE + && get_fs () != KERNEL_DS) return -EFAULT; page = __get_free_page(GFP_KERNEL); @@ -134,7 +135,7 @@ sys_create_module(const char *name_user, size_t size) error = -EEXIST; goto err1; } - if ((mod = (struct module *)vmalloc(size)) == NULL) { + if ((mod = (struct module *)module_map(size)) == NULL) { error = -ENOMEM; goto err1; } @@ -685,6 +686,7 @@ sys_get_kernel_syms(struct kernel_sym *table) { struct module *mod; int i; + struct kernel_sym ksym; lock_kernel(); for (mod = module_list, i = 0; mod; mod = mod->next) { @@ -695,8 +697,10 @@ sys_get_kernel_syms(struct kernel_sym *table) if (table == NULL) goto out; + /* So that we don't give the user our stack content */ + memset (&ksym, 0, sizeof (ksym)); + for (mod = module_list, i = 0; mod; mod = mod->next) { - struct kernel_sym ksym; struct module_symbol *msym; unsigned int j; @@ -790,7 +794,7 @@ free_module(struct module *mod) /* And free the memory. */ - vfree(mod); + module_unmap(mod); } /* diff --git a/kernel/sys.c b/kernel/sys.c index ca3d17807..27d41eed1 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -21,7 +21,6 @@ #include <linux/fcntl.h> #include <linux/acct.h> #include <linux/tty.h> -#include <linux/nametrans.h> #include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/notifier.h> @@ -397,10 +396,8 @@ int acct_process(long exitcode) fs = get_fs(); set_fs(KERNEL_DS); - acct_file.f_op->write(acct_file.f_inode, &acct_file, + acct_file.f_op->write(acct_file.f_dentry->d_inode, &acct_file, (char *)&ac, sizeof(struct acct)); - /* inode->i_status |= ST_MODIFIED is willingly *not* done here */ - set_fs(fs); } return 0; @@ -408,8 +405,6 @@ int acct_process(long exitcode) asmlinkage int sys_acct(const char *name) { - struct inode *inode = (struct inode *)0; - char *tmp; int error = -EPERM; lock_kernel(); @@ -419,10 +414,10 @@ asmlinkage int sys_acct(const char *name) if (name == (char *)0) { if (acct_active) { if (acct_file.f_op->release) - acct_file.f_op->release(acct_file.f_inode, &acct_file); + acct_file.f_op->release(acct_file.f_dentry->d_inode, &acct_file); - if (acct_file.f_inode != (struct inode *) 0) - iput(acct_file.f_inode); + if (acct_file.f_dentry != NULL) + dput(acct_file.f_dentry); acct_active = 0; } @@ -430,40 +425,51 @@ asmlinkage int sys_acct(const char *name) } else { error = -EBUSY; if (!acct_active) { - if ((error = getname(name, &tmp)) != 0) + struct dentry *dentry; + struct inode *inode; + char *tmp; + + tmp = getname(name); + error = PTR_ERR(tmp); + if (IS_ERR(tmp)) goto out; - error = open_namei(tmp, O_RDWR, 0600, &inode, 0); + dentry = open_namei(tmp, O_RDWR, 0600); putname(tmp); - if (error) + + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto out; + inode = dentry->d_inode; error = -EACCES; if (!S_ISREG(inode->i_mode)) { - iput(inode); + dput(dentry); goto out; } error = -EIO; if (!inode->i_op || !inode->i_op->default_file_ops || !inode->i_op->default_file_ops->write) { - iput(inode); + dput(dentry); goto out; } acct_file.f_mode = 3; acct_file.f_flags = 0; acct_file.f_count = 1; - acct_file.f_inode = inode; + acct_file.f_dentry = dentry; acct_file.f_pos = inode->i_size; acct_file.f_reada = 0; acct_file.f_op = inode->i_op->default_file_ops; - if(acct_file.f_op->open) - if(acct_file.f_op->open(acct_file.f_inode, &acct_file)) { - iput(inode); + if(acct_file.f_op->open) { + error = acct_file.f_op->open(inode, &acct_file); + if (error) { + dput(dentry); goto out; } + } acct_active = 1; error = 0; @@ -612,21 +618,17 @@ asmlinkage int sys_setuid(uid_t uid) */ asmlinkage int sys_setresuid(uid_t ruid, uid_t euid, uid_t suid) { - uid_t old_ruid, old_euid, old_suid; - - old_ruid = current->uid; - old_euid = current->euid; - old_suid = current->suid; - - if ((ruid != (uid_t) -1) && (ruid != current->uid) && - (ruid != current->euid) && (ruid != current->suid)) - return -EPERM; - if ((euid != (uid_t) -1) && (euid != current->uid) && - (euid != current->euid) && (euid != current->suid)) - return -EPERM; - if ((suid != (uid_t) -1) && (suid != current->uid) && - (suid != current->euid) && (suid != current->suid)) - return -EPERM; + if (current->uid != 0 && current->euid != 0 && current->suid != 0) { + if ((ruid != (uid_t) -1) && (ruid != current->uid) && + (ruid != current->euid) && (ruid != current->suid)) + return -EPERM; + if ((euid != (uid_t) -1) && (euid != current->uid) && + (euid != current->euid) && (euid != current->suid)) + return -EPERM; + if ((suid != (uid_t) -1) && (suid != current->uid) && + (suid != current->euid) && (suid != current->suid)) + return -EPERM; + } if (ruid != (uid_t) -1) { /* See above commentary about NPROC rlimit issues here. */ charge_uid(current, -1); @@ -634,8 +636,12 @@ asmlinkage int sys_setresuid(uid_t ruid, uid_t euid, uid_t suid) if(ruid) charge_uid(current, 1); } - if (euid != (uid_t) -1) + if (euid != (uid_t) -1) { + if (euid != current->euid) + current->dumpable = 0; current->euid = euid; + current->fsuid = euid; + } if (suid != (uid_t) -1) current->suid = suid; return 0; @@ -652,6 +658,46 @@ asmlinkage int sys_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid) return retval; } +/* + * Same as above, but for rgid, egid, sgid. + */ +asmlinkage int sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid) +{ + if (current->uid != 0 && current->euid != 0 && current->suid != 0) { + if ((rgid != (gid_t) -1) && (rgid != current->gid) && + (rgid != current->egid) && (rgid != current->sgid)) + return -EPERM; + if ((egid != (gid_t) -1) && (egid != current->gid) && + (egid != current->egid) && (egid != current->sgid)) + return -EPERM; + if ((sgid != (gid_t) -1) && (sgid != current->gid) && + (sgid != current->egid) && (sgid != current->sgid)) + return -EPERM; + } + if (rgid != (gid_t) -1) + current->gid = rgid; + if (egid != (gid_t) -1) { + if (egid != current->egid) + current->dumpable = 0; + current->egid = egid; + current->fsgid = egid; + } + if (sgid != (gid_t) -1) + current->sgid = sgid; + return 0; +} + +asmlinkage int sys_getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid) +{ + int retval; + + if (!(retval = put_user(current->gid, rgid)) && + !(retval = put_user(current->egid, egid))) + retval = put_user(current->sgid, sgid); + + return retval; +} + /* * "setfsuid()" sets the fsuid - the uid used for filesystem checks. This diff --git a/kernel/sysctl.c b/kernel/sysctl.c index e4bdcfc1a..205190e9a 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -16,7 +16,6 @@ #include <linux/mm.h> #include <linux/sysctl.h> #include <linux/swapctl.h> -#include <linux/nametrans.h> #include <linux/proc_fs.h> #include <linux/malloc.h> #include <linux/stat.h> @@ -103,6 +102,7 @@ struct inode_operations proc_sys_inode_operations = NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -171,10 +171,6 @@ static ctl_table kern_table[] = { {KERN_JAVA_APPLETVIEWER, "java-appletviewer", binfmt_java_appletviewer, 64, 0644, NULL, &proc_dostring, &sysctl_string }, #endif -#ifdef CONFIG_TRANS_NAMES - {KERN_NAMETRANS, "nametrans", nametrans_txt, MAX_DEFAULT_TRANSLEN, - 0644, NULL, &nametrans_dostring, &nametrans_string}, -#endif #ifdef __sparc__ {KERN_SPARC_REBOOT, "reboot-cmd", reboot_command, 256, 0644, NULL, &proc_dostring, &sysctl_string }, @@ -822,6 +818,12 @@ int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp, #else /* CONFIG_PROC_FS */ +int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + return -ENOSYS; +} + int proc_dostring(ctl_table *table, int write, struct file *filp, void *buffer, size_t *lenp) { @@ -840,6 +842,12 @@ int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp, return -ENOSYS; } +int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + return -ENOSYS; +} + #endif /* CONFIG_PROC_FS */ @@ -1040,6 +1048,12 @@ int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp, return -ENOSYS; } +int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + return -ENOSYS; +} + struct ctl_table_header * register_sysctl_table(ctl_table * table, int insert_at_head) { diff --git a/mm/Makefile b/mm/Makefile index c64eefbd2..ef3820d1c 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -9,7 +9,7 @@ O_TARGET := mm.o O_OBJS := memory.o mmap.o filemap.o mprotect.o mlock.o mremap.o \ - vmalloc.o slab.o \ + vmalloc.o slab.o simp.o\ swap.o vmscan.o page_io.o page_alloc.o swap_state.o swapfile.o include $(TOPDIR)/Rules.make diff --git a/mm/filemap.c b/mm/filemap.c index 56aa1b486..8915c1096 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -753,10 +753,7 @@ page_read_error: filp->f_reada = 1; if (page_cache) free_page(page_cache); - if (DO_UPDATE_ATIME(inode)) { - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; - } + UPDATE_ATIME(inode) if (!read) read = error; return read; @@ -777,7 +774,7 @@ static unsigned long filemap_nopage(struct vm_area_struct * area, unsigned long duplicate flushes. ... */ unsigned long offset; struct page * page, **hash; - struct inode * inode = area->vm_inode; + struct inode * inode = area->vm_dentry->d_inode; unsigned long old_page, new_page; new_page = 0; @@ -921,7 +918,6 @@ static inline int do_write_page(struct inode * inode, struct file * file, retval = -EIO; if (size == file->f_op->write(inode, file, (const char *) page, size)) retval = 0; - /* inode->i_status |= ST_MODIFIED is willingly *not* done here */ set_fs(old_fs); return retval; } @@ -932,6 +928,7 @@ static int filemap_write_page(struct vm_area_struct * vma, { int result; struct file file; + struct dentry * dentry; struct inode * inode; struct buffer_head * bh; @@ -946,14 +943,15 @@ static int filemap_write_page(struct vm_area_struct * vma, return 0; } - inode = vma->vm_inode; + dentry = vma->vm_dentry; + inode = dentry->d_inode; file.f_op = inode->i_op->default_file_ops; if (!file.f_op->write) return -EIO; file.f_mode = 3; file.f_flags = 0; file.f_count = 1; - file.f_inode = inode; + file.f_dentry = dentry; file.f_pos = offset; file.f_reada = 0; @@ -1191,12 +1189,8 @@ int generic_file_mmap(struct inode * inode, struct file * file, struct vm_area_s return -EACCES; if (!inode->i_op || !inode->i_op->readpage) return -ENOEXEC; - if (DO_UPDATE_ATIME(inode)) { - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; - } - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + UPDATE_ATIME(inode); + vma->vm_dentry = dget(file->f_dentry); vma->vm_ops = ops; return 0; } @@ -1209,7 +1203,7 @@ int generic_file_mmap(struct inode * inode, struct file * file, struct vm_area_s static int msync_interval(struct vm_area_struct * vma, unsigned long start, unsigned long end, int flags) { - if (!vma->vm_inode) + if (!vma->vm_dentry) return 0; if (vma->vm_ops->sync) { int error; @@ -1217,7 +1211,7 @@ static int msync_interval(struct vm_area_struct * vma, if (error) return error; if (flags & MS_SYNC) - return file_fsync(vma->vm_inode, NULL); + return file_fsync(vma->vm_dentry->d_inode, NULL); return 0; } return 0; diff --git a/mm/mlock.c b/mm/mlock.c index 5a69e4b55..eea100add 100644 --- a/mm/mlock.c +++ b/mm/mlock.c @@ -38,8 +38,7 @@ static inline int mlock_fixup_start(struct vm_area_struct * vma, n->vm_end = end; vma->vm_offset += vma->vm_start - n->vm_start; n->vm_flags = newflags; - if (n->vm_inode) - atomic_inc(&n->vm_inode->i_count); + n->vm_dentry = dget(vma->vm_dentry); if (n->vm_ops && n->vm_ops->open) n->vm_ops->open(n); insert_vm_struct(current->mm, n); @@ -59,8 +58,7 @@ static inline int mlock_fixup_end(struct vm_area_struct * vma, n->vm_start = start; n->vm_offset += n->vm_start - vma->vm_start; n->vm_flags = newflags; - if (n->vm_inode) - atomic_inc(&n->vm_inode->i_count); + n->vm_dentry = dget(vma->vm_dentry); if (n->vm_ops && n->vm_ops->open) n->vm_ops->open(n); insert_vm_struct(current->mm, n); @@ -89,8 +87,9 @@ static inline int mlock_fixup_middle(struct vm_area_struct * vma, vma->vm_offset += vma->vm_start - left->vm_start; right->vm_offset += right->vm_start - left->vm_start; vma->vm_flags = newflags; - if (vma->vm_inode) - atomic_add(2, &vma->vm_inode->i_count); + if (vma->vm_dentry) + vma->vm_dentry->d_count += 2; + if (vma->vm_ops && vma->vm_ops->open) { vma->vm_ops->open(left); vma->vm_ops->open(right); @@ -74,11 +74,11 @@ int vm_enough_memory(long pages) /* Remove one vm structure from the inode's i_mmap ring. */ static inline void remove_shared_vm_struct(struct vm_area_struct *vma) { - struct inode * inode = vma->vm_inode; + struct dentry * dentry = vma->vm_dentry; - if (inode) { + if (dentry) { if (vma->vm_flags & VM_DENYWRITE) - inode->i_writecount++; + dentry->d_inode->i_writecount++; if(vma->vm_next_share) vma->vm_next_share->vm_pprev_share = vma->vm_pprev_share; *vma->vm_pprev_share = vma->vm_next_share; @@ -194,7 +194,7 @@ unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len, return -EACCES; /* make sure there are no mandatory locks on the file. */ - if (locks_verify_locked(file->f_inode)) + if (locks_verify_locked(file->f_dentry->d_inode)) return -EAGAIN; /* fall through */ case MAP_PRIVATE: @@ -259,7 +259,7 @@ unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len, vma->vm_page_prot = protection_map[vma->vm_flags & 0x0f]; vma->vm_ops = NULL; vma->vm_offset = off; - vma->vm_inode = NULL; + vma->vm_dentry = NULL; vma->vm_pte = 0; do_munmap(addr, len); /* Clear old maps */ @@ -283,7 +283,7 @@ unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len, if (file) { int error = 0; if (vma->vm_flags & VM_DENYWRITE) { - if (file->f_inode->i_writecount > 0) + if (file->f_dentry->d_inode->i_writecount > 0) error = -ETXTBSY; else { /* f_op->mmap might possibly sleep @@ -291,16 +291,16 @@ unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len, * might). In any case, this takes care of any * race that this might cause. */ - file->f_inode->i_writecount--; + file->f_dentry->d_inode->i_writecount--; correct_wcount = 1; } } if (!error) - error = file->f_op->mmap(file->f_inode, file, vma); + error = file->f_op->mmap(file->f_dentry->d_inode, file, vma); if (error) { if (correct_wcount) - file->f_inode->i_writecount++; + file->f_dentry->d_inode->i_writecount++; kmem_cache_free(vm_area_cachep, vma); return error; } @@ -309,8 +309,10 @@ unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len, flags = vma->vm_flags; insert_vm_struct(mm, vma); if (correct_wcount) - file->f_inode->i_writecount++; + file->f_dentry->d_inode->i_writecount++; merge_segments(mm, vma->vm_start, vma->vm_end); + + addr = vma->vm_start; /* merge_segments might have merged our vma, so we can't use it any more */ mm->total_vm += len >> PAGE_SHIFT; @@ -387,8 +389,8 @@ static void unmap_fixup(struct vm_area_struct *area, if (addr == area->vm_start && end == area->vm_end) { if (area->vm_ops && area->vm_ops->close) area->vm_ops->close(area); - if (area->vm_inode) - iput(area->vm_inode); + if (area->vm_dentry) + dput(area->vm_dentry); return; } @@ -405,11 +407,14 @@ static void unmap_fixup(struct vm_area_struct *area, if (!mpnt) return; - *mpnt = *area; - mpnt->vm_offset += (end - area->vm_start); + mpnt->vm_mm = area->vm_mm; mpnt->vm_start = end; - if (mpnt->vm_inode) - atomic_inc(&mpnt->vm_inode->i_count); + mpnt->vm_end = area->vm_end; + mpnt->vm_page_prot = area->vm_page_prot; + mpnt->vm_flags = area->vm_flags; + mpnt->vm_ops = area->vm_ops; + mpnt->vm_offset += (end - area->vm_start); + mpnt->vm_dentry = dget(area->vm_dentry); if (mpnt->vm_ops && mpnt->vm_ops->open) mpnt->vm_ops->open(mpnt); area->vm_end = addr; /* Truncate area */ @@ -542,8 +547,8 @@ void exit_mmap(struct mm_struct * mm) } remove_shared_vm_struct(mpnt); zap_page_range(mm, start, size); - if (mpnt->vm_inode) - iput(mpnt->vm_inode); + if (mpnt->vm_dentry) + dput(mpnt->vm_dentry); kmem_cache_free(vm_area_cachep, mpnt); mpnt = next; } @@ -555,7 +560,7 @@ void exit_mmap(struct mm_struct * mm) void insert_vm_struct(struct mm_struct *mm, struct vm_area_struct *vmp) { struct vm_area_struct **pprev = &mm->mmap; - struct inode * inode; + struct dentry * dentry; /* Find where to link it in. */ while(*pprev && (*pprev)->vm_start <= vmp->vm_start) @@ -567,8 +572,9 @@ void insert_vm_struct(struct mm_struct *mm, struct vm_area_struct *vmp) *pprev = vmp; vmp->vm_pprev = pprev; - inode = vmp->vm_inode; - if (inode) { + dentry = vmp->vm_dentry; + if (dentry) { + struct inode * inode = dentry->d_inode; if (vmp->vm_flags & VM_DENYWRITE) inode->i_writecount--; @@ -615,16 +621,19 @@ void merge_segments (struct mm_struct * mm, unsigned long start_addr, unsigned l for ( ; mpnt && prev->vm_start < end_addr ; prev = mpnt, mpnt = next) { next = mpnt->vm_next; - /* To share, we must have the same inode, operations.. */ - if ((mpnt->vm_inode != prev->vm_inode) || + /* To share, we must have the same dentry, operations.. */ + if ((mpnt->vm_dentry != prev->vm_dentry)|| (mpnt->vm_pte != prev->vm_pte) || (mpnt->vm_ops != prev->vm_ops) || (mpnt->vm_flags != prev->vm_flags) || (prev->vm_end != mpnt->vm_start)) continue; - /* and if we have an inode, the offsets must be contiguous.. */ - if ((mpnt->vm_inode != NULL) || (mpnt->vm_flags & VM_SHM)) { + /* + * If we have a dentry or it's a shared memory area + * the offsets must be contiguous.. + */ + if ((mpnt->vm_dentry != NULL) || (mpnt->vm_flags & VM_SHM)) { unsigned long off = prev->vm_offset+prev->vm_end-prev->vm_start; if (off != mpnt->vm_offset) continue; @@ -645,8 +654,8 @@ void merge_segments (struct mm_struct * mm, unsigned long start_addr, unsigned l mpnt->vm_ops->close(mpnt); } remove_shared_vm_struct(mpnt); - if (mpnt->vm_inode) - atomic_dec(&mpnt->vm_inode->i_count); + if (mpnt->vm_dentry) + dput(mpnt->vm_dentry); kmem_cache_free(vm_area_cachep, mpnt); mpnt = prev; } diff --git a/mm/mprotect.c b/mm/mprotect.c index 2e46ca142..ddf4f4ed6 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -110,8 +110,7 @@ static inline int mprotect_fixup_start(struct vm_area_struct * vma, vma->vm_offset += vma->vm_start - n->vm_start; n->vm_flags = newflags; n->vm_page_prot = prot; - if (n->vm_inode) - atomic_inc(&n->vm_inode->i_count); + n->vm_dentry = dget(n->vm_dentry); if (n->vm_ops && n->vm_ops->open) n->vm_ops->open(n); insert_vm_struct(current->mm, n); @@ -133,8 +132,7 @@ static inline int mprotect_fixup_end(struct vm_area_struct * vma, n->vm_offset += n->vm_start - vma->vm_start; n->vm_flags = newflags; n->vm_page_prot = prot; - if (n->vm_inode) - atomic_inc(&n->vm_inode->i_count); + n->vm_dentry = dget(n->vm_dentry); if (n->vm_ops && n->vm_ops->open) n->vm_ops->open(n); insert_vm_struct(current->mm, n); @@ -165,8 +163,8 @@ static inline int mprotect_fixup_middle(struct vm_area_struct * vma, right->vm_offset += right->vm_start - left->vm_start; vma->vm_flags = newflags; vma->vm_page_prot = prot; - if (vma->vm_inode) - atomic_add(2, &vma->vm_inode->i_count); + if (vma->vm_dentry) + vma->vm_dentry->d_count += 2; if (vma->vm_ops && vma->vm_ops->open) { vma->vm_ops->open(left); vma->vm_ops->open(right); diff --git a/mm/mremap.c b/mm/mremap.c index a52db58de..aaabde322 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -140,8 +140,7 @@ static inline unsigned long move_vma(struct vm_area_struct * vma, new_vma->vm_start = new_addr; new_vma->vm_end = new_addr+new_len; new_vma->vm_offset = vma->vm_offset + (addr - vma->vm_start); - if (new_vma->vm_inode) - atomic_inc(&new_vma->vm_inode->i_count); + new_vma->vm_dentry = dget(vma->vm_dentry); if (new_vma->vm_ops && new_vma->vm_ops->open) new_vma->vm_ops->open(new_vma); insert_vm_struct(current->mm, new_vma); diff --git a/mm/page_io.c b/mm/page_io.c index 30d0c882e..5ebea3f09 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -98,7 +98,7 @@ void rw_swap_page(int rw, unsigned long entry, char * buf, int wait) return; wait_on_page(page); } else if (p->swap_file) { - struct inode *swapf = p->swap_file; + struct inode *swapf = p->swap_file->d_inode; unsigned int zones[PAGE_SIZE/512]; int i; if (swapf->i_op->bmap == NULL diff --git a/mm/simp.c b/mm/simp.c new file mode 100644 index 000000000..7959d6a0e --- /dev/null +++ b/mm/simp.c @@ -0,0 +1,434 @@ +#define NULL 0 +/* + * mm/simp.c -- simple allocator for cached objects + * + * (C) 1997 Thomas Schoebel-Theuer + */ + +#include <linux/simp.h> +#include <linux/smp.h> +#include <linux/mm.h> +#include <asm/spinlock.h> + +/* The next two defines can be independently enabled for debugging */ +/*#define DEBUG*/ +/*#define DEAD_BEEF*/ + +#ifdef DEAD_BEEF +#define DEBUG_BEEF 1 +#else +#define DEBUG_BEEF 0 +#endif + +#ifdef __SMP__ +#define NR_PROCESSORS NR_CPUS +#define GLOBAL_SIZE CHUNK_SIZE +#else +#define NR_PROCESSORS 1 +#define GLOBAL_SIZE PAGE_SIZE +#endif + +#define POSTBUFFER_SIZE 63 +#define ORDER 2 +#define CHUNK_SIZE (PAGE_SIZE*(1<<ORDER)) +#define CHUNK_BASE(ptr) (struct header*)(((unsigned long)(ptr)) & ~(CHUNK_SIZE-1)) +#define CHUNK_END(hdr) (void**)((char*)(hdr) + CHUNK_SIZE) + +#define COLOR_INCREMENT (8*sizeof(void*)) /* should be 1 cache line */ +#define ALIGN_CACHE(adr) ((((((unsigned long)adr) - 1) / COLOR_INCREMENT) + 1) * COLOR_INCREMENT) +#define HEADER_SIZE ALIGN_CACHE(sizeof(struct header)) +#define ELEM_SIZE ALIGN_CACHE(sizeof(struct elem)) +#define FILL_TYPE(name,wrongsize) char name[ALIGN_CACHE(wrongsize)-(wrongsize)] + +#define MAX_SIMPS ((GLOBAL_SIZE / sizeof(struct simp)) - 1) + +struct header { /* this is at the beginning of each memory region */ + /* 1st cache line */ + void ** index; + void ** fresh; + struct simp * father; + void ** emptypos; + struct header * next; + structor again_ctor; + structor first_ctor; + void * fill[1]; +#ifdef DEBUG + /* 2nd cache line */ + char magic[32]; +#endif +}; + +struct per_processor { + void ** buffer_pos; + void * postbuffer[POSTBUFFER_SIZE]; +}; + +struct simp { + /* 1st cache lines */ + struct per_processor private[NR_PROCESSORS]; + /* next cache line */ + struct header * usable_list; + spinlock_t lock; + char fill[sizeof(void*) - sizeof(spinlock_t)]; + long real_size; + long max_elems; + structor again_ctor; + structor first_ctor; + structor dtor; + long fill2; + /* next cache line */ + long create_offset; + long color; + long max_color; + long size; + long fill3[4]; + /* next cache line */ + char name[32]; +}; + +struct global_data { + /* 1st cache line */ + long changed_flag; + long nr_simps; + spinlock_t lock; + char fill[(6+8)*sizeof(void*)+sizeof(void*)-sizeof(spinlock_t)]; + /* rest */ + struct simp simps[MAX_SIMPS]; +}; + +static struct global_data * global = NULL; + +#ifdef DEBUG +static char global_magic[32] = "SIMP header SdC581oi9rY20051962\n"; +#endif + +struct simp * simp_create(char * name, long size, + structor first_ctor, + structor again_ctor, + structor dtor) +{ + struct simp * simp; + long fraction; + long real_size; + int cpu; + + if(!global) { +#ifdef __SMP__ + global = (struct global_data*)__get_free_pages(GFP_KERNEL, ORDER, 0); + memset(global, 0, CHUNK_SIZE); +#else + global = (struct global_data*)get_free_page(GFP_KERNEL); +#endif + spin_lock_init(&global->lock); + } + + spin_lock(&global->lock); + simp = &global->simps[global->nr_simps++]; + spin_unlock(&global->lock); + + if(global->nr_simps >= MAX_SIMPS) { + printk("SIMP: too many simps allocated\n"); + return NULL; + } + memset(simp, 0, sizeof(struct simp)); + spin_lock_init(&simp->lock); + strncpy(simp->name, name, 15); + simp->size = size; + simp->real_size = real_size = ALIGN_CACHE(size); + /* allow aggregation of very small objects in 2-power fractions of + * cachelines */ + fraction = COLOR_INCREMENT / 2; + while(size <= fraction && fraction >= sizeof(void*)) { + simp->real_size = fraction; + fraction >>= 1; + } + simp->first_ctor = first_ctor; + simp->again_ctor = again_ctor; + simp->dtor = dtor; + + real_size += sizeof(void*); + simp->max_elems = (CHUNK_SIZE - HEADER_SIZE) / real_size; + simp->max_color = (CHUNK_SIZE - HEADER_SIZE) % real_size; + for(cpu = 0; cpu < NR_PROCESSORS; cpu++) { + struct per_processor * private = &simp->private[cpu]; + private->buffer_pos = private->postbuffer; + } + return simp; +} + +/* Do *not* inline this, it clobbers too many registers... */ +static void alloc_header(struct simp * simp) +{ + struct header * hdr; + char * ptr; + void ** index; + long count; + + spin_unlock(&simp->lock); + for(;;) { + hdr = (struct header*)__get_free_pages(GFP_KERNEL, ORDER, 0); + if(hdr) + break; + if(!simp_garbage()) + return; + } +#ifdef DEBUG + if(CHUNK_BASE(hdr) != hdr) + panic("simp: bad kernel page alignment"); +#endif + + memset(hdr, 0, HEADER_SIZE); +#ifdef DEBUG + memcpy(hdr->magic, global_magic, sizeof(global_magic)); +#endif + hdr->father = simp; + hdr->again_ctor = simp->again_ctor; + hdr->first_ctor = simp->first_ctor; + + /* note: races on simp->color don't produce any error :-) */ + ptr = ((char*)hdr) + HEADER_SIZE + simp->color; + index = CHUNK_END(hdr); + for(count = 0; count < simp->max_elems; count++) { + *--index = ptr; + ptr += simp->real_size; + /* note: constructors are not called here in bunch but + * instead at each single simp_alloc(), in order + * to maximize chances that the cache will be + * polluted after a simp_alloc() anyway, + * and not here. */ + } + hdr->index = hdr->fresh = hdr->emptypos = index; + + spin_lock(&simp->lock); + simp->color += COLOR_INCREMENT; + if(simp->color >= simp->max_color) + simp->color = 0; + hdr->next = simp->usable_list; + simp->usable_list = hdr; +} + + +/* current x86 memcpy() is horribly moving around registers for nothing, + * is doing unnecessary work if the size is dividable by a power-of-two, + * and it clobbers way too many registers. + * This results in nearly any other register being transfered to stack. + * Fixing this would be a major win for the whole kernel! + */ +static void ** bunch_alloc(struct simp * simp, void ** buffer) +{ + struct header * hdr; + void ** index; + void ** to; + void ** end; + structor todo; + long length; + + spin_lock(&simp->lock); + hdr = simp->usable_list; + if(!hdr) { + alloc_header(simp); + hdr = simp->usable_list; + if(!hdr) { + spin_unlock(&simp->lock); + *buffer = NULL; + return buffer+1; + } + } + + index = hdr->index; + end = hdr->fresh; + todo = hdr->again_ctor; + if(index == end) { + end = CHUNK_END(hdr); + todo = hdr->first_ctor; + } + to = index + POSTBUFFER_SIZE/2; + if(to >= end) { + to = end; + if(to == CHUNK_END(hdr)) { + simp->usable_list = hdr->next; + hdr->next = NULL; + } + } + if(to > hdr->fresh) + hdr->fresh = to; + hdr->index = to; + length = ((unsigned long)to) - (unsigned long)index; + to = buffer + (length/sizeof(void**)); + + memcpy(buffer, index, length); + + spin_unlock(&simp->lock); + + if(todo) { + do { + todo(*buffer++); + } while(buffer < to); + } + return to; +} + +void * simp_alloc(struct simp * simp) +{ +#ifdef __SMP__ + const long cpu = smp_processor_id(); + struct per_processor * priv = &simp->private[cpu]; +#else +#define priv (&simp->private[0]) /*fool gcc to use no extra register*/ +#endif + void ** buffer_pos = priv->buffer_pos; + void * res; + + if(buffer_pos == priv->postbuffer) { + buffer_pos = bunch_alloc(simp, buffer_pos); + } + buffer_pos--; + res = *buffer_pos; + priv->buffer_pos = buffer_pos; + return res; +} + +#ifdef DEBUG +long check_header(struct header * hdr, void * ptr) +{ + void ** test; + + if(!hdr) { + printk("SIMP: simp_free() with NULL pointer\n"); + return 1; + } + if(strncmp(hdr->magic, global_magic, 32)) { + printk("SIMP: simpe_free() with bad ptr %p, or header corruption\n", ptr); + return 1; + } + /* This is brute force, but I don't want to pay for any + * overhead if debugging is not enabled, in particular + * no space overhead for keeping hashtables etc. */ + test = hdr->index; + while(test < CHUNK_END(hdr)) { + if(*test++ == ptr) { + printk("SIMP: trying to simp_free(%p) again\n", ptr); + return 1; + } + } + return 0; +} +#endif + +static void ** bunch_free(struct simp * simp, void ** buffer) +{ + void ** stop; + + stop = buffer - POSTBUFFER_SIZE/3; + + spin_lock(&simp->lock); + while(buffer > stop) { + void * elem = buffer[-1]; + struct header * hdr = CHUNK_BASE(elem); + void ** index = hdr->index; + index--; + hdr->index = index; + *index = elem; + if(!hdr->next) { + hdr->next = simp->usable_list; + simp->usable_list = hdr; + } + + buffer -= 2; + elem = *buffer; + hdr = CHUNK_BASE(elem); + index = hdr->index; + index--; + hdr->index = index; + *index = elem; + if(!hdr->next) { + hdr->next = simp->usable_list; + simp->usable_list = hdr; + } + } + spin_unlock(&simp->lock); + global->changed_flag = 1; + return buffer; +} + +void simp_free(void * objp) +{ + struct header * hdr; + void ** buffer_pos; + struct per_processor * private; +#ifdef __SMP__ + const long cpu = smp_processor_id(); +#else + const long cpu = 0; +#endif + + hdr = CHUNK_BASE(objp); +#ifdef DEBUG + if(check_header(hdr, objp)) + return; +#endif + + private = &hdr->father->private[cpu]; + buffer_pos = private->buffer_pos; + if(buffer_pos >= private->postbuffer+POSTBUFFER_SIZE) { + buffer_pos = bunch_free(hdr->father, buffer_pos); + } + + *buffer_pos++ = objp; + private->buffer_pos = buffer_pos; + +#ifdef DEAD_BEEF + { + unsigned int * ptr = (unsigned int*)objp; + int count = (hdr->father->real_size - ELEM_SIZE) / sizeof(unsigned int); + while(count--) + *ptr++ = 0xdeadbeef; + } +#endif +} + +long simp_garbage(void) +{ + int i; + int res; + + if(!global->changed_flag) + return 0; /* shortcut */ + /* Note: costs do not matter here. Any heavy thrashing of + * simp chunks that could be caused by pools stealing each + * other's memory has to be considered a BUG :-) + * Simply avoid memory shortages by conservative allocating + * policies. + */ + global->changed_flag = 0; + res = 0; + for(i = 0; i < global->nr_simps; i++) { + struct simp * simp = &global->simps[i]; + struct header ** base = &simp->usable_list; + struct header * del; + + spin_lock(&simp->lock); + del = *base; + while(del) { + if(del->index == del->emptypos) { + if(simp->dtor) { + void ** ptr = del->index; + while(ptr < CHUNK_END(del)) { + simp->dtor(*ptr++); + } + } + *base = del->next; +#ifdef DEBUG + memset(del, 0, CHUNK_SIZE); +#endif + free_pages((unsigned long)del, ORDER); + res++; + } else + base = &del->next; + del = *base; + } + spin_unlock(&simp->lock); + } + return res; +} + @@ -123,7 +123,7 @@ * 0 if you wish to reduce memory usage. * * SLAB_DEBUG_SUPPORT - 1 for kmem_cache_create() to honour; SLAB_DEBUG_FREE, - * SLAB_DEBUG_INITIAL, SLAB_RED_ZONE & SLAB_POISION. + * SLAB_DEBUG_INITIAL, SLAB_RED_ZONE & SLAB_POISON. * 0 for faster, smaller, code (espically in the critical paths). * * SLAB_STATS - 1 to collect stats for /proc/slabinfo. @@ -143,11 +143,11 @@ #if SLAB_DEBUG_SUPPORT #if 0 #define SLAB_C_MASK (SLAB_DEBUG_FREE|SLAB_DEBUG_INITIAL|SLAB_RED_ZONE| \ - SLAB_POISION|SLAB_HWCACHE_ALIGN|SLAB_NO_REAP| \ + SLAB_POISON|SLAB_HWCACHE_ALIGN|SLAB_NO_REAP| \ SLAB_HIGH_PACK) #endif #define SLAB_C_MASK (SLAB_DEBUG_FREE|SLAB_DEBUG_INITIAL|SLAB_RED_ZONE| \ - SLAB_POISION|SLAB_HWCACHE_ALIGN|SLAB_NO_REAP) + SLAB_POISON|SLAB_HWCACHE_ALIGN|SLAB_NO_REAP) #else #if 0 #define SLAB_C_MASK (SLAB_HWCACHE_ALIGN|SLAB_NO_REAP|SLAB_HIGH_PACK) @@ -215,9 +215,9 @@ typedef struct kmem_bufctl_s { #define SLAB_RED_MAGIC1 0x5A2CF071UL /* when obj is active */ #define SLAB_RED_MAGIC2 0x170FC2A5UL /* when obj is inactive */ -/* ...and for poisioning */ -#define SLAB_POISION_BYTE 0x5a /* byte value for poisioning */ -#define SLAB_POISION_END 0xa5 /* end-byte of poisioning */ +/* ...and for poisoning */ +#define SLAB_POISON_BYTE 0x5a /* byte value for poisoning */ +#define SLAB_POISON_END 0xa5 /* end-byte of poisoning */ #endif /* SLAB_DEBUG_SUPPORT */ @@ -546,17 +546,17 @@ kmem_freepages(kmem_cache_t *cachep, void *addr) #if SLAB_DEBUG_SUPPORT static inline void -kmem_poision_obj(kmem_cache_t *cachep, void *addr) +kmem_poison_obj(kmem_cache_t *cachep, void *addr) { - memset(addr, SLAB_POISION_BYTE, cachep->c_org_size); - *(unsigned char *)(addr+cachep->c_org_size-1) = SLAB_POISION_END; + memset(addr, SLAB_POISON_BYTE, cachep->c_org_size); + *(unsigned char *)(addr+cachep->c_org_size-1) = SLAB_POISON_END; } static inline int -kmem_check_poision_obj(kmem_cache_t *cachep, void *addr) +kmem_check_poison_obj(kmem_cache_t *cachep, void *addr) { void *end; - end = memchr(addr, SLAB_POISION_END, cachep->c_org_size); + end = memchr(addr, SLAB_POISON_END, cachep->c_org_size); if (end != (addr+cachep->c_org_size-1)) return 1; return 0; @@ -605,7 +605,7 @@ kmem_slab_destroy(kmem_cache_t *cachep, kmem_slab_t *slabp) { if (cachep->c_dtor #if SLAB_DEBUG_SUPPORT - || cachep->c_flags & (SLAB_POISION || SLAB_RED_ZONE) + || cachep->c_flags & (SLAB_POISON || SLAB_RED_ZONE) #endif /*SLAB_DEBUG_SUPPORT*/ ) { /* Doesn't use the bufctl ptrs to find objs. */ @@ -629,10 +629,10 @@ kmem_slab_destroy(kmem_cache_t *cachep, kmem_slab_t *slabp) #endif /*SLAB_DEBUG_SUPPORT*/ (cachep->c_dtor)(objp, cachep, 0); #if SLAB_DEBUG_SUPPORT - else if (cachep->c_flags & SLAB_POISION) { - if (kmem_check_poision_obj(cachep, objp)) + else if (cachep->c_flags & SLAB_POISON) { + if (kmem_check_poison_obj(cachep, objp)) printk(KERN_ERR "kmem_slab_destory: " - "Bad poision - %s\n", cachep->c_name); + "Bad poison - %s\n", cachep->c_name); } if (cachep->c_flags & SLAB_RED_ZONE) objp -= BYTES_PER_WORD; @@ -726,18 +726,18 @@ kmem_cache_create(const char *name, size_t size, size_t offset, flags &= ~SLAB_DEBUG_INITIAL; } - if ((flags & SLAB_POISION) && ctor) { - /* request for poisioning, but we can't do that with a constructor */ - printk("%sPoisioning requested, but con given - %s\n", func_nm, name); - flags &= ~SLAB_POISION; + if ((flags & SLAB_POISON) && ctor) { + /* request for poisoning, but we can't do that with a constructor */ + printk("%sPoisoning requested, but con given - %s\n", func_nm, name); + flags &= ~SLAB_POISON; } #if 0 if ((flags & SLAB_HIGH_PACK) && ctor) { printk("%sHigh pack requested, but con given - %s\n", func_nm, name); flags &= ~SLAB_HIGH_PACK; } - if ((flags & SLAB_HIGH_PACK) && (flags & (SLAB_POISION|SLAB_RED_ZONE))) { - printk("%sHigh pack requested, but with poisioning/red-zoning - %s\n", + if ((flags & SLAB_HIGH_PACK) && (flags & (SLAB_POISON|SLAB_RED_ZONE))) { + printk("%sHigh pack requested, but with poisoning/red-zoning - %s\n", func_nm, name); flags &= ~SLAB_HIGH_PACK; } @@ -1094,9 +1094,9 @@ kmem_cache_init_objs(kmem_cache_t * cachep, kmem_slab_t * slabp, void *objp, if (cachep->c_ctor) cachep->c_ctor(objp, cachep, ctor_flags); #if SLAB_DEBUG_SUPPORT - else if (cachep->c_flags & SLAB_POISION) { - /* need to poision the objs */ - kmem_poision_obj(cachep, objp); + else if (cachep->c_flags & SLAB_POISON) { + /* need to poison the objs */ + kmem_poison_obj(cachep, objp); } if (cachep->c_flags & SLAB_RED_ZONE) { @@ -1275,7 +1275,7 @@ kmem_report_alloc_err(const char *str, kmem_cache_t * cachep) } static void -kmem_report_free_err(const char *str, void *objp, kmem_cache_t * cachep) +kmem_report_free_err(const char *str, const void *objp, kmem_cache_t * cachep) { if (cachep) SLAB_STATS_INC_ERR(cachep); @@ -1386,7 +1386,7 @@ ret_obj: bufp->buf_slabp = slabp; objp = ((void*)bufp) - cachep->c_offset; finished: - /* The lock is not needed by the red-zone or poision ops, and the + /* The lock is not needed by the red-zone or poison ops, and the * obj has been removed from the slab. Should be safe to drop * the lock here. */ @@ -1395,8 +1395,8 @@ finished: if (cachep->c_flags & SLAB_RED_ZONE) goto red_zone; ret_red: - if ((cachep->c_flags & SLAB_POISION) && kmem_check_poision_obj(cachep, objp)) - kmem_report_alloc_err("Bad poision", cachep); + if ((cachep->c_flags & SLAB_POISON) && kmem_check_poison_obj(cachep, objp)) + kmem_report_alloc_err("Bad poison", cachep); #endif /* SLAB_DEBUG_SUPPORT */ return objp; } @@ -1456,7 +1456,7 @@ nul_ptr: * it should be in this state _before_ it is released. */ static inline void -__kmem_cache_free(kmem_cache_t *cachep, void *objp) +__kmem_cache_free(kmem_cache_t *cachep, const void *objp) { kmem_slab_t *slabp; kmem_bufctl_t *bufp; @@ -1514,10 +1514,10 @@ passed_extra: /* (hopefully) The most common case. */ finished: #if SLAB_DEBUG_SUPPORT - if (cachep->c_flags & SLAB_POISION) { + if (cachep->c_flags & SLAB_POISON) { if (cachep->c_flags & SLAB_RED_ZONE) objp += BYTES_PER_WORD; - kmem_poision_obj(cachep, objp); + kmem_poison_obj(cachep, objp); } #endif /* SLAB_DEBUG_SUPPORT */ spin_unlock_irqrestore(&cachep->c_spinlock, save_flags); @@ -1615,7 +1615,7 @@ kmalloc(size_t size, int flags) } void -kfree(void *objp) +kfree(const void *objp) { struct page *page; int nr; @@ -1654,7 +1654,7 @@ while(1); } void -kfree_s(void *objp, size_t size) +kfree_s(const void *objp, size_t size) { struct page *page; int nr; @@ -1861,7 +1861,7 @@ kmem_self_test(void) kmem_cache_t *test_cachep; printk(KERN_INFO "kmem_test() - start\n"); - test_cachep = kmem_cache_create("test-cachep", 16, 0, SLAB_RED_ZONE|SLAB_POISION, NULL, NULL); + test_cachep = kmem_cache_create("test-cachep", 16, 0, SLAB_RED_ZONE|SLAB_POISON, NULL, NULL); if (test_cachep) { char *objp = kmem_cache_alloc(test_cachep, SLAB_KERNEL); if (objp) { @@ -1870,12 +1870,12 @@ kmem_self_test(void) *(objp+16) = 1; kmem_cache_free(test_cachep, objp); - /* Mess up poisioning. */ + /* Mess up poisoning. */ *objp = 10; objp = kmem_cache_alloc(test_cachep, SLAB_KERNEL); kmem_cache_free(test_cachep, objp); - /* Mess up poisioning (again). */ + /* Mess up poisoning (again). */ *objp = 10; kmem_cache_shrink(test_cachep); } diff --git a/mm/swapfile.c b/mm/swapfile.c index 819ae7aa8..400274268 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -326,7 +326,7 @@ again: asmlinkage int sys_swapoff(const char * specialfile) { struct swap_info_struct * p = NULL; - struct inode * inode; + struct dentry * dentry; struct file filp; int i, type, prev; int err = -EPERM; @@ -334,19 +334,22 @@ asmlinkage int sys_swapoff(const char * specialfile) lock_kernel(); if (!suser()) goto out; - err = namei(NAM_FOLLOW_LINK, specialfile, &inode); - if (err) + + dentry = namei(specialfile); + err = PTR_ERR(dentry); + if (IS_ERR(dentry)) goto out; + prev = -1; for (type = swap_list.head; type >= 0; type = swap_info[type].next) { p = swap_info + type; if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) { if (p->swap_file) { - if (p->swap_file == inode) + if (p->swap_file == dentry) break; } else { - if (S_ISBLK(inode->i_mode) - && (p->swap_device == inode->i_rdev)) + if (S_ISBLK(dentry->d_inode->i_mode) + && (p->swap_device == dentry->d_inode->i_rdev)) break; } } @@ -354,7 +357,7 @@ asmlinkage int sys_swapoff(const char * specialfile) } err = -EINVAL; if (type < 0){ - iput(inode); + dput(dentry); goto out; } if (prev < 0) { @@ -369,7 +372,7 @@ asmlinkage int sys_swapoff(const char * specialfile) p->flags = SWP_USED; err = try_to_unuse(type); if (err) { - iput(inode); + dput(dentry); /* re-insert swap space back into swap_list */ for (prev = -1, i = swap_list.head; i >= 0; prev = i, i = swap_info[i].next) if (p->prio >= swap_info[i].prio) @@ -384,21 +387,19 @@ asmlinkage int sys_swapoff(const char * specialfile) } if(p->swap_device){ memset(&filp, 0, sizeof(filp)); - filp.f_inode = inode; + filp.f_dentry = dentry; filp.f_mode = 3; /* read write */ /* open it again to get fops */ - if( !blkdev_open(inode, &filp) && + if( !blkdev_open(dentry->d_inode, &filp) && filp.f_op && filp.f_op->release){ - filp.f_op->release(inode,&filp); - filp.f_op->release(inode,&filp); + filp.f_op->release(dentry->d_inode,&filp); + filp.f_op->release(dentry->d_inode,&filp); } } - iput(inode); + dput(dentry); nr_swap_pages -= p->pages; - iput(p->swap_file); - if (p->swap_filename) - kfree(p->swap_filename); + dput(p->swap_file); p->swap_file = NULL; p->swap_device = 0; vfree(p->swap_map); @@ -420,10 +421,8 @@ int get_swaparea_info(char *buf) len += sprintf(buf, "Filename\t\t\tType\t\tSize\tUsed\tPriority\n"); for (i = 0 ; i < nr_swapfiles ; i++, ptr++) if (ptr->flags & SWP_USED) { - if (ptr->swap_filename) - len += sprintf(buf + len, "%-31s ", ptr->swap_filename); - else - len += sprintf(buf + len, "(null)\t\t\t"); + len += sprintf(buf + len, "%-31s ", ptr->swap_file->d_name.name); + if (ptr->swap_file) len += sprintf(buf + len, "file\t\t"); else @@ -451,11 +450,10 @@ int get_swaparea_info(char *buf) asmlinkage int sys_swapon(const char * specialfile, int swap_flags) { struct swap_info_struct * p; - struct inode * swap_inode; + struct dentry * swap_dentry; unsigned int type; int i, j, prev; int error = -EPERM; - char *tmp; struct file filp; static int least_priority = 0; @@ -472,7 +470,6 @@ asmlinkage int sys_swapon(const char * specialfile, int swap_flags) if (type >= nr_swapfiles) nr_swapfiles = type+1; p->flags = SWP_USED; - p->swap_filename = NULL; p->swap_file = NULL; p->swap_device = 0; p->swap_map = NULL; @@ -488,25 +485,22 @@ asmlinkage int sys_swapon(const char * specialfile, int swap_flags) } else { p->prio = --least_priority; } - error = namei(NAM_FOLLOW_LINK, specialfile, &swap_inode); - if (error) - goto bad_swap_2; - p->swap_file = swap_inode; - error = -EBUSY; - if (atomic_read(&swap_inode->i_count) != 1) + swap_dentry = namei(specialfile); + error = PTR_ERR(swap_dentry); + if (IS_ERR(swap_dentry)) goto bad_swap_2; + + p->swap_file = swap_dentry; error = -EINVAL; - if (S_ISBLK(swap_inode->i_mode)) { - p->swap_device = swap_inode->i_rdev; + if (S_ISBLK(swap_dentry->d_inode->i_mode)) { + p->swap_device = swap_dentry->d_inode->i_rdev; set_blocksize(p->swap_device, PAGE_SIZE); - filp.f_inode = swap_inode; + filp.f_dentry = swap_dentry; filp.f_mode = 3; /* read write */ - error = blkdev_open(swap_inode, &filp); - p->swap_file = NULL; - iput(swap_inode); - if(error) + error = blkdev_open(swap_dentry->d_inode, &filp); + if (error) goto bad_swap_2; error = -ENODEV; if (!p->swap_device || @@ -520,7 +514,7 @@ asmlinkage int sys_swapon(const char * specialfile, int swap_flags) if (p->swap_device == swap_info[i].swap_device) goto bad_swap; } - } else if (!S_ISREG(swap_inode->i_mode)) + } else if (!S_ISREG(swap_dentry->d_inode->i_mode)) goto bad_swap; p->swap_lockmap = (unsigned char *) get_free_page(GFP_USER); if (!p->swap_lockmap) { @@ -580,12 +574,6 @@ asmlinkage int sys_swapon(const char * specialfile, int swap_flags) prev = i; } p->next = i; - if (!getname(specialfile, &tmp)) { - if ((p->swap_filename = - (char *) kmalloc(strlen(tmp)+1, GFP_KERNEL)) != (char *)NULL) - strcpy(p->swap_filename, tmp); - putname(tmp); - } if (prev < 0) { swap_list.head = swap_list.next = p - swap_info; } else { @@ -595,11 +583,11 @@ asmlinkage int sys_swapon(const char * specialfile, int swap_flags) goto out; bad_swap: if(filp.f_op && filp.f_op->release) - filp.f_op->release(filp.f_inode,&filp); + filp.f_op->release(filp.f_dentry->d_inode,&filp); bad_swap_2: free_page((long) p->swap_lockmap); vfree(p->swap_map); - iput(p->swap_file); + dput(p->swap_file); p->swap_device = 0; p->swap_file = NULL; p->swap_map = NULL; diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 71afe1aea..d0270d586 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -68,7 +68,7 @@ static inline void free_area_pmd(pgd_t * dir, unsigned long address, unsigned lo } } -static void free_area_pages(unsigned long address, unsigned long size) +void vmfree_area_pages(unsigned long address, unsigned long size) { pgd_t * dir; unsigned long end = address + size; @@ -125,7 +125,7 @@ static inline int alloc_area_pmd(pmd_t * pmd, unsigned long address, unsigned lo return 0; } -static int alloc_area_pages(unsigned long address, unsigned long size) +int vmalloc_area_pages(unsigned long address, unsigned long size) { pgd_t * dir; unsigned long end = address + size; @@ -181,7 +181,7 @@ void vfree(void * addr) for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) { if (tmp->addr == addr) { *p = tmp->next; - free_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size); + vmfree_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size); kfree(tmp); return; } @@ -201,7 +201,7 @@ void * vmalloc(unsigned long size) if (!area) return NULL; addr = area->addr; - if (alloc_area_pages(VMALLOC_VMADDR(addr), size)) { + if (vmalloc_area_pages(VMALLOC_VMADDR(addr), size)) { vfree(addr); return NULL; } diff --git a/mm/vmscan.c b/mm/vmscan.c index 21c178159..eeadbaa4f 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -7,7 +7,7 @@ * kswapd added: 7.1.96 sct * Removed kswapd_ctl limits, and swap out as many pages as needed * to bring the system back to free_pages_high: 2.4.97, Rik van Riel. - * Version: $Id: vmscan.c,v 1.23 1997/04/12 04:31:05 davem Exp $ + * Version: $Id: vmscan.c,v 1.3 1997/06/17 13:31:02 ralf Exp $ */ #include <linux/mm.h> @@ -362,13 +362,22 @@ static inline int do_try_to_free_page(int priority, int dma, int wait) return 1; state = 1; case 1: - if (kmem_cache_reap(i, dma, wait)) - return 1; + shrink_dcache(); state = 2; case 2: - if (shm_swap(i, dma)) + /* + * We shouldn't have a priority here: + * If we're low on memory we should + * unconditionally throw away _all_ + * kmalloc caches! + */ + if (kmem_cache_reap(0, dma, wait)) return 1; state = 3; + case 3: + if (shm_swap(i, dma)) + return 1; + state = 4; default: if (swap_out(i, dma, wait)) return 1; @@ -403,7 +412,7 @@ int try_to_free_page(int priority, int dma, int wait) int kswapd(void *unused) { int i; - char *revision="$Revision: 1.23 $", *s, *e; + char *revision="$Revision: 1.3 $", *s, *e; current->session = 1; current->pgrp = 1; diff --git a/net/Config.in b/net/Config.in index 4cd3619c3..d9ec19354 100644 --- a/net/Config.in +++ b/net/Config.in @@ -53,6 +53,6 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then # if [ "$CONFIG_LLC" = "y" ]; then # bool 'Netbeui (EXPERIMENTAL)' CONFIG_NETBEUI # fi - tristate 'WAN router' CONFIG_WAN_ROUTER + bool 'WAN router' CONFIG_WAN_ROUTER fi endmenu diff --git a/net/README b/net/README index a88ccfc5a..1cd7f5331 100644 --- a/net/README +++ b/net/README @@ -6,19 +6,19 @@ Code Section Bug Report Contact 802 [other ] alan@lxorguk.ukuu.org.uk [token ring ] pnorton@cts.com appletalk alan@lxorguk.ukuu.org.uk and netatalk@umich.edu -ax25 jsn@cs.nott.ac.uk +ax25 g4klx@g4klx.demon.co.uk core alan@lxorguk.ukuu.org.uk decnet SteveW@ACM.org ethernet alan@lxorguk.ukuu.org.uk ipv4 davem@caip.rutgers.edu,Eric.Schenk@dna.lth.se ipv6 davem@caip.rutgers.edu,Eric.Schenk@dna.lth.se ipx alan@lxorguk.ukuu.org.uk,greg@caldera.com -lapb jsn@cs.nott.ac.uk -netrom jsn@cs.nott.ac.uk -rose jsn@cs.nott.ac.uk +lapb g4klx@g4klx.demon.co.uk +netrom g4klx@g4klx.demon.co.uk +rose g4klx@g4klx.demon.co.uk wanrouter genek@compuserve.com and dm@sangoma.com unix alan@lxorguk.ukuu.org.uk -x25 jsn@cs.nott.ac.uk +x25 g4klx@g4klx.demon.co.uk If in doubt contact me <alan@lxorguk.ukuu.org.uk> first. diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c index de05f7047..a98ed27d3 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -1540,11 +1540,33 @@ static int atalk_rcv(struct sk_buff *skb, struct device *dev, struct packet_type ddp_dl->header_length + ddp->deh_len)); *((__u16 *)ddp)=ntohs(*((__u16 *)ddp)); /* Mend the byte order */ + /* * Send the buffer onwards */ - skb=skb_unshare(skb, GFP_ATOMIC, FREE_READ); + /* + * Now we must always be careful. If it's come from + * localtalk to ethertalk it might not fit + * + * Order matters here: If a packet has to be copied + * to make a new headroom (rare hopefully) then it + * won't need unsharing. + * + * Note. ddp-> becomes invalid at the realloc. + */ + + if(skb_headroom(skb)<22) + /* 22 bytes - 12 ether, 2 len, 3 802.2 5 snap */ + skb=skb_realloc_headroom(skb, 32); + else + skb=skb_unshare(skb, GFP_ATOMIC, FREE_READ); + + /* + * If the buffer didnt vanish into the lack of + * space bitbucket we can send it. + */ + if(skb) { skb->arp = 1; /* Resolved */ @@ -1702,10 +1724,14 @@ static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, int len, return(-EINVAL); if(usat->sat_family != AF_APPLETALK) return -EINVAL; -#if 0 /* netatalk doesn't implement this check */ + /* netatalk doesn't implement this check */ if(usat->sat_addr.s_node==ATADDR_BCAST && !sk->broadcast) + { + printk(KERN_INFO "SO_BROADCAST: Fix your netatalk as it will break before 2.2\n"); +#if 0 return -EPERM; #endif + } } else { @@ -1806,7 +1832,7 @@ static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, int len, { if((!(rt->flags&RTF_GATEWAY))&&(!(dev->flags&IFF_LOOPBACK))) { - struct sk_buff *skb2=skb_clone(skb, GFP_KERNEL); + struct sk_buff *skb2=skb_copy(skb, GFP_KERNEL); if(skb2) { loopback=1; diff --git a/net/appletalk/sysctl_net_atalk.c b/net/appletalk/sysctl_net_atalk.c index 6d5159ddc..c2c1a8c64 100644 --- a/net/appletalk/sysctl_net_atalk.c +++ b/net/appletalk/sysctl_net_atalk.c @@ -6,6 +6,7 @@ * Dynamic registration, added aarp entries. (5/30/97 Chris Horn) */ +#include <linux/config.h> #include <linux/mm.h> #include <linux/sysctl.h> @@ -14,7 +15,7 @@ extern int sysctl_aarp_tick_time; extern int sysctl_aarp_retransmit_limit; extern int sysctl_aarp_resolve_time; - +#ifdef CONFIG_SYSCTL static ctl_table atalk_table[] = { {NET_ATALK_AARP_EXPIRY_TIME, "aarp-expiry-time", &sysctl_aarp_expiry_time, sizeof(int), 0644, NULL, &proc_dointvec_jiffies}, @@ -39,16 +40,25 @@ static ctl_table atalk_root_table[] = { static struct ctl_table_header *atalk_table_header; -inline void atalk_register_sysctl(void) +void atalk_register_sysctl(void) { atalk_table_header = register_sysctl_table(atalk_root_table, 1); } -inline void atalk_unregister_sysctl(void) +void atalk_unregister_sysctl(void) { unregister_sysctl_table(atalk_table_header); } +#else +void atalk_register_sysctl(void) +{ +} + +void atalk_unregister_sysctl(void) +{ +} +#endif diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index 37b679600..8e5992747 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -92,6 +92,7 @@ * AX.25 036 Jonathan(G4KLX) Major restructuring. * Joerg(DL1BKE) Fixed DAMA Slave. * Jonathan(G4KLX) Fix widlcard listen parameter setting. + * AX.25 037 Jonathan(G4KLX) New timer architecture. */ #include <linux/config.h> @@ -161,8 +162,7 @@ static void ax25_remove_socket(ax25_cb *ax25) ax25_cb *s; unsigned long flags; - save_flags(flags); - cli(); + save_flags(flags); cli(); if ((s = ax25_list) == ax25) { ax25_list = s->next; @@ -196,16 +196,8 @@ static void ax25_kill_by_device(struct device *dev) for (s = ax25_list; s != NULL; s = s->next) { if (s->ax25_dev == ax25_dev) { - s->state = AX25_STATE_0; s->ax25_dev = NULL; - if (s->sk != NULL) { - s->sk->state = TCP_CLOSE; - s->sk->err = ENETUNREACH; - s->sk->shutdown |= SEND_SHUTDOWN; - if (!s->sk->dead) - s->sk->state_change(s->sk); - s->sk->dead = 1; - } + ax25_disconnect(s, ENETUNREACH); } } } @@ -308,7 +300,7 @@ struct sock *ax25_find_socket(ax25_address *my_addr, ax25_address *dest_addr, in * Find an AX.25 control block given both ends. It will only pick up * floating AX.25 control blocks or non Raw socket bound control blocks. */ -ax25_cb *ax25_find_cb(ax25_address *my_addr, ax25_address *dest_addr, ax25_digi *digi, struct device *dev) +ax25_cb *ax25_find_cb(ax25_address *src_addr, ax25_address *dest_addr, ax25_digi *digi, struct device *dev) { ax25_cb *s; unsigned long flags; @@ -319,11 +311,16 @@ ax25_cb *ax25_find_cb(ax25_address *my_addr, ax25_address *dest_addr, ax25_digi for (s = ax25_list; s != NULL; s = s->next) { if (s->sk != NULL && s->sk->type != SOCK_SEQPACKET) continue; - if (ax25cmp(&s->source_addr, my_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->ax25_dev->dev == dev) { - if (digi != NULL) { - if (s->digipeat == NULL && digi->ndigi != 0) + if (s->ax25_dev == NULL) + continue; + if (ax25cmp(&s->source_addr, src_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->ax25_dev->dev == dev) { + if (digi != NULL && digi->ndigi != 0) { + if (s->digipeat == NULL) continue; - if (s->digipeat != NULL && ax25digicmp(s->digipeat, digi) != 0) + if (ax25digicmp(s->digipeat, digi) != 0) + continue; + } else { + if (s->digipeat != NULL && s->digipeat->ndigi != 0) continue; } restore_flags(flags); @@ -402,10 +399,13 @@ void ax25_destroy_socket(ax25_cb *ax25) /* Not static as it's used by the timer struct sk_buff *skb; unsigned long flags; - save_flags(flags); - cli(); + save_flags(flags); cli(); - del_timer(&ax25->timer); + ax25_stop_heartbeat(ax25); + ax25_stop_t1timer(ax25); + ax25_stop_t2timer(ax25); + ax25_stop_t3timer(ax25); + ax25_stop_idletimer(ax25); ax25_remove_socket(ax25); ax25_clear_queues(ax25); /* Flush the queues */ @@ -414,7 +414,7 @@ void ax25_destroy_socket(ax25_cb *ax25) /* Not static as it's used by the timer while ((skb = skb_dequeue(&ax25->sk->receive_queue)) != NULL) { if (skb->sk != ax25->sk) { /* A pending connection */ skb->sk->dead = 1; /* Queue the unaccepted socket for death */ - ax25_set_timer(skb->sk->protinfo.ax25); + ax25_start_heartbeat(skb->sk->protinfo.ax25); skb->sk->protinfo.ax25->state = AX25_STATE_0; } @@ -451,13 +451,9 @@ static int ax25_ctl_ioctl(const unsigned int cmd, void *arg) struct ax25_ctl_struct ax25_ctl; ax25_dev *ax25_dev; ax25_cb *ax25; - unsigned long flags; - int err; - - if ((err = verify_area(VERIFY_READ, arg, sizeof(ax25_ctl))) != 0) - return err; - copy_from_user(&ax25_ctl, arg, sizeof(ax25_ctl)); + if (copy_from_user(&ax25_ctl, arg, sizeof(ax25_ctl))) + return -EFAULT; if ((ax25_dev = ax25_addr_ax25dev(&ax25_ctl.port_addr)) == NULL) return -ENODEV; @@ -467,22 +463,12 @@ static int ax25_ctl_ioctl(const unsigned int cmd, void *arg) switch (ax25_ctl.cmd) { case AX25_KILL: - ax25_clear_queues(ax25); ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25->state = AX25_STATE_0; #ifdef CONFIG_AX25_DAMA_SLAVE if (ax25_dev->dama.slave && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE) ax25_dama_off(ax25); #endif - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ENETRESET; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } - ax25_set_timer(ax25); + ax25_disconnect(ax25, ENETRESET); break; case AX25_WINDOW: @@ -499,22 +485,14 @@ static int ax25_ctl_ioctl(const unsigned int cmd, void *arg) case AX25_T1: if (ax25_ctl.arg < 1) return -EINVAL; - ax25->rtt = (ax25_ctl.arg * AX25_SLOWHZ) / 2; - ax25->t1 = ax25_ctl.arg * AX25_SLOWHZ; - save_flags(flags); cli(); - if (ax25->t1timer > ax25->t1) - ax25->t1timer = ax25->t1; - restore_flags(flags); + ax25->rtt = (ax25_ctl.arg * HZ) / 2; + ax25->t1 = ax25_ctl.arg * HZ; break; case AX25_T2: if (ax25_ctl.arg < 1) return -EINVAL; - save_flags(flags); cli(); - ax25->t2 = ax25_ctl.arg * AX25_SLOWHZ; - if (ax25->t2timer > ax25->t2) - ax25->t2timer = ax25->t2; - restore_flags(flags); + ax25->t2 = ax25_ctl.arg * HZ; break; case AX25_N2: @@ -527,21 +505,13 @@ static int ax25_ctl_ioctl(const unsigned int cmd, void *arg) case AX25_T3: if (ax25_ctl.arg < 0) return -EINVAL; - save_flags(flags); cli(); - ax25->t3 = ax25_ctl.arg * AX25_SLOWHZ; - if (ax25->t3timer != 0) - ax25->t3timer = ax25->t3; - restore_flags(flags); + ax25->t3 = ax25_ctl.arg * HZ; break; case AX25_IDLE: if (ax25_ctl.arg < 0) return -EINVAL; - save_flags(flags); cli(); - ax25->idle = ax25_ctl.arg * AX25_SLOWHZ * 60; - if (ax25->idletimer != 0) - ax25->idletimer = ax25->idle; - restore_flags(flags); + ax25->idle = ax25_ctl.arg * 60 * HZ; break; case AX25_PACLEN: @@ -622,6 +592,10 @@ ax25_cb *ax25_create_cb(void) skb_queue_head_init(&ax25->reseq_queue); init_timer(&ax25->timer); + init_timer(&ax25->t1timer); + init_timer(&ax25->t2timer); + init_timer(&ax25->t3timer); + init_timer(&ax25->idletimer); ax25_fillin_cb(ax25, NULL); @@ -664,13 +638,14 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op case AX25_T1: if (opt < 1) return -EINVAL; - sk->protinfo.ax25->rtt = (opt * AX25_SLOWHZ) / 2; + sk->protinfo.ax25->rtt = (opt * HZ) / 2; + sk->protinfo.ax25->t1 = opt * HZ; return 0; case AX25_T2: if (opt < 1) return -EINVAL; - sk->protinfo.ax25->t2 = opt * AX25_SLOWHZ; + sk->protinfo.ax25->t2 = opt * HZ; return 0; case AX25_N2: @@ -682,13 +657,13 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op case AX25_T3: if (opt < 1) return -EINVAL; - sk->protinfo.ax25->t3 = opt * AX25_SLOWHZ; + sk->protinfo.ax25->t3 = opt * HZ; return 0; case AX25_IDLE: if (opt < 0) return -EINVAL; - sk->protinfo.ax25->idle = opt * AX25_SLOWHZ * 60; + sk->protinfo.ax25->idle = opt * 60 * HZ; return 0; case AX25_BACKOFF: @@ -738,11 +713,11 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op break; case AX25_T1: - val = (sk->protinfo.ax25->t1 * 2) / AX25_SLOWHZ; + val = sk->protinfo.ax25->t1 / HZ; break; case AX25_T2: - val = sk->protinfo.ax25->t2 / AX25_SLOWHZ; + val = sk->protinfo.ax25->t2 / HZ; break; case AX25_N2: @@ -750,11 +725,11 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op break; case AX25_T3: - val = sk->protinfo.ax25->t3 / AX25_SLOWHZ; + val = sk->protinfo.ax25->t3 / HZ; break; case AX25_IDLE: - val = sk->protinfo.ax25->idle / (AX25_SLOWHZ * 60); + val = sk->protinfo.ax25->idle / (60 * HZ); break; case AX25_BACKOFF: @@ -963,22 +938,14 @@ static int ax25_release(struct socket *sock, struct socket *peer) if (sk->type == SOCK_SEQPACKET) { switch (sk->protinfo.ax25->state) { case AX25_STATE_0: - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; + ax25_disconnect(sk->protinfo.ax25, 0); ax25_destroy_socket(sk->protinfo.ax25); break; case AX25_STATE_1: case AX25_STATE_2: - ax25_clear_queues(sk->protinfo.ax25); ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - sk->protinfo.ax25->state = AX25_STATE_0; - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; + ax25_disconnect(sk->protinfo.ax25, 0); ax25_destroy_socket(sk->protinfo.ax25); break; @@ -990,31 +957,34 @@ static int ax25_release(struct socket *sock, struct socket *peer) case AX25_PROTO_STD_SIMPLEX: case AX25_PROTO_STD_DUPLEX: ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - sk->protinfo.ax25->t3timer = 0; + ax25_stop_t2timer(sk->protinfo.ax25); + ax25_stop_t3timer(sk->protinfo.ax25); + ax25_stop_idletimer(sk->protinfo.ax25); break; #ifdef AX25_CONFIG_DAMA_SLAVE case AX25_PROTO_DAMA_SLAVE: - sk->protinfo.ax25->t3timer = 0; + ax25_stop_t3timer(sk->protinfo.ax25); break; #endif } - sk->protinfo.ax25->t1timer = sk->protinfo.ax25->t1 = ax25_calculate_t1(sk->protinfo.ax25); - sk->protinfo.ax25->state = AX25_STATE_2; - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; + ax25_calculate_t1(sk->protinfo.ax25); + ax25_start_t1timer(sk->protinfo.ax25); + sk->protinfo.ax25->state = AX25_STATE_2; + sk->state = TCP_CLOSE; + sk->shutdown |= SEND_SHUTDOWN; sk->state_change(sk); - sk->dead = 1; - sk->destroy = 1; + sk->dead = 1; + sk->destroy = 1; break; default: break; } } else { - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; + sk->state = TCP_CLOSE; + sk->shutdown |= SEND_SHUTDOWN; sk->state_change(sk); - sk->dead = 1; + sk->dead = 1; ax25_destroy_socket(sk->protinfo.ax25); } @@ -1092,8 +1062,9 @@ static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags) { struct sock *sk = sock->sk; - struct sockaddr_ax25 *addr = (struct sockaddr_ax25 *)uaddr; - int err; + struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr; + ax25_digi *digi = NULL; + int ct = 0, err; if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) { sock->state = SS_CONNECTED; @@ -1114,36 +1085,36 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25)) return -EINVAL; - if (addr->sax25_family != AF_AX25) + if (fsa->fsa_ax25.sax25_family != AF_AX25) return -EINVAL; + if (sk->protinfo.ax25->digipeat != NULL) { + kfree(sk->protinfo.ax25->digipeat); + sk->protinfo.ax25->digipeat = NULL; + } + /* * Handle digi-peaters to be used. */ - if (addr_len == sizeof(struct full_sockaddr_ax25) && addr->sax25_ndigis != 0) { - int ct = 0; - struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)addr; - + if (addr_len == sizeof(struct full_sockaddr_ax25) && fsa->fsa_ax25.sax25_ndigis != 0) { /* Valid number of digipeaters ? */ - if (addr->sax25_ndigis < 1 || addr->sax25_ndigis > AX25_MAX_DIGIS) + if (fsa->fsa_ax25.sax25_ndigis < 1 || fsa->fsa_ax25.sax25_ndigis > AX25_MAX_DIGIS) return -EINVAL; - if (sk->protinfo.ax25->digipeat == NULL) { - if ((sk->protinfo.ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) - return -ENOBUFS; - } + if ((digi = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) + return -ENOBUFS; - sk->protinfo.ax25->digipeat->ndigi = addr->sax25_ndigis; - sk->protinfo.ax25->digipeat->lastrepeat = -1; + digi->ndigi = fsa->fsa_ax25.sax25_ndigis; + digi->lastrepeat = -1; - while (ct < addr->sax25_ndigis) { + while (ct < fsa->fsa_ax25.sax25_ndigis) { if ((fsa->fsa_digipeater[ct].ax25_call[6] & AX25_HBIT) && sk->protinfo.ax25->iamdigi) { - sk->protinfo.ax25->digipeat->repeated[ct] = 1; - sk->protinfo.ax25->digipeat->lastrepeat = ct; + digi->repeated[ct] = 1; + digi->lastrepeat = ct; } else { - sk->protinfo.ax25->digipeat->repeated[ct] = 0; + digi->repeated[ct] = 0; } - sk->protinfo.ax25->digipeat->calls[ct] = fsa->fsa_digipeater[ct]; + digi->calls[ct] = fsa->fsa_digipeater[ct]; ct++; } } @@ -1154,7 +1125,7 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le * been filled in, error if it hasn't. */ if (sk->zapped) { - if ((err = ax25_rt_autobind(sk->protinfo.ax25, &addr->sax25_call)) < 0) + if ((err = ax25_rt_autobind(sk->protinfo.ax25, &fsa->fsa_ax25.sax25_call)) < 0) return err; ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->ax25_dev); ax25_insert_socket(sk->protinfo.ax25); @@ -1163,10 +1134,13 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le return -EHOSTUNREACH; } - if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&sk->protinfo.ax25->source_addr, &addr->sax25_call, NULL, sk->protinfo.ax25->ax25_dev->dev) != NULL) + if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&sk->protinfo.ax25->source_addr, &fsa->fsa_ax25.sax25_call, digi, sk->protinfo.ax25->ax25_dev->dev) != NULL) { + if (digi != NULL) kfree(digi); return -EADDRINUSE; /* Already such a connection */ + } - sk->protinfo.ax25->dest_addr = addr->sax25_call; + sk->protinfo.ax25->dest_addr = fsa->fsa_ax25.sax25_call; + sk->protinfo.ax25->digipeat = digi; /* First the easy one */ if (sk->type != SOCK_SEQPACKET) { @@ -1198,7 +1172,8 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le } sk->protinfo.ax25->state = AX25_STATE_1; - ax25_set_timer(sk->protinfo.ax25); /* Start going SABM SABM until a UA or a give up and DM */ + + ax25_start_heartbeat(sk->protinfo.ax25); /* Now the loop */ if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) @@ -1532,38 +1507,35 @@ static int ax25_shutdown(struct socket *sk, int how) static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; - struct ax25_info_struct ax25_info; - int err; - long amount = 0; switch (cmd) { - case TIOCOUTQ: - if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int))) != 0) - return err; + case TIOCOUTQ: { + long amount; amount = sk->sndbuf - atomic_read(&sk->wmem_alloc); if (amount < 0) amount = 0; - put_user(amount, (int *)arg); + if (put_user(amount, (int *)arg)) + return -EFAULT; return 0; + } case TIOCINQ: { struct sk_buff *skb; + long amount = 0L; /* These two are safe on a single CPU system as only user tasks fiddle here */ if ((skb = skb_peek(&sk->receive_queue)) != NULL) amount = skb->len; - if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int))) != 0) - return err; - put_user(amount, (int *)arg); + if (put_user(amount, (int *)arg)) + return -EFAULT; return 0; } case SIOCGSTAMP: if (sk != NULL) { - if (sk->stamp.tv_sec==0) + if (sk->stamp.tv_sec == 0) return -ENOENT; - if ((err = verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval))) != 0) - return err; - copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)); + if (copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval))) + return -EFAULT; return 0; } return -EINVAL; @@ -1572,22 +1544,22 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) case SIOCAX25DELUID: /* Delete a uid from the uid/call map table */ case SIOCAX25GETUID: { struct sockaddr_ax25 sax25; - if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(struct sockaddr_ax25))) != 0) - return err; - copy_from_user(&sax25, (void *)arg, sizeof(sax25)); + if (copy_from_user(&sax25, (void *)arg, sizeof(sax25))) + return -EFAULT; return ax25_uid_ioctl(cmd, &sax25); } - case SIOCAX25NOUID: /* Set the default policy (default/bar) */ - if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(unsigned long))) != 0) - return err; + case SIOCAX25NOUID: { /* Set the default policy (default/bar) */ + long amount; if (!suser()) return -EPERM; - get_user(amount, (long *)arg); + if (get_user(amount, (long *)arg)) + return -EFAULT; if (amount > AX25_NOUID_BLOCK) return -EINVAL; ax25_uid_policy = amount; return 0; + } case SIOCADDRT: case SIOCDELRT: @@ -1601,33 +1573,33 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) return -EPERM; return ax25_ctl_ioctl(cmd, (void *)arg); - case SIOCAX25GETINFO: - if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(ax25_info))) != 0) - return err; - ax25_info.t1 = sk->protinfo.ax25->t1; - ax25_info.t2 = sk->protinfo.ax25->t2; - ax25_info.t3 = sk->protinfo.ax25->t3; - ax25_info.idle = sk->protinfo.ax25->idle; + case SIOCAX25GETINFO: { + struct ax25_info_struct ax25_info; + ax25_info.t1 = sk->protinfo.ax25->t1 / HZ; + ax25_info.t2 = sk->protinfo.ax25->t2 / HZ; + ax25_info.t3 = sk->protinfo.ax25->t3 / HZ; + ax25_info.idle = sk->protinfo.ax25->idle / (60 * HZ); ax25_info.n2 = sk->protinfo.ax25->n2; - ax25_info.t1timer = sk->protinfo.ax25->t1timer; - ax25_info.t2timer = sk->protinfo.ax25->t2timer; - ax25_info.t3timer = sk->protinfo.ax25->t3timer; - ax25_info.idletimer = sk->protinfo.ax25->idletimer; + ax25_info.t1timer = ax25_display_timer(&sk->protinfo.ax25->t1timer) / HZ; + ax25_info.t2timer = ax25_display_timer(&sk->protinfo.ax25->t2timer) / HZ; + ax25_info.t3timer = ax25_display_timer(&sk->protinfo.ax25->t3timer) / HZ; + ax25_info.idletimer = ax25_display_timer(&sk->protinfo.ax25->idletimer) / (60 * HZ); ax25_info.n2count = sk->protinfo.ax25->n2count; ax25_info.state = sk->protinfo.ax25->state; ax25_info.rcv_q = atomic_read(&sk->rmem_alloc); ax25_info.snd_q = atomic_read(&sk->wmem_alloc); - copy_to_user((void *)arg, &ax25_info, sizeof(ax25_info)); + if (copy_to_user((void *)arg, &ax25_info, sizeof(ax25_info))) + return -EFAULT; return 0; + } case SIOCAX25ADDFWD: case SIOCAX25DELFWD: { struct ax25_fwd_struct ax25_fwd; if (!suser()) return -EPERM; - if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(ax25_fwd))) != 0) - return err; - copy_from_user(&ax25_fwd, (void *)arg, sizeof(ax25_fwd)); + if (copy_from_user(&ax25_fwd, (void *)arg, sizeof(ax25_fwd))) + return -EFAULT; return ax25_fwd_ioctl(cmd, &ax25_fwd); } @@ -1655,13 +1627,14 @@ static int ax25_get_info(char *buffer, char **start, off_t offset, int length, i { ax25_cb *ax25; const char *devname; + char callbuf[15]; int len = 0; off_t pos = 0; off_t begin = 0; cli(); - len += sprintf(buffer, "dest_addr src_addr dev st vs vr va t1 t2 t3 idle n2 rtt wnd paclen Snd-Q Rcv-Q\n"); + len += sprintf(buffer, "dest_addr src_addr dev st vs vr va t1 t2 t3 idle n2 rtt wnd paclen Snd-Q Rcv-Q\n"); for (ax25 = ax25_list; ax25 != NULL; ax25 = ax25->next) { if (ax25->ax25_dev == NULL) @@ -1671,20 +1644,28 @@ static int ax25_get_info(char *buffer, char **start, off_t offset, int length, i len += sprintf(buffer + len, "%-9s ", ax2asc(&ax25->dest_addr)); - len += sprintf(buffer + len, "%-9s %-4s %2d %3d %3d %3d %3d/%03d %2d/%02d %3d/%03d %3d/%03d %2d/%02d %3d %3d %5d", - ax2asc(&ax25->source_addr), devname, + + sprintf(callbuf, "%s%c", ax2asc(&ax25->source_addr), + (ax25->iamdigi) ? '*' : ' '); + + len += sprintf(buffer + len, "%-10s %-4s %2d %3d %3d %3d %3lu/%03lu %2lu/%02lu %3lu/%03lu %3lu/%03lu %2d/%02d %3lu %3d %5d", + callbuf, + devname, ax25->state, - ax25->vs, ax25->vr, ax25->va, - ax25->t1timer / AX25_SLOWHZ, - ax25->t1 / AX25_SLOWHZ, - ax25->t2timer / AX25_SLOWHZ, - ax25->t2 / AX25_SLOWHZ, - ax25->t3timer / AX25_SLOWHZ, - ax25->t3 / AX25_SLOWHZ, - ax25->idletimer / (AX25_SLOWHZ * 60), - ax25->idle / (AX25_SLOWHZ * 60), - ax25->n2count, ax25->n2, - ax25->rtt / AX25_SLOWHZ, + ax25->vs, + ax25->vr, + ax25->va, + ax25_display_timer(&ax25->t1timer) / HZ, + ax25->t1 / HZ, + ax25_display_timer(&ax25->t2timer) / HZ, + ax25->t2 / HZ, + ax25_display_timer(&ax25->t3timer) / HZ, + ax25->t3 / HZ, + ax25_display_timer(&ax25->idletimer) / (60 * HZ), + ax25->idle / (60 * HZ), + ax25->n2count, + ax25->n2, + ax25->rtt / HZ, ax25->window, ax25->paclen); @@ -1764,7 +1745,7 @@ static struct notifier_block ax25_dev_notifier = { EXPORT_SYMBOL(ax25_encapsulate); EXPORT_SYMBOL(ax25_rebuild_header); EXPORT_SYMBOL(ax25_findbyuid); -EXPORT_SYMBOL(ax25_link_up); +EXPORT_SYMBOL(ax25_find_cb); EXPORT_SYMBOL(ax25_linkfail_register); EXPORT_SYMBOL(ax25_linkfail_release); EXPORT_SYMBOL(ax25_listen_register); @@ -1777,6 +1758,7 @@ EXPORT_SYMBOL(ax25cmp); EXPORT_SYMBOL(ax2asc); EXPORT_SYMBOL(asc2ax); EXPORT_SYMBOL(null_ax25_address); +EXPORT_SYMBOL(ax25_display_timer); #ifdef CONFIG_PROC_FS static struct proc_dir_entry proc_ax25_route = { @@ -1815,7 +1797,7 @@ __initfunc(void ax25_proto_init(struct net_proto *pro)) proc_net_register(&proc_ax25_calls); #endif - printk(KERN_INFO "G4KLX/GW4PTS AX.25 for Linux. Version 0.36 for Linux NET3.038 (Linux 2.1)\n"); + printk(KERN_INFO "G4KLX/GW4PTS AX.25 for Linux. Version 0.37 for Linux NET3.038 (Linux 2.1)\n"); } #ifdef MODULE diff --git a/net/ax25/ax25_addr.c b/net/ax25/ax25_addr.c index 77bfabe73..5daf92fa5 100644 --- a/net/ax25/ax25_addr.c +++ b/net/ax25/ax25_addr.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -165,27 +165,23 @@ unsigned char *ax25_addr_parse(unsigned char *buf, int len, ax25_address *src, a if (len < 14) return NULL; - if (flags != NULL) { - *flags = 0; + *flags = 0; - if (buf[6] & AX25_CBIT) - *flags = AX25_COMMAND; - if (buf[13] & AX25_CBIT) - *flags = AX25_RESPONSE; - } + if (buf[6] & AX25_CBIT) + *flags = AX25_COMMAND; + if (buf[13] & AX25_CBIT) + *flags = AX25_RESPONSE; if (dama != NULL) *dama = ~buf[13] & AX25_DAMA_FLAG; /* Copy to, from */ - if (dest != NULL) - memcpy(dest, buf + 0, AX25_ADDR_LEN); - - if (src != NULL) - memcpy(src, buf + 7, AX25_ADDR_LEN); + memcpy(dest, buf + 0, AX25_ADDR_LEN); + memcpy(src, buf + 7, AX25_ADDR_LEN); buf += 2 * AX25_ADDR_LEN; len -= 2 * AX25_ADDR_LEN; + digi->lastrepeat = -1; digi->ndigi = 0; @@ -193,15 +189,14 @@ unsigned char *ax25_addr_parse(unsigned char *buf, int len, ax25_address *src, a if (d >= AX25_MAX_DIGIS) return NULL; /* Max of 6 digis */ if (len < 7) return NULL; /* Short packet */ - if (digi != NULL) { - memcpy(&digi->calls[d], buf, AX25_ADDR_LEN); - digi->ndigi = d + 1; - if (buf[6] & AX25_HBIT) { - digi->repeated[d] = 1; - digi->lastrepeat = d; - } else { - digi->repeated[d] = 0; - } + memcpy(&digi->calls[d], buf, AX25_ADDR_LEN); + digi->ndigi = d + 1; + + if (buf[6] & AX25_HBIT) { + digi->repeated[d] = 1; + digi->lastrepeat = d; + } else { + digi->repeated[d] = 0; } buf += AX25_ADDR_LEN; @@ -285,15 +280,15 @@ int ax25_addr_size(ax25_digi *dp) */ void ax25_digi_invert(ax25_digi *in, ax25_digi *out) { - int ct = 0; + int ct; out->ndigi = in->ndigi; out->lastrepeat = in->ndigi - in->lastrepeat - 2; /* Invert the digipeaters */ + for (ct = 0; ct < in->ndigi; ct++) { + out->calls[ct] = in->calls[in->ndigi - ct - 1]; - while (ct < in->ndigi) { - out->calls[ct] = in->calls[in->ndigi - ct - 1]; if (ct <= out->lastrepeat) { out->calls[ct].ax25_call[6] |= AX25_HBIT; out->repeated[ct] = 1; @@ -301,7 +296,6 @@ void ax25_digi_invert(ax25_digi *in, ax25_digi *out) out->calls[ct].ax25_call[6] &= ~AX25_HBIT; out->repeated[ct] = 0; } - ct++; } } diff --git a/net/ax25/ax25_dev.c b/net/ax25/ax25_dev.c index 6468faf77..3f4f46ad4 100644 --- a/net/ax25/ax25_dev.c +++ b/net/ax25/ax25_dev.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * diff --git a/net/ax25/ax25_ds_in.c b/net/ax25/ax25_ds_in.c index 1394c9ab7..6b5e68236 100644 --- a/net/ax25/ax25_ds_in.c +++ b/net/ax25/ax25_ds_in.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -18,6 +18,7 @@ * History * AX.25 036 Jonathan(G4KLX) Cloned from ax25_in.c * Joerg(DL1BKE) Fixed it. + * AX.25 037 Jonathan(G4KLX) New timer architecture. */ #include <linux/config.h> @@ -64,9 +65,9 @@ static int ax25_ds_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int framet case AX25_UA: ax25_calculate_rtt(ax25); - ax25->t1timer = 0; - ax25->t3timer = ax25->t3; - ax25->idletimer = ax25->idle; + ax25_stop_t1timer(ax25); + ax25_start_t3timer(ax25); + ax25_start_idletimer(ax25); ax25->vs = 0; ax25->va = 0; ax25->vr = 0; @@ -90,23 +91,11 @@ static int ax25_ds_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int framet break; case AX25_DM: - if (pf) { - ax25_clear_queues(ax25); - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ECONNREFUSED; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } - } + if (pf) ax25_disconnect(ax25, ECONNREFUSED); break; default: - if (pf) - ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); + if (pf) ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); break; } @@ -128,31 +117,15 @@ static int ax25_ds_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int framet case AX25_DISC: ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25->state = AX25_STATE_0; ax25_dama_off(ax25); - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, 0); break; case AX25_DM: case AX25_UA: if (pf) { - ax25->state = AX25_STATE_0; ax25_dama_off(ax25); - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, 0); } break; @@ -187,10 +160,10 @@ static int ax25_ds_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int framet ax25->modulus = AX25_MODULUS; ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); + ax25_stop_t1timer(ax25); + ax25_start_t3timer(ax25); + ax25_start_idletimer(ax25); ax25->condition = 0x00; - ax25->t1timer = 0; - ax25->t3timer = ax25->t3; - ax25->idletimer = ax25->idle; ax25->vs = 0; ax25->va = 0; ax25->vr = 0; @@ -199,34 +172,14 @@ static int ax25_ds_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int framet break; case AX25_DISC: - ax25_clear_queues(ax25); ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25->t3timer = 0; - ax25->state = AX25_STATE_0; ax25_dama_off(ax25); - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, 0); break; case AX25_DM: - ax25_clear_queues(ax25); - ax25->t3timer = 0; - ax25->state = AX25_STATE_0; ax25_dama_off(ax25); - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ECONNRESET; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, ECONNRESET); break; case AX25_RR: @@ -250,9 +203,9 @@ static int ax25_ds_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int framet if (ax25_validate_nr(ax25, nr)) { ax25_frames_acked(ax25, nr); ax25_calculate_rtt(ax25); + ax25_stop_t1timer(ax25); + ax25_start_t3timer(ax25); ax25->n2count = 0; - ax25->t1timer = 0; - ax25->t3timer = ax25->t3; ax25_requeue_frames(ax25); if (type == AX25_COMMAND && pf) ax25_ds_enquiry_response(ax25); @@ -281,18 +234,15 @@ static int ax25_ds_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int framet if (ns == ax25->vr) { ax25->vr = (ax25->vr + 1) % AX25_MODULUS; queued = ax25_rx_iframe(ax25, skb); - if (ax25->condition & AX25_COND_OWN_RX_BUSY) { + if (ax25->condition & AX25_COND_OWN_RX_BUSY) ax25->vr = ns; /* ax25->vr - 1 */ - if (pf) ax25_ds_enquiry_response(ax25); - break; - } ax25->condition &= ~AX25_COND_REJECT; if (pf) { ax25_ds_enquiry_response(ax25); } else { if (!(ax25->condition & AX25_COND_ACK_PENDING)) { - ax25->t2timer = ax25->t2; ax25->condition |= AX25_COND_ACK_PENDING; + ax25_start_t2timer(ax25); } } } else { diff --git a/net/ax25/ax25_ds_subr.c b/net/ax25/ax25_ds_subr.c index 6037f16b4..89ca64f3f 100644 --- a/net/ax25/ax25_ds_subr.c +++ b/net/ax25/ax25_ds_subr.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -19,6 +19,7 @@ * AX.25 036 Jonathan(G4KLX) Cloned from ax25_out.c and ax25_subr.c. * Joerg(DL1BKE) Changed ax25_ds_enquiry_response(), * fixed ax25_dama_on() and ax25_dama_off(). + * AX.25 037 Jonathan(G4KLX) New timer architecture. */ #include <linux/config.h> @@ -91,7 +92,7 @@ void ax25_ds_enquiry_response(ax25_cb *ax25) else ax25->n2count = 0; - ax25->t3timer = ax25->t3; + ax25_start_t3timer(ax25); ax25_ds_set_timer(ax25->ax25_dev); for (ax25o = ax25_list; ax25o != NULL; ax25o = ax25o->next) { @@ -114,7 +115,7 @@ void ax25_ds_enquiry_response(ax25_cb *ax25) if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2 || skb_peek(&ax25o->ack_queue) != NULL) ax25_ds_t1_timeout(ax25o); - ax25o->t3timer = ax25o->t3; + ax25_start_t3timer(ax25o); } } @@ -122,9 +123,10 @@ void ax25_ds_establish_data_link(ax25_cb *ax25) { ax25->condition &= AX25_COND_DAMA_MODE; ax25->n2count = 0; - ax25->t3timer = ax25->t3; - ax25->t2timer = 0; - ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25); + ax25_calculate_t1(ax25); + ax25_start_t1timer(ax25); + ax25_stop_t2timer(ax25); + ax25_start_t3timer(ax25); } /* diff --git a/net/ax25/ax25_ds_timer.c b/net/ax25/ax25_ds_timer.c index 847be5790..841149996 100644 --- a/net/ax25/ax25_ds_timer.c +++ b/net/ax25/ax25_ds_timer.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -12,6 +12,7 @@ * History * AX.25 036 Jonathan(G4KLX) Cloned from ax25_timer.c. * Joerg(DL1BKE) Added DAMA Slave Timeout timer + * AX.25 037 Jonathan(G4KLX) New timer architecture. */ #include <linux/config.h> @@ -93,41 +94,21 @@ static void ax25_ds_timeout(unsigned long arg) if (ax25->ax25_dev != ax25_dev || !(ax25->condition & AX25_COND_DAMA_MODE)) continue; - ax25_link_failed(&ax25->dest_addr, ax25_dev->dev); - ax25_clear_queues(ax25); ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25->state = AX25_STATE_0; - - if (ax25->sk != NULL) { - SOCK_DEBUG(ax25->sk, "AX.25 DAMA Slave Timeout\n"); - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ETIMEDOUT; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } - - ax25_set_timer(ax25); /* notify socket... */ + ax25_disconnect(ax25, ETIMEDOUT); } ax25_dev_dama_off(ax25_dev); } -/* - * AX.25 TIMER - * - * This routine is called every 100ms. Decrement timer by this - * amount - if expired then process the event. - */ -void ax25_ds_timer(ax25_cb *ax25) +void ax25_ds_heartbeat_expiry(ax25_cb *ax25) { switch (ax25->state) { + case AX25_STATE_0: /* Magic here: If we listen() and a new link dies before it is accepted() it isn't 'dead' so doesn't get removed. */ if (ax25->sk == NULL || ax25->sk->destroy || (ax25->sk->state == TCP_LISTEN && ax25->sk->dead)) { - del_timer(&ax25->timer); ax25_destroy_socket(ax25); return; } @@ -144,71 +125,51 @@ void ax25_ds_timer(ax25_cb *ax25) } } break; - - default: - break; - } - - /* dl1bke 960114: T3 works much like the IDLE timeout, but - * gets reloaded with every frame for this - * connection. - */ - - if (ax25->t3timer > 0 && --ax25->t3timer == 0) { - ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev); - ax25_clear_queues(ax25); - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - - ax25->state = AX25_STATE_0; - ax25_dama_off(ax25); - - if (ax25->sk != NULL) { - SOCK_DEBUG(ax25->sk, "AX.25 T3 Timeout\n"); - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ETIMEDOUT; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } - - ax25_set_timer(ax25); - - return; } - /* dl1bke 960228: close the connection when IDLE expires. - * unlike T3 this timer gets reloaded only on - * I frames. - */ - - if (ax25->idletimer > 0 && --ax25->idletimer == 0) { - ax25_clear_queues(ax25); - - ax25->n2count = 0; - ax25->t3timer = ax25->t3; + ax25_start_heartbeat(ax25); +} + +/* dl1bke 960114: T3 works much like the IDLE timeout, but + * gets reloaded with every frame for this + * connection. + */ +void ax25_ds_t3timer_expiry(ax25_cb *ax25) +{ + ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); + ax25_dama_off(ax25); + ax25_disconnect(ax25, ETIMEDOUT); +} - /* state 1 or 2 should not happen, but... */ +/* dl1bke 960228: close the connection when IDLE expires. + * unlike T3 this timer gets reloaded only on + * I frames. + */ +void ax25_ds_idletimer_expiry(ax25_cb *ax25) +{ + ax25_clear_queues(ax25); - if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2) - ax25->state = AX25_STATE_0; - else - ax25->state = AX25_STATE_2; + ax25->n2count = 0; - ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25); + /* state 1 or 2 should not happen, but... */ - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - ax25->sk->destroy = 1; - } + if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2) + ax25->state = AX25_STATE_0; + else + ax25->state = AX25_STATE_2; + + ax25_calculate_t1(ax25); + ax25_start_t1timer(ax25); + ax25_start_t3timer(ax25); + + if (ax25->sk != NULL) { + ax25->sk->state = TCP_CLOSE; + ax25->sk->err = 0; + ax25->sk->shutdown |= SEND_SHUTDOWN; + if (!ax25->sk->dead) + ax25->sk->state_change(ax25->sk); + ax25->sk->dead = 1; } - - ax25_set_timer(ax25); } /* dl1bke 960114: The DAMA protocol requires to send data and SABM/DISC @@ -222,20 +183,12 @@ void ax25_ds_timer(ax25_cb *ax25) void ax25_ds_t1_timeout(ax25_cb *ax25) { switch (ax25->state) { + case AX25_STATE_1: if (ax25->n2count == ax25->n2) { if (ax25->modulus == AX25_MODULUS) { - ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev); - ax25_clear_queues(ax25); - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ETIMEDOUT; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, ETIMEDOUT); + return; } else { ax25->modulus = AX25_MODULUS; ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; @@ -253,19 +206,9 @@ void ax25_ds_t1_timeout(ax25_cb *ax25) case AX25_STATE_2: if (ax25->n2count == ax25->n2) { - ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev); - ax25_clear_queues(ax25); - ax25->state = AX25_STATE_0; ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ETIMEDOUT; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, ETIMEDOUT); + return; } else { ax25->n2count++; } @@ -273,28 +216,17 @@ void ax25_ds_t1_timeout(ax25_cb *ax25) case AX25_STATE_3: if (ax25->n2count == ax25->n2) { - ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev); - ax25_clear_queues(ax25); ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE); - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - SOCK_DEBUG(ax25->sk, "AX.25 link Failure\n"); - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ETIMEDOUT; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, ETIMEDOUT); + return; } else { ax25->n2count++; } break; } - ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25); - - ax25_set_timer(ax25); + ax25_calculate_t1(ax25); + ax25_start_t1timer(ax25); } #endif diff --git a/net/ax25/ax25_iface.c b/net/ax25/ax25_iface.c index d27a3af23..7e6ad845c 100644 --- a/net/ax25/ax25_iface.c +++ b/net/ax25/ax25_iface.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -44,7 +44,7 @@ static struct protocol_struct { static struct linkfail_struct { struct linkfail_struct *next; - void (*func)(ax25_address *, struct device *); + void (*func)(ax25_cb *, int); } *linkfail_list = NULL; static struct listen_struct { @@ -114,7 +114,7 @@ void ax25_protocol_release(unsigned int pid) restore_flags(flags); } -int ax25_linkfail_register(void (*func)(ax25_address *, struct device *)) +int ax25_linkfail_register(void (*func)(ax25_cb *, int)) { struct linkfail_struct *linkfail; unsigned long flags; @@ -135,7 +135,7 @@ int ax25_linkfail_register(void (*func)(ax25_address *, struct device *)) return 1; } -void ax25_linkfail_release(void (*func)(ax25_address *, struct device *)) +void ax25_linkfail_release(void (*func)(ax25_cb *, int)) { struct linkfail_struct *s, *linkfail = linkfail_list; unsigned long flags; @@ -248,21 +248,12 @@ int ax25_listen_mine(ax25_address *callsign, struct device *dev) return 0; } -void ax25_link_failed(ax25_address *callsign, struct device *dev) +void ax25_link_failed(ax25_cb *ax25, int reason) { struct linkfail_struct *linkfail; for (linkfail = linkfail_list; linkfail != NULL; linkfail = linkfail->next) - (linkfail->func)(callsign, dev); -} - -/* - * Return the state of an AX.25 link given source, destination, and - * device. - */ -int ax25_link_up(ax25_address *src, ax25_address *dest, ax25_digi *digi, struct device *dev) -{ - return ax25_find_cb(src, dest, digi, dev) != NULL; + (linkfail->func)(ax25, reason); } int ax25_protocol_is_registered(unsigned int pid) diff --git a/net/ax25/ax25_in.c b/net/ax25/ax25_in.c index 2e6090d76..a17109bff 100644 --- a/net/ax25/ax25_in.c +++ b/net/ax25/ax25_in.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -35,6 +35,7 @@ * AX.25 035 Hans(PE1AYX) Fixed interface to IP layer. * AX.25 036 Jonathan(G4KLX) Move DAMA code into own file. * Joerg(DL1BKE) Fixed DAMA Slave. + * AX.25 037 Jonathan(G4KLX) New timer architecture. */ #include <linux/config.h> @@ -136,7 +137,7 @@ int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb) if (skb == NULL) return 0; - ax25->idletimer = ax25->idle; + ax25_start_idletimer(ax25); pid = *skb->data; @@ -193,8 +194,6 @@ static int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type, i if (ax25->state == AX25_STATE_0) return 0; - del_timer(&ax25->timer); - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { case AX25_PROTO_STD_SIMPLEX: case AX25_PROTO_STD_DUPLEX: @@ -211,8 +210,6 @@ static int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type, i #endif } - ax25_set_timer(ax25); - return queued; } @@ -413,7 +410,6 @@ static int ax25_rcv(struct sk_buff *skb, struct device *dev, ax25_address *dev_a } ax25_fillin_cb(ax25, ax25_dev); - ax25->idletimer = ax25->idle; } ax25->source_addr = dest; @@ -453,12 +449,13 @@ static int ax25_rcv(struct sk_buff *skb, struct device *dev, ax25_address *dev_a ax25_dama_on(ax25); #endif - ax25->t3timer = ax25->t3; - ax25->state = AX25_STATE_3; + ax25->state = AX25_STATE_3; ax25_insert_socket(ax25); - ax25_set_timer(ax25); + ax25_start_heartbeat(ax25); + ax25_start_t3timer(ax25); + ax25_start_idletimer(ax25); if (sk != NULL) { if (!sk->dead) diff --git a/net/ax25/ax25_ip.c b/net/ax25/ax25_ip.c index 009428fa9..d26f008dd 100644 --- a/net/ax25/ax25_ip.c +++ b/net/ax25/ax25_ip.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -105,20 +105,25 @@ int ax25_encapsulate(struct sk_buff *skb, struct device *dev, unsigned short typ int ax25_rebuild_header(struct sk_buff *skb) { struct sk_buff *ourskb; - int mode; unsigned char *bp = skb->data; struct device *dev = skb->dev; + ax25_address *src, *dst; + ax25_route *route; ax25_dev *ax25_dev; + dst = (ax25_address *)(bp + 1); + src = (ax25_address *)(bp + 8); + if (arp_find(bp + 1, skb)) return 1; if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) return 1; + route = ax25_rt_find_route(dst, dev); + if (bp[16] == AX25_P_IP) { - mode = ax25_ip_mode_get((ax25_address *)(bp + 1), dev); - if (mode == 'V' || (mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) { + if (route->ip_mode == 'V' || (route->ip_mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) { /* * We copy the buffer and release the original thereby * keeping it straight @@ -146,7 +151,7 @@ int ax25_rebuild_header(struct sk_buff *skb) skb_pull(ourskb, AX25_HEADER_LEN - 1); /* Keep PID */ - ax25_send_frame(ourskb, ax25_dev->values[AX25_VALUES_PACLEN], (ax25_address *)(bp + 8), (ax25_address *)(bp + 1), NULL, dev); + ax25_send_frame(ourskb, ax25_dev->values[AX25_VALUES_PACLEN], src, dst, route->digipeat, dev); return 1; } @@ -160,15 +165,19 @@ int ax25_rebuild_header(struct sk_buff *skb) bp[14] |= AX25_EBIT; bp[14] |= AX25_SSSID_SPARE; - if ((ourskb = ax25_dg_build_path(skb, (ax25_address *)(bp + 1), dev)) == NULL) { - kfree_skb(skb, FREE_WRITE); - return 1; + if (route->digipeat != NULL) { + if ((ourskb = ax25_rt_build_path(skb, src, dst, route->digipeat)) == NULL) { + kfree_skb(skb, FREE_WRITE); + return 1; + } + + skb = ourskb; } - ourskb->dev = dev; - ourskb->priority = SOPRI_NORMAL; + skb->dev = dev; + skb->priority = SOPRI_NORMAL; - ax25_queue_xmit(ourskb); + ax25_queue_xmit(skb); return 1; } diff --git a/net/ax25/ax25_out.c b/net/ax25/ax25_out.c index db7207b28..4550302d7 100644 --- a/net/ax25/ax25_out.c +++ b/net/ax25/ax25_out.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -26,6 +26,7 @@ * AX.25 I-Frames. Added PACLEN parameter. * Joerg(DL1BKE) Fixed a problem with buffer allocation * for fragments. + * AX.25 037 Jonathan(G4KLX) New timer architecture. */ #include <linux/config.h> @@ -52,7 +53,7 @@ #include <linux/mm.h> #include <linux/interrupt.h> -int ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax25_address *dest, ax25_digi *digi, struct device *dev) +ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax25_address *dest, ax25_digi *digi, struct device *dev) { ax25_dev *ax25_dev; ax25_cb *ax25; @@ -65,15 +66,14 @@ int ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax25_add */ if ((ax25 = ax25_find_cb(src, dest, digi, dev)) != NULL) { ax25_output(ax25, paclen, skb); - ax25->idletimer = ax25->idle; - return 1; /* It already existed */ + return ax25; /* It already existed */ } if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) - return 0; + return NULL; if ((ax25 = ax25_create_cb()) == NULL) - return 0; + return NULL; ax25_fillin_cb(ax25, ax25_dev); @@ -83,11 +83,9 @@ int ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax25_add if (digi != NULL) { if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { ax25_free_cb(ax25); - return 0; + return NULL; } *ax25->digipeat = *digi; - } else { - ax25_rt_build_path(ax25, dest, dev); } switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { @@ -106,19 +104,15 @@ int ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax25_add #endif } - /* idle timeouts only for mode vc connections */ - - ax25->idletimer = ax25->idle; - ax25_insert_socket(ax25); ax25->state = AX25_STATE_1; - ax25_set_timer(ax25); + ax25_start_heartbeat(ax25); ax25_output(ax25, paclen, skb); - return 1; /* We had to create it */ + return ax25; /* We had to create it */ } /* @@ -195,10 +189,8 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb) } if (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_STD_SIMPLEX || - ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_STD_DUPLEX) { - if (ax25->state == AX25_STATE_3 || ax25->state == AX25_STATE_4) - ax25_kick(ax25); - } + ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_STD_DUPLEX) + ax25_kick(ax25); } /* @@ -228,6 +220,8 @@ static void ax25_send_iframe(ax25_cb *ax25, struct sk_buff *skb, int poll_bit) frame[1] |= (ax25->vr << 1); } + ax25_start_idletimer(ax25); + ax25_transmit_buffer(ax25, skb, AX25_COMMAND); } @@ -237,76 +231,80 @@ void ax25_kick(ax25_cb *ax25) int last = 1; unsigned short start, end, next; - del_timer(&ax25->timer); + if (ax25->state != AX25_STATE_3 && ax25->state != AX25_STATE_4) + return; + + if (ax25->condition & AX25_COND_PEER_RX_BUSY) + return; + + if (skb_peek(&ax25->write_queue) == NULL) + return; start = (skb_peek(&ax25->ack_queue) == NULL) ? ax25->va : ax25->vs; end = (ax25->va + ax25->window) % ax25->modulus; - if (!(ax25->condition & AX25_COND_PEER_RX_BUSY) && - start != end && - skb_peek(&ax25->write_queue) != NULL) { + if (start == end) + return; - ax25->vs = start; + ax25->vs = start; - /* - * Transmit data until either we're out of data to send or - * the window is full. Send a poll on the final I frame if - * the window is filled. - */ + /* + * Transmit data until either we're out of data to send or + * the window is full. Send a poll on the final I frame if + * the window is filled. + */ - /* - * Dequeue the frame and copy it. - */ - skb = skb_dequeue(&ax25->write_queue); + /* + * Dequeue the frame and copy it. + */ + skb = skb_dequeue(&ax25->write_queue); - do { - if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { - skb_queue_head(&ax25->write_queue, skb); - break; - } + do { + if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { + skb_queue_head(&ax25->write_queue, skb); + break; + } - if (skb->sk != NULL) - skb_set_owner_w(skbn, skb->sk); + if (skb->sk != NULL) + skb_set_owner_w(skbn, skb->sk); - next = (ax25->vs + 1) % ax25->modulus; - last = (next == end); + next = (ax25->vs + 1) % ax25->modulus; + last = (next == end); - /* - * Transmit the frame copy. - * bke 960114: do not set the Poll bit on the last frame - * in DAMA mode. - */ - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_send_iframe(ax25, skbn, (last) ? AX25_POLLON : AX25_POLLOFF); - break; + /* + * Transmit the frame copy. + * bke 960114: do not set the Poll bit on the last frame + * in DAMA mode. + */ + switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { + case AX25_PROTO_STD_SIMPLEX: + case AX25_PROTO_STD_DUPLEX: + ax25_send_iframe(ax25, skbn, (last) ? AX25_POLLON : AX25_POLLOFF); + break; #ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - ax25_send_iframe(ax25, skbn, AX25_POLLOFF); - break; + case AX25_PROTO_DAMA_SLAVE: + ax25_send_iframe(ax25, skbn, AX25_POLLOFF); + break; #endif - } + } - ax25->vs = next; + ax25->vs = next; - /* - * Requeue the original data frame. - */ - skb_queue_tail(&ax25->ack_queue, skb); + /* + * Requeue the original data frame. + */ + skb_queue_tail(&ax25->ack_queue, skb); - } while (!last && (skb = skb_dequeue(&ax25->write_queue)) != NULL); + } while (!last && (skb = skb_dequeue(&ax25->write_queue)) != NULL); - ax25->condition &= ~AX25_COND_ACK_PENDING; + ax25->condition &= ~AX25_COND_ACK_PENDING; - if (ax25->t1timer == 0) { - ax25->t3timer = 0; - ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25); - } + if (!ax25_t1timer_running(ax25)) { + ax25_stop_t3timer(ax25); + ax25_calculate_t1(ax25); + ax25_start_t1timer(ax25); } - - ax25_set_timer(ax25); } void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type) @@ -316,14 +314,7 @@ void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type) int headroom; if (ax25->ax25_dev == NULL) { - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ENETUNREACH; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, ENETUNREACH); return; } @@ -381,12 +372,13 @@ void ax25_check_iframes_acked(ax25_cb *ax25, unsigned short nr) if (ax25->vs == nr) { ax25_frames_acked(ax25, nr); ax25_calculate_rtt(ax25); - ax25->t1timer = 0; - ax25->t3timer = ax25->t3; + ax25_stop_t1timer(ax25); + ax25_start_t3timer(ax25); } else { if (ax25->va != nr) { ax25_frames_acked(ax25, nr); - ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25); + ax25_calculate_t1(ax25); + ax25_start_t1timer(ax25); } } } diff --git a/net/ax25/ax25_route.c b/net/ax25/ax25_route.c index 3cda48a17..2c7d082a9 100644 --- a/net/ax25/ax25_route.c +++ b/net/ax25/ax25_route.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -120,13 +120,12 @@ int ax25_rt_ioctl(unsigned int cmd, void *arg) struct ax25_routes_struct route; struct ax25_route_opt_struct rt_option; ax25_dev *ax25_dev; - int i, err; + int i; switch (cmd) { case SIOCADDRT: - if ((err = verify_area(VERIFY_READ, arg, sizeof(route))) != 0) - return err; - copy_from_user(&route, arg, sizeof(route)); + if (copy_from_user(&route, arg, sizeof(route))) + return -EFAULT; if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL) return -EINVAL; if (route.digi_count > AX25_MAX_DIGIS) @@ -175,9 +174,8 @@ int ax25_rt_ioctl(unsigned int cmd, void *arg) break; case SIOCDELRT: - if ((err = verify_area(VERIFY_READ, arg, sizeof(route))) != 0) - return err; - copy_from_user(&route, arg, sizeof(route)); + if (copy_from_user(&route, arg, sizeof(route))) + return -EFAULT; if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL) return -EINVAL; ax25_rt = ax25_route_list; @@ -206,9 +204,8 @@ int ax25_rt_ioctl(unsigned int cmd, void *arg) break; case SIOCAX25OPTRT: - if ((err = verify_area(VERIFY_READ, arg, sizeof(rt_option))) != 0) - return err; - copy_from_user(&rt_option, arg, sizeof(rt_option)); + if (copy_from_user(&rt_option, arg, sizeof(rt_option))) + return -EFAULT; if ((ax25_dev = ax25_addr_ax25dev(&rt_option.port_addr)) == NULL) return -EINVAL; for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { @@ -390,48 +387,32 @@ int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr) * dl1bke 960117: build digipeater path * dl1bke 960301: use the default route if it exists */ -void ax25_rt_build_path(ax25_cb *ax25, ax25_address *addr, struct device *dev) +ax25_route *ax25_rt_find_route(ax25_address *addr, struct device *dev) { + static ax25_route route; ax25_route *ax25_rt; - if ((ax25_rt = ax25_find_route(addr, dev)) == NULL) - return; - - if (ax25_rt->digipeat == NULL) - return; - - if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) - return; - - if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL) - return; + if ((ax25_rt = ax25_find_route(addr, dev)) == NULL) { + route.next = NULL; + route.callsign = *addr; + route.dev = dev; + route.digipeat = NULL; + route.ip_mode = ' '; + return &route; + } - *ax25->digipeat = *ax25_rt->digipeat; - ax25_adjust_path(addr, ax25->digipeat); + return ax25_rt; } -struct sk_buff *ax25_dg_build_path(struct sk_buff *skb, ax25_address *addr, struct device *dev) +struct sk_buff *ax25_rt_build_path(struct sk_buff *skb, ax25_address *src, ax25_address *dest, ax25_digi *digi) { struct sk_buff *skbn; - ax25_route *ax25_rt; - ax25_digi digipeat; - ax25_address src, dest; unsigned char *bp; int len; skb_pull(skb, 1); /* skip KISS command */ - if ((ax25_rt = ax25_find_route(addr, dev)) == NULL) - return skb; - - if (ax25_rt->digipeat == NULL) - return skb; - - digipeat = *ax25_rt->digipeat; - - ax25_adjust_path(addr, &digipeat); - - len = ax25_rt->digipeat->ndigi * AX25_ADDR_LEN; + len = digi->ndigi * AX25_ADDR_LEN; if (skb_headroom(skb) < len) { if ((skbn = skb_realloc_headroom(skb, len)) == NULL) { @@ -447,30 +428,13 @@ struct sk_buff *ax25_dg_build_path(struct sk_buff *skb, ax25_address *addr, stru skb = skbn; } - memcpy(&dest, skb->data + 0, AX25_ADDR_LEN); - memcpy(&src, skb->data + 7, AX25_ADDR_LEN); - bp = skb_push(skb, len); - ax25_addr_build(bp, &src, &dest, ax25_rt->digipeat, AX25_COMMAND, AX25_MODULUS); + ax25_addr_build(bp, src, dest, digi, AX25_COMMAND, AX25_MODULUS); return skb; } -/* - * Return the IP mode of a given callsign/device pair. - */ -char ax25_ip_mode_get(ax25_address *callsign, struct device *dev) -{ - ax25_route *ax25_rt; - - for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) - if (ax25cmp(&ax25_rt->callsign, callsign) == 0 && ax25_rt->dev == dev) - return ax25_rt->ip_mode; - - return ' '; -} - #ifdef MODULE /* diff --git a/net/ax25/ax25_std_in.c b/net/ax25/ax25_std_in.c index 1efe08366..7b7d437e8 100644 --- a/net/ax25/ax25_std_in.c +++ b/net/ax25/ax25_std_in.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -34,6 +34,7 @@ * Modularisation changes. * AX.25 035 Hans(PE1AYX) Fixed interface to IP layer. * AX.25 036 Jonathan(G4KLX) Cloned from ax25_in.c. + * AX.25 037 Jonathan(G4KLX) New timer architecture. */ #include <linux/config.h> @@ -87,9 +88,9 @@ static int ax25_std_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frame case AX25_UA: if (pf) { ax25_calculate_rtt(ax25); - ax25->t1timer = 0; - ax25->t3timer = ax25->t3; - ax25->idletimer = ax25->idle; + ax25_stop_t1timer(ax25); + ax25_start_t3timer(ax25); + ax25_start_idletimer(ax25); ax25->vs = 0; ax25->va = 0; ax25->vr = 0; @@ -107,16 +108,7 @@ static int ax25_std_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frame case AX25_DM: if (pf) { if (ax25->modulus == AX25_MODULUS) { - ax25_clear_queues(ax25); - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ECONNREFUSED; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, ECONNREFUSED); } else { ax25->modulus = AX25_MODULUS; ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; @@ -146,30 +138,12 @@ static int ax25_std_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frame case AX25_DISC: ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, 0); break; case AX25_DM: case AX25_UA: - if (pf) { - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } - } + if (pf) ax25_disconnect(ax25, 0); break; case AX25_I: @@ -206,10 +180,11 @@ static int ax25_std_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frame ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; } ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); + ax25_stop_t1timer(ax25); + ax25_stop_t2timer(ax25); + ax25_start_t3timer(ax25); + ax25_start_idletimer(ax25); ax25->condition = 0x00; - ax25->t1timer = 0; - ax25->t3timer = ax25->t3; - ax25->idletimer = ax25->idle; ax25->vs = 0; ax25->va = 0; ax25->vr = 0; @@ -217,32 +192,12 @@ static int ax25_std_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frame break; case AX25_DISC: - ax25_clear_queues(ax25); ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25->t3timer = 0; - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, 0); break; case AX25_DM: - ax25_clear_queues(ax25); - ax25->t3timer = 0; - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ECONNRESET; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, ECONNRESET); break; case AX25_RR: @@ -268,8 +223,8 @@ static int ax25_std_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frame if (ax25_validate_nr(ax25, nr)) { ax25_frames_acked(ax25, nr); ax25_calculate_rtt(ax25); - ax25->t1timer = 0; - ax25->t3timer = ax25->t3; + ax25_stop_t1timer(ax25); + ax25_start_t3timer(ax25); ax25_requeue_frames(ax25); } else { ax25_std_nr_error_recovery(ax25); @@ -295,18 +250,15 @@ static int ax25_std_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frame if (ns == ax25->vr) { ax25->vr = (ax25->vr + 1) % ax25->modulus; queued = ax25_rx_iframe(ax25, skb); - if (ax25->condition & AX25_COND_OWN_RX_BUSY) { + if (ax25->condition & AX25_COND_OWN_RX_BUSY) ax25->vr = ns; /* ax25->vr - 1 */ - if (pf) ax25_std_enquiry_response(ax25); - break; - } ax25->condition &= ~AX25_COND_REJECT; if (pf) { ax25_std_enquiry_response(ax25); } else { if (!(ax25->condition & AX25_COND_ACK_PENDING)) { - ax25->t2timer = ax25->t2; ax25->condition |= AX25_COND_ACK_PENDING; + ax25_start_t2timer(ax25); } } } else { @@ -353,10 +305,11 @@ static int ax25_std_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frame ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; } ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); + ax25_stop_t1timer(ax25); + ax25_stop_t2timer(ax25); + ax25_start_t3timer(ax25); + ax25_start_idletimer(ax25); ax25->condition = 0x00; - ax25->t1timer = 0; - ax25->t3timer = ax25->t3; - ax25->idletimer = ax25->idle; ax25->vs = 0; ax25->va = 0; ax25->vr = 0; @@ -366,32 +319,12 @@ static int ax25_std_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frame break; case AX25_DISC: - ax25_clear_queues(ax25); ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25->t3timer = 0; - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, 0); break; case AX25_DM: - ax25_clear_queues(ax25); - ax25->t3timer = 0; - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ECONNRESET; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, ECONNRESET); break; case AX25_RR: @@ -401,11 +334,11 @@ static int ax25_std_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frame else ax25->condition |= AX25_COND_PEER_RX_BUSY; if (type == AX25_RESPONSE && pf) { - ax25->t1timer = 0; + ax25_stop_t1timer(ax25); if (ax25_validate_nr(ax25, nr)) { ax25_frames_acked(ax25, nr); if (ax25->vs == ax25->va) { - ax25->t3timer = ax25->t3; + ax25_start_t3timer(ax25); ax25->n2count = 0; ax25->state = AX25_STATE_3; } else { @@ -430,11 +363,11 @@ static int ax25_std_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frame case AX25_REJ: ax25->condition &= ~AX25_COND_PEER_RX_BUSY; if (pf && type == AX25_RESPONSE) { - ax25->t1timer = 0; + ax25_stop_t1timer(ax25); if (ax25_validate_nr(ax25, nr)) { ax25_frames_acked(ax25, nr); if (ax25->vs == ax25->va) { - ax25->t3timer = ax25->t3; + ax25_start_t3timer(ax25); ax25->n2count = 0; ax25->state = AX25_STATE_3; } else { @@ -471,18 +404,15 @@ static int ax25_std_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frame if (ns == ax25->vr) { ax25->vr = (ax25->vr + 1) % ax25->modulus; queued = ax25_rx_iframe(ax25, skb); - if (ax25->condition & AX25_COND_OWN_RX_BUSY) { + if (ax25->condition & AX25_COND_OWN_RX_BUSY) ax25->vr = ns; /* ax25->vr - 1 */ - if (pf) ax25_std_enquiry_response(ax25); - break; - } ax25->condition &= ~AX25_COND_REJECT; if (pf) { ax25_std_enquiry_response(ax25); } else { if (!(ax25->condition & AX25_COND_ACK_PENDING)) { - ax25->t2timer = ax25->t2; ax25->condition |= AX25_COND_ACK_PENDING; + ax25_start_t2timer(ax25); } } } else { @@ -533,6 +463,8 @@ int ax25_std_frame_in(ax25_cb *ax25, struct sk_buff *skb, int type) break; } + ax25_kick(ax25); + return queued; } diff --git a/net/ax25/ax25_std_subr.c b/net/ax25/ax25_std_subr.c index 3661a63fb..1b1d1c8bb 100644 --- a/net/ax25/ax25_std_subr.c +++ b/net/ax25/ax25_std_subr.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -17,6 +17,7 @@ * * History * AX.25 036 Jonathan(G4KLX) Split from ax25_out.c. + * AX.25 037 Jonathan(G4KLX) New timer architecture. */ #include <linux/config.h> @@ -62,9 +63,11 @@ void ax25_std_establish_data_link(ax25_cb *ax25) else ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND); - ax25->t3timer = 0; - ax25->t2timer = 0; - ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25); + ax25_calculate_t1(ax25); + ax25_stop_idletimer(ax25); + ax25_stop_t3timer(ax25); + ax25_stop_t2timer(ax25); + ax25_start_t1timer(ax25); } void ax25_std_transmit_enquiry(ax25_cb *ax25) @@ -76,7 +79,8 @@ void ax25_std_transmit_enquiry(ax25_cb *ax25) ax25->condition &= ~AX25_COND_ACK_PENDING; - ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25); + ax25_calculate_t1(ax25); + ax25_start_t1timer(ax25); } void ax25_std_enquiry_response(ax25_cb *ax25) diff --git a/net/ax25/ax25_std_timer.c b/net/ax25/ax25_std_timer.c index 9a4a34c0f..4e97c51b8 100644 --- a/net/ax25/ax25_std_timer.c +++ b/net/ax25/ax25_std_timer.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -19,6 +19,7 @@ * AX.25 033 Jonathan(G4KLX) Modularisation functions. * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating. * AX.25 036 Jonathan(G4KLX) Split from ax25_timer.c. + * AX.25 037 Jonathan(G4KLX) New timer architecture. */ #include <linux/config.h> @@ -44,14 +45,14 @@ #include <linux/mm.h> #include <linux/interrupt.h> -void ax25_std_timer(ax25_cb *ax25) +void ax25_std_heartbeat_expiry(ax25_cb *ax25) { switch (ax25->state) { + case AX25_STATE_0: /* Magic here: If we listen() and a new link dies before it is accepted() it isn't 'dead' so doesn't get removed. */ if (ax25->sk == NULL || ax25->sk->destroy || (ax25->sk->state == TCP_LISTEN && ax25->sk->dead)) { - del_timer(&ax25->timer); ax25_destroy_socket(ax25); return; } @@ -71,78 +72,57 @@ void ax25_std_timer(ax25_cb *ax25) break; } } - /* - * Check for frames to transmit. - */ - ax25_kick(ax25); - break; - - default: - break; } - if (ax25->t2timer > 0 && --ax25->t2timer == 0) { - if (ax25->state == AX25_STATE_3 || ax25->state == AX25_STATE_4) { - if (ax25->condition & AX25_COND_ACK_PENDING) { - ax25->condition &= ~AX25_COND_ACK_PENDING; - ax25_std_timeout_response(ax25); - } - } - } + ax25_start_heartbeat(ax25); +} - if (ax25->t3timer > 0 && --ax25->t3timer == 0) { - if (ax25->state == AX25_STATE_3) { - ax25->n2count = 0; - ax25_std_transmit_enquiry(ax25); - ax25->state = AX25_STATE_4; - } - ax25->t3timer = ax25->t3; +void ax25_std_t2timer_expiry(ax25_cb *ax25) +{ + if (ax25->condition & AX25_COND_ACK_PENDING) { + ax25->condition &= ~AX25_COND_ACK_PENDING; + ax25_std_timeout_response(ax25); } +} - if (ax25->idletimer > 0 && --ax25->idletimer == 0) { - /* dl1bke 960228: close the connection when IDLE expires */ - /* similar to DAMA T3 timeout but with */ - /* a "clean" disconnect of the connection */ - - ax25_clear_queues(ax25); - - ax25->n2count = 0; - ax25->t3timer = 0; - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25->state = AX25_STATE_2; - ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25); - - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - ax25->sk->destroy = 1; - } - } +void ax25_std_t3timer_expiry(ax25_cb *ax25) +{ + ax25->n2count = 0; + ax25_std_transmit_enquiry(ax25); + ax25->state = AX25_STATE_4; +} - if (ax25->t1timer == 0 || --ax25->t1timer > 0) { - ax25_set_timer(ax25); - return; +void ax25_std_idletimer_expiry(ax25_cb *ax25) +{ + ax25_clear_queues(ax25); + + ax25->n2count = 0; + ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); + ax25->state = AX25_STATE_2; + + ax25_calculate_t1(ax25); + ax25_start_t1timer(ax25); + ax25_stop_t2timer(ax25); + ax25_stop_t3timer(ax25); + + if (ax25->sk != NULL) { + ax25->sk->state = TCP_CLOSE; + ax25->sk->err = 0; + ax25->sk->shutdown |= SEND_SHUTDOWN; + if (!ax25->sk->dead) + ax25->sk->state_change(ax25->sk); + ax25->sk->dead = 1; } +} +void ax25_std_t1timer_expiry(ax25_cb *ax25) +{ switch (ax25->state) { case AX25_STATE_1: if (ax25->n2count == ax25->n2) { if (ax25->modulus == AX25_MODULUS) { - ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev); - ax25_clear_queues(ax25); - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ETIMEDOUT; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, ETIMEDOUT); + return; } else { ax25->modulus = AX25_MODULUS; ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; @@ -160,19 +140,9 @@ void ax25_std_timer(ax25_cb *ax25) case AX25_STATE_2: if (ax25->n2count == ax25->n2) { - ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev); - ax25_clear_queues(ax25); - ax25->state = AX25_STATE_0; ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ETIMEDOUT; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, ETIMEDOUT); + return; } else { ax25->n2count++; ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); @@ -187,19 +157,9 @@ void ax25_std_timer(ax25_cb *ax25) case AX25_STATE_4: if (ax25->n2count == ax25->n2) { - ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev); - ax25_clear_queues(ax25); ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE); - ax25->state = AX25_STATE_0; - if (ax25->sk != NULL) { - SOCK_DEBUG(ax25->sk, "AX.25 link Failure\n"); - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = ETIMEDOUT; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } + ax25_disconnect(ax25, ETIMEDOUT); + return; } else { ax25->n2count++; ax25_std_transmit_enquiry(ax25); @@ -207,9 +167,8 @@ void ax25_std_timer(ax25_cb *ax25) break; } - ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25); - - ax25_set_timer(ax25); + ax25_calculate_t1(ax25); + ax25_start_t1timer(ax25); } #endif diff --git a/net/ax25/ax25_subr.c b/net/ax25/ax25_subr.c index ba2c72297..39dfd7d42 100644 --- a/net/ax25/ax25_subr.c +++ b/net/ax25/ax25_subr.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -30,6 +30,7 @@ * AX.25 032 Joerg(DL1BKE) Added ax25_queue_length to count the number of * enqueued buffers of a socket.. * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating. + * AX.25 037 Jonathan(G4KLX) New timer architecture. */ #include <linux/config.h> @@ -259,7 +260,7 @@ void ax25_return_dm(struct device *dev, ax25_address *src, ax25_address *dest, a /* * Exponential backoff for AX.25 */ -unsigned short ax25_calculate_t1(ax25_cb *ax25) +void ax25_calculate_t1(ax25_cb *ax25) { int n, t = 2; @@ -278,7 +279,7 @@ unsigned short ax25_calculate_t1(ax25_cb *ax25) break; } - return t * ax25->rtt; + ax25->t1 = t * ax25->rtt; } /* @@ -289,8 +290,8 @@ void ax25_calculate_rtt(ax25_cb *ax25) if (ax25->backoff == 0) return; - if (ax25->t1timer > 0 && ax25->n2count == 0) - ax25->rtt = (9 * ax25->rtt + ax25->t1 - ax25->t1timer) / 10; + if (ax25_t1timer_running(ax25) && ax25->n2count == 0) + ax25->rtt = (9 * ax25->rtt + ax25->t1 - ax25_display_timer(&ax25->t1timer)) / 10; if (ax25->rtt < AX25_T1CLAMPLO) ax25->rtt = AX25_T1CLAMPLO; @@ -299,4 +300,27 @@ void ax25_calculate_rtt(ax25_cb *ax25) ax25->rtt = AX25_T1CLAMPHI; } +void ax25_disconnect(ax25_cb *ax25, int reason) +{ + ax25_clear_queues(ax25); + + ax25_stop_t1timer(ax25); + ax25_stop_t2timer(ax25); + ax25_stop_t3timer(ax25); + ax25_stop_idletimer(ax25); + + ax25->state = AX25_STATE_0; + + ax25_link_failed(ax25, reason); + + if (ax25->sk != NULL) { + ax25->sk->state = TCP_CLOSE; + ax25->sk->err = reason; + ax25->sk->shutdown |= SEND_SHUTDOWN; + if (!ax25->sk->dead) + ax25->sk->state_change(ax25->sk); + ax25->sk->dead = 1; + } +} + #endif diff --git a/net/ax25/ax25_timer.c b/net/ax25/ax25_timer.c index c0a54da11..8a384b58b 100644 --- a/net/ax25/ax25_timer.c +++ b/net/ax25/ax25_timer.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -21,6 +21,7 @@ * AX.25 036 Jonathan(G4KLX) Split Standard and DAMA code into seperate files. * Joerg(DL1BKE) Fixed DAMA Slave. We are *required* to start with * standard AX.25 mode. + * AX.25 037 Jonathan(G4KLX) New timer architecture. */ #include <linux/config.h> @@ -46,48 +47,205 @@ #include <linux/mm.h> #include <linux/interrupt.h> -static void ax25_timer(unsigned long); +static void ax25_heartbeat_expiry(unsigned long); +static void ax25_t1timer_expiry(unsigned long); +static void ax25_t2timer_expiry(unsigned long); +static void ax25_t3timer_expiry(unsigned long); +static void ax25_idletimer_expiry(unsigned long); -/* - * Linux set timer - */ -void ax25_set_timer(ax25_cb *ax25) +void ax25_start_heartbeat(ax25_cb *ax25) { - unsigned long flags; - - save_flags(flags); cli(); del_timer(&ax25->timer); - restore_flags(flags); ax25->timer.data = (unsigned long)ax25; - ax25->timer.function = &ax25_timer; - ax25->timer.expires = jiffies + (HZ / 10); + ax25->timer.function = &ax25_heartbeat_expiry; + ax25->timer.expires = jiffies + 5 * HZ; add_timer(&ax25->timer); } -/* - * AX.25 TIMER - * - * This routine is called every 100ms. Decrement timer by this - * amount - if expired then process the event. - */ -static void ax25_timer(unsigned long param) +void ax25_start_t1timer(ax25_cb *ax25) +{ + del_timer(&ax25->t1timer); + + ax25->t1timer.data = (unsigned long)ax25; + ax25->t1timer.function = &ax25_t1timer_expiry; + ax25->t1timer.expires = jiffies + ax25->t1; + + add_timer(&ax25->t1timer); +} + +void ax25_start_t2timer(ax25_cb *ax25) +{ + del_timer(&ax25->t2timer); + + ax25->t2timer.data = (unsigned long)ax25; + ax25->t2timer.function = &ax25_t2timer_expiry; + ax25->t2timer.expires = jiffies + ax25->t2; + + add_timer(&ax25->t2timer); +} + +void ax25_start_t3timer(ax25_cb *ax25) +{ + del_timer(&ax25->t3timer); + + if (ax25->t3 > 0) { + ax25->t3timer.data = (unsigned long)ax25; + ax25->t3timer.function = &ax25_t3timer_expiry; + ax25->t3timer.expires = jiffies + ax25->t3; + + add_timer(&ax25->t3timer); + } +} + +void ax25_start_idletimer(ax25_cb *ax25) +{ + del_timer(&ax25->idletimer); + + if (ax25->idle > 0) { + ax25->idletimer.data = (unsigned long)ax25; + ax25->idletimer.function = &ax25_idletimer_expiry; + ax25->idletimer.expires = jiffies + ax25->idle; + + add_timer(&ax25->idletimer); + } +} + +void ax25_stop_heartbeat(ax25_cb *ax25) +{ + del_timer(&ax25->timer); +} + +void ax25_stop_t1timer(ax25_cb *ax25) +{ + del_timer(&ax25->t1timer); +} + +void ax25_stop_t2timer(ax25_cb *ax25) +{ + del_timer(&ax25->t2timer); +} + +void ax25_stop_t3timer(ax25_cb *ax25) +{ + del_timer(&ax25->t3timer); +} + +void ax25_stop_idletimer(ax25_cb *ax25) +{ + del_timer(&ax25->idletimer); +} + +int ax25_t1timer_running(ax25_cb *ax25) +{ + return (ax25->t1timer.prev != NULL || ax25->t1timer.next != NULL); +} + +unsigned long ax25_display_timer(struct timer_list *timer) +{ + if (timer->prev == NULL && timer->next == NULL) + return 0; + + return timer->expires - jiffies; +} + +static void ax25_heartbeat_expiry(unsigned long param) +{ + ax25_cb *ax25 = (ax25_cb *)param; + + switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { + case AX25_PROTO_STD_SIMPLEX: + case AX25_PROTO_STD_DUPLEX: + ax25_std_heartbeat_expiry(ax25); + break; + +#ifdef CONFIG_AX25_DAMA_SLAVE + case AX25_PROTO_DAMA_SLAVE: + if (ax25->ax25_dev->dama.slave) + ax25_ds_heartbeat_expiry(ax25); + else + ax25_std_heartbeat_expiry(ax25); + break; +#endif + } +} + +static void ax25_t1timer_expiry(unsigned long param) +{ + ax25_cb *ax25 = (ax25_cb *)param; + + switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { + case AX25_PROTO_STD_SIMPLEX: + case AX25_PROTO_STD_DUPLEX: + ax25_std_t1timer_expiry(ax25); + break; + +#ifdef CONFIG_AX25_DAMA_SLAVE + case AX25_PROTO_DAMA_SLAVE: + if (!ax25->ax25_dev->dama.slave) + ax25_std_t1timer_expiry(ax25); + break; +#endif + } +} + +static void ax25_t2timer_expiry(unsigned long param) +{ + ax25_cb *ax25 = (ax25_cb *)param; + + switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { + case AX25_PROTO_STD_SIMPLEX: + case AX25_PROTO_STD_DUPLEX: + ax25_std_t2timer_expiry(ax25); + break; + +#ifdef CONFIG_AX25_DAMA_SLAVE + case AX25_PROTO_DAMA_SLAVE: + if (!ax25->ax25_dev->dama.slave) + ax25_std_t2timer_expiry(ax25); + break; +#endif + } +} + +static void ax25_t3timer_expiry(unsigned long param) +{ + ax25_cb *ax25 = (ax25_cb *)param; + + switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { + case AX25_PROTO_STD_SIMPLEX: + case AX25_PROTO_STD_DUPLEX: + ax25_std_t3timer_expiry(ax25); + break; + +#ifdef CONFIG_AX25_DAMA_SLAVE + case AX25_PROTO_DAMA_SLAVE: + if (ax25->ax25_dev->dama.slave) + ax25_ds_t3timer_expiry(ax25); + else + ax25_std_t3timer_expiry(ax25); + break; +#endif + } +} + +static void ax25_idletimer_expiry(unsigned long param) { ax25_cb *ax25 = (ax25_cb *)param; switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { case AX25_PROTO_STD_SIMPLEX: case AX25_PROTO_STD_DUPLEX: - ax25_std_timer(ax25); + ax25_std_idletimer_expiry(ax25); break; #ifdef CONFIG_AX25_DAMA_SLAVE case AX25_PROTO_DAMA_SLAVE: if (ax25->ax25_dev->dama.slave) - ax25_ds_timer(ax25); + ax25_ds_idletimer_expiry(ax25); else - ax25_std_timer(ax25); + ax25_std_idletimer_expiry(ax25); break; #endif } diff --git a/net/ax25/ax25_uid.c b/net/ax25/ax25_uid.c index c5113fce8..5f33147cf 100644 --- a/net/ax25/ax25_uid.c +++ b/net/ax25/ax25_uid.c @@ -1,5 +1,5 @@ /* - * AX.25 release 036 + * AX.25 release 037 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * diff --git a/net/ax25/sysctl_net_ax25.c b/net/ax25/sysctl_net_ax25.c index 1cf2bab65..000203aaf 100644 --- a/net/ax25/sysctl_net_ax25.c +++ b/net/ax25/sysctl_net_ax25.c @@ -17,14 +17,14 @@ static int min_backoff[] = {0}, max_backoff[] = {2}; static int min_conmode[] = {0}, max_conmode[] = {2}; static int min_window[] = {1}, max_window[] = {7}; static int min_ewindow[] = {1}, max_ewindow[] = {63}; -static int min_t1[] = {1}, max_t1[] = {30 * AX25_SLOWHZ}; -static int min_t2[] = {1}, max_t2[] = {20 * AX25_SLOWHZ}; -static int min_t3[] = {0}, max_t3[] = {3600 * AX25_SLOWHZ}; -static int min_idle[] = {0}, max_idle[] = {65535 * AX25_SLOWHZ}; +static int min_t1[] = {1}, max_t1[] = {30 * HZ}; +static int min_t2[] = {1}, max_t2[] = {20 * HZ}; +static int min_t3[] = {0}, max_t3[] = {3600 * HZ}; +static int min_idle[] = {0}, max_idle[] = {65535 * HZ}; static int min_n2[] = {1}, max_n2[] = {31}; static int min_paclen[] = {1}, max_paclen[] = {512}; static int min_proto[] = {0}, max_proto[] = {3}; -static int min_ds_timeout[] = {0}, max_ds_timeout[] = {65535 * AX25_SLOWHZ}; +static int min_ds_timeout[] = {0}, max_ds_timeout[] = {65535 * HZ}; static struct ctl_table_header *ax25_table_header; diff --git a/net/core/scm.c b/net/core/scm.c index d88ab0ae7..e5fa793a7 100644 --- a/net/core/scm.c +++ b/net/core/scm.c @@ -172,9 +172,9 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p) if (acc_fd < 0 || acc_fd >= NR_OPEN || (file=current->files->fd[acc_fd])==NULL) return -EBADF; - if (!file->f_inode || !file->f_inode->i_sock) + if (!file->f_dentry->d_inode || !file->f_dentry->d_inode->i_sock) return -ENOTSOCK; - p->sock = &file->f_inode->u.socket_i; + p->sock = &file->f_dentry->d_inode->u.socket_i; if (p->sock->state != SS_UNCONNECTED) return -EINVAL; } diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index c912e8b2e..b684fba33 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -7,6 +7,9 @@ #include <linux/mm.h> #include <linux/sysctl.h> +#include <linux/config.h> + +#ifdef CONFIG_SYSCTL extern __u32 sysctl_wmem_max; extern __u32 sysctl_rmem_max; @@ -33,3 +36,4 @@ ctl_table core_table[] = { &proc_dointvec_jiffies}, { 0 } }; +#endif diff --git a/net/ipv4/Config.in b/net/ipv4/Config.in index 489598994..3a5ac3b04 100644 --- a/net/ipv4/Config.in +++ b/net/ipv4/Config.in @@ -31,6 +31,7 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool 'IP: ARP daemon support (EXPERIMENTAL)' CONFIG_ARPD fi fi +bool 'IP: TCP syncookie support (not enabled per default) ' CONFIG_SYN_COOKIES comment '(it is safe to leave these untouched)' bool 'IP: PC/TCP compatibility mode' CONFIG_INET_PCTCP tristate 'IP: Reverse ARP' CONFIG_INET_RARP diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index 9ce538dc4..2428ccc55 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -52,6 +52,11 @@ else endif endif +ifeq ($(CONFIG_SYN_COOKIES),y) +IPV4_OBJS += syncookies.o +# module not supported, because it would be too messy. +endif + ifdef CONFIG_INET O_OBJS := $(IPV4_OBJS) OX_OBJS := $(IPV4X_OBJS) diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index db54b567a..0d51af255 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -388,7 +388,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, int len) err = memcpy_fromiovec(buf, msg->msg_iov, len); if (!err) { - unsigned short fs; + unsigned long fs; fs=get_fs(); set_fs(get_ds()); err=raw_sendto(sk,buf,len, msg); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 4a4c5321c..b55fb7666 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -45,6 +45,7 @@ * Pavel Krauz : Limited broadcast fixed * Alexey Kuznetsov : End of old history. Splitted to fib.c and * route.c and rewritten from scratch. + * Andi Kleen : Load-limit warning messages. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -568,7 +569,7 @@ void ip_rt_redirect(u32 old_gw, u32 daddr, u32 new_gw, return; reject_redirect: - if (ipv4_config.log_martians) + if (ipv4_config.log_martians && net_ratelimit()) printk(KERN_INFO "Redirect from %lX/%s to %lX ignored." "Path = %lX -> %lX, tos %02x\n", ntohl(old_gw), dev->name, ntohl(new_gw), @@ -636,7 +637,7 @@ void ip_rt_send_redirect(struct sk_buff *skb) if (jiffies - rt->last_error > (RT_REDIRECT_LOAD<<rt->errors)) { icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, rt->rt_gateway); rt->last_error = jiffies; - if (ipv4_config.log_martians && ++rt->errors == RT_REDIRECT_NUMBER) + if (ipv4_config.log_martians && ++rt->errors == RT_REDIRECT_NUMBER && net_ratelimit()) printk(KERN_WARNING "host %08x/%s ignores redirects for %08x to %08x.\n", rt->rt_src, rt->rt_src_dev->name, rt->rt_dst, rt->rt_gateway); } @@ -1083,12 +1084,12 @@ no_route: * Do not cache martian addresses: they should be logged (RFC1812) */ martian_destination: - if (ipv4_config.log_martians) + if (ipv4_config.log_martians && net_ratelimit()) printk(KERN_WARNING "martian destination %08x from %08x, dev %s\n", daddr, saddr, dev->name); return -EINVAL; martian_source: - if (ipv4_config.log_martians) { + if (ipv4_config.log_martians && net_ratelimit()) { /* * RFC1812 recommenadtion, if source is martian, * the only hint is MAC header. diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c new file mode 100644 index 000000000..c18b209f0 --- /dev/null +++ b/net/ipv4/syncookies.c @@ -0,0 +1,218 @@ +/* + * Syncookies implementation for the Linux kernel + * + * Copyright (C) 1997 Andi Kleen + * Based on ideas by D.J.Bernstein and Eric Schenk. + * + * 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 of the License, or (at your option) any later version. + * + * $Id: syncookies.c,v 1.1 1997/07/18 06:30:06 ralf Exp $ + * + * Missing: IPv6 support. + * Some counter so that the Administrator can see when the machine + * is under a syn flood attack. + */ + +#include <linux/config.h> +#if defined(CONFIG_SYN_COOKIES) +#include <linux/tcp.h> +#include <linux/malloc.h> +#include <linux/random.h> +#include <net/tcp.h> + +extern int sysctl_tcp_syncookies; + +static unsigned long tcp_lastsynq_overflow; + +/* + * This table has to be sorted. Only 8 entries are allowed and the + * last entry has to be duplicated. + * XXX generate a better table. + * Unresolved Issues: HIPPI with a 64k MSS is not well supported. + */ +static __u16 const msstab[] = { + 64, + 256, + 512, + 536, + 1024, + 1440, + 1460, + 4312, + 4312 +}; + +static __u32 make_syncookie(struct sk_buff *skb, __u32 counter, __u32 seq) +{ + __u32 z; + + z = secure_tcp_syn_cookie(skb->nh.iph->saddr, skb->nh.iph->daddr, + skb->h.th->source, skb->h.th->dest, + seq, + counter); + +#if 0 + printk(KERN_DEBUG + "msc: z=%u,cnt=%u,seq=%u,sadr=%u,dadr=%u,sp=%u,dp=%u\n", + z,counter,seq, + skb->nh.iph->saddr,skb->nh.iph->daddr, + ntohs(skb->h.th->source), ntohs(skb->h.th->dest)); +#endif + + return z; +} + +/* + * Generate a syncookie. + */ +__u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, + __u16 *mssp) +{ + int i; + __u32 isn; + const __u16 mss = *mssp, *w; + + tcp_lastsynq_overflow = jiffies; + + isn = make_syncookie(skb, (jiffies/HZ) >> 6, ntohl(skb->h.th->seq)); + + /* XXX sort msstab[] by probability? */ + w = msstab; + for (i = 0; i < 8; i++) + if (mss >= *w && mss < *++w) + goto found; + i--; +found: + *mssp = w[-1]; + + isn |= i; + return isn; +} + +/* This value should be dependant on TCP_TIMEOUT_INIT and + * sysctl_tcp_retries1. It's a rather complicated formula + * (exponential backoff) to compute at runtime so it's currently hardcoded + * here. + */ +#define COUNTER_TRIES 4 + +/* + * Check if a ack sequence number is a valid syncookie. + */ +static inline int cookie_check(struct sk_buff *skb, __u32 cookie) +{ + int mssind; + int i; + __u32 counter; + __u32 seq; + + if ((jiffies - tcp_lastsynq_overflow) > TCP_TIMEOUT_INIT + && tcp_lastsynq_overflow) + return 0; + + mssind = cookie & 7; + cookie &= ~7; + + counter = (jiffies/HZ)>>6; + seq = ntohl(skb->h.th->seq)-1; + for (i = 0; i < COUNTER_TRIES; i++) + if (make_syncookie(skb, counter-i, seq) == cookie) + return msstab[mssind]; + + return 0; +} + +extern struct or_calltable or_ipv4; + +static inline struct sock * +get_cookie_sock(struct sock *sk, struct sk_buff *skb, struct open_request *req, + struct dst_entry *dst) +{ + struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; + + sk = tp->af_specific->syn_recv_sock(sk, skb, req, dst); + req->sk = sk; + + /* Queue up for accept() */ + tcp_synq_queue(tp, req); + + return sk; +} + +struct sock * +cookie_v4_check(struct sock *sk, struct sk_buff *skb, struct ip_options *opt) +{ + __u32 cookie = ntohl(skb->h.th->ack_seq)-1; + struct open_request *req; + int mss; + struct rtable *rt; + + if (!sysctl_tcp_syncookies) + return sk; + if (!skb->h.th->ack) + return sk; + + mss = cookie_check(skb, cookie); + if (mss == 0) + return sk; + + req = tcp_openreq_alloc(); + if (req == NULL) + return NULL; + + req->rcv_isn = htonl(skb->h.th->seq)-1; + req->snt_isn = cookie; + req->mss = mss; + req->rmt_port = skb->h.th->source; + req->af.v4_req.loc_addr = skb->nh.iph->daddr; + req->af.v4_req.rmt_addr = skb->nh.iph->saddr; + req->class = &or_ipv4; /* for savety */ + + /* We throwed the options of the initial SYN away, so we hope + * the ACK carries the same options again (see RFC1122 4.2.3.8) + */ + if (opt && opt->optlen) { + int opt_size = sizeof(struct ip_options) + opt->optlen; + + req->af.v4_req.opt = kmalloc(opt_size, GFP_ATOMIC); + if (req->af.v4_req.opt) { + if (ip_options_echo(req->af.v4_req.opt, skb)) { + kfree_s(req->af.v4_req.opt, opt_size); + req->af.v4_req.opt = NULL; + } + } + } + + req->af.v4_req.opt = NULL; + req->snd_wscale = req->rcv_wscale = req->tstamp_ok = 0; + req->wscale_ok = 0; + req->expires = 0UL; + req->retrans = 0; + + /* + * We need to lookup the route here to get at the correct + * window size. We should better make sure that the window size + * hasn't changed since we received the original syn, but I see + * no easy way to do this. + */ + if (ip_route_output(&rt, + opt && opt->srr ? opt->faddr : + req->af.v4_req.rmt_addr,req->af.v4_req.loc_addr, + sk->ip_tos, NULL)) { + tcp_openreq_free(req); + return NULL; + } + + /* Try to redo what tcp_v4_send_synack did. */ + req->window_clamp = rt->u.dst.window; + tcp_select_initial_window(sock_rspace(sk)/2,req->mss, + &req->rcv_wnd, &req->window_clamp, + 0, &req->rcv_wscale); + + return get_cookie_sock(sk, skb, req, &rt->u.dst); +} + +#endif diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 6d7ba591f..5f804f343 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -60,8 +60,8 @@ extern int sysctl_tcp_retries2; extern int sysctl_tcp_max_delay_acks; extern int sysctl_tcp_fin_timeout; extern int sysctl_tcp_syncookies; -extern int sysctl_tcp_always_syncookie; extern int sysctl_tcp_syn_retries; +extern int sysctl_tcp_stdurg; extern int tcp_sysctl_congavoid(ctl_table *ctl, int write, struct file * filp, void *buffer, size_t *lenp); @@ -203,10 +203,12 @@ ctl_table ipv4_table[] = { {NET_IPV4_IGMP_AGE_THRESHOLD, "igmp_age_threshold", &sysctl_igmp_age_threshold, sizeof(int), 0644, NULL, &proc_dointvec}, #endif +#ifdef CONFIG_SYN_COOKIES {NET_TCP_SYNCOOKIES, "tcp_syncookies", &sysctl_tcp_syncookies, sizeof(int), 0644, NULL, &proc_dointvec}, - {NET_TCP_ALWAYS_SYNCOOKIE, "tcp_always_syncookie", - &sysctl_tcp_always_syncookie, sizeof(int), 0644, NULL, &proc_dointvec}, +#endif + {NET_TCP_STDURG, "tcp_stdurg", &sysctl_tcp_stdurg, + sizeof(int), 0644, NULL, &proc_dointvec}, {0} }; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 604bd1c84..7a6b8f55f 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_input.c,v 1.52 1997/05/31 12:36:42 freitag Exp $ + * Version: $Id: tcp_input.c,v 1.2 1997/06/17 13:31:29 ralf Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -56,15 +56,21 @@ static void tcp_cong_avoid_vanj(struct sock *sk, u32 seq, u32 ack, static void tcp_cong_avoid_vegas(struct sock *sk, u32 seq, u32 ack, u32 seq_rtt); +#ifdef CONFIG_SYSCTL +#define SYNC_INIT 0 /* let the user enable it */ +#else +#define SYNC_INIT 1 +#endif + int sysctl_tcp_cong_avoidance; int sysctl_tcp_hoe_retransmits; int sysctl_tcp_sack; int sysctl_tcp_tsack; int sysctl_tcp_timestamps; int sysctl_tcp_window_scaling; -int sysctl_tcp_syncookies; -int sysctl_tcp_always_syncookie; +int sysctl_tcp_syncookies = SYNC_INIT; int sysctl_tcp_max_delay_acks = MAX_DELAY_ACK; +int sysctl_tcp_stdurg; static tcp_sys_cong_ctl_t tcp_sys_cong_ctl_f = &tcp_cong_avoid_vanj; @@ -288,7 +294,7 @@ static int tcp_reset(struct sock *sk, struct sk_buff *skb) * FIXME: surely this can be more efficient. -- erics */ -void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp) +void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp, int no_fancy) { unsigned char *ptr; int length=(th->doff*4)-sizeof(struct tcphdr); @@ -323,21 +329,21 @@ void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp) break; case TCPOPT_WINDOW: if(opsize==TCPOLEN_WINDOW && th->syn) - if (sysctl_tcp_window_scaling) { + if (!no_fancy && sysctl_tcp_window_scaling) { tp->wscale_ok = 1; tp->snd_wscale = *(__u8 *)ptr; } break; case TCPOPT_SACK_PERM: if(opsize==TCPOLEN_SACK_PERM && th->syn) - if (sysctl_tcp_sack) + if (sysctl_tcp_sack && !no_fancy) tp->sack_ok = 1; case TCPOPT_TIMESTAMP: if(opsize==TCPOLEN_TIMESTAMP) { /* Cheaper to set again then to * test syn. Optimize this? */ - if (sysctl_tcp_timestamps) + if (sysctl_tcp_timestamps && !no_fancy) tp->tstamp_ok = 1; tp->saw_tstamp = 1; tp->rcv_tsval = ntohl(*(__u32 *)ptr); @@ -345,6 +351,8 @@ void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp) } break; case TCPOPT_SACK: + if (no_fancy) + break; tp->sacks = (opsize-2)>>3; if (tp->sacks<<3 == opsize-2) { int i; @@ -385,7 +393,7 @@ static __inline__ int tcp_fast_parse_options(struct tcphdr *th, struct tcp_opt * return 1; } } - tcp_parse_options(th,tp); + tcp_parse_options(th,tp,0); return 1; } @@ -1233,7 +1241,7 @@ static __inline__ void tcp_ack_snd_check(struct sock *sk) * place. We handle URGent data wrong. We have to - as * BSD still doesn't use the correction from RFC961. * For 1003.1g we should support a new option TCP_STDURG to permit - * either form. + * either form (or just set the sysctl tcp_stdurg). */ static void tcp_check_urg(struct sock * sk, struct tcphdr * th) @@ -1241,7 +1249,7 @@ static void tcp_check_urg(struct sock * sk, struct tcphdr * th) struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); u32 ptr = ntohs(th->urg_ptr); - if (ptr) + if (ptr && !sysctl_tcp_stdurg) ptr--; ptr += ntohl(th->seq); @@ -1459,13 +1467,11 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, /* These use the socket TOS.. * might want to be the received TOS */ - if(th->ack) + if(th->ack) return 1; /* send reset */ if(th->syn) { - __u32 isn = tp->af_specific->init_sequence(sk, skb); - - if(tp->af_specific->conn_request(sk, skb, opt, isn) < 0) + if(tp->af_specific->conn_request(sk, skb, opt, 0) < 0) return 1; /* Now we have several options: In theory there is @@ -1531,7 +1537,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, tp->fin_seq = skb->seq; tcp_set_state(sk, TCP_ESTABLISHED); - tcp_parse_options(th,tp); + tcp_parse_options(th,tp,0); /* FIXME: need to make room for SACK still */ if (tp->wscale_ok == 0) { tp->snd_wscale = tp->rcv_wscale = 0; @@ -1574,7 +1580,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, * tcp_connect. */ tcp_set_state(sk, TCP_SYN_RECV); - tcp_parse_options(th,tp); + tcp_parse_options(th,tp,0); if (tp->saw_tstamp) { tp->ts_recent = tp->rcv_tsval; tp->ts_recent_stamp = jiffies; @@ -1616,6 +1622,8 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, sk->shutdown = SHUTDOWN_MASK; isn = tp->rcv_nxt + 128000; + if (isn == 0) + isn++; sk = tp->af_specific->get_sock(skb, th); @@ -1710,8 +1718,10 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, tp->snd_wl1 = skb->seq; tp->snd_wl2 = skb->ack_seq; - } else + } else { + SOCK_DEBUG(sk, "bad ack\n"); return 1; + } break; case TCP_FIN_WAIT1: diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index c4d12a54f..d89624175 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_ipv4.c,v 1.43 1997/05/06 09:31:44 davem Exp $ + * Version: $Id: tcp_ipv4.c,v 1.1.1.1 1997/06/01 03:16:26 ralf Exp $ * * IPv4 specific functions * @@ -30,6 +30,9 @@ * David S. Miller : Change semantics of established hash, * half is devoted to TIME_WAIT sockets * and the rest go in the other half. + * Andi Kleen : Add support for syncookies and fixed + * some bugs: ip options weren't passed to + * the TCP layer, missed a check for an ACK bit. */ #include <linux/config.h> @@ -48,6 +51,7 @@ extern int sysctl_tcp_sack; extern int sysctl_tcp_tsack; extern int sysctl_tcp_timestamps; extern int sysctl_tcp_window_scaling; +extern int sysctl_tcp_syncookies; static void tcp_v4_send_reset(struct sk_buff *skb); @@ -403,7 +407,7 @@ struct sock *tcp_v4_proxy_lookup(unsigned short num, unsigned long raddr, #endif -static __u32 tcp_v4_init_sequence(struct sock *sk, struct sk_buff *skb) +static inline __u32 tcp_v4_init_sequence(struct sock *sk, struct sk_buff *skb) { return secure_tcp_sequence_number(sk->saddr, sk->daddr, skb->h.th->dest, @@ -697,6 +701,12 @@ void tcp_v4_err(struct sk_buff *skb, unsigned char *dp) } /* FIXME: What about the IP layer options size here? */ + /* FIXME: add a timeout here, to cope with broken devices that + drop all DF=1 packets. Do some more sanity checking + here to prevent DOS attacks? + This code should kick the tcp_output routine to + retransmit a packet immediately because we know that + the last packet has been dropped. -AK */ if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) { if (sk->ip_pmtudisc != IP_PMTUDISC_DONT) { int new_mtu = sk->dst_cache->pmtu - sizeof(struct iphdr) - tp->tcp_header_len; @@ -835,6 +845,8 @@ static void tcp_v4_send_synack(struct sock *sk, struct open_request *req) /* Don't offer more than they did. * This way we don't have to memorize who said what. + * FIXME: maybe this should be changed for better performance + * with syncookies. */ req->mss = min(mss, req->mss); @@ -891,17 +903,13 @@ static void tcp_v4_or_free(struct open_request *req) sizeof(struct ip_options) + req->af.v4_req.opt->optlen); } -static struct or_calltable or_ipv4 = { +struct or_calltable or_ipv4 = { tcp_v4_send_synack, tcp_v4_or_free }; -static int tcp_v4_syn_filter(struct sock *sk, struct sk_buff *skb, __u32 saddr) -{ - return 0; -} - -int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, __u32 isn) +int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, + __u32 isn) { struct ip_options *opt = (struct ip_options *) ptr; struct tcp_opt tp; @@ -909,23 +917,39 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, __u32 i struct tcphdr *th = skb->h.th; __u32 saddr = skb->nh.iph->saddr; __u32 daddr = skb->nh.iph->daddr; +#ifdef CONFIG_SYN_COOKIES + int want_cookie = 0; +#else +#define want_cookie 0 /* Argh, why doesn't gcc optimize this :( */ +#endif /* If the socket is dead, don't accept the connection. */ - if (sk->dead) { - SOCK_DEBUG(sk, "Reset on %p: Connect on dead socket.\n",sk); - tcp_statistics.TcpAttemptFails++; - return -ENOTCONN; - } - - if (sk->ack_backlog >= sk->max_ack_backlog || - tcp_v4_syn_filter(sk, skb, saddr)) { - SOCK_DEBUG(sk, "dropping syn ack:%d max:%d\n", sk->ack_backlog, - sk->max_ack_backlog); -#ifdef CONFIG_IP_TCPSF - tcp_v4_random_drop(sk); + if (sk->dead) + goto dead; + + if (sk->ack_backlog >= sk->max_ack_backlog) { +#ifdef CONFIG_SYN_COOKIES + if (sysctl_tcp_syncookies) { + static unsigned long warntime; + + if (jiffies - warntime > HZ*60) { + warntime = jiffies; + printk(KERN_INFO + "possible SYN flooding on port %d. Sending cookies.\n", ntohs(skb->h.th->dest)); + } + want_cookie = 1; + } else #endif - tcp_statistics.TcpAttemptFails++; - goto exit; + { + SOCK_DEBUG(sk, "dropping syn ack:%d max:%d\n", sk->ack_backlog, + sk->max_ack_backlog); + tcp_statistics.TcpAttemptFails++; + goto exit; + } + } else { + if (isn == 0) + isn = tcp_v4_init_sequence(sk, skb); + sk->ack_backlog++; } req = tcp_openreq_alloc(); @@ -934,15 +958,12 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, __u32 i goto exit; } - sk->ack_backlog++; - req->rcv_wnd = 0; /* So that tcp_send_synack() knows! */ req->rcv_isn = skb->seq; - req->snt_isn = isn; - tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0; + tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0; tp.in_mss = 536; - tcp_parse_options(th,&tp); + tcp_parse_options(th,&tp, want_cookie); if (tp.saw_tstamp) req->ts_recent = tp.rcv_tsval; req->mss = tp.in_mss; @@ -954,8 +975,17 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, __u32 i req->af.v4_req.loc_addr = daddr; req->af.v4_req.rmt_addr = saddr; + /* Note that we ignore the isn passed from the TIME_WAIT + * state here. That's the price we pay for cookies. + */ + if (want_cookie) + isn = cookie_v4_init_sequence(sk, skb, &req->mss); + + req->snt_isn = isn; + /* IPv4 options */ req->af.v4_req.opt = NULL; + if (opt && opt->optlen) { int opt_size = sizeof(struct ip_options) + opt->optlen; @@ -973,36 +1003,50 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, __u32 i tcp_v4_send_synack(sk, req); - req->expires = jiffies + TCP_TIMEOUT_INIT; - tcp_inc_slow_timer(TCP_SLT_SYNACK); - tcp_synq_queue(&sk->tp_pinfo.af_tcp, req); + if (want_cookie) { + if (req->af.v4_req.opt) + kfree(req->af.v4_req.opt); + tcp_openreq_free(req); + } else { + req->expires = jiffies + TCP_TIMEOUT_INIT; + tcp_inc_slow_timer(TCP_SLT_SYNACK); + tcp_synq_queue(&sk->tp_pinfo.af_tcp, req); + } sk->data_ready(sk, 0); exit: kfree_skb(skb, FREE_READ); return 0; + +dead: + SOCK_DEBUG(sk, "Reset on %p: Connect on dead socket.\n",sk); + tcp_statistics.TcpAttemptFails++; + return -ENOTCONN; } struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, - struct open_request *req) + struct open_request *req, + struct dst_entry *dst) { struct tcp_opt *newtp; struct sock *newsk; - struct rtable *rt; int snd_mss; newsk = sk_alloc(GFP_ATOMIC); - if (newsk == NULL) + if (newsk == NULL) { + if (dst) + dst_release(dst); return NULL; + } memcpy(newsk, sk, sizeof(*newsk)); /* Or else we die! -DaveM */ newsk->sklist_next = NULL; - newsk->opt = NULL; - newsk->dst_cache = NULL; + newsk->opt = req->af.v4_req.opt; + skb_queue_head_init(&newsk->write_queue); skb_queue_head_init(&newsk->receive_queue); skb_queue_head_init(&newsk->out_of_order_queue); @@ -1072,17 +1116,21 @@ struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, newsk->rcv_saddr = req->af.v4_req.loc_addr; /* options / mss / route_cache */ - newsk->opt = req->af.v4_req.opt; - if (ip_route_output(&rt, - newsk->opt && newsk->opt->srr ? newsk->opt->faddr : newsk->daddr, - newsk->saddr, newsk->ip_tos, NULL)) { - kfree(newsk); - return NULL; - } - - newsk->dst_cache = &rt->u.dst; - - snd_mss = rt->u.dst.pmtu; + if (dst == NULL) { + struct rtable *rt; + + if (ip_route_output(&rt, + newsk->opt && newsk->opt->srr ? + newsk->opt->faddr : newsk->daddr, + newsk->saddr, newsk->ip_tos, NULL)) { + kfree(newsk); + return NULL; + } + dst = &rt->u.dst; + } + newsk->dst_cache = dst; + + snd_mss = dst->pmtu; /* FIXME: is mtu really the same as snd_mss? */ newsk->mtu = snd_mss; @@ -1124,7 +1172,7 @@ struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, return newsk; } -struct sock *tcp_v4_check_req(struct sock *sk, struct sk_buff *skb) +static inline struct sock *tcp_v4_check_req(struct sock *sk, struct sk_buff *skb, struct ip_options *opt) { struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); struct open_request *req = tp->syn_wait_queue; @@ -1133,8 +1181,13 @@ struct sock *tcp_v4_check_req(struct sock *sk, struct sk_buff *skb) * as we checked the user count on tcp_rcv and we're * running from a soft interrupt. */ - if(!req) + if(!req) { +#ifdef CONFIG_SYN_COOKIES + goto checkcookie; +#else return sk; +#endif + } while(req) { if (req->af.v4_req.rmt_addr == skb->nh.iph->saddr && @@ -1147,7 +1200,7 @@ struct sock *tcp_v4_check_req(struct sock *sk, struct sk_buff *skb) * yet accepted()... */ sk = req->sk; - break; + goto ende; } /* Check for syn retransmission */ @@ -1161,20 +1214,28 @@ struct sock *tcp_v4_check_req(struct sock *sk, struct sk_buff *skb) return NULL; } - sk = tp->af_specific->syn_recv_sock(sk, skb, req); + if (!skb->h.th->ack) + return sk; + + sk = tp->af_specific->syn_recv_sock(sk, skb, req, NULL); tcp_dec_slow_timer(TCP_SLT_SYNACK); if (sk == NULL) return NULL; req->expires = 0UL; req->sk = sk; - break; + goto ende; } req = req->dl_next; } - skb_orphan(skb); - skb_set_owner_r(skb, sk); +#ifdef CONFIG_SYN_COOKIES +checkcookie: + sk = cookie_v4_check(sk, skb, opt); +#endif +ende: skb_orphan(skb); + if (sk) + skb_set_owner_r(skb, sk); return sk; } @@ -1195,20 +1256,28 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) goto ok; } - if (sk->state == TCP_LISTEN) { - struct sock *nsk; + /* + * We check packets with only the SYN bit set against the + * open_request queue too: This increases connection latency a bit, + * but is required to detect retransmitted SYNs. + * + * The ACK/SYN bit check is probably not needed here because + * it is checked later again (we play save now). + */ + if (sk->state == TCP_LISTEN && (skb->h.th->ack || skb->h.th->syn)) { + struct sock *nsk; - /* Find possible connection requests. */ - nsk = tcp_v4_check_req(sk, skb); - if (nsk == NULL) + /* Find possible connection requests. */ + nsk = tcp_v4_check_req(sk, skb, &(IPCB(skb)->opt)); + if (nsk == NULL) goto discard_it; - - release_sock(sk); - lock_sock(nsk); + + release_sock(sk); + lock_sock(nsk); sk = nsk; } - if (tcp_rcv_state_process(sk, skb, skb->h.th, NULL, skb->len) == 0) + if (tcp_rcv_state_process(sk, skb, skb->h.th, &(IPCB(skb)->opt), skb->len) == 0) goto ok; reset: @@ -1352,7 +1421,6 @@ struct tcp_func ipv4_specific = { tcp_v4_rebuild_header, tcp_v4_conn_request, tcp_v4_syn_recv_sock, - tcp_v4_init_sequence, tcp_v4_get_sock, ip_setsockopt, ip_getsockopt, diff --git a/net/ipv4/utils.c b/net/ipv4/utils.c index 4253c85db..d2b8e0089 100644 --- a/net/ipv4/utils.c +++ b/net/ipv4/utils.c @@ -13,7 +13,7 @@ * Fixes: * Alan Cox : verify_area check. * Alan Cox : removed old debugging. - * + * Andi Kleen : add net_ratelimit() * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -89,3 +89,24 @@ __u32 in_aton(const char *str) return(htonl(l)); } +/* + * This enforces a rate limit: not more than one kernel message + * every 5secs to make a denial-of-service attack impossible. + * + * All warning printk()s should be guarded by this function. + */ +int net_ratelimit(void) +{ + static unsigned long last_msg; + static int missed; + + if ((jiffies - last_msg) >= 5*HZ) { + if (missed) + printk(KERN_WARNING "ipv4: (%d messages suppressed. Flood?)\n", missed); + missed = 0; + last_msg = jiffies; + return 1; + } + missed++; + return 0; +} diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 71ff84b4b..a5a884646 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: icmp.c,v 1.9 1997/04/29 09:38:42 mj Exp $ + * $Id: icmp.c,v 1.1.1.1 1997/06/01 03:16:27 ralf Exp $ * * Based on net/ipv4/icmp.c * @@ -405,7 +405,23 @@ int icmpv6_rcv(struct sk_buff *skb, struct device *dev, case CHECKSUM_HW: if (csum_ipv6_magic(saddr, daddr, len, IPPROTO_ICMPV6, skb->csum)) { - printk(KERN_DEBUG "icmpv6 checksum failed\n"); + printk(KERN_DEBUG "ICMPv6 checksum failed [%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x > %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]\n", + ntohs(saddr->in6_u.u6_addr16[0]), + ntohs(saddr->in6_u.u6_addr16[1]), + ntohs(saddr->in6_u.u6_addr16[2]), + ntohs(saddr->in6_u.u6_addr16[3]), + ntohs(saddr->in6_u.u6_addr16[4]), + ntohs(saddr->in6_u.u6_addr16[5]), + ntohs(saddr->in6_u.u6_addr16[6]), + ntohs(saddr->in6_u.u6_addr16[7]), + ntohs(daddr->in6_u.u6_addr16[0]), + ntohs(daddr->in6_u.u6_addr16[1]), + ntohs(daddr->in6_u.u6_addr16[2]), + ntohs(daddr->in6_u.u6_addr16[3]), + ntohs(daddr->in6_u.u6_addr16[4]), + ntohs(daddr->in6_u.u6_addr16[5]), + ntohs(daddr->in6_u.u6_addr16[6]), + ntohs(daddr->in6_u.u6_addr16[7])); goto discard_it; } default: diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 4bf0207d9..51b9eff4c 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: tcp_ipv6.c,v 1.32 1997/06/04 08:28:58 davem Exp $ + * $Id: tcp_ipv6.c,v 1.2 1997/06/17 13:31:32 ralf Exp $ * * Based on: * linux/net/ipv4/tcp.c @@ -695,7 +695,7 @@ static struct or_calltable or_ipv6 = { * Can some kind of merge be done? -- erics */ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, - __u32 isn) + __u32 isn) { struct tcp_opt tp; struct open_request *req; @@ -711,6 +711,9 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, if (skb->protocol == __constant_htons(ETH_P_IP)) return tcp_v4_conn_request(sk, skb, ptr, isn); + if (isn == 0) + isn = tcp_v6_init_sequence(sk,skb); + /* * There are no SYN attacks on IPv6, yet... */ @@ -735,7 +738,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, req->snt_isn = isn; tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0; tp.in_mss = 536; - tcp_parse_options(skb->h.th,&tp); + tcp_parse_options(skb->h.th,&tp,0); if (tp.saw_tstamp) req->ts_recent = tp.rcv_tsval; req->mss = tp.in_mss; @@ -778,10 +781,10 @@ static void tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len, } static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, - struct open_request *req) + struct open_request *req, + struct dst_entry *dst) { struct ipv6_pinfo *np; - struct dst_entry *dst; struct flowi fl; struct tcp_opt *newtp; struct sock *newsk; @@ -791,11 +794,11 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, * v6 mapped */ - newsk = tcp_v4_syn_recv_sock(sk, skb, req); + newsk = tcp_v4_syn_recv_sock(sk, skb, req, dst); - if (newsk == NULL) + if (newsk == NULL) return NULL; - + np = &newsk->net_pinfo.af_inet6; ipv6_addr_set(&np->daddr, 0, 0, __constant_htonl(0x0000FFFF), @@ -813,8 +816,11 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, } newsk = sk_alloc(GFP_ATOMIC); - if (newsk == NULL) + if (newsk == NULL) { + if (dst) + dst_release(dst); return NULL; + } memcpy(newsk, sk, sizeof(*newsk)); @@ -902,18 +908,20 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, ipv6_addr_copy(&np->rcv_saddr, &req->af.v6_req.loc_addr); np->oif = req->af.v6_req.dev; - /* - * options / mss / route cache - */ - - fl.proto = IPPROTO_TCP; - fl.nl_u.ip6_u.daddr = &np->daddr; - fl.nl_u.ip6_u.saddr = &np->saddr; - fl.dev = np->oif; - fl.uli_u.ports.dport = newsk->dummy_th.dest; - fl.uli_u.ports.sport = newsk->dummy_th.source; - - dst = ip6_route_output(newsk, &fl); + if (dst == NULL) { + /* + * options / mss / route cache + */ + + fl.proto = IPPROTO_TCP; + fl.nl_u.ip6_u.daddr = &np->daddr; + fl.nl_u.ip6_u.saddr = &np->saddr; + fl.dev = np->oif; + fl.uli_u.ports.dport = newsk->dummy_th.dest; + fl.uli_u.ports.sport = newsk->dummy_th.source; + + dst = ip6_route_output(newsk, &fl); + } ip6_dst_store(newsk, dst); @@ -1051,7 +1059,7 @@ struct sock *tcp_v6_check_req(struct sock *sk, struct sk_buff *skb) } skb_orphan(skb); - sk = tp->af_specific->syn_recv_sock(sk, skb, req); + sk = tp->af_specific->syn_recv_sock(sk, skb, req, NULL); tcp_dec_slow_timer(TCP_SLT_SYNACK); @@ -1308,7 +1316,6 @@ static struct tcp_func ipv6_specific = { tcp_v6_rebuild_header, tcp_v6_conn_request, tcp_v6_syn_recv_sock, - tcp_v6_init_sequence, tcp_v6_get_sock, ipv6_setsockopt, ipv6_getsockopt, @@ -1328,7 +1335,6 @@ static struct tcp_func ipv6_mapped = { tcp_v4_rebuild_header, tcp_v6_conn_request, tcp_v6_syn_recv_sock, - tcp_v6_init_sequence, tcp_v6_get_sock, ipv6_setsockopt, ipv6_getsockopt, diff --git a/net/lapb/lapb_iface.c b/net/lapb/lapb_iface.c index 2be6e565e..77382683c 100644 --- a/net/lapb/lapb_iface.c +++ b/net/lapb/lapb_iface.c @@ -1,8 +1,5 @@ /* - * LAPB release 001 - * - * This is ALPHA test software. This code may break your machine, randomly fail to work with new - * releases, misbehave and/or generally screw up. It might even work. + * LAPB release 002 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -14,6 +11,7 @@ * * History * LAPB 001 Jonathan Naylor Started Coding + * LAPB 002 Jonathan Naylor New timer architecture. */ #include <linux/config.h> @@ -63,8 +61,7 @@ static void lapb_remove_cb(lapb_cb *lapb) lapb_cb *s; unsigned long flags; - save_flags(flags); - cli(); + save_flags(flags); cli(); if ((s = lapb_list) == lapb) { lapb_list = s->next; @@ -92,8 +89,7 @@ static void lapb_insert_cb(lapb_cb *lapb) { unsigned long flags; - save_flags(flags); - cli(); + save_flags(flags); cli(); lapb->next = lapb_list; lapb_list = lapb; @@ -130,11 +126,11 @@ static lapb_cb *lapb_create_cb(void) memset(lapb, 0x00, sizeof(*lapb)); - skb_queue_head_init(&lapb->input_queue); skb_queue_head_init(&lapb->write_queue); skb_queue_head_init(&lapb->ack_queue); - init_timer(&lapb->timer); + init_timer(&lapb->t1timer); + init_timer(&lapb->t2timer); lapb->t1 = LAPB_DEFAULT_T1; lapb->t2 = LAPB_DEFAULT_T2; @@ -161,9 +157,7 @@ int lapb_register(void *token, struct lapb_register_struct *callbacks) lapb_insert_cb(lapb); - lapb->t1timer = lapb->t1; - - lapb_set_timer(lapb); + lapb_start_t1timer(lapb); return LAPB_OK; } @@ -175,7 +169,8 @@ int lapb_unregister(void *token) if ((lapb = lapb_tokentostruct(token)) == NULL) return LAPB_BADTOKEN; - del_timer(&lapb->timer); + lapb_stop_t1timer(lapb); + lapb_stop_t2timer(lapb); lapb_clear_queues(lapb); @@ -193,16 +188,24 @@ int lapb_getparms(void *token, struct lapb_parms_struct *parms) if ((lapb = lapb_tokentostruct(token)) == NULL) return LAPB_BADTOKEN; - parms->t1 = lapb->t1; - parms->t1timer = lapb->t1timer; - parms->t2 = lapb->t2; - parms->t2timer = lapb->t2timer; + parms->t1 = lapb->t1 / HZ; + parms->t2 = lapb->t2 / HZ; parms->n2 = lapb->n2; parms->n2count = lapb->n2count; parms->state = lapb->state; parms->window = lapb->window; parms->mode = lapb->mode; + if (lapb->t1timer.prev == NULL && lapb->t1timer.next == NULL) + parms->t1timer = 0; + else + parms->t1timer = (lapb->t1timer.expires - jiffies) / HZ; + + if (lapb->t2timer.prev == NULL && lapb->t2timer.next == NULL) + parms->t2timer = 0; + else + parms->t2timer = (lapb->t2timer.expires - jiffies) / HZ; + return LAPB_OK; } @@ -235,8 +238,8 @@ int lapb_setparms(void *token, struct lapb_parms_struct *parms) lapb->window = parms->window; } - lapb->t1 = parms->t1; - lapb->t2 = parms->t2; + lapb->t1 = parms->t1 * HZ; + lapb->t2 = parms->t2 * HZ; lapb->n2 = parms->n2; return LAPB_OK; @@ -287,8 +290,8 @@ int lapb_disconnect_request(void *token) printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", lapb->token); #endif lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND); - lapb->state = LAPB_STATE_0; - lapb->t1timer = lapb->t1; + lapb->state = LAPB_STATE_0; + lapb_start_t1timer(lapb); return LAPB_NOTCONNECTED; case LAPB_STATE_2: @@ -298,9 +301,9 @@ int lapb_disconnect_request(void *token) lapb_clear_queues(lapb); lapb->n2count = 0; lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND); - lapb->t1timer = lapb->t1; - lapb->t2timer = 0; - lapb->state = LAPB_STATE_2; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_2; #if LAPB_DEBUG > 1 printk(KERN_DEBUG "lapb: (%p) S3 DISC(1)\n", lapb->token); @@ -336,7 +339,7 @@ int lapb_data_received(void *token, struct sk_buff *skb) if ((lapb = lapb_tokentostruct(token)) == NULL) return LAPB_BADTOKEN; - skb_queue_tail(&lapb->input_queue, skb); + lapb_data_input(lapb, skb); return LAPB_OK; } diff --git a/net/lapb/lapb_in.c b/net/lapb/lapb_in.c index 9aa367115..126b93673 100644 --- a/net/lapb/lapb_in.c +++ b/net/lapb/lapb_in.c @@ -1,8 +1,5 @@ /* - * LAPB release 001 - * - * This is ALPHA test software. This code may break your machine, randomly fail to work with new - * releases, misbehave and/or generally screw up. It might even work. + * LAPB release 002 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -14,6 +11,7 @@ * * History * LAPB 001 Jonathan Naulor Started Coding + * LAPB 002 Jonathan Naylor New timer architecture. */ #include <linux/config.h> @@ -63,10 +61,10 @@ static void lapb_state0_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ printk(KERN_DEBUG "lapb: (%p) S0 -> S3\n", lapb->token); #endif lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE); + lapb_stop_t1timer(lapb); + lapb_stop_t2timer(lapb); lapb->state = LAPB_STATE_3; lapb->condition = 0x00; - lapb->t1timer = 0; - lapb->t2timer = 0; lapb->n2count = 0; lapb->vs = 0; lapb->vr = 0; @@ -87,10 +85,10 @@ static void lapb_state0_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ printk(KERN_DEBUG "lapb: (%p) S0 -> S3\n", lapb->token); #endif lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE); + lapb_stop_t1timer(lapb); + lapb_stop_t2timer(lapb); lapb->state = LAPB_STATE_3; lapb->condition = 0x00; - lapb->t1timer = 0; - lapb->t2timer = 0; lapb->n2count = 0; lapb->vs = 0; lapb->vr = 0; @@ -176,10 +174,10 @@ static void lapb_state1_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ #if LAPB_DEBUG > 0 printk(KERN_DEBUG "lapb: (%p) S1 -> S3\n", lapb->token); #endif + lapb_stop_t1timer(lapb); + lapb_stop_t2timer(lapb); lapb->state = LAPB_STATE_3; lapb->condition = 0x00; - lapb->t1timer = 0; - lapb->t2timer = 0; lapb->n2count = 0; lapb->vs = 0; lapb->vr = 0; @@ -197,9 +195,9 @@ static void lapb_state1_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", lapb->token); #endif lapb_clear_queues(lapb); - lapb->state = LAPB_STATE_0; - lapb->t1timer = lapb->t1; - lapb->t2timer = 0; + lapb->state = LAPB_STATE_0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); lapb_disconnect_indication(lapb, LAPB_REFUSED); } break; @@ -243,9 +241,9 @@ static void lapb_state2_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ #if LAPB_DEBUG > 0 printk(KERN_DEBUG "lapb: (%p) S2 -> S0\n", lapb->token); #endif - lapb->state = LAPB_STATE_0; - lapb->t1timer = lapb->t1; - lapb->t2timer = 0; + lapb->state = LAPB_STATE_0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); lapb_disconnect_confirmation(lapb, LAPB_OK); } break; @@ -258,9 +256,9 @@ static void lapb_state2_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ #if LAPB_DEBUG > 0 printk(KERN_DEBUG "lapb: (%p) S2 -> S0\n", lapb->token); #endif - lapb->state = LAPB_STATE_0; - lapb->t1timer = lapb->t1; - lapb->t2timer = 0; + lapb->state = LAPB_STATE_0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); lapb_disconnect_confirmation(lapb, LAPB_NOTCONNECTED); } break; @@ -309,9 +307,9 @@ static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ printk(KERN_DEBUG "lapb: (%p) S3 TX UA(%d)\n", lapb->token, frame->pf); #endif lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE); + lapb_stop_t1timer(lapb); + lapb_stop_t2timer(lapb); lapb->condition = 0x00; - lapb->t1timer = 0; - lapb->t2timer = 0; lapb->n2count = 0; lapb->vs = 0; lapb->vr = 0; @@ -329,9 +327,9 @@ static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ printk(KERN_DEBUG "lapb: (%p) S3 TX UA(%d)\n", lapb->token, frame->pf); #endif lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE); + lapb_stop_t1timer(lapb); + lapb_stop_t2timer(lapb); lapb->condition = 0x00; - lapb->t1timer = 0; - lapb->t2timer = 0; lapb->n2count = 0; lapb->vs = 0; lapb->vr = 0; @@ -354,9 +352,9 @@ static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ #endif lapb_clear_queues(lapb); lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE); - lapb->state = LAPB_STATE_0; - lapb->t1timer = lapb->t1; - lapb->t2timer = 0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_0; lapb_disconnect_indication(lapb, LAPB_OK); break; @@ -368,9 +366,9 @@ static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ printk(KERN_DEBUG "lapb: (%p) S3 -> S0\n", lapb->token); #endif lapb_clear_queues(lapb); - lapb->state = LAPB_STATE_0; - lapb->t1timer = lapb->t1; - lapb->t2timer = 0; + lapb->state = LAPB_STATE_0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); lapb_disconnect_indication(lapb, LAPB_NOTCONNECTED); break; @@ -389,10 +387,10 @@ static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ #if LAPB_DEBUG > 0 printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token); #endif - lapb->state = LAPB_STATE_4; - lapb->t1timer = lapb->t1; - lapb->t2timer = 0; - lapb->n2count = 0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_4; + lapb->n2count = 0; } break; @@ -411,10 +409,10 @@ static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ #if LAPB_DEBUG > 0 printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token); #endif - lapb->state = LAPB_STATE_4; - lapb->t1timer = lapb->t1; - lapb->t2timer = 0; - lapb->n2count = 0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_4; + lapb->n2count = 0; } break; @@ -426,7 +424,7 @@ static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ lapb_check_need_response(lapb, frame->cr, frame->pf); if (lapb_validate_nr(lapb, frame->nr)) { lapb_frames_acked(lapb, frame->nr); - lapb->t1timer = 0; + lapb_stop_t1timer(lapb); lapb->n2count = 0; lapb_requeue_frames(lapb); } else { @@ -436,10 +434,10 @@ static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ #if LAPB_DEBUG > 0 printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token); #endif - lapb->state = LAPB_STATE_4; - lapb->t1timer = lapb->t1; - lapb->t2timer = 0; - lapb->n2count = 0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_4; + lapb->n2count = 0; } break; @@ -454,10 +452,10 @@ static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ #if LAPB_DEBUG > 0 printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token); #endif - lapb->state = LAPB_STATE_4; - lapb->t1timer = lapb->t1; - lapb->t2timer = 0; - lapb->n2count = 0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_4; + lapb->n2count = 0; break; } if (lapb->condition & LAPB_PEER_RX_BUSY_CONDITION) { @@ -473,8 +471,8 @@ static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ lapb_enquiry_response(lapb); } else { if (!(lapb->condition & LAPB_ACK_PENDING_CONDITION)) { - lapb->t2timer = lapb->t2; lapb->condition |= LAPB_ACK_PENDING_CONDITION; + lapb_start_t2timer(lapb); } } } else { @@ -514,10 +512,10 @@ static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ #if LAPB_DEBUG > 0 printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token); #endif - lapb->state = LAPB_STATE_4; - lapb->t1timer = lapb->t1; - lapb->t2timer = 0; - lapb->n2count = 0; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); + lapb->state = LAPB_STATE_4; + lapb->n2count = 0; break; default: @@ -552,10 +550,10 @@ static void lapb_state4_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ printk(KERN_DEBUG "lapb: (%p) S4 -> S3\n", lapb->token); #endif lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE); + lapb_stop_t1timer(lapb); + lapb_stop_t2timer(lapb); lapb->state = LAPB_STATE_3; lapb->condition = 0x00; - lapb->t1timer = 0; - lapb->t2timer = 0; lapb->n2count = 0; lapb->vs = 0; lapb->vr = 0; @@ -576,10 +574,10 @@ static void lapb_state4_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ printk(KERN_DEBUG "lapb: (%p) S4 -> S3\n", lapb->token); #endif lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE); + lapb_stop_t1timer(lapb); + lapb_stop_t2timer(lapb); lapb->state = LAPB_STATE_3; lapb->condition = 0x00; - lapb->t1timer = 0; - lapb->t2timer = 0; lapb->n2count = 0; lapb->vs = 0; lapb->vr = 0; @@ -626,6 +624,8 @@ void lapb_data_input(lapb_cb *lapb, struct sk_buff *skb) lapb_state4_machine(lapb, skb, &frame); break; } + + lapb_kick(lapb); } #endif diff --git a/net/lapb/lapb_out.c b/net/lapb/lapb_out.c index 1256e3a3c..9e1cdf475 100644 --- a/net/lapb/lapb_out.c +++ b/net/lapb/lapb_out.c @@ -1,8 +1,5 @@ /* - * LAPB release 001 - * - * This is ALPHA test software. This code may break your machine, randomly fail to work with new - * releases, misbehave and/or generally screw up. It might even work. + * LAPB release 002 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -14,6 +11,7 @@ * * History * LAPB 001 Jonathan Naylor Started Coding + * LAPB 002 Jonathan Naylor New timer architecture. */ #include <linux/config.h> @@ -77,8 +75,6 @@ void lapb_kick(lapb_cb *lapb) struct sk_buff *skb, *skbn; unsigned short modulus, start, end; - del_timer(&lapb->timer); - modulus = (lapb->mode & LAPB_EXTENDED) ? LAPB_EMODULUS : LAPB_SMODULUS; start = (skb_peek(&lapb->ack_queue) == NULL) ? lapb->va : lapb->vs; @@ -120,11 +116,9 @@ void lapb_kick(lapb_cb *lapb) lapb->condition &= ~LAPB_ACK_PENDING_CONDITION; - if (lapb->t1timer == 0) - lapb->t1timer = lapb->t1; + if (!lapb_t1timer_running(lapb)) + lapb_start_t1timer(lapb); } - - lapb_set_timer(lapb); } void lapb_transmit_buffer(lapb_cb *lapb, struct sk_buff *skb, int type) @@ -184,8 +178,8 @@ void lapb_establish_data_link(lapb_cb *lapb) lapb_send_control(lapb, LAPB_SABM, LAPB_POLLON, LAPB_COMMAND); } - lapb->t2timer = 0; - lapb->t1timer = lapb->t1; + lapb_start_t1timer(lapb); + lapb_stop_t2timer(lapb); } void lapb_enquiry_response(lapb_cb *lapb) @@ -214,12 +208,12 @@ void lapb_check_iframes_acked(lapb_cb *lapb, unsigned short nr) { if (lapb->vs == nr) { lapb_frames_acked(lapb, nr); - lapb->t1timer = 0; + lapb_stop_t1timer(lapb); lapb->n2count = 0; } else { if (lapb->va != nr) { lapb_frames_acked(lapb, nr); - lapb->t1timer = lapb->t1; + lapb_start_t1timer(lapb); } } } diff --git a/net/lapb/lapb_subr.c b/net/lapb/lapb_subr.c index 626e08927..3f7f0a84e 100644 --- a/net/lapb/lapb_subr.c +++ b/net/lapb/lapb_subr.c @@ -1,8 +1,5 @@ /* - * LAPB release 001 - * - * This is ALPHA test software. This code may break your machine, randomly fail to work with new - * releases, misbehave and/or generally screw up. It might even work. + * LAPB release 002 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -45,9 +42,6 @@ void lapb_clear_queues(lapb_cb *lapb) { struct sk_buff *skb; - while ((skb = skb_dequeue(&lapb->input_queue)) != NULL) - kfree_skb(skb, FREE_READ); - while ((skb = skb_dequeue(&lapb->write_queue)) != NULL) kfree_skb(skb, FREE_WRITE); diff --git a/net/lapb/lapb_timer.c b/net/lapb/lapb_timer.c index 2679ff514..757fd10d9 100644 --- a/net/lapb/lapb_timer.c +++ b/net/lapb/lapb_timer.c @@ -1,8 +1,5 @@ /* - * LAPB release 001 - * - * This is ALPHA test software. This code may break your machine, randomly fail to work with new - * releases, misbehave and/or generally screw up. It might even work. + * LAPB release 002 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -14,6 +11,7 @@ * * History * LAPB 001 Jonathan Naylor Started Coding + * LAPB 002 Jonathan Naylor New timer architecture. */ #include <linux/config.h> @@ -38,72 +36,60 @@ #include <linux/interrupt.h> #include <net/lapb.h> -static void lapb_timer(unsigned long); +static void lapb_t1timer_expiry(unsigned long); +static void lapb_t2timer_expiry(unsigned long); -/* - * Linux set timer - */ -void lapb_set_timer(lapb_cb *lapb) +void lapb_start_t1timer(lapb_cb *lapb) { - unsigned long flags; + del_timer(&lapb->t1timer); + + lapb->t1timer.data = (unsigned long)lapb; + lapb->t1timer.function = &lapb_t1timer_expiry; + lapb->t1timer.expires = jiffies + lapb->t1; - save_flags(flags); cli(); - del_timer(&lapb->timer); - restore_flags(flags); + add_timer(&lapb->t1timer); +} + +void lapb_start_t2timer(lapb_cb *lapb) +{ + del_timer(&lapb->t2timer); - lapb->timer.data = (unsigned long)lapb; - lapb->timer.function = &lapb_timer; - lapb->timer.expires = jiffies + (HZ / 10); + lapb->t2timer.data = (unsigned long)lapb; + lapb->t2timer.function = &lapb_t2timer_expiry; + lapb->t2timer.expires = jiffies + lapb->t2; - add_timer(&lapb->timer); + add_timer(&lapb->t2timer); } -/* - * LAPB TIMER - * - * This routine is called every 100ms. Decrement timer by this - * amount - if expired then process the event. - */ -static void lapb_timer(unsigned long param) +void lapb_stop_t1timer(lapb_cb *lapb) +{ + del_timer(&lapb->t1timer); +} + +void lapb_stop_t2timer(lapb_cb *lapb) +{ + del_timer(&lapb->t2timer); +} + +int lapb_t1timer_running(lapb_cb *lapb) +{ + return (lapb->t1timer.prev != NULL || lapb->t1timer.next != NULL); +} + +static void lapb_t2timer_expiry(unsigned long param) { lapb_cb *lapb = (lapb_cb *)param; - struct sk_buff *skb; - - /* - * Process all packet received since the last clock tick. - */ - while ((skb = skb_dequeue(&lapb->input_queue)) != NULL) - lapb_data_input(lapb, skb); - - /* - * If in a data transfer state, transmit any data. - */ - if (lapb->state == LAPB_STATE_3) - lapb_kick(lapb); - - /* - * T2 expiry code. - */ - if (lapb->t2timer > 0 && --lapb->t2timer == 0) { - if (lapb->state == LAPB_STATE_3) { - if (lapb->condition & LAPB_ACK_PENDING_CONDITION) { - lapb->condition &= ~LAPB_ACK_PENDING_CONDITION; - lapb_timeout_response(lapb); - } - } - } - /* - * If T1 isn't running, or hasn't timed out yet, keep going. - */ - if (lapb->t1timer == 0 || --lapb->t1timer > 0) { - lapb_set_timer(lapb); - return; + if (lapb->condition & LAPB_ACK_PENDING_CONDITION) { + lapb->condition &= ~LAPB_ACK_PENDING_CONDITION; + lapb_timeout_response(lapb); } +} + +static void lapb_t1timer_expiry(unsigned long param) +{ + lapb_cb *lapb = (lapb_cb *)param; - /* - * T1 has expired. - */ switch (lapb->state) { /* @@ -120,12 +106,12 @@ static void lapb_timer(unsigned long param) case LAPB_STATE_1: if (lapb->n2count == lapb->n2) { lapb_clear_queues(lapb); - lapb->state = LAPB_STATE_0; - lapb->t2timer = 0; + lapb->state = LAPB_STATE_0; lapb_disconnect_indication(lapb, LAPB_TIMEDOUT); #if LAPB_DEBUG > 0 printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", lapb->token); #endif + return; } else { lapb->n2count++; if (lapb->mode & LAPB_EXTENDED) { @@ -148,12 +134,12 @@ static void lapb_timer(unsigned long param) case LAPB_STATE_2: if (lapb->n2count == lapb->n2) { lapb_clear_queues(lapb); - lapb->state = LAPB_STATE_0; - lapb->t2timer = 0; + lapb->state = LAPB_STATE_0; lapb_disconnect_confirmation(lapb, LAPB_TIMEDOUT); #if LAPB_DEBUG > 0 printk(KERN_DEBUG "lapb: (%p) S2 -> S0\n", lapb->token); #endif + return; } else { lapb->n2count++; #if LAPB_DEBUG > 1 @@ -169,12 +155,13 @@ static void lapb_timer(unsigned long param) case LAPB_STATE_3: if (lapb->n2count == lapb->n2) { lapb_clear_queues(lapb); - lapb->state = LAPB_STATE_0; - lapb->t2timer = 0; + lapb->state = LAPB_STATE_0; + lapb_stop_t2timer(lapb); lapb_disconnect_indication(lapb, LAPB_TIMEDOUT); #if LAPB_DEBUG > 0 printk(KERN_DEBUG "lapb: (%p) S3 -> S0\n", lapb->token); #endif + return; } else { lapb->n2count++; lapb_requeue_frames(lapb); @@ -187,12 +174,12 @@ static void lapb_timer(unsigned long param) case LAPB_STATE_4: if (lapb->n2count == lapb->n2) { lapb_clear_queues(lapb); - lapb->state = LAPB_STATE_0; - lapb->t2timer = 0; + lapb->state = LAPB_STATE_0; lapb_disconnect_indication(lapb, LAPB_TIMEDOUT); #if LAPB_DEBUG > 0 printk(KERN_DEBUG "lapb: (%p) S4 -> S0\n", lapb->token); #endif + return; } else { lapb->n2count++; lapb_transmit_frmr(lapb); @@ -200,9 +187,7 @@ static void lapb_timer(unsigned long param) break; } - lapb->t1timer = lapb->t1; - - lapb_set_timer(lapb); + lapb_start_t1timer(lapb); } #endif diff --git a/net/netlink.c b/net/netlink.c index 539ec4295..2c7eb9dd0 100644 --- a/net/netlink.c +++ b/net/netlink.c @@ -68,7 +68,7 @@ int netlink_donothing(int minor, struct sk_buff *skb) static unsigned int netlink_poll(struct file *file, poll_table * wait) { unsigned int mask; - unsigned int minor = MINOR(file->f_inode->i_rdev); + unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); poll_wait(&read_space_wait[minor], wait); mask = POLLOUT | POLLWRNORM; diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index 22e1afbee..dd80a211b 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -1,5 +1,5 @@ /* - * NET/ROM release 006 + * NET/ROM release 007 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -28,6 +28,8 @@ * Alan(GW4PTS) Started POSIXisms * NET/ROM 006 Alan(GW4PTS) Brought in line with the ANK changes * Jonathan(G4KLX) Removed hdrincl. + * NET/ROM 007 Jonathan(G4KLX) New timer architecture. + * Impmented Idle timer. */ #include <linux/config.h> @@ -122,8 +124,7 @@ static void nr_remove_socket(struct sock *sk) struct sock *s; unsigned long flags; - save_flags(flags); - cli(); + save_flags(flags); cli(); if ((s = nr_list) == sk) { nr_list = s->next; @@ -152,15 +153,8 @@ static void nr_kill_by_device(struct device *dev) struct sock *s; for (s = nr_list; s != NULL; s = s->next) { - if (s->protinfo.nr->device == dev) { - s->protinfo.nr->state = NR_STATE_0; - s->protinfo.nr->device = NULL; - s->state = TCP_CLOSE; - s->err = ENETUNREACH; - s->shutdown |= SEND_SHUTDOWN; - s->state_change(s); - s->dead = 1; - } + if (s->protinfo.nr->device == dev) + nr_disconnect(s, ENETUNREACH); } } @@ -187,8 +181,7 @@ static void nr_insert_socket(struct sock *sk) { unsigned long flags; - save_flags(flags); - cli(); + save_flags(flags); cli(); sk->next = nr_list; nr_list = sk; @@ -289,10 +282,13 @@ void nr_destroy_socket(struct sock *sk) /* Not static as it's used by the timer struct sk_buff *skb; unsigned long flags; - save_flags(flags); - cli(); + save_flags(flags); cli(); - del_timer(&sk->timer); + nr_stop_heartbeat(sk); + nr_stop_t1timer(sk); + nr_stop_t2timer(sk); + nr_stop_t4timer(sk); + nr_stop_idletimer(sk); nr_remove_socket(sk); nr_clear_queues(sk); /* Flush the queues */ @@ -300,7 +296,7 @@ void nr_destroy_socket(struct sock *sk) /* Not static as it's used by the timer while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) { if (skb->sk != sk) { /* A pending connection */ skb->sk->dead = 1; /* Queue the unaccepted socket for death */ - nr_set_timer(skb->sk); + nr_start_heartbeat(skb->sk); skb->sk->protinfo.nr->state = NR_STATE_0; } @@ -345,13 +341,13 @@ static int nr_setsockopt(struct socket *sock, int level, int optname, case NETROM_T1: if (opt < 1) return -EINVAL; - sk->protinfo.nr->t1 = opt * NR_SLOWHZ; + sk->protinfo.nr->t1 = opt * HZ; return 0; case NETROM_T2: if (opt < 1) return -EINVAL; - sk->protinfo.nr->t2 = opt * NR_SLOWHZ; + sk->protinfo.nr->t2 = opt * HZ; return 0; case NETROM_N2: @@ -363,13 +359,13 @@ static int nr_setsockopt(struct socket *sock, int level, int optname, case NETROM_T4: if (opt < 1) return -EINVAL; - sk->protinfo.nr->t4 = opt * NR_SLOWHZ; + sk->protinfo.nr->t4 = opt * HZ; return 0; case NETROM_IDLE: - if (opt < 1) + if (opt < 0) return -EINVAL; - sk->protinfo.nr->idle = opt * 60 * NR_SLOWHZ; + sk->protinfo.nr->idle = opt * 60 * HZ; return 0; default: @@ -392,11 +388,11 @@ static int nr_getsockopt(struct socket *sock, int level, int optname, switch (optname) { case NETROM_T1: - val = (sk->protinfo.nr->t1 * 2) / NR_SLOWHZ; + val = sk->protinfo.nr->t1 / HZ; break; case NETROM_T2: - val = sk->protinfo.nr->t2 / NR_SLOWHZ; + val = sk->protinfo.nr->t2 / HZ; break; case NETROM_N2: @@ -404,11 +400,11 @@ static int nr_getsockopt(struct socket *sock, int level, int optname, break; case NETROM_T4: - val = sk->protinfo.nr->t4 / NR_SLOWHZ; + val = sk->protinfo.nr->t4 / HZ; break; case NETROM_IDLE: - val = sk->protinfo.nr->idle / (NR_SLOWHZ * 60); + val = sk->protinfo.nr->idle / (60 * HZ); break; default: @@ -463,6 +459,11 @@ static int nr_create(struct socket *sock, int protocol) skb_queue_head_init(&nr->reseq_queue); skb_queue_head_init(&nr->frag_queue); + init_timer(&nr->t1timer); + init_timer(&nr->t2timer); + init_timer(&nr->t4timer); + init_timer(&nr->idletimer); + nr->t1 = sysctl_netrom_transport_timeout; nr->t2 = sysctl_netrom_transport_acknowledge_delay; nr->n2 = sysctl_netrom_transport_maximum_tries; @@ -507,6 +508,11 @@ static struct sock *nr_make_new(struct sock *osk) skb_queue_head_init(&nr->reseq_queue); skb_queue_head_init(&nr->frag_queue); + init_timer(&nr->t1timer); + init_timer(&nr->t2timer); + init_timer(&nr->t4timer); + init_timer(&nr->idletimer); + nr->t1 = osk->protinfo.nr->t1; nr->t2 = osk->protinfo.nr->t2; nr->n2 = osk->protinfo.nr->n2; @@ -539,39 +545,20 @@ static int nr_release(struct socket *sock, struct socket *peer) switch (sk->protinfo.nr->state) { case NR_STATE_0: - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; + case NR_STATE_2: + nr_disconnect(sk, 0); nr_destroy_socket(sk); break; case NR_STATE_1: - sk->protinfo.nr->state = NR_STATE_0; - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; - nr_destroy_socket(sk); - break; - - case NR_STATE_2: - nr_write_internal(sk, NR_DISCACK); - sk->protinfo.nr->state = NR_STATE_0; - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; - nr_destroy_socket(sk); - break; - case NR_STATE_3: nr_clear_queues(sk); sk->protinfo.nr->n2count = 0; nr_write_internal(sk, NR_DISCREQ); - sk->protinfo.nr->t1timer = sk->protinfo.nr->t1; - sk->protinfo.nr->t2timer = 0; - sk->protinfo.nr->t4timer = 0; + nr_start_t1timer(sk); + nr_stop_t2timer(sk); + nr_stop_t4timer(sk); + nr_stop_idletimer(sk); sk->protinfo.nr->state = NR_STATE_2; sk->state = TCP_CLOSE; sk->shutdown |= SEND_SHUTDOWN; @@ -704,9 +691,12 @@ static int nr_connect(struct socket *sock, struct sockaddr *uaddr, /* Move to connecting socket, start sending Connect Requests */ sock->state = SS_CONNECTING; sk->state = TCP_SYN_SENT; + nr_establish_data_link(sk); + sk->protinfo.nr->state = NR_STATE_1; - nr_set_timer(sk); + + nr_start_heartbeat(sk); /* Now the loop */ if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) @@ -838,13 +828,13 @@ int nr_rx_frame(struct sk_buff *skb, struct device *dev) circuit_index = skb->data[15]; circuit_id = skb->data[16]; - frametype = skb->data[19]; + frametype = skb->data[19] & 0x0F; #ifdef CONFIG_INET /* * Check for an incoming IP over NET/ROM frame. */ - if ((frametype & 0x0F) == NR_PROTOEXT && circuit_index == NR_PROTO_IP && circuit_id == NR_PROTO_IP) { + if (frametype == NR_PROTOEXT && circuit_index == NR_PROTO_IP && circuit_id == NR_PROTO_IP) { skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN); skb->h.raw = skb->data; @@ -856,11 +846,11 @@ int nr_rx_frame(struct sk_buff *skb, struct device *dev) * Find an existing socket connection, based on circuit ID, if it's * a Connect Request base it on their circuit ID. */ - if (((frametype & 0x0F) != NR_CONNREQ && (sk = nr_find_socket(circuit_index, circuit_id)) != NULL) || - ((frametype & 0x0F) == NR_CONNREQ && (sk = nr_find_peer(circuit_index, circuit_id)) != NULL)) { + if ((frametype != NR_CONNREQ && (sk = nr_find_socket(circuit_index, circuit_id)) != NULL) || + (frametype == NR_CONNREQ && (sk = nr_find_peer(circuit_index, circuit_id)) != NULL)) { skb->h.raw = skb->data; - if ((frametype & 0x0F) == NR_CONNACK && skb->len == 22) + if (frametype == NR_CONNACK && skb->len == 22) sk->protinfo.nr->bpqext = 1; else sk->protinfo.nr->bpqext = 0; @@ -868,8 +858,16 @@ int nr_rx_frame(struct sk_buff *skb, struct device *dev) return nr_process_rx_frame(sk, skb); } - if ((frametype & 0x0F) != NR_CONNREQ) - return 0; + switch (frametype) { + case NR_CONNREQ: + break; + case NR_DISCREQ: + case NR_DISCACK: + return 0; + default: + nr_transmit_dm(skb); + return 0; + } sk = nr_find_listener(dest); @@ -905,8 +903,8 @@ int nr_rx_frame(struct sk_buff *skb, struct device *dev) /* L4 timeout negotiation */ if (skb->len == 37) { timeout = skb->data[36] * 256 + skb->data[35]; - if (timeout * NR_SLOWHZ < make->protinfo.nr->t1) - make->protinfo.nr->t1 = timeout * NR_SLOWHZ; + if (timeout * HZ < make->protinfo.nr->t1) + make->protinfo.nr->t1 = timeout * HZ; make->protinfo.nr->bpqext = 1; } else { make->protinfo.nr->bpqext = 0; @@ -927,7 +925,8 @@ int nr_rx_frame(struct sk_buff *skb, struct device *dev) skb_queue_head(&sk->receive_queue, skb); - nr_set_timer(make); + nr_start_heartbeat(make); + nr_start_idletimer(make); if (!sk->dead) sk->data_ready(sk, skb->len); @@ -973,6 +972,7 @@ static int nr_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct s sax.sax25_family = AF_NETROM; sax.sax25_call = sk->protinfo.nr->dest_addr; } + SOCK_DEBUG(sk, "NET/ROM: sendto: Addresses built.\n"); /* Build a packet */ @@ -1074,37 +1074,35 @@ static int nr_shutdown(struct socket *sk, int how) static int nr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; - int err; - long amount = 0; switch (cmd) { - case TIOCOUTQ: - if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int))) != 0) - return err; + case TIOCOUTQ: { + long amount; amount = sk->sndbuf - atomic_read(&sk->wmem_alloc); if (amount < 0) amount = 0; - put_user(amount, (int *)arg); + if (put_user(amount, (int *)arg)) + return -EFAULT; return 0; + } case TIOCINQ: { struct sk_buff *skb; + long amount = 0L; /* These two are safe on a single CPU system as only user tasks fiddle here */ if ((skb = skb_peek(&sk->receive_queue)) != NULL) - amount = skb->len - 20; - if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int))) != 0) - return err; - put_user(amount, (int *)arg); + amount = skb->len; + if (put_user(amount, (int *)arg)) + return -EFAULT; return 0; } case SIOCGSTAMP: if (sk != NULL) { - if (sk->stamp.tv_sec==0) + if (sk->stamp.tv_sec == 0) return -ENOENT; - if ((err = verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval))) != 0) - return err; - copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)); + if (copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval))) + return -EFAULT; return 0; } return -EINVAL; @@ -1146,7 +1144,7 @@ static int nr_get_info(char *buffer, char **start, off_t offset, int length, int cli(); - len += sprintf(buffer, "user_addr dest_node src_node dev my your st vs vr va t1 t2 n2 wnd Snd-Q Rcv-Q\n"); + len += sprintf(buffer, "user_addr dest_node src_node dev my your st vs vr va t1 t2 t4 idle n2 wnd Snd-Q Rcv-Q\n"); for (s = nr_list; s != NULL; s = s->next) { if ((dev = s->protinfo.nr->device) == NULL) @@ -1158,20 +1156,30 @@ static int nr_get_info(char *buffer, char **start, off_t offset, int length, int ax2asc(&s->protinfo.nr->user_addr)); len += sprintf(buffer + len, "%-9s ", ax2asc(&s->protinfo.nr->dest_addr)); - len += sprintf(buffer + len, "%-9s %-3s %02X/%02X %02X/%02X %2d %3d %3d %3d %3d/%03d %2d/%02d %2d/%02d %3d %5d %5d\n", + len += sprintf(buffer + len, "%-9s %-3s %02X/%02X %02X/%02X %2d %3d %3d %3d %3lu/%03lu %2lu/%02lu %3lu/%03lu %3lu/%03lu %2d/%02d %3d %5d %5d\n", ax2asc(&s->protinfo.nr->source_addr), - devname, s->protinfo.nr->my_index, s->protinfo.nr->my_id, - s->protinfo.nr->your_index, s->protinfo.nr->your_id, + devname, + s->protinfo.nr->my_index, + s->protinfo.nr->my_id, + s->protinfo.nr->your_index, + s->protinfo.nr->your_id, s->protinfo.nr->state, - s->protinfo.nr->vs, s->protinfo.nr->vr, s->protinfo.nr->va, - s->protinfo.nr->t1timer / NR_SLOWHZ, - s->protinfo.nr->t1 / NR_SLOWHZ, - s->protinfo.nr->t2timer / NR_SLOWHZ, - s->protinfo.nr->t2 / NR_SLOWHZ, + s->protinfo.nr->vs, + s->protinfo.nr->vr, + s->protinfo.nr->va, + ax25_display_timer(&s->protinfo.nr->t1timer) / HZ, + s->protinfo.nr->t1 / HZ, + ax25_display_timer(&s->protinfo.nr->t2timer) / HZ, + s->protinfo.nr->t2 / HZ, + ax25_display_timer(&s->protinfo.nr->t4timer) / HZ, + s->protinfo.nr->t4 / HZ, + ax25_display_timer(&s->protinfo.nr->idletimer) / (60 * HZ), + s->protinfo.nr->idle / (60 * HZ), s->protinfo.nr->n2count, s->protinfo.nr->n2, s->protinfo.nr->window, - atomic_read(&s->wmem_alloc), atomic_read(&s->rmem_alloc)); + atomic_read(&s->wmem_alloc), + atomic_read(&s->rmem_alloc)); pos = begin + len; @@ -1269,7 +1277,7 @@ __initfunc(void nr_proto_init(struct net_proto *pro)) sock_register(&nr_family_ops); register_netdevice_notifier(&nr_dev_notifier); - printk(KERN_INFO "G4KLX NET/ROM for Linux. Version 0.6 for AX25.035 Linux 2.1\n"); + printk(KERN_INFO "G4KLX NET/ROM for Linux. Version 0.7 for AX25.037 Linux 2.1\n"); ax25_protocol_register(AX25_P_NETROM, nr_route_frame); ax25_linkfail_register(nr_link_failed); diff --git a/net/netrom/nr_dev.c b/net/netrom/nr_dev.c index 2d88bec2b..f7b617dcc 100644 --- a/net/netrom/nr_dev.c +++ b/net/netrom/nr_dev.c @@ -1,5 +1,5 @@ /* - * NET/ROM release 006 + * NET/ROM release 007 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * diff --git a/net/netrom/nr_in.c b/net/netrom/nr_in.c index c13e92666..a0d3148c2 100644 --- a/net/netrom/nr_in.c +++ b/net/netrom/nr_in.c @@ -1,5 +1,5 @@ /* - * NET/ROM release 006 + * NET/ROM release 007 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -21,6 +21,7 @@ * Darryl(G7LED) Added missing INFO with NAK case, optimized * INFOACK handling, removed reconnect on error. * NET/ROM 006 Jonathan(G4KLX) Hdrincl removal changes. + * NET/ROM 007 Jonathan(G4KLX) New timer architecture. */ #include <linux/config.h> @@ -54,6 +55,8 @@ static int nr_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more) skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN); + nr_start_idletimer(sk); + if (more) { sk->protinfo.nr->fraglen += skb->len; skb_queue_tail(&sk->protinfo.nr->frag_queue, skb); @@ -90,11 +93,10 @@ static int nr_state1_machine(struct sock *sk, struct sk_buff *skb, int frametype switch (frametype) { case NR_CONNACK: + nr_stop_t1timer(sk); + nr_start_idletimer(sk); sk->protinfo.nr->your_index = skb->data[17]; sk->protinfo.nr->your_id = skb->data[18]; - sk->protinfo.nr->t1timer = 0; - sk->protinfo.nr->t2timer = 0; - sk->protinfo.nr->t4timer = 0; sk->protinfo.nr->vs = 0; sk->protinfo.nr->va = 0; sk->protinfo.nr->vr = 0; @@ -103,20 +105,12 @@ static int nr_state1_machine(struct sock *sk, struct sk_buff *skb, int frametype sk->protinfo.nr->n2count = 0; sk->protinfo.nr->window = skb->data[20]; sk->state = TCP_ESTABLISHED; - /* For WAIT_SABM connections we will produce an accept ready socket here */ if (!sk->dead) sk->state_change(sk); break; case NR_CONNACK | NR_CHOKE_FLAG: - nr_clear_queues(sk); - sk->protinfo.nr->state = NR_STATE_0; - sk->state = TCP_CLOSE; - sk->err = ECONNREFUSED; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + nr_disconnect(sk, ECONNREFUSED); break; default: @@ -139,13 +133,7 @@ static int nr_state2_machine(struct sock *sk, struct sk_buff *skb, int frametype nr_write_internal(sk, NR_DISCACK); case NR_DISCACK: - sk->protinfo.nr->state = NR_STATE_0; - sk->state = TCP_CLOSE; - sk->err = 0; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + nr_disconnect(sk, 0); break; default: @@ -178,26 +166,12 @@ static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype break; case NR_DISCREQ: - nr_clear_queues(sk); nr_write_internal(sk, NR_DISCACK); - sk->protinfo.nr->state = NR_STATE_0; - sk->state = TCP_CLOSE; - sk->err = 0; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + nr_disconnect(sk, 0); break; case NR_DISCACK: - nr_clear_queues(sk); - sk->protinfo.nr->state = NR_STATE_0; - sk->state = TCP_CLOSE; - sk->err = ECONNRESET; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + nr_disconnect(sk, ECONNRESET); break; case NR_INFOACK: @@ -206,10 +180,10 @@ static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype case NR_INFOACK | NR_NAK_FLAG | NR_CHOKE_FLAG: if (frametype & NR_CHOKE_FLAG) { sk->protinfo.nr->condition |= NR_COND_PEER_RX_BUSY; - sk->protinfo.nr->t4timer = sk->protinfo.nr->t4; + nr_start_t4timer(sk); } else { sk->protinfo.nr->condition &= ~NR_COND_PEER_RX_BUSY; - sk->protinfo.nr->t4timer = 0; + nr_stop_t4timer(sk); } if (!nr_validate_nr(sk, nr)) { break; @@ -236,10 +210,10 @@ static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype case NR_INFO | NR_NAK_FLAG | NR_CHOKE_FLAG | NR_MORE_FLAG: if (frametype & NR_CHOKE_FLAG) { sk->protinfo.nr->condition |= NR_COND_PEER_RX_BUSY; - sk->protinfo.nr->t4timer = sk->protinfo.nr->t4; + nr_start_t4timer(sk); } else { sk->protinfo.nr->condition &= ~NR_COND_PEER_RX_BUSY; - sk->protinfo.nr->t4timer = 0; + nr_stop_t4timer(sk); } if (nr_validate_nr(sk, nr)) { if (frametype & NR_NAK_FLAG) { @@ -286,8 +260,8 @@ static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype nr_enquiry_response(sk); } else { if (!(sk->protinfo.nr->condition & NR_COND_ACK_PENDING)) { - sk->protinfo.nr->t2timer = sk->protinfo.nr->t2; sk->protinfo.nr->condition |= NR_COND_ACK_PENDING; + nr_start_t2timer(sk); } } break; @@ -307,8 +281,6 @@ int nr_process_rx_frame(struct sock *sk, struct sk_buff *skb) if (sk->protinfo.nr->state == NR_STATE_0) return 0; - del_timer(&sk->timer); - frametype = skb->data[19]; switch (sk->protinfo.nr->state) { @@ -323,7 +295,7 @@ int nr_process_rx_frame(struct sock *sk, struct sk_buff *skb) break; } - nr_set_timer(sk); + nr_kick(sk); return queued; } diff --git a/net/netrom/nr_out.c b/net/netrom/nr_out.c index 7c053b482..4c3eb61d8 100644 --- a/net/netrom/nr_out.c +++ b/net/netrom/nr_out.c @@ -1,5 +1,5 @@ /* - * NET/ROM release 006 + * NET/ROM release 007 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -13,6 +13,7 @@ * NET/ROM 001 Jonathan(G4KLX) Cloned from ax25_out.c * NET/ROM 003 Jonathan(G4KLX) Added NET/ROM fragmentation. * Darryl(G7LED) Fixed NAK, to give out correct reponse. + * NET/ROM 007 Jonathan(G4KLX) New timer architecture. */ #include <linux/config.h> @@ -83,8 +84,7 @@ void nr_output(struct sock *sk, struct sk_buff *skb) skb_queue_tail(&sk->write_queue, skb); /* Throw it on the queue */ } - if (sk->protinfo.nr->state == NR_STATE_3) - nr_kick(sk); + nr_kick(sk); } /* @@ -102,6 +102,8 @@ static void nr_send_iframe(struct sock *sk, struct sk_buff *skb) if (sk->protinfo.nr->condition & NR_COND_OWN_RX_BUSY) skb->data[4] |= NR_CHOKE_FLAG; + nr_start_idletimer(sk); + nr_transmit_buffer(sk, skb); } @@ -125,7 +127,8 @@ void nr_send_nak_frame(struct sock *sk) sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING; sk->protinfo.nr->vl = sk->protinfo.nr->vr; - sk->protinfo.nr->t1timer = 0; + + nr_stop_t1timer(sk); } void nr_kick(struct sock *sk) @@ -133,57 +136,60 @@ void nr_kick(struct sock *sk) struct sk_buff *skb, *skbn; unsigned short start, end; - del_timer(&sk->timer); + if (sk->protinfo.nr->state != NR_STATE_3) + return; + + if (sk->protinfo.nr->condition & NR_COND_PEER_RX_BUSY) + return; + + if (skb_peek(&sk->write_queue) == NULL) + return; start = (skb_peek(&sk->protinfo.nr->ack_queue) == NULL) ? sk->protinfo.nr->va : sk->protinfo.nr->vs; end = (sk->protinfo.nr->va + sk->protinfo.nr->window) % NR_MODULUS; - if (!(sk->protinfo.nr->condition & NR_COND_PEER_RX_BUSY) && - start != end && - skb_peek(&sk->write_queue) != NULL) { - - sk->protinfo.nr->vs = start; + if (start == end) + return; - /* - * Transmit data until either we're out of data to send or - * the window is full. - */ + sk->protinfo.nr->vs = start; - /* - * Dequeue the frame and copy it. - */ - skb = skb_dequeue(&sk->write_queue); + /* + * Transmit data until either we're out of data to send or + * the window is full. + */ - do { - if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { - skb_queue_head(&sk->write_queue, skb); - break; - } + /* + * Dequeue the frame and copy it. + */ + skb = skb_dequeue(&sk->write_queue); - skb_set_owner_w(skbn, sk); + do { + if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { + skb_queue_head(&sk->write_queue, skb); + break; + } - /* - * Transmit the frame copy. - */ - nr_send_iframe(sk, skbn); + skb_set_owner_w(skbn, sk); - sk->protinfo.nr->vs = (sk->protinfo.nr->vs + 1) % NR_MODULUS; + /* + * Transmit the frame copy. + */ + nr_send_iframe(sk, skbn); - /* - * Requeue the original data frame. - */ - skb_queue_tail(&sk->protinfo.nr->ack_queue, skb); + sk->protinfo.nr->vs = (sk->protinfo.nr->vs + 1) % NR_MODULUS; - } while (sk->protinfo.nr->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL); + /* + * Requeue the original data frame. + */ + skb_queue_tail(&sk->protinfo.nr->ack_queue, skb); - sk->protinfo.nr->vl = sk->protinfo.nr->vr; - sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING; + } while (sk->protinfo.nr->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL); - if (sk->protinfo.nr->t1timer == 0) - sk->protinfo.nr->t1timer = sk->protinfo.nr->t1; - } + sk->protinfo.nr->vl = sk->protinfo.nr->vr; + sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING; - nr_set_timer(sk); + if (!nr_t1timer_running(sk)) + nr_start_t1timer(sk); } void nr_transmit_buffer(struct sock *sk, struct sk_buff *skb) @@ -211,13 +217,7 @@ void nr_transmit_buffer(struct sock *sk, struct sk_buff *skb) if (!nr_route_frame(skb, NULL)) { kfree_skb(skb, FREE_WRITE); - - sk->state = TCP_CLOSE; - sk->err = ENETUNREACH; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + nr_disconnect(sk, ENETUNREACH); } } @@ -233,8 +233,10 @@ void nr_establish_data_link(struct sock *sk) nr_write_internal(sk, NR_CONNREQ); - sk->protinfo.nr->t2timer = 0; - sk->protinfo.nr->t1timer = sk->protinfo.nr->t1; + nr_stop_t2timer(sk); + nr_stop_t4timer(sk); + nr_stop_idletimer(sk); + nr_start_t1timer(sk); } /* @@ -261,12 +263,12 @@ void nr_check_iframes_acked(struct sock *sk, unsigned short nr) { if (sk->protinfo.nr->vs == nr) { nr_frames_acked(sk, nr); - sk->protinfo.nr->t1timer = 0; + nr_stop_t1timer(sk); sk->protinfo.nr->n2count = 0; } else { if (sk->protinfo.nr->va != nr) { nr_frames_acked(sk, nr); - sk->protinfo.nr->t1timer = sk->protinfo.nr->t1; + nr_start_t1timer(sk); } } } diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c index 41399a53c..ffbb240c4 100644 --- a/net/netrom/nr_route.c +++ b/net/netrom/nr_route.c @@ -1,5 +1,5 @@ /* - * NET/ROM release 006 + * NET/ROM release 007 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -93,6 +93,7 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 nr_neigh->callsign = *ax25; nr_neigh->digipeat = NULL; + nr_neigh->ax25 = NULL; nr_neigh->dev = dev; nr_neigh->quality = sysctl_netrom_default_path_quality; nr_neigh->locked = 0; @@ -372,6 +373,7 @@ static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct dev nr_neigh->callsign = *callsign; nr_neigh->digipeat = NULL; + nr_neigh->ax25 = NULL; nr_neigh->dev = dev; nr_neigh->quality = quality; nr_neigh->locked = 1; @@ -582,7 +584,7 @@ static ax25_digi *nr_call_to_digi(int ndigis, ax25_address *digipeaters) } ax25_digi.ndigi = ndigis; - ax25_digi.lastrepeat = 0; + ax25_digi.lastrepeat = -1; return &ax25_digi; } @@ -594,14 +596,12 @@ int nr_rt_ioctl(unsigned int cmd, void *arg) { struct nr_route_struct nr_route; struct device *dev; - int err; switch (cmd) { case SIOCADDRT: - if ((err = verify_area(VERIFY_READ, arg, sizeof(struct nr_route_struct))) != 0) - return err; - copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct)); + if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct))) + return -EFAULT; if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL) return -EINVAL; if (nr_route.ndigis < 0 || nr_route.ndigis > AX25_MAX_DIGIS) @@ -623,9 +623,8 @@ int nr_rt_ioctl(unsigned int cmd, void *arg) } case SIOCDELRT: - if ((err = verify_area(VERIFY_READ, arg, sizeof(struct nr_route_struct))) != 0) - return err; - copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct)); + if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct))) + return -EFAULT; if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL) return -EINVAL; switch (nr_route.type) { @@ -653,17 +652,19 @@ int nr_rt_ioctl(unsigned int cmd, void *arg) * A level 2 link has timed out, therefore it appears to be a poor link, * then don't use that neighbour until it is reset. */ -void nr_link_failed(ax25_address *callsign, struct device *dev) +void nr_link_failed(ax25_cb *ax25, int reason) { struct nr_neigh *nr_neigh; struct nr_node *nr_node; for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) - if (ax25cmp(&nr_neigh->callsign, callsign) == 0 && nr_neigh->dev == dev) + if (nr_neigh->ax25 == ax25) break; if (nr_neigh == NULL) return; + nr_neigh->ax25 = NULL; + if (++nr_neigh->failed < sysctl_netrom_link_fails_count) return; for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next) @@ -724,7 +725,9 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25) dptr = skb_push(skb, 1); *dptr = AX25_P_NETROM; - return ax25_send_frame(skb, 256, (ax25_address *)dev->dev_addr, &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev); + nr_neigh->ax25 = ax25_send_frame(skb, 256, (ax25_address *)dev->dev_addr, &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev); + + return (nr_neigh->ax25 != NULL); } int nr_nodes_get_info(char *buffer, char **start, off_t offset, diff --git a/net/netrom/nr_subr.c b/net/netrom/nr_subr.c index 5eae25279..d31141876 100644 --- a/net/netrom/nr_subr.c +++ b/net/netrom/nr_subr.c @@ -1,5 +1,5 @@ /* - * NET/ROM release 006 + * NET/ROM release 007 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -12,6 +12,7 @@ * History * NET/ROM 001 Jonathan(G4KLX) Cloned from ax25_subr.c * NET/ROM 003 Jonathan(G4KLX) Added G8BPQ NET/ROM extensions. + * NET/ROM 007 Jonathan(G4KLX) New timer architecture. */ #include <linux/config.h> @@ -172,7 +173,7 @@ void nr_write_internal(struct sock *sk, int frametype) switch (frametype & 0x0F) { case NR_CONNREQ: - timeout = sk->protinfo.nr->t1 / NR_SLOWHZ; + timeout = sk->protinfo.nr->t1 / HZ; *dptr++ = sk->protinfo.nr->my_index; *dptr++ = sk->protinfo.nr->my_id; *dptr++ = 0; @@ -268,4 +269,25 @@ void nr_transmit_dm(struct sk_buff *skb) kfree_skb(skbn, FREE_WRITE); } +void nr_disconnect(struct sock *sk, int reason) +{ + nr_stop_t1timer(sk); + nr_stop_t2timer(sk); + nr_stop_t4timer(sk); + nr_stop_idletimer(sk); + + nr_clear_queues(sk); + + sk->protinfo.nr->state = NR_STATE_0; + + sk->state = TCP_CLOSE; + sk->err = reason; + sk->shutdown |= SEND_SHUTDOWN; + + if (!sk->dead) + sk->state_change(sk); + + sk->dead = 1; +} + #endif diff --git a/net/netrom/nr_timer.c b/net/netrom/nr_timer.c index cc96f26dd..b3fbd012e 100644 --- a/net/netrom/nr_timer.c +++ b/net/netrom/nr_timer.c @@ -1,5 +1,5 @@ /* - * NET/ROM release 006 + * NET/ROM release 007 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -11,6 +11,8 @@ * * History * NET/ROM 001 Jonathan(G4KLX) Cloned from ax25_timer.c + * NET/ROM 007 Jonathan(G4KLX) New timer architecture. + * Implemented idle timer. */ #include <linux/config.h> @@ -37,42 +39,110 @@ #include <linux/interrupt.h> #include <net/netrom.h> -static void nr_timer(unsigned long); +static void nr_heartbeat_expiry(unsigned long); +static void nr_t1timer_expiry(unsigned long); +static void nr_t2timer_expiry(unsigned long); +static void nr_t4timer_expiry(unsigned long); +static void nr_idletimer_expiry(unsigned long); -/* - * Linux set timer - */ -void nr_set_timer(struct sock *sk) +void nr_start_t1timer(struct sock *sk) +{ + del_timer(&sk->protinfo.nr->t1timer); + + sk->protinfo.nr->t1timer.data = (unsigned long)sk; + sk->protinfo.nr->t1timer.function = &nr_t1timer_expiry; + sk->protinfo.nr->t1timer.expires = jiffies + sk->protinfo.nr->t1; + + add_timer(&sk->protinfo.nr->t1timer); +} + +void nr_start_t2timer(struct sock *sk) +{ + del_timer(&sk->protinfo.nr->t2timer); + + sk->protinfo.nr->t2timer.data = (unsigned long)sk; + sk->protinfo.nr->t2timer.function = &nr_t2timer_expiry; + sk->protinfo.nr->t2timer.expires = jiffies + sk->protinfo.nr->t2; + + add_timer(&sk->protinfo.nr->t2timer); +} + +void nr_start_t4timer(struct sock *sk) +{ + del_timer(&sk->protinfo.nr->t4timer); + + sk->protinfo.nr->t4timer.data = (unsigned long)sk; + sk->protinfo.nr->t4timer.function = &nr_t4timer_expiry; + sk->protinfo.nr->t4timer.expires = jiffies + sk->protinfo.nr->t4; + + add_timer(&sk->protinfo.nr->t4timer); +} + +void nr_start_idletimer(struct sock *sk) { - unsigned long flags; + del_timer(&sk->protinfo.nr->idletimer); + + if (sk->protinfo.nr->idle > 0) { + sk->protinfo.nr->idletimer.data = (unsigned long)sk; + sk->protinfo.nr->idletimer.function = &nr_idletimer_expiry; + sk->protinfo.nr->idletimer.expires = jiffies + sk->protinfo.nr->idle; + + add_timer(&sk->protinfo.nr->idletimer); + } +} - save_flags(flags); cli(); +void nr_start_heartbeat(struct sock *sk) +{ del_timer(&sk->timer); - restore_flags(flags); sk->timer.data = (unsigned long)sk; - sk->timer.function = &nr_timer; - sk->timer.expires = jiffies + (HZ / 10); + sk->timer.function = &nr_heartbeat_expiry; + sk->timer.expires = jiffies + 5 * HZ; add_timer(&sk->timer); } -/* - * NET/ROM TIMER - * - * This routine is called every 100ms. Decrement timer by this - * amount - if expired then process the event. - */ -static void nr_timer(unsigned long param) +void nr_stop_t1timer(struct sock *sk) +{ + del_timer(&sk->protinfo.nr->t1timer); +} + +void nr_stop_t2timer(struct sock *sk) +{ + del_timer(&sk->protinfo.nr->t2timer); +} + +void nr_stop_t4timer(struct sock *sk) +{ + del_timer(&sk->protinfo.nr->t4timer); +} + +void nr_stop_idletimer(struct sock *sk) +{ + del_timer(&sk->protinfo.nr->idletimer); +} + +void nr_stop_heartbeat(struct sock *sk) +{ + del_timer(&sk->timer); +} + +int nr_t1timer_running(struct sock *sk) +{ + return (sk->protinfo.nr->t1timer.prev != NULL || + sk->protinfo.nr->t1timer.next != NULL); +} + +static void nr_heartbeat_expiry(unsigned long param) { struct sock *sk = (struct sock *)param; switch (sk->protinfo.nr->state) { + case NR_STATE_0: /* Magic here: If we listen() and a new link dies before it is accepted() it isn't 'dead' so doesn't get removed. */ if (sk->destroy || (sk->state == TCP_LISTEN && sk->dead)) { - del_timer(&sk->timer); nr_destroy_socket(sk); return; } @@ -90,45 +160,63 @@ static void nr_timer(unsigned long param) nr_write_internal(sk, NR_INFOACK); break; } - /* - * Check for frames to transmit. - */ - nr_kick(sk); - break; - - default: break; } - if (sk->protinfo.nr->t2timer > 0 && --sk->protinfo.nr->t2timer == 0) { - if (sk->protinfo.nr->state == NR_STATE_3) { - if (sk->protinfo.nr->condition & NR_COND_ACK_PENDING) { - sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING; - nr_enquiry_response(sk); - } - } - } + nr_start_heartbeat(sk); +} - if (sk->protinfo.nr->t4timer > 0 && --sk->protinfo.nr->t4timer == 0) { - sk->protinfo.nr->condition &= ~NR_COND_PEER_RX_BUSY; - } +static void nr_t2timer_expiry(unsigned long param) +{ + struct sock *sk = (struct sock *)param; - if (sk->protinfo.nr->t1timer == 0 || --sk->protinfo.nr->t1timer > 0) { - nr_set_timer(sk); - return; + if (sk->protinfo.nr->condition & NR_COND_ACK_PENDING) { + sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING; + nr_enquiry_response(sk); } +} + +static void nr_t4timer_expiry(unsigned long param) +{ + struct sock *sk = (struct sock *)param; + + sk->protinfo.nr->condition &= ~NR_COND_PEER_RX_BUSY; +} + +static void nr_idletimer_expiry(unsigned long param) +{ + struct sock *sk = (struct sock *)param; + + nr_clear_queues(sk); + + sk->protinfo.nr->n2count = 0; + nr_write_internal(sk, NR_DISCREQ); + sk->protinfo.nr->state = NR_STATE_2; + + nr_start_t1timer(sk); + nr_stop_t2timer(sk); + nr_stop_t4timer(sk); + + sk->state = TCP_CLOSE; + sk->err = 0; + sk->shutdown |= SEND_SHUTDOWN; + + if (!sk->dead) + sk->state_change(sk); + + sk->dead = 1; +} + +static void nr_t1timer_expiry(unsigned long param) +{ + struct sock *sk = (struct sock *)param; switch (sk->protinfo.nr->state) { + case NR_STATE_1: if (sk->protinfo.nr->n2count == sk->protinfo.nr->n2) { - nr_clear_queues(sk); - sk->protinfo.nr->state = NR_STATE_0; - sk->state = TCP_CLOSE; - sk->err = ETIMEDOUT; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + nr_disconnect(sk, ETIMEDOUT); + return; } else { sk->protinfo.nr->n2count++; nr_write_internal(sk, NR_CONNREQ); @@ -137,14 +225,8 @@ static void nr_timer(unsigned long param) case NR_STATE_2: if (sk->protinfo.nr->n2count == sk->protinfo.nr->n2) { - nr_clear_queues(sk); - sk->protinfo.nr->state = NR_STATE_0; - sk->state = TCP_CLOSE; - sk->err = ETIMEDOUT; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + nr_disconnect(sk, ETIMEDOUT); + return; } else { sk->protinfo.nr->n2count++; nr_write_internal(sk, NR_DISCREQ); @@ -153,14 +235,8 @@ static void nr_timer(unsigned long param) case NR_STATE_3: if (sk->protinfo.nr->n2count == sk->protinfo.nr->n2) { - nr_clear_queues(sk); - sk->protinfo.nr->state = NR_STATE_0; - sk->state = TCP_CLOSE; - sk->err = ETIMEDOUT; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + nr_disconnect(sk, ETIMEDOUT); + return; } else { sk->protinfo.nr->n2count++; nr_requeue_frames(sk); @@ -168,9 +244,7 @@ static void nr_timer(unsigned long param) break; } - sk->protinfo.nr->t1timer = sk->protinfo.nr->t1; - - nr_set_timer(sk); + nr_start_t1timer(sk); } #endif diff --git a/net/netrom/sysctl_net_netrom.c b/net/netrom/sysctl_net_netrom.c index c6a415ee6..3ce3e71f2 100644 --- a/net/netrom/sysctl_net_netrom.c +++ b/net/netrom/sysctl_net_netrom.c @@ -17,16 +17,16 @@ static int min_quality[] = {0}, max_quality[] = {255}; static int min_obs[] = {0}, max_obs[] = {255}; static int min_ttl[] = {0}, max_ttl[] = {255}; -static int min_t1[] = {5 * NR_SLOWHZ}; -static int max_t1[] = {600 * NR_SLOWHZ}; +static int min_t1[] = {5 * HZ}; +static int max_t1[] = {600 * HZ}; static int min_n2[] = {2}, max_n2[] = {127}; -static int min_t2[] = {1 * NR_SLOWHZ}; -static int max_t2[] = {60 * NR_SLOWHZ}; -static int min_t4[] = {1 * NR_SLOWHZ}; -static int max_t4[] = {1000 * NR_SLOWHZ}; +static int min_t2[] = {1 * HZ}; +static int max_t2[] = {60 * HZ}; +static int min_t4[] = {1 * HZ}; +static int max_t4[] = {1000 * HZ}; static int min_window[] = {1}, max_window[] = {127}; -static int min_idle[] = {0 * NR_SLOWHZ}; -static int max_idle[] = {65535 * NR_SLOWHZ}; +static int min_idle[] = {0 * HZ}; +static int max_idle[] = {65535 * HZ}; static int min_route[] = {0}, max_route[] = {1}; static int min_fails[] = {1}, max_fails[] = {10}; diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index 9896de9cb..134eee17a 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -1,5 +1,5 @@ /* - * ROSE release 002 + * ROSE release 003 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -17,6 +17,9 @@ * ROSE 002 Jonathan(G4KLX) Changed hdrincl to qbitincl. * Added random number facilities entry. * Variable number of ROSE devices. + * ROSE 003 Jonathan(G4KLX) New timer architecture. + * Implemented idle timer. + * Added use count to neighbour. */ #include <linux/config.h> @@ -172,8 +175,7 @@ static void rose_remove_socket(struct sock *sk) struct sock *s; unsigned long flags; - save_flags(flags); - cli(); + save_flags(flags); cli(); if ((s = rose_list) == sk) { rose_list = s->next; @@ -204,13 +206,9 @@ void rose_kill_by_neigh(struct rose_neigh *neigh) for (s = rose_list; s != NULL; s = s->next) { if (s->protinfo.rose->neighbour == neigh) { - s->protinfo.rose->state = ROSE_STATE_0; + rose_disconnect(s, ENETUNREACH, ROSE_OUT_OF_ORDER, 0); + s->protinfo.rose->neighbour->use--; s->protinfo.rose->neighbour = NULL; - s->state = TCP_CLOSE; - s->err = ENETUNREACH; - s->shutdown |= SEND_SHUTDOWN; - s->state_change(s); - s->dead = 1; } } } @@ -224,13 +222,9 @@ static void rose_kill_by_device(struct device *dev) for (s = rose_list; s != NULL; s = s->next) { if (s->protinfo.rose->device == dev) { - s->protinfo.rose->state = ROSE_STATE_0; + rose_disconnect(s, ENETUNREACH, ROSE_OUT_OF_ORDER, 0); + s->protinfo.rose->neighbour->use--; s->protinfo.rose->device = NULL; - s->state = TCP_CLOSE; - s->err = ENETUNREACH; - s->shutdown |= SEND_SHUTDOWN; - s->state_change(s); - s->dead = 1; } } } @@ -265,8 +259,7 @@ static void rose_insert_socket(struct sock *sk) { unsigned long flags; - save_flags(flags); - cli(); + save_flags(flags); cli(); sk->next = rose_list; rose_list = sk; @@ -283,8 +276,7 @@ static struct sock *rose_find_listener(rose_address *addr, ax25_address *call) unsigned long flags; struct sock *s; - save_flags(flags); - cli(); + save_flags(flags); cli(); for (s = rose_list; s != NULL; s = s->next) { if (rosecmp(&s->protinfo.rose->source_addr, addr) == 0 && ax25cmp(&s->protinfo.rose->source_call, call) == 0 && s->protinfo.rose->source_ndigis == 0 && s->state == TCP_LISTEN) { @@ -312,8 +304,7 @@ struct sock *rose_find_socket(unsigned int lci, struct rose_neigh *neigh) struct sock *s; unsigned long flags; - save_flags(flags); - cli(); + save_flags(flags); cli(); for (s = rose_list; s != NULL; s = s->next) { if (s->protinfo.rose->lci == lci && s->protinfo.rose->neighbour == neigh) { @@ -371,10 +362,11 @@ void rose_destroy_socket(struct sock *sk) /* Not static as it's used by the time struct sk_buff *skb; unsigned long flags; - save_flags(flags); - cli(); + save_flags(flags); cli(); - del_timer(&sk->timer); + rose_stop_heartbeat(sk); + rose_stop_idletimer(sk); + rose_stop_timer(sk); rose_remove_socket(sk); rose_clear_queues(sk); /* Flush the queues */ @@ -382,7 +374,7 @@ void rose_destroy_socket(struct sock *sk) /* Not static as it's used by the time while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) { if (skb->sk != sk) { /* A pending connection */ skb->sk->dead = 1; /* Queue the unaccepted socket for death */ - rose_set_timer(skb->sk); + rose_start_heartbeat(skb->sk); skb->sk->protinfo.rose->state = ROSE_STATE_0; } @@ -424,34 +416,38 @@ static int rose_setsockopt(struct socket *sock, int level, int optname, return -EFAULT; switch (optname) { + case ROSE_DEFER: + sk->protinfo.rose->defer = opt ? 1 : 0; + return 0; + case ROSE_T1: if (opt < 1) return -EINVAL; - sk->protinfo.rose->t1 = opt * ROSE_SLOWHZ; + sk->protinfo.rose->t1 = opt * HZ; return 0; case ROSE_T2: if (opt < 1) return -EINVAL; - sk->protinfo.rose->t2 = opt * ROSE_SLOWHZ; + sk->protinfo.rose->t2 = opt * HZ; return 0; case ROSE_T3: if (opt < 1) return -EINVAL; - sk->protinfo.rose->t3 = opt * ROSE_SLOWHZ; + sk->protinfo.rose->t3 = opt * HZ; return 0; case ROSE_HOLDBACK: if (opt < 1) return -EINVAL; - sk->protinfo.rose->hb = opt * ROSE_SLOWHZ; + sk->protinfo.rose->hb = opt * HZ; return 0; case ROSE_IDLE: - if (opt < 1) + if (opt < 0) return -EINVAL; - sk->protinfo.rose->idle = opt * 60 * ROSE_SLOWHZ; + sk->protinfo.rose->idle = opt * 60 * HZ; return 0; case ROSE_QBITINCL: @@ -477,24 +473,28 @@ static int rose_getsockopt(struct socket *sock, int level, int optname, return -EFAULT; switch (optname) { + case ROSE_DEFER: + val = sk->protinfo.rose->defer; + break; + case ROSE_T1: - val = sk->protinfo.rose->t1 / ROSE_SLOWHZ; + val = sk->protinfo.rose->t1 / HZ; break; case ROSE_T2: - val = sk->protinfo.rose->t2 / ROSE_SLOWHZ; + val = sk->protinfo.rose->t2 / HZ; break; case ROSE_T3: - val = sk->protinfo.rose->t3 / ROSE_SLOWHZ; + val = sk->protinfo.rose->t3 / HZ; break; case ROSE_HOLDBACK: - val = sk->protinfo.rose->hb / ROSE_SLOWHZ; + val = sk->protinfo.rose->hb / HZ; break; case ROSE_IDLE: - val = sk->protinfo.rose->idle / (ROSE_SLOWHZ * 60); + val = sk->protinfo.rose->idle / (60 * HZ); break; case ROSE_QBITINCL: @@ -550,7 +550,10 @@ static int rose_create(struct socket *sock, int protocol) sock->ops = &rose_proto_ops; sk->protocol = protocol; - sk->mtu = ROSE_MTU; /* 128 */ + sk->mtu = ROSE_MTU; /* 253 */ + + init_timer(&rose->timer); + init_timer(&rose->idletimer); skb_queue_head_init(&rose->frag_queue); @@ -592,6 +595,9 @@ static struct sock *rose_make_new(struct sock *osk) sk->sleep = osk->sleep; sk->zapped = osk->zapped; + init_timer(&rose->timer); + init_timer(&rose->idletimer); + skb_queue_head_init(&rose->frag_queue); rose->t1 = osk->protinfo.rose->t1; @@ -600,6 +606,7 @@ static struct sock *rose_make_new(struct sock *osk) rose->hb = osk->protinfo.rose->hb; rose->idle = osk->protinfo.rose->idle; + rose->defer = osk->protinfo.rose->defer; rose->device = osk->protinfo.rose->device; rose->qbitincl = osk->protinfo.rose->qbitincl; @@ -625,28 +632,24 @@ static int rose_release(struct socket *sock, struct socket *peer) switch (sk->protinfo.rose->state) { case ROSE_STATE_0: - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; + rose_disconnect(sk, 0, -1, -1); rose_destroy_socket(sk); break; case ROSE_STATE_2: - sk->protinfo.rose->state = ROSE_STATE_0; - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; + sk->protinfo.rose->neighbour->use--; + rose_disconnect(sk, 0, -1, -1); rose_destroy_socket(sk); - break; + break; case ROSE_STATE_1: case ROSE_STATE_3: case ROSE_STATE_4: + case ROSE_STATE_5: rose_clear_queues(sk); + rose_stop_idletimer(sk); rose_write_internal(sk, ROSE_CLEAR_REQUEST); - sk->protinfo.rose->timer = sk->protinfo.rose->t3; + rose_start_t3timer(sk); sk->protinfo.rose->state = ROSE_STATE_2; sk->state = TCP_CLOSE; sk->shutdown |= SEND_SHUTDOWN; @@ -714,6 +717,7 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le { struct sock *sk = sock->sk; struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr; + unsigned char cause, diagnostic; ax25_address *user; struct device *dev; @@ -739,7 +743,7 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le if (addr->srose_family != AF_ROSE) return -EINVAL; - if ((sk->protinfo.rose->neighbour = rose_get_neigh(&addr->srose_addr)) == NULL) + if ((sk->protinfo.rose->neighbour = rose_get_neigh(&addr->srose_addr, &cause, &diagnostic)) == NULL) return -ENETUNREACH; if ((sk->protinfo.rose->lci = rose_new_lci(sk->protinfo.rose->neighbour)) == 0) @@ -775,10 +779,12 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le sk->state = TCP_SYN_SENT; sk->protinfo.rose->state = ROSE_STATE_1; - sk->protinfo.rose->timer = sk->protinfo.rose->t1; - rose_write_internal(sk, ROSE_CALL_REQUEST); - rose_set_timer(sk); + sk->protinfo.rose->neighbour->use++; + + rose_write_internal(sk, ROSE_CALL_REQUEST); + rose_start_heartbeat(sk); + rose_start_t1timer(sk); /* Now the loop */ if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) @@ -911,11 +917,8 @@ int rose_rx_call_request(struct sk_buff *skb, struct device *dev, struct rose_ne /* * skb->data points to the rose frame start */ - - /* - * XXX This is an error. - */ if (!rose_parse_facilities(skb, &facilities)) { + rose_transmit_clear_request(neigh, lci, ROSE_INVALID_FACILITY, 76); return 0; } @@ -925,7 +928,7 @@ int rose_rx_call_request(struct sk_buff *skb, struct device *dev, struct rose_ne * We can't accept the Call Request. */ if (sk == NULL || sk->ack_backlog == sk->max_ack_backlog || (make = rose_make_new(sk)) == NULL) { - rose_transmit_clear_request(neigh, lci, 0x01); + rose_transmit_clear_request(neigh, lci, ROSE_NETWORK_CONGESTION, 120); return 0; } @@ -944,14 +947,21 @@ int rose_rx_call_request(struct sk_buff *skb, struct device *dev, struct rose_ne make->protinfo.rose->neighbour = neigh; make->protinfo.rose->device = dev; - rose_write_internal(make, ROSE_CALL_ACCEPTED); + make->protinfo.rose->neighbour->use++; + + if (sk->protinfo.rose->defer) { + make->protinfo.rose->state = ROSE_STATE_5; + } else { + rose_write_internal(make, ROSE_CALL_ACCEPTED); + make->protinfo.rose->state = ROSE_STATE_3; + rose_start_idletimer(make); + } make->protinfo.rose->condition = 0x00; make->protinfo.rose->vs = 0; make->protinfo.rose->va = 0; make->protinfo.rose->vr = 0; make->protinfo.rose->vl = 0; - make->protinfo.rose->state = ROSE_STATE_3; sk->ack_backlog++; make->pair = sk; @@ -959,7 +969,7 @@ int rose_rx_call_request(struct sk_buff *skb, struct device *dev, struct rose_ne skb_queue_head(&sk->receive_queue, skb); - rose_set_timer(make); + rose_start_heartbeat(make); if (!sk->dead) sk->data_ready(sk, skb->len); @@ -1146,37 +1156,35 @@ static int rose_shutdown(struct socket *sk, int how) static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; - int err; - long amount = 0; switch (cmd) { - case TIOCOUTQ: - if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int))) != 0) - return err; + case TIOCOUTQ: { + long amount; amount = sk->sndbuf - atomic_read(&sk->wmem_alloc); if (amount < 0) amount = 0; - put_user(amount, (unsigned int *)arg); + if (put_user(amount, (unsigned int *)arg)) + return -EFAULT; return 0; + } case TIOCINQ: { struct sk_buff *skb; + long amount = 0L; /* These two are safe on a single CPU system as only user tasks fiddle here */ if ((skb = skb_peek(&sk->receive_queue)) != NULL) - amount = skb->len - 20; - if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int))) != 0) - return err; - put_user(amount, (unsigned int *)arg); + amount = skb->len; + if (put_user(amount, (unsigned int *)arg)) + return -EFAULT; return 0; } case SIOCGSTAMP: if (sk != NULL) { - if (sk->stamp.tv_sec==0) + if (sk->stamp.tv_sec == 0) return -ENOENT; - if ((err = verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval))) != 0) - return err; - copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)); + if (copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval))) + return -EFAULT; return 0; } return -EINVAL; @@ -1195,20 +1203,51 @@ static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) case SIOCADDRT: case SIOCDELRT: + case SIOCRSCLRRT: if (!suser()) return -EPERM; return rose_rt_ioctl(cmd, (void *)arg); + case SIOCRSGCAUSE: { + struct rose_cause_struct rose_cause; + rose_cause.cause = sk->protinfo.rose->cause; + rose_cause.diagnostic = sk->protinfo.rose->diagnostic; + if (copy_to_user((void *)arg, &rose_cause, sizeof(struct rose_cause_struct))) + return -EFAULT; + return 0; + } + + case SIOCRSSCAUSE: { + struct rose_cause_struct rose_cause; + if (copy_from_user(&rose_cause, (void *)arg, sizeof(struct rose_cause_struct))) + return -EFAULT; + sk->protinfo.rose->cause = rose_cause.cause; + sk->protinfo.rose->diagnostic = rose_cause.diagnostic; + return 0; + } + case SIOCRSL2CALL: if (!suser()) return -EPERM; - if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(ax25_address))) != 0) - return err; if (ax25cmp(&rose_callsign, &null_ax25_address) != 0) ax25_listen_release(&rose_callsign, NULL); - copy_from_user(&rose_callsign, (void *)arg, sizeof(ax25_address)); + if (copy_from_user(&rose_callsign, (void *)arg, sizeof(ax25_address))) + return -EFAULT; if (ax25cmp(&rose_callsign, &null_ax25_address) != 0) ax25_listen_register(&rose_callsign, NULL); return 0; + case SIOCRSACCEPT: + if (sk->protinfo.rose->state == ROSE_STATE_5) { + rose_write_internal(sk, ROSE_CALL_ACCEPTED); + rose_start_idletimer(sk); + sk->protinfo.rose->condition = 0x00; + sk->protinfo.rose->vs = 0; + sk->protinfo.rose->va = 0; + sk->protinfo.rose->vr = 0; + sk->protinfo.rose->vl = 0; + sk->protinfo.rose->state = ROSE_STATE_3; + } + return 0; + default: return dev_ioctl(cmd, (void *)arg); } @@ -1228,7 +1267,7 @@ static int rose_get_info(char *buffer, char **start, off_t offset, int length, i cli(); - len += sprintf(buffer, "dest_addr dest_call src_addr src_call dev lci st vs vr va t t1 t2 t3 hb Snd-Q Rcv-Q\n"); + len += sprintf(buffer, "dest_addr dest_call src_addr src_call dev lci st vs vr va t t1 t2 t3 hb idle Snd-Q Rcv-Q\n"); for (s = rose_list; s != NULL; s = s->next) { if ((dev = s->protinfo.rose->device) == NULL) @@ -1245,17 +1284,24 @@ static int rose_get_info(char *buffer, char **start, off_t offset, int length, i else callsign = ax2asc(&s->protinfo.rose->source_call); - len += sprintf(buffer + len, "%-10s %-9s %-5s %3.3X %d %d %d %d %3d %3d %3d %3d %3d %5d %5d\n", - rose2asc(&s->protinfo.rose->source_addr), callsign, - devname, s->protinfo.rose->lci & 0x0FFF, + len += sprintf(buffer + len, "%-10s %-9s %-5s %3.3X %d %d %d %d %3lu %3lu %3lu %3lu %3lu %3lu/%03lu %5d %5d\n", + rose2asc(&s->protinfo.rose->source_addr), + callsign, + devname, + s->protinfo.rose->lci & 0x0FFF, s->protinfo.rose->state, - s->protinfo.rose->vs, s->protinfo.rose->vr, s->protinfo.rose->va, - s->protinfo.rose->timer / ROSE_SLOWHZ, - s->protinfo.rose->t1 / ROSE_SLOWHZ, - s->protinfo.rose->t2 / ROSE_SLOWHZ, - s->protinfo.rose->t3 / ROSE_SLOWHZ, - s->protinfo.rose->hb / ROSE_SLOWHZ, - atomic_read(&s->wmem_alloc), atomic_read(&s->rmem_alloc)); + s->protinfo.rose->vs, + s->protinfo.rose->vr, + s->protinfo.rose->va, + ax25_display_timer(&s->protinfo.rose->timer) / HZ, + s->protinfo.rose->t1 / HZ, + s->protinfo.rose->t2 / HZ, + s->protinfo.rose->t3 / HZ, + s->protinfo.rose->hb / HZ, + ax25_display_timer(&s->protinfo.rose->idletimer) / (60 * HZ), + s->protinfo.rose->idle / (60 * HZ), + atomic_read(&s->wmem_alloc), + atomic_read(&s->rmem_alloc)); pos = begin + len; @@ -1360,7 +1406,7 @@ __initfunc(void rose_proto_init(struct net_proto *pro)) sock_register(&rose_family_ops); register_netdevice_notifier(&rose_dev_notifier); - printk(KERN_INFO "G4KLX ROSE for Linux. Version 0.2 for AX25.035 Linux 2.1\n"); + printk(KERN_INFO "G4KLX ROSE for Linux. Version 0.3 for AX25.037 Linux 2.1\n"); ax25_protocol_register(AX25_P_ROSE, rose_route_frame); ax25_linkfail_register(rose_link_failed); diff --git a/net/rose/rose_dev.c b/net/rose/rose_dev.c index 73d0aa552..7861220ee 100644 --- a/net/rose/rose_dev.c +++ b/net/rose/rose_dev.c @@ -1,5 +1,5 @@ /* - * ROSE release 002 + * ROSE release 003 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -101,7 +101,7 @@ static int rose_rebuild_header(struct sk_buff *skb) unsigned char *bp = (unsigned char *)skb->data; struct sk_buff *skbn; - if (!arp_find(bp + 7, skb)) { + if (arp_find(bp + 7, skb)) { kfree_skb(skb, FREE_WRITE); return 1; } diff --git a/net/rose/rose_in.c b/net/rose/rose_in.c index 3c3e17b2b..1ac11528d 100644 --- a/net/rose/rose_in.c +++ b/net/rose/rose_in.c @@ -1,5 +1,5 @@ /* - * ROSE release 002 + * ROSE release 003 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -17,6 +17,8 @@ * * History * ROSE 001 Jonathan(G4KLX) Cloned from nr_in.c + * ROSE 002 Jonathan(G4KLX) Return cause and diagnostic codes from Clear Requests. + * ROSE 003 Jonathan(G4KLX) New timer architecture. */ #include <linux/config.h> @@ -48,6 +50,8 @@ static int rose_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more) { struct sk_buff *skbo, *skbn = skb; + rose_start_idletimer(sk); + if (more) { sk->protinfo.rose->fraglen += skb->len; skb_queue_tail(&sk->protinfo.rose->frag_queue, skb); @@ -89,8 +93,9 @@ static int rose_state1_machine(struct sock *sk, struct sk_buff *skb, int framety switch (frametype) { case ROSE_CALL_ACCEPTED: + rose_stop_timer(sk); + rose_start_idletimer(sk); sk->protinfo.rose->condition = 0x00; - sk->protinfo.rose->timer = 0; sk->protinfo.rose->vs = 0; sk->protinfo.rose->va = 0; sk->protinfo.rose->vr = 0; @@ -102,15 +107,9 @@ static int rose_state1_machine(struct sock *sk, struct sk_buff *skb, int framety break; case ROSE_CLEAR_REQUEST: - rose_clear_queues(sk); rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); - sk->protinfo.rose->state = ROSE_STATE_0; - sk->state = TCP_CLOSE; - sk->err = ECONNREFUSED; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + rose_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]); + sk->protinfo.rose->neighbour->use--; break; default: @@ -131,15 +130,13 @@ static int rose_state2_machine(struct sock *sk, struct sk_buff *skb, int framety case ROSE_CLEAR_REQUEST: rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); + rose_disconnect(sk, 0, skb->data[3], skb->data[4]); + sk->protinfo.rose->neighbour->use--; + break; + case ROSE_CLEAR_CONFIRMATION: - rose_clear_queues(sk); - sk->protinfo.rose->state = ROSE_STATE_0; - sk->state = TCP_CLOSE; - sk->err = 0; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + rose_disconnect(sk, 0, -1, -1); + sk->protinfo.rose->neighbour->use--; break; default: @@ -161,9 +158,10 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety switch (frametype) { case ROSE_RESET_REQUEST: + rose_stop_timer(sk); + rose_start_idletimer(sk); rose_write_internal(sk, ROSE_RESET_CONFIRMATION); sk->protinfo.rose->condition = 0x00; - sk->protinfo.rose->timer = 0; sk->protinfo.rose->vs = 0; sk->protinfo.rose->vr = 0; sk->protinfo.rose->va = 0; @@ -171,15 +169,9 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety break; case ROSE_CLEAR_REQUEST: - rose_clear_queues(sk); rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); - sk->protinfo.rose->state = ROSE_STATE_0; - sk->state = TCP_CLOSE; - sk->err = 0; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + rose_disconnect(sk, 0, skb->data[3], skb->data[4]); + sk->protinfo.rose->neighbour->use--; break; case ROSE_RR: @@ -189,7 +181,6 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety else sk->protinfo.rose->condition &= ~ROSE_COND_PEER_RX_BUSY; if (!rose_validate_nr(sk, nr)) { - rose_clear_queues(sk); rose_write_internal(sk, ROSE_RESET_REQUEST); sk->protinfo.rose->condition = 0x00; sk->protinfo.rose->vs = 0; @@ -197,7 +188,8 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety sk->protinfo.rose->va = 0; sk->protinfo.rose->vl = 0; sk->protinfo.rose->state = ROSE_STATE_4; - sk->protinfo.rose->timer = sk->protinfo.rose->t2; + rose_start_t2timer(sk); + rose_stop_idletimer(sk); } else { if (sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) { sk->protinfo.rose->va = nr; @@ -210,7 +202,6 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety case ROSE_DATA: /* XXX */ sk->protinfo.rose->condition &= ~ROSE_COND_PEER_RX_BUSY; if (!rose_validate_nr(sk, nr)) { - rose_clear_queues(sk); rose_write_internal(sk, ROSE_RESET_REQUEST); sk->protinfo.rose->condition = 0x00; sk->protinfo.rose->vs = 0; @@ -218,7 +209,8 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety sk->protinfo.rose->va = 0; sk->protinfo.rose->vl = 0; sk->protinfo.rose->state = ROSE_STATE_4; - sk->protinfo.rose->timer = sk->protinfo.rose->t2; + rose_start_t2timer(sk); + rose_stop_idletimer(sk); break; } if (sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) { @@ -242,11 +234,11 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety */ if (((sk->protinfo.rose->vl + sysctl_rose_window_size) % ROSE_MODULUS) == sk->protinfo.rose->vr) { sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; - sk->protinfo.rose->timer = 0; + rose_stop_timer(sk); rose_enquiry_response(sk); } else { sk->protinfo.rose->condition |= ROSE_COND_ACK_PENDING; - sk->protinfo.rose->timer = sk->protinfo.rose->hb; + rose_start_hbtimer(sk); } break; @@ -270,7 +262,8 @@ static int rose_state4_machine(struct sock *sk, struct sk_buff *skb, int framety case ROSE_RESET_REQUEST: rose_write_internal(sk, ROSE_RESET_CONFIRMATION); case ROSE_RESET_CONFIRMATION: - sk->protinfo.rose->timer = 0; + rose_stop_timer(sk); + rose_start_idletimer(sk); sk->protinfo.rose->condition = 0x00; sk->protinfo.rose->va = 0; sk->protinfo.rose->vr = 0; @@ -280,16 +273,9 @@ static int rose_state4_machine(struct sock *sk, struct sk_buff *skb, int framety break; case ROSE_CLEAR_REQUEST: - rose_clear_queues(sk); rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); - sk->protinfo.rose->timer = 0; - sk->protinfo.rose->state = ROSE_STATE_0; - sk->state = TCP_CLOSE; - sk->err = 0; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + rose_disconnect(sk, 0, skb->data[3], skb->data[4]); + sk->protinfo.rose->neighbour->use--; break; default: @@ -299,6 +285,22 @@ static int rose_state4_machine(struct sock *sk, struct sk_buff *skb, int framety return 0; } +/* + * State machine for state 5, Awaiting Call Acceptance State. + * The handling of the timer(s) is in file rose_timer.c + * Handling of state 0 and connection release is in af_rose.c. + */ +static int rose_state5_machine(struct sock *sk, struct sk_buff *skb, int frametype) +{ + if (frametype == ROSE_CLEAR_REQUEST) { + rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); + rose_disconnect(sk, 0, skb->data[3], skb->data[4]); + sk->protinfo.rose->neighbour->use--; + } + + return 0; +} + /* Higher level upcall for a LAPB frame */ int rose_process_rx_frame(struct sock *sk, struct sk_buff *skb) { @@ -307,8 +309,6 @@ int rose_process_rx_frame(struct sock *sk, struct sk_buff *skb) if (sk->protinfo.rose->state == ROSE_STATE_0) return 0; - del_timer(&sk->timer); - frametype = rose_decode(skb, &ns, &nr, &q, &d, &m); switch (sk->protinfo.rose->state) { @@ -324,9 +324,12 @@ int rose_process_rx_frame(struct sock *sk, struct sk_buff *skb) case ROSE_STATE_4: queued = rose_state4_machine(sk, skb, frametype); break; + case ROSE_STATE_5: + queued = rose_state5_machine(sk, skb, frametype); + break; } - rose_set_timer(sk); + rose_kick(sk); return queued; } diff --git a/net/rose/rose_link.c b/net/rose/rose_link.c index 86626511e..b481e485f 100644 --- a/net/rose/rose_link.c +++ b/net/rose/rose_link.c @@ -1,5 +1,5 @@ /* - * ROSE release 002 + * ROSE release 003 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -11,6 +11,7 @@ * * History * ROSE 001 Jonathan(G4KLX) Cloned from rose_timer.c + * ROSE 003 Jonathan(G4KLX) New timer architecture. */ #include <linux/config.h> @@ -38,53 +39,64 @@ #include <linux/firewall.h> #include <net/rose.h> -static void rose_link_timer(unsigned long); +static void rose_ftimer_expiry(unsigned long); +static void rose_t0timer_expiry(unsigned long); -/* - * Linux set timer - */ -void rose_link_set_timer(struct rose_neigh *neigh) +void rose_start_ftimer(struct rose_neigh *neigh) { - unsigned long flags; + del_timer(&neigh->ftimer); - save_flags(flags); cli(); - del_timer(&neigh->timer); - restore_flags(flags); + neigh->ftimer.data = (unsigned long)neigh; + neigh->ftimer.function = &rose_ftimer_expiry; + neigh->ftimer.expires = jiffies + sysctl_rose_link_fail_timeout; - neigh->timer.data = (unsigned long)neigh; - neigh->timer.function = &rose_link_timer; - neigh->timer.expires = jiffies + (HZ / 10); + add_timer(&neigh->ftimer); +} + +void rose_start_t0timer(struct rose_neigh *neigh) +{ + del_timer(&neigh->t0timer); - add_timer(&neigh->timer); + neigh->t0timer.data = (unsigned long)neigh; + neigh->t0timer.function = &rose_t0timer_expiry; + neigh->t0timer.expires = jiffies + sysctl_rose_restart_request_timeout; + + add_timer(&neigh->t0timer); } -/* - * ROSE Link Timer - * - * This routine is called every 100ms. Decrement timer by this - * amount - if expired then process the event. - */ -static void rose_link_timer(unsigned long param) +void rose_stop_ftimer(struct rose_neigh *neigh) { - struct rose_neigh *neigh = (struct rose_neigh *)param; + del_timer(&neigh->ftimer); +} - if (neigh->ftimer > 0) - neigh->ftimer--; +void rose_stop_t0timer(struct rose_neigh *neigh) +{ + del_timer(&neigh->t0timer); +} - if (neigh->t0timer > 0) { - neigh->t0timer--; +int rose_ftimer_running(struct rose_neigh *neigh) +{ + return (neigh->ftimer.prev != NULL || neigh->ftimer.next != NULL); +} - if (neigh->t0timer == 0) { - rose_transmit_restart_request(neigh); - neigh->dce_mode = 0; - neigh->t0timer = sysctl_rose_restart_request_timeout; - } - } +int rose_t0timer_running(struct rose_neigh *neigh) +{ + return (neigh->t0timer.prev != NULL || neigh->t0timer.next != NULL); +} - if (neigh->ftimer > 0 || neigh->t0timer > 0) - rose_link_set_timer(neigh); - else - del_timer(&neigh->timer); +static void rose_ftimer_expiry(unsigned long param) +{ +} + +static void rose_t0timer_expiry(unsigned long param) +{ + struct rose_neigh *neigh = (struct rose_neigh *)param; + + rose_transmit_restart_request(neigh); + + neigh->dce_mode = 0; + + rose_start_t0timer(neigh); } /* @@ -101,7 +113,9 @@ static int rose_send_frame(struct sk_buff *skb, struct rose_neigh *neigh) else rose_call = &rose_callsign; - return ax25_send_frame(skb, 256, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev); + neigh->ax25 = ax25_send_frame(skb, 256, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev); + + return (neigh->ax25 != NULL); } /* @@ -118,7 +132,9 @@ static int rose_link_up(struct rose_neigh *neigh) else rose_call = &rose_callsign; - return ax25_link_up(rose_call, &neigh->callsign, neigh->digipeat, neigh->dev); + neigh->ax25 = ax25_find_cb(rose_call, &neigh->callsign, neigh->digipeat, neigh->dev); + + return (neigh->ax25 != NULL); } /* @@ -130,17 +146,15 @@ void rose_link_rx_restart(struct sk_buff *skb, struct rose_neigh *neigh, unsigne switch (frametype) { case ROSE_RESTART_REQUEST: - neigh->t0timer = 0; + rose_stop_t0timer(neigh); neigh->restarted = 1; - neigh->dce_mode = (skb->data[3] == 0x00); - del_timer(&neigh->timer); + neigh->dce_mode = (skb->data[3] == ROSE_DTE_ORIGINATED); rose_transmit_restart_confirmation(neigh); break; case ROSE_RESTART_CONFIRMATION: - neigh->t0timer = 0; + rose_stop_t0timer(neigh); neigh->restarted = 1; - del_timer(&neigh->timer); break; case ROSE_DIAGNOSTIC: @@ -181,7 +195,7 @@ void rose_transmit_restart_request(struct rose_neigh *neigh) *dptr++ = ROSE_GFI; *dptr++ = 0x00; *dptr++ = ROSE_RESTART_REQUEST; - *dptr++ = 0x00; + *dptr++ = ROSE_DTE_ORIGINATED; *dptr++ = 0; if (!rose_send_frame(skb, neigh)) @@ -247,7 +261,7 @@ void rose_transmit_diagnostic(struct rose_neigh *neigh, unsigned char diag) * This routine is called when a Clear Request is needed outside of the context * of a connected socket. */ -void rose_transmit_clear_request(struct rose_neigh *neigh, unsigned int lci, unsigned char cause) +void rose_transmit_clear_request(struct rose_neigh *neigh, unsigned int lci, unsigned char cause, unsigned char diagnostic) { struct sk_buff *skb; unsigned char *dptr; @@ -267,7 +281,7 @@ void rose_transmit_clear_request(struct rose_neigh *neigh, unsigned int lci, uns *dptr++ = ((lci >> 0) & 0xFF); *dptr++ = ROSE_CLEAR_REQUEST; *dptr++ = cause; - *dptr++ = 0x00; + *dptr++ = diagnostic; if (!rose_send_frame(skb, neigh)) kfree_skb(skb, FREE_WRITE); @@ -294,11 +308,10 @@ void rose_transmit_link(struct sk_buff *skb, struct rose_neigh *neigh) } else { skb_queue_tail(&neigh->queue, skb); - if (neigh->t0timer == 0) { + if (!rose_t0timer_running(neigh)) { rose_transmit_restart_request(neigh); neigh->dce_mode = 0; - neigh->t0timer = sysctl_rose_restart_request_timeout; - rose_link_set_timer(neigh); + rose_start_t0timer(neigh); } } } diff --git a/net/rose/rose_out.c b/net/rose/rose_out.c index f0e212dc3..0ed9f7480 100644 --- a/net/rose/rose_out.c +++ b/net/rose/rose_out.c @@ -1,5 +1,5 @@ /* - * ROSE release 002 + * ROSE release 003 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -11,6 +11,7 @@ * * History * ROSE 001 Jonathan(G4KLX) Cloned from nr_out.c + * ROSE 003 Jonathan(G4KLX) New timer architecture. */ #include <linux/config.h> @@ -80,8 +81,7 @@ void rose_output(struct sock *sk, struct sk_buff *skb) skb_queue_tail(&sk->write_queue, skb); /* Throw it on the queue */ } - if (sk->protinfo.rose->state == ROSE_STATE_3) - rose_kick(sk); + rose_kick(sk); } /* @@ -96,6 +96,8 @@ static void rose_send_iframe(struct sock *sk, struct sk_buff *skb) skb->data[2] |= (sk->protinfo.rose->vr << 5) & 0xE0; skb->data[2] |= (sk->protinfo.rose->vs << 1) & 0x0E; + rose_start_idletimer(sk); + rose_transmit_link(skb, sk->protinfo.rose->neighbour); } @@ -104,36 +106,41 @@ void rose_kick(struct sock *sk) struct sk_buff *skb; unsigned short end; - del_timer(&sk->timer); + if (sk->protinfo.rose->state != ROSE_STATE_3) + return; + + if (sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) + return; + + if (skb_peek(&sk->write_queue) == NULL) + return; end = (sk->protinfo.rose->va + sysctl_rose_window_size) % ROSE_MODULUS; - if (!(sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) && - sk->protinfo.rose->vs != end && - skb_peek(&sk->write_queue) != NULL) { - /* - * Transmit data until either we're out of data to send or - * the window is full. - */ + if (sk->protinfo.rose->vs == end) + return; - skb = skb_dequeue(&sk->write_queue); + /* + * Transmit data until either we're out of data to send or + * the window is full. + */ - do { - /* - * Transmit the frame. - */ - rose_send_iframe(sk, skb); + skb = skb_dequeue(&sk->write_queue); - sk->protinfo.rose->vs = (sk->protinfo.rose->vs + 1) % ROSE_MODULUS; + do { + /* + * Transmit the frame. + */ + rose_send_iframe(sk, skb); - } while (sk->protinfo.rose->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL); + sk->protinfo.rose->vs = (sk->protinfo.rose->vs + 1) % ROSE_MODULUS; - sk->protinfo.rose->vl = sk->protinfo.rose->vr; - sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; - sk->protinfo.rose->timer = 0; - } + } while (sk->protinfo.rose->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL); - rose_set_timer(sk); + sk->protinfo.rose->vl = sk->protinfo.rose->vr; + sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; + + rose_stop_timer(sk); } /* @@ -150,7 +157,8 @@ void rose_enquiry_response(struct sock *sk) sk->protinfo.rose->vl = sk->protinfo.rose->vr; sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; - sk->protinfo.rose->timer = 0; + + rose_stop_timer(sk); } void rose_check_iframes_acked(struct sock *sk, unsigned short nr) diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c index 5b1338609..43358644c 100644 --- a/net/rose/rose_route.c +++ b/net/rose/rose_route.c @@ -1,5 +1,5 @@ /* - * ROSE release 002 + * ROSE release 003 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -15,6 +15,8 @@ * address masks. * ROSE 002 Jonathan(G4KLX) Uprated through routing of packets. * Routing loop detection. + * ROSE 003 Jonathan(G4KLX) New timer architecture. + * Added use count to neighbours. */ #include <linux/config.h> @@ -80,24 +82,32 @@ static int rose_add_node(struct rose_route_struct *rose_route, struct device *de rose_neigh->callsign = rose_route->neighbour; rose_neigh->digipeat = NULL; + rose_neigh->ax25 = NULL; rose_neigh->dev = dev; rose_neigh->count = 0; + rose_neigh->use = 0; rose_neigh->dce_mode = 0; rose_neigh->number = rose_neigh_no++; rose_neigh->restarted = 0; + skb_queue_head_init(&rose_neigh->queue); - rose_neigh->t0timer = 0; - rose_neigh->ftimer = 0; - init_timer(&rose_neigh->timer); + + init_timer(&rose_neigh->ftimer); + init_timer(&rose_neigh->t0timer); if (rose_route->ndigis != 0) { if ((rose_neigh->digipeat = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) { kfree(rose_neigh); return -ENOMEM; } - rose_neigh->digipeat->ndigi = rose_route->ndigis; - for (i = 0; i < rose_route->ndigis; i++) - rose_neigh->digipeat->calls[i] = rose_route->digipeaters[i]; + + rose_neigh->digipeat->ndigi = rose_route->ndigis; + rose_neigh->digipeat->lastrepeat = -1; + + for (i = 0; i < rose_route->ndigis; i++) { + rose_neigh->digipeat->calls[i] = rose_route->digipeaters[i]; + rose_neigh->digipeat->repeated[i] = 0; + } } save_flags(flags); cli(); @@ -207,13 +217,13 @@ static void rose_remove_neigh(struct rose_neigh *rose_neigh) unsigned long flags; struct sk_buff *skb; - del_timer(&rose_neigh->timer); + rose_stop_ftimer(rose_neigh); + rose_stop_t0timer(rose_neigh); while ((skb = skb_dequeue(&rose_neigh->queue)) != NULL) kfree_skb(skb, FREE_WRITE); - save_flags(flags); - cli(); + save_flags(flags); cli(); if ((s = rose_neigh_list) == rose_neigh) { rose_neigh_list = rose_neigh->next; @@ -244,9 +254,14 @@ static void rose_remove_route(struct rose_route *rose_route) { struct rose_route *s; unsigned long flags; - - save_flags(flags); - cli(); + + if (rose_route->neigh1 != NULL) + rose_route->neigh1->use--; + + if (rose_route->neigh2 != NULL) + rose_route->neigh2->use--; + + save_flags(flags); cli(); if ((s = rose_route_list) == rose_route) { rose_route_list = rose_route->next; @@ -295,7 +310,7 @@ static int rose_del_node(struct rose_route_struct *rose_route, struct device *de if (rose_node->neighbour[i] == rose_neigh) { rose_neigh->count--; - if (rose_neigh->count == 0) + if (rose_neigh->count == 0 && rose_neigh->use == 0) rose_remove_neigh(rose_neigh); rose_node->count--; @@ -381,6 +396,35 @@ void rose_route_device_down(struct device *dev) } /* + * Clear all nodes and neighbours out, except for neighbours with + * active connections going through them. + */ +static int rose_clear_routes(void) +{ + struct rose_neigh *s, *rose_neigh = rose_neigh_list; + struct rose_node *t, *rose_node = rose_node_list; + + while (rose_node != NULL) { + t = rose_node; + rose_node = rose_node->next; + + rose_remove_node(t); + } + + while (rose_neigh != NULL) { + s = rose_neigh; + rose_neigh = rose_neigh->next; + + s->count = 0; + + if (s->use == 0) + rose_remove_neigh(s); + } + + return 0; +} + +/* * Check that the device given is a valid AX.25 interface that is "up". */ struct device *rose_ax25_dev_get(char *devname) @@ -440,20 +484,31 @@ struct rose_route *rose_route_free_lci(unsigned int lci, struct rose_neigh *neig /* * Find a neighbour given a ROSE address. */ -struct rose_neigh *rose_get_neigh(rose_address *addr) +struct rose_neigh *rose_get_neigh(rose_address *addr, unsigned char *cause, unsigned char *diagnostic) { struct rose_node *node; + int failed = 0; int i; for (node = rose_node_list; node != NULL; node = node->next) { if (rosecmpm(addr, &node->address, node->mask) == 0) { for (i = 0; i < node->count; i++) { - if (node->neighbour[i]->ftimer == 0) + if (!rose_ftimer_running(node->neighbour[i])) return node->neighbour[i]; + else + failed = 1; } } } + if (failed) { + *cause = ROSE_OUT_OF_ORDER; + *diagnostic = 0; + } else { + *cause = ROSE_NOT_OBTAINABLE; + *diagnostic = 0; + } + return NULL; } @@ -464,14 +519,12 @@ int rose_rt_ioctl(unsigned int cmd, void *arg) { struct rose_route_struct rose_route; struct device *dev; - int err; switch (cmd) { case SIOCADDRT: - if ((err = verify_area(VERIFY_READ, arg, sizeof(struct rose_route_struct))) != 0) - return err; - copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct)); + if (copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct))) + return -EFAULT; if ((dev = rose_ax25_dev_get(rose_route.device)) == NULL) return -EINVAL; if (rose_dev_get(&rose_route.address) != NULL) /* Can't add routes to ourself */ @@ -482,13 +535,15 @@ int rose_rt_ioctl(unsigned int cmd, void *arg) return rose_add_node(&rose_route, dev); case SIOCDELRT: - if ((err = verify_area(VERIFY_READ, arg, sizeof(struct rose_route_struct))) != 0) - return err; - copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct)); + if (copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct))) + return -EFAULT; if ((dev = rose_ax25_dev_get(rose_route.device)) == NULL) return -EINVAL; return rose_del_node(&rose_route, dev); + case SIOCRSCLRRT: + return rose_clear_routes(); + default: return -EINVAL; } @@ -502,10 +557,9 @@ static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh) struct sk_buff *skb; rose_neigh->restarted = 0; - rose_neigh->t0timer = 0; - rose_neigh->ftimer = sysctl_rose_link_fail_timeout; - rose_link_set_timer(rose_neigh); + rose_stop_t0timer(rose_neigh); + rose_start_ftimer(rose_neigh); while ((skb = skb_dequeue(&rose_neigh->queue)) != NULL) kfree_skb(skb, FREE_WRITE); @@ -523,13 +577,15 @@ static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh) } if (rose_route->neigh1 == rose_neigh) { + rose_route->neigh1->use--; rose_route->neigh1 = NULL; - rose_transmit_clear_request(rose_route->neigh2, rose_route->lci2, 0x0D); + rose_transmit_clear_request(rose_route->neigh2, rose_route->lci2, ROSE_OUT_OF_ORDER, 0); } if (rose_route->neigh2 == rose_neigh) { + rose_route->neigh2->use--; rose_route->neigh2 = NULL; - rose_transmit_clear_request(rose_route->neigh1, rose_route->lci1, 0x0D); + rose_transmit_clear_request(rose_route->neigh1, rose_route->lci1, ROSE_OUT_OF_ORDER, 0); } rose_route = rose_route->next; @@ -541,16 +597,18 @@ static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh) * then don't use that neighbour until it is reset. Blow away all through * routes and connections using this route. */ -void rose_link_failed(ax25_address *callsign, struct device *dev) +void rose_link_failed(ax25_cb *ax25, int reason) { struct rose_neigh *rose_neigh; for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) - if (ax25cmp(&rose_neigh->callsign, callsign) == 0 && rose_neigh->dev == dev) + if (rose_neigh->ax25 == ax25) break; if (rose_neigh == NULL) return; + rose_neigh->ax25 = NULL; + rose_del_route_by_neigh(rose_neigh); rose_kill_by_neigh(rose_neigh); } @@ -583,6 +641,7 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) struct sock *sk; unsigned short frametype; unsigned int lci, new_lci; + unsigned char cause, diagnostic; struct device *dev; unsigned long flags; @@ -604,7 +663,7 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) /* * Obviously the link is working, halt the ftimer. */ - rose_neigh->ftimer = 0; + rose_stop_ftimer(rose_neigh); /* * LCI of zero is always for us, and its always a restart @@ -631,7 +690,7 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) return rose_rx_call_request(skb, dev, rose_neigh, lci); if (!sysctl_rose_routing_control) { - rose_transmit_clear_request(rose_neigh, lci, 0x0D); + rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 0); return 0; } @@ -679,7 +738,10 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) if (frametype != ROSE_CALL_REQUEST) /* XXX */ return 0; - rose_parse_facilities(skb, &facilities); + if (!rose_parse_facilities(skb, &facilities)) { + rose_transmit_clear_request(rose_neigh, lci, ROSE_INVALID_FACILITY, 76); + return 0; + } /* * Check for routing loops. @@ -691,25 +753,25 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) ax25cmp(&facilities.source_call, &rose_route->dest_call) == 0) { printk(KERN_DEBUG "ROSE: routing loop from %s\n", rose2asc(src_addr)); printk(KERN_DEBUG "ROSE: to %s\n", rose2asc(dest_addr)); - rose_transmit_clear_request(rose_neigh, lci, 0x0D); + rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 120); return 0; } } - if ((new_neigh = rose_get_neigh(dest_addr)) == NULL) { - printk(KERN_DEBUG "ROSE: no route to %s\n", rose2asc(dest_addr)); - rose_transmit_clear_request(rose_neigh, lci, 0x0D); + if ((new_neigh = rose_get_neigh(dest_addr, &cause, &diagnostic)) == NULL) { + if (cause == ROSE_NOT_OBTAINABLE) + printk(KERN_DEBUG "ROSE: no route to %s\n", rose2asc(dest_addr)); + rose_transmit_clear_request(rose_neigh, lci, cause, diagnostic); return 0; } if ((new_lci = rose_new_lci(new_neigh)) == 0) { - printk(KERN_DEBUG "ROSE: no spare VCs to %s\n", rose2asc(dest_addr)); - rose_transmit_clear_request(rose_neigh, lci, 0x0D); + rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 71); return 0; } if ((rose_route = kmalloc(sizeof(*rose_route), GFP_ATOMIC)) == NULL) { - rose_transmit_clear_request(rose_neigh, lci, 0x0D); + rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 120); return 0; } @@ -723,6 +785,9 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) rose_route->lci2 = new_lci; rose_route->neigh2 = new_neigh; + rose_route->neigh1->use++; + rose_route->neigh2->use++; + save_flags(flags); cli(); rose_route->next = rose_route_list; rose_route_list = rose_route; @@ -790,21 +855,30 @@ int rose_neigh_get_info(char *buffer, char **start, off_t offset, int len = 0; off_t pos = 0; off_t begin = 0; + int i; cli(); - len += sprintf(buffer, "addr callsign dev count mode restart t0 tf\n"); + len += sprintf(buffer, "addr callsign dev count use mode restart t0 tf digipeaters\n"); for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) { - len += sprintf(buffer + len, "%05d %-9s %-4s %3d %3s %3s %3d %3d\n", + len += sprintf(buffer + len, "%05d %-9s %-4s %3d %3d %3s %3s %3lu %3lu", rose_neigh->number, ax2asc(&rose_neigh->callsign), rose_neigh->dev ? rose_neigh->dev->name : "???", rose_neigh->count, + rose_neigh->use, (rose_neigh->dce_mode) ? "DCE" : "DTE", (rose_neigh->restarted) ? "yes" : "no", - rose_neigh->t0timer / ROSE_SLOWHZ, - rose_neigh->ftimer / ROSE_SLOWHZ); + ax25_display_timer(&rose_neigh->t0timer) / HZ, + ax25_display_timer(&rose_neigh->ftimer) / HZ); + + if (rose_neigh->digipeat != NULL) { + for (i = 0; i < rose_neigh->digipeat->ndigi; i++) + len += sprintf(buffer + len, " %s", ax2asc(&rose_neigh->digipeat->calls[i])); + } + + len += sprintf(buffer + len, "\n"); pos = begin + len; diff --git a/net/rose/rose_subr.c b/net/rose/rose_subr.c index 4e0530cb4..ee710bd6e 100644 --- a/net/rose/rose_subr.c +++ b/net/rose/rose_subr.c @@ -1,5 +1,5 @@ /* - * ROSE release 002 + * ROSE release 003 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -11,6 +11,8 @@ * * History * ROSE 001 Jonathan(G4KLX) Cloned from nr_subr.c + * ROSE 002 Jonathan(G4KLX) Centralised disconnect processing. + * ROSE 003 Jonathan(G4KLX) Added use count to neighbours. */ #include <linux/config.h> @@ -92,12 +94,8 @@ void rose_write_internal(struct sock *sk, int frametype) case ROSE_CALL_ACCEPTED: case ROSE_CLEAR_REQUEST: case ROSE_RESET_REQUEST: - case ROSE_DIAGNOSTIC: len += 2; break; - case ROSE_INTERRUPT: - len += 1; - break; } if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL) @@ -137,24 +135,23 @@ void rose_write_internal(struct sock *sk, int frametype) break; case ROSE_CLEAR_REQUEST: - case ROSE_RESET_REQUEST: *dptr++ = ROSE_GFI | lci1; *dptr++ = lci2; *dptr++ = frametype; - *dptr++ = 0x00; /* XXX */ - *dptr++ = 0x00; /* XXX */ + *dptr++ = sk->protinfo.rose->cause; + *dptr++ = sk->protinfo.rose->diagnostic; break; - case ROSE_INTERRUPT: + case ROSE_RESET_REQUEST: *dptr++ = ROSE_GFI | lci1; *dptr++ = lci2; *dptr++ = frametype; - *dptr++ = 0x00; /* XXX */ + *dptr++ = ROSE_DTE_ORIGINATED; + *dptr++ = 0; break; case ROSE_RR: case ROSE_RNR: - case ROSE_REJ: *dptr++ = ROSE_GFI | lci1; *dptr++ = lci2; *dptr = frametype; @@ -162,7 +159,6 @@ void rose_write_internal(struct sock *sk, int frametype) break; case ROSE_CLEAR_CONFIRMATION: - case ROSE_INTERRUPT_CONFIRMATION: case ROSE_RESET_CONFIRMATION: *dptr++ = ROSE_GFI | lci1; *dptr++ = lci2; @@ -191,23 +187,15 @@ int rose_decode(struct sk_buff *skb, int *ns, int *nr, int *q, int *d, int *m) case ROSE_CALL_ACCEPTED: case ROSE_CLEAR_REQUEST: case ROSE_CLEAR_CONFIRMATION: - case ROSE_INTERRUPT: - case ROSE_INTERRUPT_CONFIRMATION: case ROSE_RESET_REQUEST: case ROSE_RESET_CONFIRMATION: - case ROSE_RESTART_REQUEST: - case ROSE_RESTART_CONFIRMATION: - case ROSE_REGISTRATION_REQUEST: - case ROSE_REGISTRATION_CONFIRMATION: - case ROSE_DIAGNOSTIC: return frame[2]; default: break; } if ((frame[2] & 0x1F) == ROSE_RR || - (frame[2] & 0x1F) == ROSE_RNR || - (frame[2] & 0x1F) == ROSE_REJ) { + (frame[2] & 0x1F) == ROSE_RNR) { *nr = (frame[2] >> 5) & 0x07; return frame[2] & 0x1F; } @@ -437,4 +425,30 @@ int rose_create_facilities(unsigned char *buffer, rose_cb *rose) return len; } +void rose_disconnect(struct sock *sk, int reason, int cause, int diagnostic) +{ + rose_stop_timer(sk); + rose_stop_idletimer(sk); + + rose_clear_queues(sk); + + sk->protinfo.rose->lci = 0; + sk->protinfo.rose->state = ROSE_STATE_0; + + if (cause != -1) + sk->protinfo.rose->cause = cause; + + if (diagnostic != -1) + sk->protinfo.rose->diagnostic = diagnostic; + + sk->state = TCP_CLOSE; + sk->err = reason; + sk->shutdown |= SEND_SHUTDOWN; + + if (!sk->dead) + sk->state_change(sk); + + sk->dead = 1; +} + #endif diff --git a/net/rose/rose_timer.c b/net/rose/rose_timer.c index 572975e5d..718a64ec0 100644 --- a/net/rose/rose_timer.c +++ b/net/rose/rose_timer.c @@ -1,5 +1,5 @@ /* - * ROSE release 002 + * ROSE release 003 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -11,6 +11,8 @@ * * History * ROSE 001 Jonathan(G4KLX) Cloned from nr_timer.c + * ROSE 003 Jonathan(G4KLX) New timer architecture. + * Implemented idle timer. */ #include <linux/config.h> @@ -37,42 +39,103 @@ #include <linux/interrupt.h> #include <net/rose.h> -static void rose_timer(unsigned long); +static void rose_heartbeat_expiry(unsigned long); +static void rose_timer_expiry(unsigned long); +static void rose_idletimer_expiry(unsigned long); -/* - * Linux set timer - */ -void rose_set_timer(struct sock *sk) +void rose_start_heartbeat(struct sock *sk) { - unsigned long flags; - - save_flags(flags); cli(); del_timer(&sk->timer); - restore_flags(flags); sk->timer.data = (unsigned long)sk; - sk->timer.function = &rose_timer; - sk->timer.expires = jiffies + (HZ / 10); + sk->timer.function = &rose_heartbeat_expiry; + sk->timer.expires = jiffies + 5 * HZ; add_timer(&sk->timer); } -/* - * ROSE Timer - * - * This routine is called every 100ms. Decrement timer by this - * amount - if expired then process the event. - */ -static void rose_timer(unsigned long param) +void rose_start_t1timer(struct sock *sk) +{ + del_timer(&sk->protinfo.rose->timer); + + sk->protinfo.rose->timer.data = (unsigned long)sk; + sk->protinfo.rose->timer.function = &rose_timer_expiry; + sk->protinfo.rose->timer.expires = jiffies + sk->protinfo.rose->t1; + + add_timer(&sk->protinfo.rose->timer); +} + +void rose_start_t2timer(struct sock *sk) +{ + del_timer(&sk->protinfo.rose->timer); + + sk->protinfo.rose->timer.data = (unsigned long)sk; + sk->protinfo.rose->timer.function = &rose_timer_expiry; + sk->protinfo.rose->timer.expires = jiffies + sk->protinfo.rose->t2; + + add_timer(&sk->protinfo.rose->timer); +} + +void rose_start_t3timer(struct sock *sk) +{ + del_timer(&sk->protinfo.rose->timer); + + sk->protinfo.rose->timer.data = (unsigned long)sk; + sk->protinfo.rose->timer.function = &rose_timer_expiry; + sk->protinfo.rose->timer.expires = jiffies + sk->protinfo.rose->t3; + + add_timer(&sk->protinfo.rose->timer); +} + +void rose_start_hbtimer(struct sock *sk) +{ + del_timer(&sk->protinfo.rose->timer); + + sk->protinfo.rose->timer.data = (unsigned long)sk; + sk->protinfo.rose->timer.function = &rose_timer_expiry; + sk->protinfo.rose->timer.expires = jiffies + sk->protinfo.rose->hb; + + add_timer(&sk->protinfo.rose->timer); +} + +void rose_start_idletimer(struct sock *sk) +{ + del_timer(&sk->protinfo.rose->idletimer); + + if (sk->protinfo.rose->idle > 0) { + sk->protinfo.rose->idletimer.data = (unsigned long)sk; + sk->protinfo.rose->idletimer.function = &rose_idletimer_expiry; + sk->protinfo.rose->idletimer.expires = jiffies + sk->protinfo.rose->idle; + + add_timer(&sk->protinfo.rose->idletimer); + } +} + +void rose_stop_heartbeat(struct sock *sk) +{ + del_timer(&sk->timer); +} + +void rose_stop_timer(struct sock *sk) +{ + del_timer(&sk->protinfo.rose->timer); +} + +void rose_stop_idletimer(struct sock *sk) +{ + del_timer(&sk->protinfo.rose->idletimer); +} + +static void rose_heartbeat_expiry(unsigned long param) { struct sock *sk = (struct sock *)param; switch (sk->protinfo.rose->state) { + case ROSE_STATE_0: /* Magic here: If we listen() and a new link dies before it is accepted() it isn't 'dead' so doesn't get removed. */ if (sk->destroy || (sk->state == TCP_LISTEN && sk->dead)) { - del_timer(&sk->timer); rose_destroy_socket(sk); return; } @@ -87,57 +150,62 @@ static void rose_timer(unsigned long param) sk->protinfo.rose->condition &= ~ROSE_COND_OWN_RX_BUSY; sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; sk->protinfo.rose->vl = sk->protinfo.rose->vr; - sk->protinfo.rose->timer = 0; rose_write_internal(sk, ROSE_RR); + rose_stop_timer(sk); /* HB */ break; } - /* - * Check for frames to transmit. - */ - rose_kick(sk); - break; - - default: break; } - if (sk->protinfo.rose->timer == 0 || --sk->protinfo.rose->timer > 0) { - rose_set_timer(sk); - return; - } + rose_start_heartbeat(sk); +} + +static void rose_timer_expiry(unsigned long param) +{ + struct sock *sk = (struct sock *)param; - /* - * Timer has expired, it may have been T1, T2, T3 or HB. We can tell - * by the socket state. - */ switch (sk->protinfo.rose->state) { - case ROSE_STATE_3: /* HB */ - if (sk->protinfo.rose->condition & ROSE_COND_ACK_PENDING) { - sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; - rose_enquiry_response(sk); - } - break; case ROSE_STATE_1: /* T1 */ case ROSE_STATE_4: /* T2 */ rose_write_internal(sk, ROSE_CLEAR_REQUEST); sk->protinfo.rose->state = ROSE_STATE_2; - sk->protinfo.rose->timer = sk->protinfo.rose->t3; + rose_start_t3timer(sk); break; case ROSE_STATE_2: /* T3 */ - rose_clear_queues(sk); - sk->protinfo.rose->state = ROSE_STATE_0; - sk->state = TCP_CLOSE; - sk->err = ETIMEDOUT; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + sk->protinfo.rose->neighbour->use--; + rose_disconnect(sk, ETIMEDOUT, -1, -1); + break; + + case ROSE_STATE_3: /* HB */ + if (sk->protinfo.rose->condition & ROSE_COND_ACK_PENDING) { + sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING; + rose_enquiry_response(sk); + } break; } +} + +static void rose_idletimer_expiry(unsigned long param) +{ + struct sock *sk = (struct sock *)param; + + rose_clear_queues(sk); + + rose_write_internal(sk, ROSE_CLEAR_REQUEST); + sk->protinfo.rose->state = ROSE_STATE_2; + + rose_start_t3timer(sk); + + sk->state = TCP_CLOSE; + sk->err = 0; + sk->shutdown |= SEND_SHUTDOWN; + + if (!sk->dead) + sk->state_change(sk); - rose_set_timer(sk); + sk->dead = 1; } #endif diff --git a/net/rose/sysctl_net_rose.c b/net/rose/sysctl_net_rose.c index 409f79b52..ad45e3b47 100644 --- a/net/rose/sysctl_net_rose.c +++ b/net/rose/sysctl_net_rose.c @@ -11,13 +11,13 @@ #include <net/ax25.h> #include <net/rose.h> -static int min_timer[] = {1 * ROSE_SLOWHZ}; -static int max_timer[] = {300 * ROSE_SLOWHZ}; -static int min_idle[] = {0 * ROSE_SLOWHZ}; -static int max_idle[] = {65535 * ROSE_SLOWHZ}; +static int min_timer[] = {1 * HZ}; +static int max_timer[] = {300 * HZ}; +static int min_idle[] = {0 * HZ}; +static int max_idle[] = {65535 * HZ}; static int min_route[] = {0}, max_route[] = {1}; -static int min_ftimer[] = {60 * ROSE_SLOWHZ}; -static int max_ftimer[] = {600 * ROSE_SLOWHZ}; +static int min_ftimer[] = {60 * HZ}; +static int max_ftimer[] = {600 * HZ}; static int min_maxvcs[] = {1}, max_maxvcs[] = {254}; static int min_window[] = {1}, max_window[] = {7}; diff --git a/net/socket.c b/net/socket.c index 2587083bc..4b722e127 100644 --- a/net/socket.c +++ b/net/socket.c @@ -149,7 +149,11 @@ static int sockets_in_use = 0; #define MAX_SOCK_ADDR 128 /* 108 for Unix domain - 16 for IP, 16 for IPX, 24 for IPv6, - about 80 for AX.25 */ + about 80 for AX.25 + must be at least one bigger than + the AF_UNIX size (see net/unix/af_unix.c + :unix_mkname()). + */ int move_addr_to_kernel(void *uaddr, int ulen, void *kaddr) { @@ -206,13 +210,23 @@ static int get_fd(struct inode *inode) return -ENFILE; } + file->f_dentry = d_alloc_root(inode, NULL); + if (!file->f_dentry) { + put_filp(file); + put_unused_fd(fd); + return -ENOMEM; + } + + /* + * The socket maintains a reference to the inode, so we + * have to increment the count. + */ + inode->i_count++; + current->files->fd[fd] = file; file->f_op = &socket_file_ops; file->f_mode = 3; file->f_flags = O_RDWR; - file->f_inode = inode; - if (inode) - atomic_inc(&inode->i_count); file->f_pos = 0; } return fd; @@ -238,11 +252,11 @@ extern __inline__ struct socket *sockfd_lookup(int fd, int *err) return NULL; } - inode = file->f_inode; + inode = file->f_dentry->d_inode; if (!inode || !inode->i_sock || !socki_lookup(inode)) { *err = -ENOTSOCK; - fput(file,inode); + fput(file); return NULL; } @@ -251,7 +265,7 @@ extern __inline__ struct socket *sockfd_lookup(int fd, int *err) extern __inline__ void sockfd_put(struct socket *sock) { - fput(sock->file,sock->inode); + fput(sock->file); } /* @@ -266,6 +280,7 @@ struct socket *sock_alloc(void) inode = get_empty_inode(); if (!inode) return NULL; + sock = socki_lookup(inode); inode->i_mode = S_IFSOCK; @@ -459,7 +474,7 @@ static unsigned int sock_poll(struct file *file, poll_table * wait) { struct socket *sock; - sock = socki_lookup(file->f_inode); + sock = socki_lookup(file->f_dentry->d_inode); /* * We can't return errors to poll, so it's either yes or no. @@ -1291,7 +1306,7 @@ int sock_fcntl(struct file *filp, unsigned int cmd, unsigned long arg) { struct socket *sock; - sock = socki_lookup (filp->f_inode); + sock = socki_lookup (filp->f_dentry->d_inode); if (sock && sock->ops && sock->ops->fcntl) return sock->ops->fcntl(sock, cmd, arg); return(-EINVAL); diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 1dfdf1832..f41213ad6 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -162,8 +162,16 @@ static int unix_mkname(struct sockaddr_un * sunaddr, int len, unsigned *hashp) return -EINVAL; if (sunaddr->sun_path[0]) { - if (len >= sizeof(*sunaddr)) - len = sizeof(*sunaddr)-1; + /* + * This may look like an off by one error but it is + * a bit more subtle. 108 is the longest valid AF_UNIX + * path for a binding. sun_path[108] doesnt as such + * exist. However in kernel space we are guaranteed that + * it is a valid memory location in our kernel + * address buffer. + */ + if (len > sizeof(*sunaddr)) + len = sizeof(*sunaddr); ((char *)sunaddr)[len]=0; len = strlen(sunaddr->sun_path)+1+sizeof(short); return len; @@ -450,24 +458,18 @@ retry: static unix_socket *unix_find_other(struct sockaddr_un *sunname, int len, int type, unsigned hash, int *error) { - int old_fs; - int err; - struct inode *inode; unix_socket *u; if (sunname->sun_path[0]) { - old_fs=get_fs(); - set_fs(get_ds()); - err = open_namei(sunname->sun_path, 2, S_IFSOCK, &inode, NULL); - set_fs(old_fs); - if(err<0) - { - *error=err; + struct dentry *dentry; + dentry = open_namei(sunname->sun_path, 2, S_IFSOCK); + if (IS_ERR(dentry)) { + *error = PTR_ERR(dentry); return NULL; } - u=unix_find_socket_byinode(inode); - iput(inode); + u=unix_find_socket_byinode(dentry->d_inode); + dput(dentry); if (u && u->type != type) { *error=-EPROTOTYPE; @@ -491,8 +493,8 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sock *sk = sock->sk; struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr; - struct inode * inode; - int old_fs; + struct dentry * dentry; + struct inode * inode = NULL; int err; unsigned hash; struct unix_address *addr; @@ -545,15 +547,16 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) addr->hash = UNIX_HASH_SIZE; sk->protinfo.af_unix.addr = addr; - old_fs=get_fs(); - set_fs(get_ds()); - err=do_mknod(sunaddr->sun_path, S_IFSOCK|S_IRWXUGO, 0); - if (!err) - err=open_namei(sunaddr->sun_path, 2, S_IFSOCK, &inode, NULL); - - set_fs(old_fs); - + dentry = do_mknod(sunaddr->sun_path, S_IFSOCK|S_IRWXUGO, 0); + err = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + inode = dentry->d_inode; + inode->i_count++; /* HATEFUL - we should use the dentry */ + dput(dentry); + err = 0; + } + if(err<0) { unix_release_addr(addr); @@ -799,7 +802,7 @@ static int unix_accept(struct socket *sock, struct socket *newsock, int flags) } if (sk->protinfo.af_unix.inode) { - atomic_inc(&sk->protinfo.af_unix.inode->i_count); + sk->protinfo.af_unix.inode->i_count++; /* Should use dentry */ newsk->protinfo.af_unix.inode=sk->protinfo.af_unix.inode; } diff --git a/net/unix/garbage.c b/net/unix/garbage.c index 2a10304df..cf0d634bc 100644 --- a/net/unix/garbage.c +++ b/net/unix/garbage.c @@ -68,7 +68,7 @@ static int in_stack = 0; /* first free entry in stack */ extern inline unix_socket *unix_get_socket(struct file *filp) { unix_socket * u_sock = NULL; - struct inode *inode = filp->f_inode; + struct inode *inode = filp->f_dentry->d_inode; /* * Socket ? diff --git a/net/unix/sysctl_net_unix.c b/net/unix/sysctl_net_unix.c index 18ce57684..f487ae95a 100644 --- a/net/unix/sysctl_net_unix.c +++ b/net/unix/sysctl_net_unix.c @@ -13,6 +13,9 @@ #include <linux/mm.h> #include <linux/sysctl.h> +#include <linux/config.h> + +#ifdef CONFIG_SYSCTL extern int sysctl_unix_destroy_delay; extern int sysctl_unix_delete_delay; @@ -26,3 +29,4 @@ ctl_table unix_table[] = { &proc_dointvec_jiffies}, {0} }; +#endif diff --git a/net/wanrouter/patchlevel b/net/wanrouter/patchlevel index 3eefcb9dd..b2292bed7 100644 --- a/net/wanrouter/patchlevel +++ b/net/wanrouter/patchlevel @@ -1 +1,2 @@ -1.0.0 +1.0.3-modified + diff --git a/net/wanrouter/wanmain.c b/net/wanrouter/wanmain.c index 4c0042082..66b99dedc 100644 --- a/net/wanrouter/wanmain.c +++ b/net/wanrouter/wanmain.c @@ -11,15 +11,17 @@ * * Author: Gene Kozin <genek@compuserve.com> * -* Copyright: (c) 1995-1996 Sangoma Technologies Inc. +* Copyright: (c) 1995-1997 Sangoma Technologies Inc. * * 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 of the License, or (at your option) any later version. * ============================================================================ -* Dec 27, 1996 Gene Kozin Initial version (based on Sangoma's WANPIPE) +* Jun 27, 1997 Alan Cox realigned with vendor code +* Jan 16, 1997 Gene Kozin router_devlist made public * Jan 31, 1997 Alan Cox Hacked it about a bit for 2.1 +* Dec 27, 1996 Gene Kozin Initial version (based on Sangoma's WANPIPE) *****************************************************************************/ #include <linux/stddef.h> /* offsetof(), etc. */ @@ -78,9 +80,9 @@ static int delete_interface (wan_device_t* wandev, char* name, int forse); */ static char fullname[] = "WAN Router"; -static char copyright[] = "(c) 1995-1996 Sangoma Technologies Inc."; +static char copyright[] = "(c) 1995-1997 Sangoma Technologies Inc."; static char modname[] = ROUTER_NAME; /* short module name */ -static wan_device_t* devlist = NULL; /* list of registered devices */ +wan_device_t * router_devlist = NULL; /* list of registered devices */ static int devcnt = 0; /* @@ -199,8 +201,8 @@ int register_wan_device(wan_device_t* wandev) wandev->ndev = 0; wandev->dev = NULL; - wandev->next = devlist; - devlist = wandev; + wandev->next = router_devlist; + router_devlist = wandev; ++devcnt; MOD_INC_USE_COUNT; /* prevent module from unloading */ return 0; @@ -225,7 +227,7 @@ int unregister_wan_device(char* name) if (name == NULL) return -EINVAL; - for (wandev = devlist, prev = NULL; + for (wandev = router_devlist, prev = NULL; wandev && strcmp(wandev->name, name); prev = wandev, wandev = wandev->next) ; @@ -246,7 +248,7 @@ int unregister_wan_device(char* name) if (prev) prev->next = wandev->next; else - devlist = wandev->next; + router_devlist = wandev->next; --devcnt; wanrouter_proc_delete(wandev); MOD_DEC_USE_COUNT; @@ -613,7 +615,7 @@ static wan_device_t* find_device (char* name) { wan_device_t* wandev; - for (wandev = devlist;wandev && strcmp(wandev->name, name); + for (wandev = router_devlist;wandev && strcmp(wandev->name, name); wandev = wandev->next); return wandev; } diff --git a/net/wanrouter/wanproc.c b/net/wanrouter/wanproc.c index 04a49d803..9c3fe9b2a 100644 --- a/net/wanrouter/wanproc.c +++ b/net/wanrouter/wanproc.c @@ -1,20 +1,22 @@ /***************************************************************************** -* wanproc.c WAN Multiprotocol Router Module. proc filesystem interface. +* wanproc.c WAN Router Module. /proc filesystem interface. * * This module is completely hardware-independent and provides * access to the router using Linux /proc filesystem. * * Author: Gene Kozin <genek@compuserve.com> * -* Copyright: (c) 1995-1996 Sangoma Technologies Inc. +* Copyright: (c) 1995-1997 Sangoma Technologies Inc. * * 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 of the License, or (at your option) any later version. * ============================================================================ -* Dec 13, 1996 Gene Kozin Initial version (based on Sangoma's WANPIPE) +* Jun 29, 1997 Alan Cox Merged with 1.0.3 vendor code +* Jan 29, 1997 Gene Kozin v1.0.1. Implemented /proc read routines * Jan 30, 1997 Alan Cox Hacked around for 2.1 +* Dec 13, 1996 Gene Kozin Initial version (based on Sangoma's WANPIPE) *****************************************************************************/ #include <linux/stddef.h> /* offsetof(), etc. */ @@ -39,7 +41,7 @@ #define max(a,b) (((a)>(b))?(a):(b)) #endif -#define ROUTER_PAGE_SZ 4000 /* buffer size for printing proc info */ +#define PROC_BUFSZ 4000 /* buffer size for printing proc info */ /****** Data Types **********************************************************/ @@ -60,7 +62,6 @@ static long router_proc_read(struct inode* inode, struct file* file, char* buf, /* Methods for preparing data for reading proc entries */ -static int about_get_info(char* buf, char** start, off_t offs, int len, int dummy); static int config_get_info(char* buf, char** start, off_t offs, int len, int dummy); static int status_get_info(char* buf, char** start, off_t offs, int len, int dummy); static int wandev_get_info(char* buf, char** start, off_t offs, int len, int dummy); @@ -76,7 +77,6 @@ static int wandev_get_info(char* buf, char** start, off_t offs, int len, int dum */ static char name_root[] = ROUTER_NAME; -static char name_info[] = "about"; static char name_conf[] = "config"; static char name_stat[] = "status"; @@ -84,9 +84,8 @@ static char name_stat[] = "status"; * Structures for interfacing with the /proc filesystem. * Router creates its own directory /proc/net/router with the folowing * entries: - * About general information (version, copyright, etc.) - * Conf device configuration - * Stat global device statistics + * config device configuration + * status global device statistics * <device> entry for each WAN device */ @@ -194,29 +193,6 @@ static struct proc_dir_entry proc_router = }; /* - * /proc/net/router/about - */ - -static struct proc_dir_entry proc_router_info = -{ - 0, /* .low_ino */ - sizeof(name_info) - 1, /* .namelen */ - name_info, /* .name */ - 0444 | S_IFREG, /* .mode */ - 1, /* .nlink */ - 0, /* .uid */ - 0, /* .gid */ - 0, /* .size */ - &router_inode, /* .ops */ - &about_get_info, /* .get_info */ - NULL, /* .fill_node */ - NULL, /* .next */ - NULL, /* .parent */ - NULL, /* .subdir */ - NULL, /* .data */ -}; - -/* * /proc/net/router/config */ @@ -262,6 +238,16 @@ static struct proc_dir_entry proc_router_stat = NULL, /* .data */ }; +/* Strings */ +static char conf_hdr[] = + "Device name | port |IRQ|DMA|mem.addr|mem.size|" + "option1|option2|option3|option4\n"; + +static char stat_hdr[] = + "Device name |station|interface|clocking|baud rate| MTU |ndev" + "|link state\n"; + + /* * Interface functions */ @@ -272,11 +258,10 @@ static struct proc_dir_entry proc_router_stat = __initfunc(int wanrouter_proc_init (void)) { - int err = proc_register(&proc_net, &proc_router); + int err = proc_register(proc_net, &proc_router); if (!err) { - proc_register(&proc_router, &proc_router_info); proc_register(&proc_router, &proc_router_conf); proc_register(&proc_router, &proc_router_stat); } @@ -289,10 +274,9 @@ __initfunc(int wanrouter_proc_init (void)) void wanrouter_proc_cleanup (void) { - proc_unregister(&proc_router, proc_router_info.low_ino); proc_unregister(&proc_router, proc_router_conf.low_ino); proc_unregister(&proc_router, proc_router_stat.low_ino); - proc_unregister(&proc_net, proc_router.low_ino); + proc_unregister(proc_net, proc_router.low_ino); } /* @@ -367,7 +351,7 @@ static long router_proc_read(struct inode* inode, struct file* file, if ((dent == NULL) || (dent->get_info == NULL)) return 0; - page = kmalloc(ROUTER_PAGE_SZ, GFP_KERNEL); + page = kmalloc(PROC_BUFSZ, GFP_KERNEL); if (page == NULL) return -ENOBUFS; @@ -387,49 +371,86 @@ static long router_proc_read(struct inode* inode, struct file* file, } /* - * Prepare data for reading 'About' entry. - * Return length of data. - */ - -static int about_get_info(char* buf, char** start, off_t offs, int len, - int dummy) -{ - int cnt = 0; - - cnt += sprintf(&buf[cnt], "%12s : %u.%u\n", - "version", ROUTER_VERSION, ROUTER_RELEASE); - return cnt; -} - -/* * Prepare data for reading 'Config' entry. * Return length of data. - * NOT YET IMPLEMENTED */ static int config_get_info(char* buf, char** start, off_t offs, int len, int dummy) { - int cnt = 0; + int cnt = sizeof(conf_hdr) - 1; + wan_device_t* wandev; + strcpy(buf, conf_hdr); + for (wandev = router_devlist; + wandev && (cnt < (PROC_BUFSZ - 80)); + wandev = wandev->next) + { + if (wandev->state) cnt += sprintf(&buf[cnt], + "%-15s|0x%-4X|%3u|%3u|0x%-6lX|0x%-6X|%7u|%7u|%7u|%7u\n", + wandev->name, + wandev->ioport, + wandev->irq, + wandev->dma, + wandev->maddr, + wandev->msize, + wandev->hw_opt[0], + wandev->hw_opt[1], + wandev->hw_opt[2], + wandev->hw_opt[3]); + } - cnt += sprintf(&buf[cnt], "%12s : %u.%u\n", - "version", ROUTER_VERSION, ROUTER_RELEASE); return cnt; } /* * Prepare data for reading 'Status' entry. * Return length of data. - * NOT YET IMPLEMENTED */ static int status_get_info(char* buf, char** start, off_t offs, int len, int dummy) { - int cnt = 0; - - cnt += sprintf(&buf[cnt], "%12s : %u.%u\n", - "version", ROUTER_VERSION, ROUTER_RELEASE); + int cnt = sizeof(stat_hdr) - 1; + wan_device_t* wandev; + strcpy(buf, stat_hdr); + for (wandev = router_devlist; + wandev && (cnt < (PROC_BUFSZ - 80)); + wandev = wandev->next) + { + if (!wandev->state) continue; + cnt += sprintf(&buf[cnt], + "%-15s|%-7s|%-9s|%-8s|%9u|%5u|%3u |", + wandev->name, + wandev->station ? " DCE" : " DTE", + wandev->interface ? " V.35" : " RS-232", + wandev->clocking ? "internal" : "external", + wandev->bps, + wandev->mtu, + wandev->ndev) + ; + switch (wandev->state) + { + case WAN_UNCONFIGURED: + cnt += sprintf(&buf[cnt], "%-12s\n", "unconfigured"); + break; + + case WAN_DISCONNECTED: + cnt += sprintf(&buf[cnt], "%-12s\n", "disconnected"); + break; + + case WAN_CONNECTING: + cnt += sprintf(&buf[cnt], "%-12s\n", "connecting"); + break; + + case WAN_CONNECTED: + cnt += sprintf(&buf[cnt], "%-12s\n", "connected"); + break; + + default: + cnt += sprintf(&buf[cnt], "%-12s\n", "invalid"); + break; + } + } return cnt; } @@ -449,7 +470,52 @@ static int wandev_get_info(char* buf, char** start, off_t offs, int len, if ((wandev == NULL) || (wandev->magic != ROUTER_MAGIC)) return 0; - cnt += sprintf(&buf[cnt], "%12s : %s\n", "name", wandev->name); + if (!wandev->state) + return sprintf(&buf[cnt], "device is not configured!\n") + ; + + /* Update device statistics */ + if (wandev->update) wandev->update(wandev); + + cnt += sprintf(&buf[cnt], "%30s: %12lu\n", + "total frames received", wandev->stats.rx_packets) + ; + cnt += sprintf(&buf[cnt], "%30s: %12lu\n", + "receiver overrun errors", wandev->stats.rx_over_errors) + ; + cnt += sprintf(&buf[cnt], "%30s: %12lu\n", + "CRC errors", wandev->stats.rx_crc_errors) + ; + cnt += sprintf(&buf[cnt], "%30s: %12lu\n", + "frame length errors", wandev->stats.rx_length_errors) + ; + cnt += sprintf(&buf[cnt], "%30s: %12lu\n", + "frame format errors", wandev->stats.rx_frame_errors) + ; + cnt += sprintf(&buf[cnt], "%30s: %12lu\n", + "aborted frames received", wandev->stats.rx_missed_errors) + ; + cnt += sprintf(&buf[cnt], "%30s: %12lu\n", + "reveived frames dropped", wandev->stats.rx_dropped) + ; + cnt += sprintf(&buf[cnt], "%30s: %12lu\n", + "other receive errors", wandev->stats.rx_errors) + ; + cnt += sprintf(&buf[cnt], "\n%30s: %12lu\n", + "total frames transmitted", wandev->stats.tx_packets) + ; + cnt += sprintf(&buf[cnt], "%30s: %12lu\n", + "aborted frames transmitted", wandev->stats.tx_aborted_errors) + ; + cnt += sprintf(&buf[cnt], "%30s: %12lu\n", + "transmit frames dropped", wandev->stats.tx_dropped) + ; + cnt += sprintf(&buf[cnt], "%30s: %12lu\n", + "transmit collisions", wandev->stats.collisions) + ; + cnt += sprintf(&buf[cnt], "%30s: %12lu\n", + "other transmit errors", wandev->stats.tx_errors) + ; return cnt; } diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index a77380648..f59dd3a51 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -1,5 +1,5 @@ /* - * X.25 Packet Layer release 001 + * X.25 Packet Layer release 002 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. @@ -14,6 +14,8 @@ * * History * X.25 001 Jonathan Naylor Started coding. + * X.25 002 Jonathan Naylor Centralised disconnect handling. + * New timer architecture. */ #include <linux/config.h> @@ -54,8 +56,6 @@ int sysctl_x25_reset_request_timeout = X25_DEFAULT_T22; int sysctl_x25_clear_request_timeout = X25_DEFAULT_T23; int sysctl_x25_ack_holdback_timeout = X25_DEFAULT_T2; -static unsigned int lci = 1; - static struct sock *volatile x25_list = NULL; static struct proto_ops x25_proto_ops; @@ -173,16 +173,9 @@ static void x25_kill_by_device(struct device *dev) { struct sock *s; - for (s = x25_list; s != NULL; s = s->next) { - if (s->protinfo.x25->neighbour->dev == dev) { - s->protinfo.x25->state = X25_STATE_0; - s->state = TCP_CLOSE; - s->err = ENETUNREACH; - s->shutdown |= SEND_SHUTDOWN; - s->state_change(s); - s->dead = 1; - } - } + for (s = x25_list; s != NULL; s = s->next) + if (s->protinfo.x25->neighbour->dev == dev) + x25_disconnect(s, ENETUNREACH, 0, 0); } /* @@ -254,9 +247,9 @@ static struct sock *x25_find_listener(x25_address *addr) } /* - * Find a connected X.25 socket given my LCI. + * Find a connected X.25 socket given my LCI and neighbour. */ -struct sock *x25_find_socket(unsigned int lci) +struct sock *x25_find_socket(unsigned int lci, struct x25_neigh *neigh) { struct sock *s; unsigned long flags; @@ -265,7 +258,7 @@ struct sock *x25_find_socket(unsigned int lci) cli(); for (s = x25_list; s != NULL; s = s->next) { - if (s->protinfo.x25->lci == lci) { + if (s->protinfo.x25->lci == lci && s->protinfo.x25->neighbour == neigh) { restore_flags(flags); return s; } @@ -278,14 +271,13 @@ struct sock *x25_find_socket(unsigned int lci) /* * Find a unique LCI for a given device. */ -unsigned int x25_new_lci(void) +unsigned int x25_new_lci(struct x25_neigh *neigh) { - lci++; - if (lci > 4095) lci = 1; + unsigned int lci = 1; - while (x25_find_socket(lci) != NULL) { + while (x25_find_socket(lci, neigh) != NULL) { lci++; - if (lci > 4095) lci = 1; + if (lci == 4096) return 0; } return lci; @@ -318,7 +310,8 @@ void x25_destroy_socket(struct sock *sk) /* Not static as it's used by the timer save_flags(flags); cli(); - del_timer(&sk->timer); + x25_stop_heartbeat(sk); + x25_stop_timer(sk); x25_remove_socket(sk); x25_clear_queues(sk); /* Flush the queues */ @@ -326,7 +319,7 @@ void x25_destroy_socket(struct sock *sk) /* Not static as it's used by the timer while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) { if (skb->sk != sk) { /* A pending connection */ skb->sk->dead = 1; /* Queue the unaccepted socket for death */ - x25_set_timer(skb->sk); + x25_start_heartbeat(skb->sk); skb->sk->protinfo.x25->state = X25_STATE_0; } @@ -469,6 +462,8 @@ static int x25_create(struct socket *sock, int protocol) sock_init_data(sock, sk); + init_timer(&x25->timer); + sock->ops = &x25_proto_ops; sk->protocol = protocol; sk->mtu = X25_DEFAULT_PACKET_SIZE; /* X25_PS128 */ @@ -523,6 +518,8 @@ static struct sock *x25_make_new(struct sock *osk) x25->qbitincl = osk->protinfo.x25->qbitincl; + init_timer(&x25->timer); + return sk; } @@ -545,28 +542,17 @@ static int x25_release(struct socket *sock, struct socket *peer) switch (sk->protinfo.x25->state) { case X25_STATE_0: - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; - x25_destroy_socket(sk); - break; - case X25_STATE_2: - sk->protinfo.x25->state = X25_STATE_0; - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; + x25_disconnect(sk, 0, 0, 0); x25_destroy_socket(sk); - break; + break; case X25_STATE_1: case X25_STATE_3: case X25_STATE_4: x25_clear_queues(sk); x25_write_internal(sk, X25_CLEAR_REQUEST); - sk->protinfo.x25->timer = sk->protinfo.x25->t23; + x25_start_t23timer(sk); sk->protinfo.x25->state = X25_STATE_2; sk->state = TCP_CLOSE; sk->shutdown |= SEND_SHUTDOWN; @@ -644,6 +630,9 @@ static int x25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len if ((sk->protinfo.x25->neighbour = x25_get_neigh(dev)) == NULL) return -ENETUNREACH; + if ((sk->protinfo.x25->lci = x25_new_lci(sk->protinfo.x25->neighbour)) == 0) + return -ENETUNREACH; + if (sk->zapped) /* Must bind first - autobinding does not work */ return -EINVAL; @@ -651,17 +640,17 @@ static int x25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len memset(&sk->protinfo.x25->source_addr, '\0', X25_ADDR_LEN); sk->protinfo.x25->dest_addr = addr->sx25_addr; - sk->protinfo.x25->lci = x25_new_lci(); /* Move to connecting socket, start sending Connect Requests */ sock->state = SS_CONNECTING; sk->state = TCP_SYN_SENT; sk->protinfo.x25->state = X25_STATE_1; - sk->protinfo.x25->timer = sk->protinfo.x25->t21; + x25_write_internal(sk, X25_CALL_REQUEST); - x25_set_timer(sk); + x25_start_heartbeat(sk); + x25_start_t21timer(sk); /* Now the loop */ if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) @@ -850,7 +839,7 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *neigh, unsigned i skb_queue_head(&sk->receive_queue, skb); - x25_set_timer(make); + x25_start_heartbeat(make); if (!sk->dead) sk->data_ready(sk, skb->len); @@ -987,8 +976,7 @@ static int x25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct x25_output(sk, skb); } - if (sk->protinfo.x25->state == X25_STATE_3) - x25_kick(sk); + x25_kick(sk); return len; } @@ -1072,30 +1060,27 @@ static int x25_shutdown(struct socket *sk, int how) static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { - struct x25_facilities facilities; - struct x25_calluserdata calluserdata; struct sock *sk = sock->sk; - int err; - long amount = 0; switch (cmd) { - case TIOCOUTQ: - if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned long))) != 0) - return err; + case TIOCOUTQ: { + long amount; amount = sk->sndbuf - atomic_read(&sk->wmem_alloc); if (amount < 0) amount = 0; - put_user(amount, (unsigned long *)arg); + if (put_user(amount, (unsigned long *)arg)) + return -EFAULT; return 0; + } case TIOCINQ: { struct sk_buff *skb; + long amount = 0L; /* These two are safe on a single CPU system as only user tasks fiddle here */ if ((skb = skb_peek(&sk->receive_queue)) != NULL) - amount = skb->len - 20; - if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned long))) != 0) - return err; - put_user(amount, (unsigned long *)arg); + amount = skb->len; + if (put_user(amount, (unsigned long *)arg)) + return -EFAULT; return 0; } @@ -1103,9 +1088,8 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) if (sk != NULL) { if (sk->stamp.tv_sec == 0) return -ENOENT; - if ((err = verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval))) != 0) - return err; - copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)); + if (copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval))) + return -EFAULT; return 0; } return -EINVAL; @@ -1134,17 +1118,18 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) if (!suser()) return -EPERM; return x25_subscr_ioctl(cmd, (void *)arg); - case SIOCX25GFACILITIES: - if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(facilities))) != 0) - return err; + case SIOCX25GFACILITIES: { + struct x25_facilities facilities; facilities = sk->protinfo.x25->facilities; - copy_to_user((void *)arg, &facilities, sizeof(facilities)); + if (copy_to_user((void *)arg, &facilities, sizeof(facilities))) + return -EFAULT; return 0; + } - case SIOCX25SFACILITIES: - if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(facilities))) != 0) - return err; - copy_from_user(&facilities, (void *)arg, sizeof(facilities)); + case SIOCX25SFACILITIES: { + struct x25_facilities facilities; + if (copy_from_user(&facilities, (void *)arg, sizeof(facilities))) + return -EFAULT; if (sk->state != TCP_LISTEN) return -EINVAL; if (facilities.pacsize_in < X25_PS16 || facilities.pacsize_in > X25_PS4096) @@ -1168,22 +1153,33 @@ static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) return -EINVAL; sk->protinfo.x25->facilities = facilities; return 0; + } - case SIOCX25GCALLUSERDATA: - if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(calluserdata))) != 0) - return err; + case SIOCX25GCALLUSERDATA: { + struct x25_calluserdata calluserdata; calluserdata = sk->protinfo.x25->calluserdata; - copy_to_user((void *)arg, &calluserdata, sizeof(calluserdata)); + if (copy_to_user((void *)arg, &calluserdata, sizeof(calluserdata))) + return -EFAULT; return 0; + } - case SIOCX25SCALLUSERDATA: - if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(calluserdata))) != 0) - return err; - copy_from_user(&calluserdata, (void *)arg, sizeof(calluserdata)); + case SIOCX25SCALLUSERDATA: { + struct x25_calluserdata calluserdata; + if (copy_from_user(&calluserdata, (void *)arg, sizeof(calluserdata))) + return -EFAULT; if (calluserdata.cudlength > X25_MAX_CUD_LEN) return -EINVAL; sk->protinfo.x25->calluserdata = calluserdata; return 0; + } + + case SIOCX25GCAUSEDIAG: { + struct x25_causediag causediag; + causediag = sk->protinfo.x25->causediag; + if (copy_to_user((void *)arg, &causediag, sizeof(causediag))) + return -EFAULT; + return 0; + } default: return dev_ioctl(cmd, (void *)arg); @@ -1212,18 +1208,22 @@ static int x25_get_info(char *buffer, char **start, off_t offset, int length, in else devname = s->protinfo.x25->neighbour->dev->name; - len += sprintf(buffer + len, "%-10s %-10s %-5s %3.3X %d %d %d %d %3d %3d %3d %3d %3d %5d %5d\n", + len += sprintf(buffer + len, "%-10s %-10s %-5s %3.3X %d %d %d %d %3lu %3lu %3lu %3lu %3lu %5d %5d\n", (s->protinfo.x25->dest_addr.x25_addr[0] == '\0') ? "*" : s->protinfo.x25->dest_addr.x25_addr, (s->protinfo.x25->source_addr.x25_addr[0] == '\0') ? "*" : s->protinfo.x25->source_addr.x25_addr, - devname, s->protinfo.x25->lci & 0x0FFF, + devname, + s->protinfo.x25->lci & 0x0FFF, s->protinfo.x25->state, - s->protinfo.x25->vs, s->protinfo.x25->vr, s->protinfo.x25->va, - s->protinfo.x25->timer / X25_SLOWHZ, - s->protinfo.x25->t2 / X25_SLOWHZ, - s->protinfo.x25->t21 / X25_SLOWHZ, - s->protinfo.x25->t22 / X25_SLOWHZ, - s->protinfo.x25->t23 / X25_SLOWHZ, - atomic_read(&s->wmem_alloc), atomic_read(&s->rmem_alloc)); + s->protinfo.x25->vs, + s->protinfo.x25->vr, + s->protinfo.x25->va, + x25_display_timer(s) / HZ, + s->protinfo.x25->t2 / HZ, + s->protinfo.x25->t21 / HZ, + s->protinfo.x25->t22 / HZ, + s->protinfo.x25->t23 / HZ, + atomic_read(&s->wmem_alloc), + atomic_read(&s->rmem_alloc)); pos = begin + len; @@ -1310,7 +1310,7 @@ __initfunc(void x25_proto_init(struct net_proto *pro)) register_netdevice_notifier(&x25_dev_notifier); - printk(KERN_INFO "X.25 for Linux. Version 0.1 for Linux 2.1.15\n"); + printk(KERN_INFO "X.25 for Linux. Version 0.2 for Linux 2.1.15\n"); #ifdef CONFIG_SYSCTL x25_register_sysctl(); diff --git a/net/x25/sysctl_net_x25.c b/net/x25/sysctl_net_x25.c index 8454ac9d9..42893df32 100644 --- a/net/x25/sysctl_net_x25.c +++ b/net/x25/sysctl_net_x25.c @@ -13,8 +13,8 @@ #include <linux/init.h> #include <net/x25.h> -static int min_timer[] = {1 * X25_SLOWHZ}; -static int max_timer[] = {300 * X25_SLOWHZ}; +static int min_timer[] = {1 * HZ}; +static int max_timer[] = {300 * HZ}; static struct ctl_table_header *x25_table_header; diff --git a/net/x25/x25_dev.c b/net/x25/x25_dev.c index 6b02a9441..e4cd99ae7 100644 --- a/net/x25/x25_dev.c +++ b/net/x25/x25_dev.c @@ -1,5 +1,5 @@ /* - * X.25 Packet Layer release 001 + * X.25 Packet Layer release 002 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. @@ -73,7 +73,7 @@ static int x25_receive_data(struct sk_buff *skb, struct x25_neigh *neigh) /* * Find an existing socket. */ - if ((sk = x25_find_socket(lci)) != NULL) { + if ((sk = x25_find_socket(lci, neigh)) != NULL) { skb->h.raw = skb->data; return x25_process_rx_frame(sk, skb); } diff --git a/net/x25/x25_facilities.c b/net/x25/x25_facilities.c index 13759d1ea..af072ce22 100644 --- a/net/x25/x25_facilities.c +++ b/net/x25/x25_facilities.c @@ -1,5 +1,5 @@ /* - * X.25 Packet Layer release 001 + * X.25 Packet Layer release 002 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. diff --git a/net/x25/x25_in.c b/net/x25/x25_in.c index 82e5c0817..96b459a4e 100644 --- a/net/x25/x25_in.c +++ b/net/x25/x25_in.c @@ -1,5 +1,5 @@ /* - * X.25 Packet Layer release 001 + * X.25 Packet Layer release 002 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. @@ -14,6 +14,8 @@ * * History * X.25 001 Jonathan Naylor Started coding. + * X.25 002 Jonathan Naylor Centralised disconnection code. + * New timer architecture. */ #include <linux/config.h> @@ -88,8 +90,8 @@ static int x25_state1_machine(struct sock *sk, struct sk_buff *skb, int frametyp switch (frametype) { case X25_CALL_ACCEPTED: + x25_stop_timer(sk); sk->protinfo.x25->condition = 0x00; - sk->protinfo.x25->timer = 0; sk->protinfo.x25->vs = 0; sk->protinfo.x25->va = 0; sk->protinfo.x25->vr = 0; @@ -114,15 +116,8 @@ static int x25_state1_machine(struct sock *sk, struct sk_buff *skb, int frametyp break; case X25_CLEAR_REQUEST: - x25_clear_queues(sk); x25_write_internal(sk, X25_CLEAR_CONFIRMATION); - sk->protinfo.x25->state = X25_STATE_0; - sk->state = TCP_CLOSE; - sk->err = ECONNREFUSED; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + x25_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]); break; default: @@ -143,15 +138,11 @@ static int x25_state2_machine(struct sock *sk, struct sk_buff *skb, int frametyp case X25_CLEAR_REQUEST: x25_write_internal(sk, X25_CLEAR_CONFIRMATION); + x25_disconnect(sk, 0, skb->data[3], skb->data[4]); + break; + case X25_CLEAR_CONFIRMATION: - x25_clear_queues(sk); - sk->protinfo.x25->state = X25_STATE_0; - sk->state = TCP_CLOSE; - sk->err = 0; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + x25_disconnect(sk, 0, 0, 0); break; default: @@ -177,8 +168,8 @@ static int x25_state3_machine(struct sock *sk, struct sk_buff *skb, int frametyp case X25_RESET_REQUEST: x25_write_internal(sk, X25_RESET_CONFIRMATION); + x25_stop_timer(sk); sk->protinfo.x25->condition = 0x00; - sk->protinfo.x25->timer = 0; sk->protinfo.x25->vs = 0; sk->protinfo.x25->vr = 0; sk->protinfo.x25->va = 0; @@ -186,15 +177,8 @@ static int x25_state3_machine(struct sock *sk, struct sk_buff *skb, int frametyp break; case X25_CLEAR_REQUEST: - x25_clear_queues(sk); x25_write_internal(sk, X25_CLEAR_CONFIRMATION); - sk->protinfo.x25->state = X25_STATE_0; - sk->state = TCP_CLOSE; - sk->err = 0; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + x25_disconnect(sk, 0, skb->data[3], skb->data[4]); break; case X25_RR: @@ -207,13 +191,13 @@ static int x25_state3_machine(struct sock *sk, struct sk_buff *skb, int frametyp if (!x25_validate_nr(sk, nr)) { x25_clear_queues(sk); x25_write_internal(sk, X25_RESET_REQUEST); + x25_start_t22timer(sk); sk->protinfo.x25->condition = 0x00; sk->protinfo.x25->vs = 0; sk->protinfo.x25->vr = 0; sk->protinfo.x25->va = 0; sk->protinfo.x25->vl = 0; sk->protinfo.x25->state = X25_STATE_4; - sk->protinfo.x25->timer = sk->protinfo.x25->t22; } else { if (sk->protinfo.x25->condition & X25_COND_PEER_RX_BUSY) { sk->protinfo.x25->va = nr; @@ -228,13 +212,13 @@ static int x25_state3_machine(struct sock *sk, struct sk_buff *skb, int frametyp if (!x25_validate_nr(sk, nr)) { x25_clear_queues(sk); x25_write_internal(sk, X25_RESET_REQUEST); + x25_start_t22timer(sk); sk->protinfo.x25->condition = 0x00; sk->protinfo.x25->vs = 0; sk->protinfo.x25->vr = 0; sk->protinfo.x25->va = 0; sk->protinfo.x25->vl = 0; sk->protinfo.x25->state = X25_STATE_4; - sk->protinfo.x25->timer = sk->protinfo.x25->t22; break; } if (sk->protinfo.x25->condition & X25_COND_PEER_RX_BUSY) { @@ -258,11 +242,11 @@ static int x25_state3_machine(struct sock *sk, struct sk_buff *skb, int frametyp */ if (((sk->protinfo.x25->vl + sk->protinfo.x25->facilities.winsize_in) % modulus) == sk->protinfo.x25->vr) { sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING; - sk->protinfo.x25->timer = 0; + x25_stop_timer(sk); x25_enquiry_response(sk); } else { sk->protinfo.x25->condition |= X25_COND_ACK_PENDING; - sk->protinfo.x25->timer = sk->protinfo.x25->t2; + x25_start_t2timer(sk); } break; @@ -307,7 +291,7 @@ static int x25_state4_machine(struct sock *sk, struct sk_buff *skb, int frametyp case X25_RESET_REQUEST: x25_write_internal(sk, X25_RESET_CONFIRMATION); case X25_RESET_CONFIRMATION: - sk->protinfo.x25->timer = 0; + x25_stop_timer(sk); sk->protinfo.x25->condition = 0x00; sk->protinfo.x25->va = 0; sk->protinfo.x25->vr = 0; @@ -317,16 +301,8 @@ static int x25_state4_machine(struct sock *sk, struct sk_buff *skb, int frametyp break; case X25_CLEAR_REQUEST: - x25_clear_queues(sk); x25_write_internal(sk, X25_CLEAR_CONFIRMATION); - sk->protinfo.x25->timer = 0; - sk->protinfo.x25->state = X25_STATE_0; - sk->state = TCP_CLOSE; - sk->err = 0; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + x25_disconnect(sk, 0, skb->data[3], skb->data[4]); break; default: @@ -344,8 +320,6 @@ int x25_process_rx_frame(struct sock *sk, struct sk_buff *skb) if (sk->protinfo.x25->state == X25_STATE_0) return 0; - del_timer(&sk->timer); - frametype = x25_decode(sk, skb, &ns, &nr, &q, &d, &m); switch (sk->protinfo.x25->state) { @@ -363,7 +337,7 @@ int x25_process_rx_frame(struct sock *sk, struct sk_buff *skb) break; } - x25_set_timer(sk); + x25_kick(sk); return queued; } diff --git a/net/x25/x25_link.c b/net/x25/x25_link.c index f44c0a2c5..1742d802f 100644 --- a/net/x25/x25_link.c +++ b/net/x25/x25_link.c @@ -1,5 +1,5 @@ /* - * X.25 Packet Layer release 001 + * X.25 Packet Layer release 002 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. @@ -14,6 +14,7 @@ * * History * X.25 001 Jonathan Naylor Started coding. + * X.25 002 Jonathan Naylor New timer architecture. */ #include <linux/config.h> @@ -43,49 +44,34 @@ static struct x25_neigh *x25_neigh_list = NULL; -static void x25_link_timer(unsigned long); +static void x25_t20timer_expiry(unsigned long); /* * Linux set/reset timer routines */ -static void x25_link_set_timer(struct x25_neigh *neigh) +static void x25_start_t20timer(struct x25_neigh *neigh) { - unsigned long flags; - - save_flags(flags); cli(); - del_timer(&neigh->timer); - restore_flags(flags); + del_timer(&neigh->t20timer); - neigh->timer.data = (unsigned long)neigh; - neigh->timer.function = &x25_link_timer; - neigh->timer.expires = jiffies + (HZ / 1); + neigh->t20timer.data = (unsigned long)neigh; + neigh->t20timer.function = &x25_t20timer_expiry; + neigh->t20timer.expires = jiffies + neigh->t20; - add_timer(&neigh->timer); + add_timer(&neigh->t20timer); } -/* - * X.25 Link TIMER - * - * This routine is called every second. Decrement timer by this - * amount - if expired then process the event. - */ -static void x25_link_timer(unsigned long param) +static void x25_t20timer_expiry(unsigned long param) { struct x25_neigh *neigh = (struct x25_neigh *)param; - if (neigh->t20timer == 0 || --neigh->t20timer > 0) { - x25_link_set_timer(neigh); - return; - } - - /* - * T20 for a link has expired. - */ x25_transmit_restart_request(neigh); - neigh->t20timer = neigh->t20; + x25_start_t20timer(neigh); +} - x25_link_set_timer(neigh); +static void x25_stop_t20timer(struct x25_neigh *neigh) +{ + del_timer(&neigh->t20timer); } /* @@ -97,16 +83,14 @@ void x25_link_control(struct sk_buff *skb, struct x25_neigh *neigh, unsigned sho switch (frametype) { case X25_RESTART_REQUEST: - neigh->t20timer = 0; - neigh->state = X25_LINK_STATE_3; - del_timer(&neigh->timer); + x25_stop_t20timer(neigh); + neigh->state = X25_LINK_STATE_3; x25_transmit_restart_confirmation(neigh); break; case X25_RESTART_CONFIRMATION: - neigh->t20timer = 0; - neigh->state = X25_LINK_STATE_3; - del_timer(&neigh->timer); + x25_stop_t20timer(neigh); + neigh->state = X25_LINK_STATE_3; break; case X25_DIAGNOSTIC: @@ -272,9 +256,8 @@ void x25_link_established(struct x25_neigh *neigh) break; case X25_LINK_STATE_1: x25_transmit_restart_request(neigh); - neigh->state = X25_LINK_STATE_2; - neigh->t20timer = neigh->t20; - x25_link_set_timer(neigh); + neigh->state = X25_LINK_STATE_2; + x25_start_t20timer(neigh); break; } } @@ -300,12 +283,12 @@ void x25_link_device_up(struct device *dev) return; skb_queue_head_init(&x25_neigh->queue); - init_timer(&x25_neigh->timer); + + init_timer(&x25_neigh->t20timer); x25_neigh->dev = dev; x25_neigh->state = X25_LINK_STATE_0; x25_neigh->extended = 0; - x25_neigh->t20timer = 0; x25_neigh->t20 = sysctl_x25_restart_request_timeout; save_flags(flags); cli(); @@ -323,10 +306,9 @@ static void x25_remove_neigh(struct x25_neigh *x25_neigh) while ((skb = skb_dequeue(&x25_neigh->queue)) != NULL) kfree_skb(skb, FREE_WRITE); - del_timer(&x25_neigh->timer); + x25_stop_t20timer(x25_neigh); - save_flags(flags); - cli(); + save_flags(flags); cli(); if ((s = x25_neigh_list) == x25_neigh) { x25_neigh_list = x25_neigh->next; @@ -387,25 +369,22 @@ int x25_subscr_ioctl(unsigned int cmd, void *arg) struct x25_subscrip_struct x25_subscr; struct x25_neigh *x25_neigh; struct device *dev; - int err; switch (cmd) { case SIOCX25GSUBSCRIP: - if ((err = verify_area(VERIFY_WRITE, arg, sizeof(struct x25_subscrip_struct))) != 0) - return err; if ((dev = x25_dev_get(x25_subscr.device)) == NULL) return -EINVAL; if ((x25_neigh = x25_get_neigh(dev)) == NULL) return -EINVAL; x25_subscr.extended = x25_neigh->extended; - copy_to_user(arg, &x25_subscr, sizeof(struct x25_subscrip_struct)); + if (copy_to_user(arg, &x25_subscr, sizeof(struct x25_subscrip_struct))) + return -EFAULT; break; case SIOCX25SSUBSCRIP: - if ((err = verify_area(VERIFY_READ, arg, sizeof(struct x25_subscrip_struct))) != 0) - return err; - copy_from_user(&x25_subscr, arg, sizeof(struct x25_subscrip_struct)); + if (copy_from_user(&x25_subscr, arg, sizeof(struct x25_subscrip_struct))) + return -EFAULT; if ((dev = x25_dev_get(x25_subscr.device)) == NULL) return -EINVAL; if ((x25_neigh = x25_get_neigh(dev)) == NULL) diff --git a/net/x25/x25_out.c b/net/x25/x25_out.c index 321baa5d6..aa8fc2c1b 100644 --- a/net/x25/x25_out.c +++ b/net/x25/x25_out.c @@ -1,5 +1,5 @@ /* - * X.25 Packet Layer release 001 + * X.25 Packet Layer release 002 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. @@ -14,6 +14,7 @@ * * History * X.25 001 Jonathan Naylor Started coding. + * X.25 002 Jonathan Naylor New timer architecture. */ #include <linux/config.h> @@ -129,7 +130,8 @@ void x25_kick(struct sock *sk) unsigned short end; int modulus; - del_timer(&sk->timer); + if (sk->protinfo.x25->state != X25_STATE_3) + return; /* * Transmit interrupt data. @@ -140,38 +142,39 @@ void x25_kick(struct sock *sk) x25_transmit_link(skb, sk->protinfo.x25->neighbour); } + if (sk->protinfo.x25->condition & X25_COND_PEER_RX_BUSY) + return; + + if (skb_peek(&sk->write_queue) == NULL) + return; + modulus = (sk->protinfo.x25->neighbour->extended) ? X25_EMODULUS : X25_SMODULUS; end = (sk->protinfo.x25->va + sk->protinfo.x25->facilities.winsize_out) % modulus; + if (sk->protinfo.x25->vs == end) + return; + /* - * Transmit normal stream data. + * Transmit data until either we're out of data to send or + * the window is full. */ - if (!(sk->protinfo.x25->condition & X25_COND_PEER_RX_BUSY) && - sk->protinfo.x25->vs != end && - skb_peek(&sk->write_queue) != NULL) { - /* - * Transmit data until either we're out of data to send or - * the window is full. - */ - skb = skb_dequeue(&sk->write_queue); + skb = skb_dequeue(&sk->write_queue); - do { - /* - * Transmit the frame. - */ - x25_send_iframe(sk, skb); + do { + /* + * Transmit the frame. + */ + x25_send_iframe(sk, skb); - sk->protinfo.x25->vs = (sk->protinfo.x25->vs + 1) % modulus; + sk->protinfo.x25->vs = (sk->protinfo.x25->vs + 1) % modulus; - } while (sk->protinfo.x25->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL); + } while (sk->protinfo.x25->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL); - sk->protinfo.x25->vl = sk->protinfo.x25->vr; - sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING; - sk->protinfo.x25->timer = 0; - } + sk->protinfo.x25->vl = sk->protinfo.x25->vr; + sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING; - x25_set_timer(sk); + x25_stop_timer(sk); } /* @@ -188,7 +191,8 @@ void x25_enquiry_response(struct sock *sk) sk->protinfo.x25->vl = sk->protinfo.x25->vr; sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING; - sk->protinfo.x25->timer = 0; + + x25_stop_timer(sk); } void x25_check_iframes_acked(struct sock *sk, unsigned short nr) diff --git a/net/x25/x25_route.c b/net/x25/x25_route.c index 820c11cd4..9c3204537 100644 --- a/net/x25/x25_route.c +++ b/net/x25/x25_route.c @@ -1,5 +1,5 @@ /* - * X.25 Packet Layer release 001 + * X.25 Packet Layer release 002 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. @@ -183,14 +183,12 @@ int x25_route_ioctl(unsigned int cmd, void *arg) { struct x25_route_struct x25_route; struct device *dev; - int err; switch (cmd) { case SIOCADDRT: - if ((err = verify_area(VERIFY_READ, arg, sizeof(struct x25_route_struct))) != 0) - return err; - copy_from_user(&x25_route, arg, sizeof(struct x25_route_struct)); + if (copy_from_user(&x25_route, arg, sizeof(struct x25_route_struct))) + return -EFAULT; if (x25_route.sigdigits < 0 || x25_route.sigdigits > 15) return -EINVAL; if ((dev = x25_dev_get(x25_route.device)) == NULL) @@ -198,9 +196,8 @@ int x25_route_ioctl(unsigned int cmd, void *arg) return x25_add_route(&x25_route.address, x25_route.sigdigits, dev); case SIOCDELRT: - if ((err = verify_area(VERIFY_READ, arg, sizeof(struct x25_route_struct))) != 0) - return err; - copy_from_user(&x25_route, arg, sizeof(struct x25_route_struct)); + if (copy_from_user(&x25_route, arg, sizeof(struct x25_route_struct))) + return -EFAULT; if (x25_route.sigdigits < 0 || x25_route.sigdigits > 15) return -EINVAL; if ((dev = x25_dev_get(x25_route.device)) == NULL) @@ -227,7 +224,8 @@ int x25_routes_get_info(char *buffer, char **start, off_t offset, int length, in for (x25_route = x25_route_list; x25_route != NULL; x25_route = x25_route->next) { len += sprintf(buffer + len, "%-15s %-6d %-5s\n", - x25_route->address.x25_addr, x25_route->sigdigits, + x25_route->address.x25_addr, + x25_route->sigdigits, (x25_route->dev != NULL) ? x25_route->dev->name : "???"); pos = begin + len; diff --git a/net/x25/x25_subr.c b/net/x25/x25_subr.c index 75e58af98..f2aff6d12 100644 --- a/net/x25/x25_subr.c +++ b/net/x25/x25_subr.c @@ -1,5 +1,5 @@ /* - * X.25 Packet Layer release 001 + * X.25 Packet Layer release 002 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. @@ -14,6 +14,7 @@ * * History * X.25 001 Jonathan Naylor Started coding. + * X.25 002 Jonathan Naylor Centralised disconnection processing. */ #include <linux/config.h> @@ -281,4 +282,25 @@ int x25_decode(struct sock *sk, struct sk_buff *skb, int *ns, int *nr, int *q, i return X25_ILLEGAL; } +void x25_disconnect(struct sock *sk, int reason, unsigned char cause, unsigned char diagnostic) +{ + x25_clear_queues(sk); + x25_stop_timer(sk); + + sk->protinfo.x25->lci = 0; + sk->protinfo.x25->state = X25_STATE_0; + + sk->protinfo.x25->causediag.cause = cause; + sk->protinfo.x25->causediag.diagnostic = diagnostic; + + sk->state = TCP_CLOSE; + sk->err = reason; + sk->shutdown |= SEND_SHUTDOWN; + + if (!sk->dead) + sk->state_change(sk); + + sk->dead = 1; +} + #endif diff --git a/net/x25/x25_timer.c b/net/x25/x25_timer.c index e625c41ed..90b6513c9 100644 --- a/net/x25/x25_timer.c +++ b/net/x25/x25_timer.c @@ -1,5 +1,5 @@ /* - * X.25 Packet Layer release 001 + * X.25 Packet Layer release 002 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. @@ -14,6 +14,8 @@ * * History * X.25 001 Jonathan Naylor Started coding. + * X.25 002 Jonathan Naylor New timer architecture. + * Centralised disconnection processing. */ #include <linux/config.h> @@ -39,42 +41,93 @@ #include <linux/interrupt.h> #include <net/x25.h> -static void x25_timer(unsigned long); +static void x25_heartbeat_expiry(unsigned long); +static void x25_timer_expiry(unsigned long); -/* - * Linux set timer - */ -void x25_set_timer(struct sock *sk) +void x25_start_heartbeat(struct sock *sk) { - unsigned long flags; - - save_flags(flags); cli(); del_timer(&sk->timer); - restore_flags(flags); sk->timer.data = (unsigned long)sk; - sk->timer.function = &x25_timer; - sk->timer.expires = jiffies + (HZ / 1); + sk->timer.function = &x25_heartbeat_expiry; + sk->timer.expires = jiffies + 5 * HZ; add_timer(&sk->timer); } -/* - * X.25 TIMER - * - * This routine is called every second. Decrement timer by this - * amount - if expired then process the event. - */ -static void x25_timer(unsigned long param) +void x25_stop_heartbeat(struct sock *sk) +{ + del_timer(&sk->timer); +} + +void x25_start_t2timer(struct sock *sk) +{ + del_timer(&sk->protinfo.x25->timer); + + sk->protinfo.x25->timer.data = (unsigned long)sk; + sk->protinfo.x25->timer.function = &x25_timer_expiry; + sk->protinfo.x25->timer.expires = jiffies + sk->protinfo.x25->t2; + + add_timer(&sk->protinfo.x25->timer); +} + +void x25_start_t21timer(struct sock *sk) +{ + del_timer(&sk->protinfo.x25->timer); + + sk->protinfo.x25->timer.data = (unsigned long)sk; + sk->protinfo.x25->timer.function = &x25_timer_expiry; + sk->protinfo.x25->timer.expires = jiffies + sk->protinfo.x25->t21; + + add_timer(&sk->protinfo.x25->timer); +} + +void x25_start_t22timer(struct sock *sk) +{ + del_timer(&sk->protinfo.x25->timer); + + sk->protinfo.x25->timer.data = (unsigned long)sk; + sk->protinfo.x25->timer.function = &x25_timer_expiry; + sk->protinfo.x25->timer.expires = jiffies + sk->protinfo.x25->t22; + + add_timer(&sk->protinfo.x25->timer); +} + +void x25_start_t23timer(struct sock *sk) +{ + del_timer(&sk->protinfo.x25->timer); + + sk->protinfo.x25->timer.data = (unsigned long)sk; + sk->protinfo.x25->timer.function = &x25_timer_expiry; + sk->protinfo.x25->timer.expires = jiffies + sk->protinfo.x25->t23; + + add_timer(&sk->protinfo.x25->timer); +} + +void x25_stop_timer(struct sock *sk) +{ + del_timer(&sk->protinfo.x25->timer); +} + +unsigned long x25_display_timer(struct sock *sk) +{ + if (sk->protinfo.x25->timer.prev == NULL && + sk->protinfo.x25->timer.next == NULL) + return 0; + + return sk->protinfo.x25->timer.expires - jiffies; +} + +static void x25_heartbeat_expiry(unsigned long param) { struct sock *sk = (struct sock *)param; switch (sk->protinfo.x25->state) { + case X25_STATE_0: /* Magic here: If we listen() and a new link dies before it is accepted() it isn't 'dead' so doesn't get removed. */ if (sk->destroy || (sk->state == TCP_LISTEN && sk->dead)) { - del_timer(&sk->timer); x25_destroy_socket(sk); return; } @@ -89,30 +142,26 @@ static void x25_timer(unsigned long param) sk->protinfo.x25->condition &= ~X25_COND_OWN_RX_BUSY; sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING; sk->protinfo.x25->vl = sk->protinfo.x25->vr; - sk->protinfo.x25->timer = 0; x25_write_internal(sk, X25_RR); + x25_stop_timer(sk); break; } - /* - * Check for frames to transmit. - */ - x25_kick(sk); - break; - - default: break; } - if (sk->protinfo.x25->timer == 0 || --sk->protinfo.x25->timer > 0) { - x25_set_timer(sk); - return; - } + x25_start_heartbeat(sk); +} + +/* + * Timer has expired, it may have been T2, T21, T22, or T23. We can tell + * by the state machine state. + */ +static void x25_timer_expiry(unsigned long param) +{ + struct sock *sk = (struct sock *)param; - /* - * Timer has expired, it may have been T2, T21, T22, or T23. We can tell - * by the state machine state. - */ switch (sk->protinfo.x25->state) { + case X25_STATE_3: /* T2 */ if (sk->protinfo.x25->condition & X25_COND_ACK_PENDING) { sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING; @@ -124,22 +173,13 @@ static void x25_timer(unsigned long param) case X25_STATE_4: /* T22 */ x25_write_internal(sk, X25_CLEAR_REQUEST); sk->protinfo.x25->state = X25_STATE_2; - sk->protinfo.x25->timer = sk->protinfo.x25->t23; + x25_start_t23timer(sk); break; case X25_STATE_2: /* T23 */ - x25_clear_queues(sk); - sk->protinfo.x25->state = X25_STATE_0; - sk->state = TCP_CLOSE; - sk->err = ETIMEDOUT; - sk->shutdown |= SEND_SHUTDOWN; - if (!sk->dead) - sk->state_change(sk); - sk->dead = 1; + x25_disconnect(sk, ETIMEDOUT, 0, 0); break; } - - x25_set_timer(sk); } #endif diff --git a/scripts/ksymoops.cc b/scripts/ksymoops.cc index 28d93f56f..d1edca9c1 100644 --- a/scripts/ksymoops.cc +++ b/scripts/ksymoops.cc @@ -31,6 +31,7 @@ // * Only resolves operands of jump and call instructions. #include <fstream.h> +#include <strstream.h> #include <iomanip.h> #include <stdio.h> #include <string.h> @@ -184,9 +185,23 @@ NameList::decode(unsigned char* code, long eip_addr) char buf[1024]; int lines = 0; + int eip_seen = 0; + long offset; while (fgets(buf, sizeof(buf), objdump_FILE)) { + if (eip_seen && buf[4] == ':') { + // assume objdump from binutils 2.8..., reformat to old style + offset = strtol(buf, 0, 16); + char newbuf[sizeof(buf)]; + memset(newbuf, '\0', sizeof(newbuf)); + ostrstream ost(newbuf, sizeof(newbuf)); + ost.width(8); + ost << offset; + ost << " <_EIP+" << offset << ">: " << &buf[6] << ends; + strcpy(buf, newbuf); + } if (!strnequ(&buf[9], "<_EIP", 5)) continue; + eip_seen = 1; if (strstr(buf, " is out of bounds")) break; lines++; @@ -195,19 +210,28 @@ NameList::decode(unsigned char* code, long eip_addr) cout << buf; continue; } - long offset = strtol(buf, 0, 16); - char* bp_0 = strchr(buf, '>') + 2; + offset = strtol(buf, 0, 16); + char* bp_0 = strchr(buf, '>'); KSym* ksym = find(eip_addr + offset); + if (bp_0) + bp_0 += 2; + else + bp_0 = strchr(buf, ':'); if (ksym) cout << *ksym << ' '; - char* bp = bp_0; + char *bp_1 = strstr(bp_0, "\t"); // objdump from binutils 2.8... + if (bp_1) + ++bp_1; + else + bp_1 = bp_0; + char *bp = bp_1; while (!isspace(*bp)) bp++; while (isspace(*bp)) bp++; - if (*bp != '0') { + if (!isxdigit(*bp)) { cout << bp_0; - } else if (*bp_0 == 'j' || strnequ(bp_0, "call", 4)) { // a jump or call insn + } else if (*bp_1 == 'j' || strnequ(bp_1, "call", 4)) { // a jump or call insn long rel_addr = strtol(bp, 0, 16); ksym = find(eip_addr + rel_addr); if (ksym) { |