diff -u --recursive --new-file v2.1.74/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v2.1.74/linux/Documentation/Configure.help Sun Dec 21 16:17:44 1997 +++ linux/Documentation/Configure.help Sun Dec 21 17:41:24 1997 @@ -601,6 +601,14 @@ info. If you need this feature (for any protocol, like IP) say Y; if unsure, say N. +Socket filtering +CONFIG_FILTER + The Linux Socket Filter is a deviation of the Berkely Packet Filter. + Through Socket Filtering you can have the kernel decide whether the + data is good and to continue processing it. Linux Socket Filtering + works on all socket types except TCP for now. See the text file + linux/Documentation/networking/filter.txt for more information. + Network firewalls CONFIG_FIREWALL A firewall is a computer which protects a local network from the @@ -643,17 +651,6 @@ /etc/rc.d/rc.local) in addition. If unsure, say Y. -Socket Security API Support (EXPERIMENTAL) -CONFIG_NET_SECURITY - Enable use of the socket security API. This option only really - supports security (via encryption of all traffic) over IPv4 links, - and then only if you add a security protocol which is also supported - at the other end of the link; Linux itself does not include any - security protocols, but you can use the enskip package at - ftp.tik.ee.ethz.ch/pub/packages/skip/. That package also contains - documentation of the API to be used for secure sockets. If unsure, - say N. - Sun floppy controller support CONFIG_BLK_DEV_SUNFD This is support for floppy drives on Sun Sparc workstations. Say Y @@ -1637,12 +1634,30 @@ hear that the GNU boycott of Apple is over, so even politically correct people are allowed to say Y here. -IP-over-DDP support +Appletalk-IP driver support CONFIG_IPDDP + Appletalk-IP is a method Macintosh users use IP services. This driver + allows you to either send your IP traffic over an Appletalk network to + an Appletalk-IP router, or you can have your machine act as an Appletalk-IP + router and route Appletalk-IP traffic for your Macintosh users. Both + modes require seperate user-space support software, please see each + individual options help to get the correct URL of the software. + +Appletalk-IP Encapsulation support +CONFIG_IPDDP_ENCAP This allows IP networking for users who only have Appletalk networking available. This feature is experimental. Please see http://www.maths.unm.edu/~bradford/ltpc.html for support software. +Appletalk-IP Decapsulation support +CONFIG_IPDDP_DECAP + This allows you to provide IP services to your Appletalk users. + It does not matter what interface the Macs are comming into your + Linux box on, be it Localtalk, Ethertalk, PPPtalk, etc. The only + dependent variable is if the Appletalk layer supports the protocol + you need. User space software is required to run this driver, you + can pick it up at http://spacs1.spacs.k12.wi.us/~jschlst/MacGate.html + Apple/Farallon LocalTalk PC card support CONFIG_LTPC This allows you to use the AppleTalk PC card to connect to LocalTalk @@ -1952,6 +1967,20 @@ understand if you say Y here; it will enlarge your kernel by about 12KB. If in doubt, say Y. +SCSI logging facility +CONFIG_SCSI_LOGGING + This turns on a logging facility that can be used to debug a number + of problems. Normally no logging output will appear, but you can + enable logging with a shell command like: + 'echo "scsi log token [level]" > /proc/scsi/scsi' + There are a number of things that can be used for 'token', and this + allows you to select the types of information you want, and the level + allows you to select the level of verbosity. If you say 'N' here, + it may be harder to track down some types of scsi problems. If + you say 'Y' here your kernel will be somewhat larger, but there + should be no noticable performance impact as long as you have logging + turned off. + AdvanSys SCSI support CONFIG_SCSI_ADVANSYS This is a driver for all SCSI host adapters manufactured by @@ -2586,6 +2615,15 @@ your EPP chipset is from the SMC series, you are likely to have to set this value greater than 0. +SCSI Debug host simulator. +CONFIG_SCSI_DEBUG + This is a host adapter simulator that can be programmed to simulate a + large number of conditions that could occur on a real bus. The advantage + is that many hard to reproduce problems can be tested in a controlled + environment where there is reduced risk of losing important data. + This is primarily of use to people trying to debug the middle and upper + layers of the scsi subsystem. + Network device support? CONFIG_NETDEVICES You can say N here in case you don't intend to connect to any other @@ -6231,123 +6269,6 @@ removed from the running kernel whenever you want), say M and read Documentation/modules.txt. If unsure, say Y. -Support for Cyrix processors -CONFIG_CYRIX - This enables recognition of Cyrix processors. Without it - /proc/cpuinfo will list your processor as an unknown model - of Cyrix. With it it will list the correct details. It should - be safe to say Y here regardless of what processor you are - actually using. If this option is not enabled none of the - Cyrix feature options are available. - -Enable suspend on halt power saving feature -CONFIG_CYRIX_SUSP_HLT - Suspend on halt causes the processor to enter a low power state - when the "hlt" instruction is executed. This is disabled at - power up and many BIOSs leave it that way. You probably want it - enabled since it dramatically reduces the operating temperature - of the processor. In a few rare cases there may be problems - with some bus master DMA cards if this is enabled. - -No I/O recovery delays -CONFIG_CYRIX_FAST_IO - Historically programmers used "jmp $+2" instructions to create - delays between I/O instructions. The branch prediction of 5x86 - and higher processors renders this ineffective and so a selectable - delay is implemented for I/O instructions in the processor. Linux - uses dummy I/O instructions where necessary rather than jumps - and so the extra processor imposed delay should not be necessary. - Enabling this option removes this delay. - -5x86 performance features -CONFIG_CYRIX_5X86 - The Cyrix 5x86 has several performance feature which are enabled - using on-chip registers. This code attempts to ensure that the - useful features are set to suit Linux. Read Documentation/CPU/Cyrix - before enabling this. - WARNING: If this is enabled you may find that the only way to - reboot is to power cycle the machine. Even a hard reboot seems - to fail on some systems. - -6x86 performance features -CONFIG_CYRIX_6X86 - The Cyrix 6x86 has several performance feature which are enabled - using on-chip registers. Most are normally enabled by the BIOS - however this code ensures that all the useful ones are set to - suit Linux. Read Documentation/CPU/Cyrix before enabling this. - -Avoid unnecessary locked cycles -CONFIG_CYRIX_6X86_NOLOCK - Enabling this option causes the 6x86 not to use locked bus cycles - except for page table access and interrupt acknowledge cycles. - This allows the data used by descriptor tables, xchg instructions - and instructions preceeded by the LOCK prefix to be cached leading - to improved performance. Enabling this option has no effect if - an SMP kernel is being built - SMP requires locked cycles to - guarantee processor synchronization. - -Allocate L1 cache lines on write misses -CONFIG_CYRIX_6X86_WTALLOC - If this is enabled L1 cache write misses will cause a cache line - to be allocated. This may result in increased performance. On the - other hand it may cause excessive trashing of the L1 cache when - copying or zeroing pages. In general you _probably_ win... - -Branch Target Buffer features -CONFIG_CYRIX_6X86_BTB - The Cyrix 6x86 has branch prediction logic which is normally - only set to handle short branches (as in small loops and ifs). - This code attempts on configure the branch prediction logic - appropriately. Read Documentation/CPU/Cyrix before enabling this. - -Variable sized paging mechanism (VSPM) -CONFIG_CYRIX_6X86_VSPM - Variable sized paging mechanism (VSPM) is a feature of the Cyrix - 6x86 family of processors that allows large regions of memory - to be mapped in one go, significantly reducing the amount of work - the MMU has to do compared with traditional paging. However VSPM - is known to be buggy in many 6x86 chip revisions. Please read - Documentation/CPU/Cyrix before enabling this. - WARNING: If this is enabled you may find that the only way to - reboot is to power cycle the machine. Even a hard reboot seems - to fail on some systems. - -Allocate L1 cache lines on write misses -CONFIG_AMD_K5_WTALLOC - If this is enabled L1 cache write misses will cause a cache line - to be allocated. This may result in increased performance. On the - other hand it may cause excessive trashing of the L1 cache when - copying or zeroing pages. In general you _probably_ win... - -Allocate L1 cache lines on write misses -CONFIG_AMD_K6_WTALLOC - If this is enabled L1 cache write misses will cause a cache line - to be allocated. This may result in increased performance. On the - other hand it may cause excessive trashing of the L1 cache when - copying or zeroing pages. In general you _probably_ win... - -Use write cacheability detection -CONFIG_AMD_K6_WTALLOC_WCDE - Write cacheability detection requires the system logic to assert - the cache enable bus signal during a write cycle. Some chipsets - do this and some do not. Some, such as Triton, do but not at - the appropriate point during the write cycle. Cacheability - detection is not normally useful unless you have memory mapped - devices which exist outside the 640k-1M range but within your - actual memory. (There is another option that disables write - allocate for the 15M-16M range commonly used by older VLB - video cards). You probably do not want to enable this. - -No write allocate between 15MB-16MB -CONFIG_AMD_K6_WTALLOC_WAE15M - There were a small number of cards, mainly VESA Local Bus - video cards, that were memory mapped to the 15M-16M address - range. If you have such a card you do not want write allocate - to delay or reorder writes to this space so you must enable - this option. Other memory mapped cards are either outside the - systems memory space or are in the 640k-1M range which is - not subject to write allocate so this option is not normally - required. # m68k-specific kernel options # Documented by Chris Lawrence et al. diff -u --recursive --new-file v2.1.74/linux/Documentation/cyrix.txt linux/Documentation/cyrix.txt --- v2.1.74/linux/Documentation/cyrix.txt Sat Nov 29 11:25:08 1997 +++ linux/Documentation/cyrix.txt Wed Dec 31 16:00:00 1969 @@ -1,201 +0,0 @@ - Cyrix Processor Support - ----------------------- - -Processor Recognition ---------------------- - -Cyrix processors prior to the 6x86MX power up in a default mode -designed to avoid compatibility problems with software that -makes assumptions about processor capabilities based solely on -the apparent family of processor. Unless special handling is -provided Cyrix chips will be identified as some unknown model -of 486. - - The Cyrix processor recognition kernel build option compiles -in code that enables the CPUID instruction on Cyrix processors -and that uses the Cyrix specific DEVID feature to identify the -particular type of Cyrix chip present. - - The 6x86MX and later processors have CPUID enabled by default -however special handling is still required to read the specific -processor type using DEVID since the CPUID information gives -family 6, model 0 - i.e. an A step PPro. - - The combination of CPUID and DEVID allows all current Cyrix -processors to be recognised and listed correctly in /proc/cpuinfo. -This includes Cx486, 5x86, 6x86, Gx86 (aka MediaGx) and 6x86MX. - - Processor recognition is required for all other Cyrix specific -options. - - -Suspend on Halt Power Saving ----------------------------- - -The suspend on halt power saving feature allows the processor to -enter a low power mode when the "hlt" instruction is executed. This -results in dramatically reduced operating temperatures if you do -not spend long periods of time running processor intensive tasks. -Cyrix processors allow this feature to be enabled an disabled -through their configuration registers. The default state on reset -is disabled and many (most?) BIOSs leave it disabled hence a -kernel configuration option is provided that adds code to explicitly -enabled suspend on halt when Linux boots. - - However there appear to be a few rare cases in which the -combination of suspend on halt and some bus master DMA cards can -cause the system to lock up. If this appears to happen you may -need to leave suspend on halt in its default state. (Note that -an option to _disable_ suspend on halt is not provided. If your -BIOS enables it you have to live with it) - - -5x86 Performance Features -------------------------- - -The 5x86 contains a performance control register that allows -several performance enhancing features to be turned on. Unfortunately -many of these features do not appear to work correctly. The 5x86 -performance features kernel build option will attempt to set -the performance control register appropriately but it is -impossible to guarantee that even these conservative settings -will work on all chips. - - WARNING: If this is enabled you may find that the only way to -reboot is to power cycle the machine. Even a hard reboot seems -to fail on some systems. - - -6x86 Performance Features -------------------------- - -Like the 5x86 the 6x86 has several Cyrix specific performance -features. Normally a 6x86 aware BIOS will set these to reasonable, -if not fully optimal, settings. The 6x86 performance features -kernel build option mostly just fine tunes them. - - -6x86 Branch Prediction ----------------------- - -The 6x86 uses speculative execution coupled with several levels -of branch prediction to maximise processing speed. While the -default power up state is reasonable the branch prediction logic -is configurable and there may be some benefit in fine tuning it. - - Unfortunately Cyrix offer no documentation on how to configure -branch prediction and IBM have only partial documentation available. -Further detail and speculation is available from the 6x86opt package -by Mikael Johansson . - - The initial power up state of the 6x86 configures the branch -prediction logic to handle short branches. The 6x86 branch target -buffer features kernel build option enables code that enables -handling of long branches as well. It is not clear if this will -benefit in your particular case or not. - - -6x86 Variable Sized Paging Mechanism ------------------------------------- - -The variable sized paging mechanism (VSPM) is a feature of the Cyrix -6x86 family of processors that allows large regions of memory -to be mapped using a single MMU entry rather than many individual -page sized entries. This significantly reduces the overhead in -accessing such regions. It is also ideally suited to use for the -linear mapping of physical memory to kernel memory used by Linux. - - The Cyrix documenation offers only a brief paragraph of explanation. -Unfortunately the observed behaviour of VSPM suggests that even -this little information is not entirely correct. Worse still, no one -at Cyrix is able to answer questions concerning VSPM. Given that -every revision of 6x86 has *different* VSPM bugs this is not entirely -surprising! Work arounds are in place for the known bugs in step 1, -revisions 4, 5 and 6 chips. Revision 7 is also believed to work. - - WARNING: There appears to be no way to disable a VSPM entry once -it has been created short of a hard reset (which may mean a power -cycle). Failure to clear the VSPM entries means that programs that -use virtual memory layouts different from Linux will crash unexpectedly -after Linux has been running. This includes Windows NT/95, programs -that use DOS extenders etc. - - By experiment: - - * VSPM pages must be power of two sizes. A single 24MB page fails. - This is not entirely surprising but the documentation could give - the impression that VSPM supports arbitrary sizes. - - * Documentation suggests there are 8 VSPM slots (3 bit index) but - tests show the upper four slots mirror the lower four. - - * VSPM entries appear to override traditional page table entries - so we must not overlap the start of the vmalloc region. - - The following only apply to 1 rev 6 and lower chips. 1 rev 7 and - above do not appear to have these problems. - - * With a 16MB page followed by an 8MB page I always get a write - fault on the last 4k of the 8MB page. With 8MB plus 4MB I can't - even boot. - If we have such a memory size we map the first power of two - with a VSPM entry and use traditional paging for the rest. - - * Do not try and create a mapping with dirty and accessed flags - clear - a step 1 rev 5 chip will crash. - - * The valid bit in a VSPM entry is non-functional. There is no way - to invalidate a VSPM entry once it has been written. - - * Attempting to replace a VSPM entry with one that maps a zero - sized region from 0 to 0 crashes the CPU. - - -What more could be done ------------------------ - - Disabling write allocate while we do page copies/clears will -avoid unnecessary cache trashing. However it will also reduce -the apparent memory bandwidth for the operation so it runs -slower (with write allocate the write to memory becomes delayed -and happens asynchronously). Rumour has it that disabling -write allocate for such operations is generally good on an -Intel chip. Disabling and re-enabling write allocate on a -Cyrix would take and extra 60-65 clock cycles each. - - The 6x86 allows Address regions to be set up and en/disabling -certain features for these regions. In order to optimize, we could -analyse the setup done (or not done) by the BIOS and optimize it. -However, it is worth noting that the BIOS probably has more -hardware specific details coded in it than we could ever determine -by any form of probing so if it sets something up in a particular -way the motherboard designers may have had very good reasons for -doing it. Trying to play fast and loose may not be such a good -idea for the general case. - - * Setting up regions fo the main memory: RCE, WWO, WL(?), WG - - * Setting up VGA text (0x000a0000) and graphics memory (PCI: - e.g. 0xe0000000) to RCD, WG - - * Setting up BIOS regions to RCD or RCE, WT - - * Not touching SMM space (ARR3) - - * Disabling WG for Memory Mapped IO - -(RCE/D = Region cache enable/disable, WWO = Weak Write Ordering, -WL = Weak Locking, WG = Write Gathering, WT = Write Through.) - - -Where to get information ------------------------- - - There is a databook in PDF format (6X-DBOOK.PDF), which can be down- -loaded from Cyrix' WWW server, which contains a description of the -Configuration Registers CCR0 - CCR5, the Device Identification Registers -DIR0 + DIR1 and the Address Region ARRx and Region Control -RCRx registers and an incomplete description of the VSPM mechanism. -More about CPU detection, VSPM and more undocumented features can be -found on the Pentium Compiler Group homepage (http://www.goof.com/pcg) -and by following the links found in the docs. diff -u --recursive --new-file v2.1.74/linux/Documentation/filesystems/coda.txt linux/Documentation/filesystems/coda.txt --- v2.1.74/linux/Documentation/filesystems/coda.txt Wed Dec 10 11:12:42 1997 +++ linux/Documentation/filesystems/coda.txt Sun Dec 21 14:45:14 1997 @@ -8,103 +8,128 @@ (version 1.0) as well as improvements we envisage. ______________________________________________________________________ - Table of Contents: + Table of Contents - 1. Introduction - 2. Servicing Coda filesystem calls - 3. The message layer - 3.1. Implementation details - 4. The interface at the call level - 4.1. Data structures shared by the kernel and Venus - 4.2. The pioctl interface - 4.3. root - 4.4. lookup - 4.5. getattr - 4.6. setattr - 4.7. access - 4.8. create - 4.9. mkdir - 4.10. link - 4.11. synlink - 4.12. remove - 4.13. rmdir - 4.14. readlink - 4.15. open - 4.16. close - 4.17. ioctl - 4.18. rename - 4.19. readdir - 4.20. vget - 4.21. fsync - 4.22. inactive - 4.23. rdwr - 4.24. odymount - 4.25. ody_lookup - 4.26. ody_expand - 4.27. prefetch - 4.28. signal - 5. The minicache and downcalls - 5.1. INVALIDATE - 5.2. FLUSH - 5.3. PURGEUSER - 5.4. ZAPFILE - 5.5. ZAPDIR - 5.6. ZAPVNODE - 5.7. PURGEFID - 5.8. REPLACE - 6. Initialization and cleanup - 6.1. Requirements + + + + + + + + + + + + 1. Introduction + + 2. Servicing Coda filesystem calls + + 3. The message layer + + 3.1 Implementation details + + 4. The interface at the call level + + 4.1 Data structures shared by the kernel and Venus + 4.2 The pioctl interface + 4.3 root + 4.4 lookup + 4.5 getattr + 4.6 setattr + 4.7 access + 4.8 create + 4.9 mkdir + 4.10 link + 4.11 symlink + 4.12 remove + 4.13 rmdir + 4.14 readlink + 4.15 open + 4.16 close + 4.17 ioctl + 4.18 rename + 4.19 readdir + 4.20 vget + 4.21 fsync + 4.22 inactive + 4.23 rdwr + 4.24 odymount + 4.25 ody_lookup + 4.26 ody_expand + 4.27 prefetch + 4.28 signal + + 5. The minicache and downcalls + + 5.1 INVALIDATE + 5.2 FLUSH + 5.3 PURGEUSER + 5.4 ZAPFILE + 5.5 ZAPDIR + 5.6 ZAPVNODE + 5.7 PURGEFID + 5.8 REPLACE + + 6. Initialization and cleanup + + 6.1 Requirements + + ______________________________________________________________________ 0wpage 11.. IInnttrroodduuccttiioonn + + A key component in the Coda Distributed File System is the cache manager, _V_e_n_u_s. + When processes on a Coda enabled system access files in the Coda filesystem, requests are directed at the filesystem layer in the operating system. The operating system will communicate with Venus to @@ -168,7 +193,7 @@ the Coda FS layer, so the Coda FS driver must expose the VFS interface as applicable in the operating system. These differ very significantly among operating systems, but share features such as facilities to - read/write and create and remove object. The Coda FS layer services + read/write and create and remove objects. The Coda FS layer services such VFS requests in by invoking on or more well defined services offered by the cache manager Venus. When the replies from Venus have come back to the FS driver, servicing of the VFS call continues and @@ -176,13 +201,18 @@ returns to the process. As a result of this design a basic interface exposed by the FS driver - must allow Venus to handle manage message traffic. In particular - Venus must be able to retrieve and place messages and to be notified - of the arrival of a new message. The notification must be through a - mechanism which does not block Venus since Venus must attend to other - tasks even when no messages are waiting or being processed. + must allow Venus to manage message traffic. In particular Venus must + be able to retrieve and place messages and to be notified of the + arrival of a new message. The notification must be through a mechanism + which does not block Venus since Venus must attend to other tasks even + when no messages are waiting or being processed. + + + + - Interfaces of Coda FS Driver + + Interfaces of the Coda FS Driver Furthermore the FS layer provides for a special path of communication between a user process and Venus, called the pioctl interface. The @@ -210,8 +240,10 @@ 33.. TThhee mmeessssaaggee llaayyeerr + + At the lowest level the communication between Venus and the FS driver - proceeds through messages. The synchronization of between processes + proceeds through messages. The synchronization between processes requesting Coda file service and Venus relies on blocking and waking up processes. The Coda FS driver processes VFS- and pioctl-requests on behalf of a process P, creates messages for Venus, awaits replies @@ -256,6 +288,7 @@ namely when Venus calls sendmsg_to_kernel. At this moment the Coda FS driver looks at the contents of the message and decides if: + +o the message is a reply for a suspended thread P. If so it removes the message from the processing queue and marks the message as WRITTEN. Finally, the FS driver unblocks P (still in the kernel @@ -266,7 +299,7 @@ +o The message is a _d_o_w_n_c_a_l_l. A downcall is a request from Venus to the FS Driver. The FS driver processes the request immediately - (usually a cach eviction or replacement) and when finishes + (usually a cache eviction or replacement) and when finishes sendmsg_to_kernel returns. Now P awakes and continues processing upcall. There are some @@ -277,6 +310,12 @@ deallocate message structure and return. The FS routine can proceed with its processing. + + + + + + Sleeping and IPC arrangements In case P is woken up by a signal and not by Venus, it will first look @@ -291,6 +330,8 @@ extra field "handle_signals" could be added in the message structure to indicate points of no return have been passed.--) + + 33..11.. IImmpplleemmeennttaattiioonn ddeettaaiillss The Unix implementation of this mechanism has been through the @@ -312,11 +353,13 @@ 44.. TThhee iinntteerrffaaccee aatt tthhee ccaallll lleevveell + This section describes the upcalls a Coda FS driver can make to Venus. Each of these upcalls make use of two structures: inputArgs and outputArgs. In pseudo BNF form the structures take the following form: + struct inputArgs { u_long opcode; u_long unique; /* Keep multiple outstanding msgs distinct */ @@ -335,6 +378,8 @@ }; + + Before going on let us elucidate the role of the various fields. The inputArgs start with the opcode which defines the type of service requested from Venus. There are approximately 30 upcalls at present @@ -346,8 +391,12 @@ Before delving into the specific calls we need to discuss a variety of data structures shared by the kernel and Venus. + + + 44..11.. DDaattaa ssttrruuccttuurreess sshhaarreedd bbyy tthhee kkeerrnneell aanndd VVeennuuss + The CodaCred structure defines a variety of user and group id's as they are set for the calling process. The vuid_t and guid_t are 32 bit unsigned integers. It also defines group member ship in an array. On @@ -361,10 +410,13 @@ vgid_t cr_groups[NGROUPS]; /* Group membership for caller */ }; + + NNOOTTEE It is questionable if we need CodaCreds in Venus. Finally Venus doesn't know about groups, although it does create files with the default uid/gid. Perhaps the list of group membership is superfluous. + The next item is the fundamental identifier used to identify Coda files, the ViceFid. A fid of a file uniquely defines a file or directory in the Coda filesystem within a _c_e_l_l. (-- A _c_e_l_l is a @@ -372,12 +424,15 @@ control machine or SCM. See the Coda Administration manual for a detailed description of the role of the SCM.--) + typedef struct ViceFid { VolumeId Volume; VnodeId Vnode; Unique_t Unique; } ViceFid; + + Each of the constituent fields: VolumeId, VnodeId and Unique_t are unsigned 32 bit integers. We envisage that a further field will need to be prefixed to identify the Coda cell; this will probably take the @@ -388,6 +443,23 @@ exchange information. It has room for future extensions such as support for device files (currently not present in Coda). + + + + + + + + + + + + + + + + + struct coda_vattr { enum coda_vtype va_type; /* vnode type (for create) */ u_short va_mode; /* files access mode and type */ @@ -410,9 +482,13 @@ long va_spare; /* remain quad aligned */ }; + + + 44..22.. TThhee ppiiooccttll iinntteerrffaaccee - Coda specific requests can be made by application through a pioctl + + Coda specific requests can be made by application through the pioctl interface. The pioctl is implemented as an ordinary ioctl on a ficticious file /coda/.CONTROL. The piocl call opens this file, gets a file handle and makes the ioctl call. Finally it closes the file. @@ -429,14 +505,19 @@ int follow; } data; + + where + struct ViceIoctl { caddr_t in, out; /* Data to be transferred in, or out */ short in_size; /* Size of input buffer <= 2K */ short out_size; /* Maximum size of output buffer, <= 2K */ }; + + The path must be a Coda file, otherwise the ioctl upcall will not be made. @@ -449,6 +530,7 @@ 44..33.. rroooott + AArrgguummeennttss iinn empty @@ -459,6 +541,8 @@ ViceFid VFid; } cfs_root; + + DDeessccrriippttiioonn This call is made to Venus during the initialization of the Coda filesystem. If the result is zero, the cfs_root structure contains the ViceFid of the root of the Coda filesystem. If a non-zero @@ -470,6 +554,7 @@ 44..44.. llooookkuupp + SSuummmmaarryy Find the ViceFid and type of an object in a directory if it exists. @@ -482,6 +567,8 @@ char *name; /* Place holder for data. */ } cfs_lookup; + + oouutt struct cfs_lookup_out { @@ -489,6 +576,8 @@ int vtype; } cfs_lookup; + + DDeessccrriippttiioonn This call is made to determine the ViceFid and filetype of a directory entry. The directory entry requested carries name name and Venus will search the directory identified by cfs_lookup_in.VFid. @@ -512,6 +601,7 @@ 44..55.. ggeettaattttrr + SSuummmmaarryy Get the attributes of a file. AArrgguummeennttss @@ -523,12 +613,16 @@ struct coda_vattr attr; /* XXXXX */ } cfs_getattr; + + oouutt struct cfs_getattr_out { struct coda_vattr attr; } cfs_getattr; + + DDeessccrriippttiioonn This call returns the attributes of the file identified by fid. @@ -549,6 +643,7 @@ 44..66.. sseettaattttrr + SSuummmmaarryy Set the attributes of a file. AArrgguummeennttss @@ -560,6 +655,9 @@ struct coda_vattr attr; } cfs_setattr; + + + oouutt empty @@ -577,6 +675,7 @@ 44..77.. aacccceessss + SSuummmmaarryy AArrgguummeennttss @@ -588,11 +687,13 @@ int flags; } cfs_access; + + oouutt empty DDeessccrriippttiioonn Verify if access to the object identified by VFid for - operetions described by flags is permitted. The result indicates if + operations described by flags is permitted. The result indicates if access will be granted. It is important to remember that Coda uses ACL's to enforce protection and that ultimately the servers, not the clients enforce the security of the system. The result of this call @@ -605,6 +706,7 @@ 44..88.. ccrreeaattee + SSuummmmaarryy Invoked to create a file AArrgguummeennttss @@ -619,6 +721,9 @@ char *name; /* Place holder for data. */ } cfs_create; + + + oouutt struct cfs_create_out { @@ -626,6 +731,8 @@ struct coda_vattr attr; } cfs_create; + + DDeessccrriippttiioonn This upcall is invoked to request creation of a file. The file will be created in the directory identified by VFid, its name will be name, and the mode will be mode. If excl is set an error will @@ -637,6 +744,7 @@ instantiate a vnode, inode or filehandle at kernel level for the new object. + EErrrroorrss A variety of errors can occur. Permissions may be insufficient. If the object exists and is not a file the error EISDIR is returned under Unix. @@ -657,6 +765,7 @@ 44..99.. mmkkddiirr + SSuummmmaarryy Create a new directory. AArrgguummeennttss @@ -669,6 +778,8 @@ char *name; /* Place holder for data. */ } cfs_mkdir; + + oouutt struct cfs_mkdir_out { @@ -676,6 +787,9 @@ struct coda_vattr attr; } cfs_mkdir; + + + DDeessccrriippttiioonn This call is similar to create but creates a directory. Only the mode field in the input parameters is used for creation. Upon successful creation, the attr returned contains the attributes of @@ -693,6 +807,7 @@ 44..1100.. lliinnkk + SSuummmmaarryy Create a link to an existing file. AArrgguummeennttss @@ -705,6 +820,8 @@ char *tname; /* Place holder for data. */ } cfs_link; + + oouutt empty @@ -716,7 +833,8 @@ EErrrroorrss The usual errors can occur.0wpage - 44..1111.. ssyynnlliinnkk + 44..1111.. ssyymmlliinnkk + SSuummmmaarryy create a symbolic link @@ -731,12 +849,14 @@ char *tname; } cfs_symlink; + + oouutt none DDeessccrriippttiioonn Create a symbolic link. The link is to be placed in the directory identified by VFid and named tname. It should point to the - pathname srcname. The attributes of the newly creaeted object are to + pathname srcname. The attributes of the newly created object are to be set to attr. EErrrroorrss @@ -748,6 +868,7 @@ 44..1122.. rreemmoovvee + SSuummmmaarryy Remove a file AArrgguummeennttss @@ -759,6 +880,8 @@ char *name; /* Place holder for data. */ } cfs_remove; + + oouutt none @@ -774,6 +897,7 @@ 44..1133.. rrmmddiirr + SSuummmmaarryy Remove a directory AArrgguummeennttss @@ -785,6 +909,8 @@ char *name; /* Place holder for data. */ } cfs_rmdir; + + oouutt none @@ -800,6 +926,7 @@ 44..1144.. rreeaaddlliinnkk + SSuummmmaarryy Read the value of a symbolic link. AArrgguummeennttss @@ -810,6 +937,8 @@ ViceFid VFid; } cfs_readlink; + + oouutt struct cfs_readlink_out { @@ -817,6 +946,8 @@ caddr_t data; /* Place holder for data. */ } cfs_readlink; + + DDeessccrriippttiioonn This routine reads the contents of symbolic link identified by VFid into the buffer data. The buffer data must be able to hold any name up to CFS_MAXNAMLEN (PATH or NAM??). @@ -827,6 +958,7 @@ 44..1155.. ooppeenn + SSuummmmaarryy Open a file. AArrgguummeennttss @@ -838,6 +970,8 @@ int flags; } cfs_open; + + oouutt struct cfs_open_out { @@ -845,6 +979,8 @@ ino_t inode; } cfs_open; + + DDeessccrriippttiioonn This request asks Venus to place the file identified by VFid in its cache and to note that the calling process wishes to open it with flags as in open(2). The return value to the kernel differs @@ -852,7 +988,6 @@ informed of the device and inode number of the container file in the fields dev and inode. For Windows the path of the container file is returned to the kernel. - EErrrroorrss NNOOTTEE Currently the cfs_open_out structure is not properly adapted to @@ -864,6 +999,7 @@ 44..1166.. cclloossee + SSuummmmaarryy Close a file, update it on the servers. AArrgguummeennttss @@ -875,6 +1011,8 @@ int flags; } cfs_close; + + oouutt none @@ -896,6 +1034,7 @@ 44..1177.. iiooccttll + SSuummmmaarryy Do an ioctl on a file. This includes the piocl interface. AArrgguummeennttss @@ -910,13 +1049,18 @@ char *data; /* Place holder for data. */ } cfs_ioctl; + + oouutt + struct cfs_ioctl_out { int len; caddr_t data; /* Place holder for data. */ } cfs_ioctl; + + DDeessccrriippttiioonn Do an ioctl operation on a file. The command, len and data arguments are filled as usual. flags is not used by Venus. @@ -925,10 +1069,12 @@ NNOOTTEE Another bogus parameter. flags is not used. What is the business about PREFETCHING in the Venus' code? + 0wpage 44..1188.. rreennaammee + SSuummmmaarryy Rename a fid. AArrgguummeennttss @@ -942,6 +1088,8 @@ char *destname; } cfs_rename; + + oouutt none @@ -956,6 +1104,7 @@ 44..1199.. rreeaaddddiirr + SSuummmmaarryy Read directory entries. AArrgguummeennttss @@ -968,6 +1117,9 @@ int offset; } cfs_readdir; + + + oouutt struct cfs_readdir_out { @@ -975,6 +1127,8 @@ caddr_t data; /* Place holder for data. */ } cfs_readdir; + + DDeessccrriippttiioonn Read directory entries from VFid starting at offset and read at most count bytes. Returns the data into data and indicates the size returned size. @@ -989,15 +1143,18 @@ 44..2200.. vvggeett + SSuummmmaarryy instructs Venus to do an FSDB->Get. AArrgguummeennttss iinn - struct cfs_fsync_in { + struct cfs_vget_in { ViceFid VFid; - } cfs_fsync; + } cfs_vget; + + oouutt @@ -1006,6 +1163,8 @@ int vtype; } cfs_vget; + + DDeessccrriippttiioonn This upcall asks Venus to do a get operation on an fsobj labelled by VFid. @@ -1020,6 +1179,7 @@ 44..2211.. ffssyynncc + SSuummmmaarryy Tell Venus to update the RVM attributes of a file. AArrgguummeennttss @@ -1030,6 +1190,8 @@ ViceFid VFid; } cfs_fsync; + + oouutt none @@ -1045,6 +1207,7 @@ 44..2222.. iinnaaccttiivvee + SSuummmmaarryy Tell Venus a vnode is no longer in use. AArrgguummeennttss @@ -1055,6 +1218,8 @@ ViceFid VFid; } cfs_inactive; + + oouutt none @@ -1068,6 +1233,7 @@ 44..2233.. rrddwwrr + SSuummmmaarryy Read or write from a file AArrgguummeennttss @@ -1083,6 +1249,9 @@ caddr_t data; /* Place holder for data. */ } cfs_rdwr; + + + oouutt struct cfs_rdwr_out { @@ -1091,6 +1260,8 @@ caddr_t data; /* Place holder for data. */ } cfs_rdwr; + + DDeessccrriippttiioonn This upcall asks Venus to read or write from a file. EErrrroorrss @@ -1099,10 +1270,12 @@ read/write operations never reach Venus. I have been told the operation does not work. It is not currently used. + 0wpage 44..2244.. ooddyymmoouunntt + SSuummmmaarryy Allows mounting multiple Coda "filesystems" on one Unix mount point. @@ -1114,12 +1287,16 @@ char *name; /* Place holder for data. */ } ody_mount; + + oouutt struct ody_mount_out { ViceFid VFid; } ody_mount; + + DDeessccrriippttiioonn Asks Venus to return the rootfid of a Coda system named name. The fid is returned in VFid. @@ -1133,12 +1310,14 @@ 44..2255.. ooddyy__llooookkuupp + SSuummmmaarryy Looks up something. AArrgguummeennttss iinn irrelevant + oouutt irrelevant @@ -1152,6 +1331,7 @@ 44..2266.. ooddyy__eexxppaanndd + SSuummmmaarryy expands something in a dynamic set. AArrgguummeennttss @@ -1171,6 +1351,7 @@ 44..2277.. pprreeffeettcchh + SSuummmmaarryy Prefetch a dynamic set. AArrgguummeennttss @@ -1188,10 +1369,12 @@ NNOOTTEE Gut it. It isn't working and isn't used by Coda. + 0wpage 44..2288.. ssiiggnnaall + SSuummmmaarryy Send Venus a signal about an upcall. AArrgguummeennttss @@ -1219,6 +1402,7 @@ 55.. TThhee mmiinniiccaacchhee aanndd ddoowwnnccaallllss + The Coda FS Driver can cache results of lookup and access upcalls, to limit the frequency of upcalls. Upcalls carry a price since a process context switch needs to take place. The counterpart of caching the @@ -1227,8 +1411,8 @@ The kernel code generally has to maintain a structure which links the internal file handles (called vnodes in BSD, inodes in Linux and - FileHandles in Windows) with the ViceFid's which Venus maintains. - Ther reason is that frequent translations back and forth are needed in + FileHandles in Windows) with the ViceFid's which Venus maintains. The + reason is that frequent translations back and forth are needed in order to make upcalls and use the results of upcalls. Such linking objects are called ccnnooddeess. @@ -1246,7 +1430,7 @@ The lookup call in the Coda FS Driver may request the cnode of the desired object from the cache, by passing it's name, directory and the CodaCred's of the caller. The cache will return the cnode or indicate - and it cannot be found. The Coda FS Driver must be careful to + that it cannot be found. The Coda FS Driver must be careful to invalidate cache entries when it modifies or removes objects. When Venus obtains information that indicates that cache entries are @@ -1255,12 +1439,17 @@ the kind described below. The Coda FS Driver does not return an error unless the downcall data could not be read into kernel memory. + 55..11.. IINNVVAALLIIDDAATTEE + No information is available on this call. + 55..22.. FFLLUUSSHH + + AArrgguummeennttss None SSuummmmaarryy Flush the name cache entirely. @@ -1270,25 +1459,33 @@ systems allow the kernel name cache to be switched off dynamically. When this is done, this downcall is made. + 55..33.. PPUURRGGEEUUSSEERR + AArrgguummeennttss struct cfs_purgeuser_out {/* CFS_PURGEUSER is a venus->kernel call */ struct CodaCred cred; } cfs_purgeuser; + + DDeessccrriippttiioonn Remove all entries in the cache carrying the Cred. This call is issued when tokes for a user expire or are flushed. + 55..44.. ZZAAPPFFIILLEE + AArrgguummeennttss struct cfs_zapfile_out { /* CFS_ZAPFILE is a venus->kernel call */ ViceFid CodaFid; } cfs_zapfile; + + DDeessccrriippttiioonn Remove all entries which have the (dir vnode, name) pair. This is issued as a result of an invalidation of cached attributes of a vnode. @@ -1297,20 +1494,28 @@ zapfile routine takes different arguments. Linux does not implement the invalidation of attributes correctly. + + 55..55.. ZZAAPPDDIIRR + AArrgguummeennttss struct cfs_zapdir_out { /* CFS_ZAPDIR is a venus->kernel call */ ViceFid CodaFid; } cfs_zapdir; + + DDeessccrriippttiioonn Remove all entries in the cache lying in a directory CodaFid, and all children of this directory. This call is issed when - Venus receives a callback on the this directory. + Venus receives a callback on the directory. + 55..66.. ZZAAPPVVNNOODDEE + + AArrgguummeennttss struct cfs_zapvnode_out { /* CFS_ZAPVNODE is a venus->kernel call */ @@ -1318,11 +1523,15 @@ ViceFid VFid; } cfs_zapvnode; + + DDeessccrriippttiioonn Remove all entries in the cache carrying the cred and VFid as in the arguments. This downcall is probably never issued. + 55..77.. PPUURRGGEEFFIIDD + SSuummmmaarryy AArrgguummeennttss @@ -1331,12 +1540,17 @@ ViceFid CodaFid; } cfs_purgefid; + + DDeessccrriippttiioonn Flush the attribute for the file. If it is a dir (odd vnode), purge its children from the namecache remove the file from the namecache. + + 55..88.. RREEPPLLAACCEE + SSuummmmaarryy Replace the Fid's for a collection of names. AArrgguummeennttss @@ -1346,6 +1560,8 @@ ViceFid OldFid; } cfs_replace; + + DDeessccrriippttiioonn This routine replaces a ViceFid in the name cache with another. It is added to allow Venus during reintegration to replace locally allocated temp fids while disconnected with global fids even @@ -1355,11 +1571,13 @@ 66.. IInniittiiaalliizzaattiioonn aanndd cclleeaannuupp + This section gives brief hints as to desirable features for the Coda FS Driver at startup and upon shutdown or Venus failures. Before entering the discussion it is useful to repeat that the Coda FS Driver maintains the following data: + 1. message queues 2. cnodes @@ -1383,8 +1601,10 @@ Currently the _p_i_o_c_t_l passes through the VFS for Coda so we can treat these similarly. + 66..11.. RReeqquuiirreemmeennttss + The following requirements should be accomodated: 1. The message queueus should have open and close routines. On Unix @@ -1399,6 +1619,7 @@ +o Close will free all memory allocated by the message queues. + 2. At open the namecache shall be initialized to empty state. 3. Before the message queues are open, all VFS operations will fail. @@ -1424,4 +1645,6 @@ NNOOTTEE NetBSD in particular but also Linux have not implemented the above requirements fully. For smooth operation this needs to be corrected. + + diff -u --recursive --new-file v2.1.74/linux/Documentation/filesystems/vfat.txt linux/Documentation/filesystems/vfat.txt --- v2.1.74/linux/Documentation/filesystems/vfat.txt Sat Oct 25 02:44:14 1997 +++ linux/Documentation/filesystems/vfat.txt Sat Dec 20 21:33:20 1997 @@ -43,6 +43,10 @@ be the short alias instead of 'longfi~1.txt'. quiet -- Stops printing certain warning messages. +check=s|r|n -- Case sensitivity checking setting. + s: strict, case sensitive + r: relaxed, case insensitive + n: normal, default setting, currently case insensitive : 0,1,yes,no,true,false diff -u --recursive --new-file v2.1.74/linux/Documentation/networking/filter.txt linux/Documentation/networking/filter.txt --- v2.1.74/linux/Documentation/networking/filter.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/networking/filter.txt Sun Dec 21 17:41:24 1997 @@ -0,0 +1,42 @@ +filter.txt: Linux Socket Filtering +Written by: Jay Schulist + +Introduction +============ + + Linux Socket Filtering is a deviation of the Berkely +Packet Filter. There are some distinct differences between +the BSD and Linux Kernel Filtering. + +Linux Socket Filtering (LSF) allows a user-space program to +attach a filter onto any socket and allow or disallow certain +types of data to come through the socket. LSF follows exactly +the same filter code structure as the BSD Berkely Packet Filter +(BPF), so refering to the BSD bpf.4 manpage is very helpful in +creating filters. + +LSF is much simpler that BPF. One does not have to worry about +devices or anything like that. You simply create your filter +code, send it to the kernel via the SO_ATTACH_FILTER ioctl and +if you filter code passes the kernel check on it, you then +immediately begin filtering data on that socket. + +You can also detach filters from your socket via the +SO_DETACH_FILTER ioctl. This will probably not be used much +since when you close a socket that has a filter on it the +filter is automagicly removed. The other less common case +may be adding a differnt filter on the same socket you had another +filter that is still running, the kernel takes care of removing +the old one and placing your new one in its place, assumming your +filter has passed the checks, otherwise if it fails the old filter +will remain on that socket. + +Examples +======== + +Ioctls- +setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &Filter, sizeof(Filter)); +setsockopt(sockfd, SOL_SOCKET, SO_DETACH_FILTER, &value, sizeof(value)); + +See the BSD bpf.4 manpage and the BSD Packet Filter paper written by +Steven McCanne and Van Jacobson of Lawrence Berkely Laboratory. diff -u --recursive --new-file v2.1.74/linux/Documentation/networking/ipddp.txt linux/Documentation/networking/ipddp.txt --- v2.1.74/linux/Documentation/networking/ipddp.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/networking/ipddp.txt Sun Dec 21 17:41:24 1997 @@ -0,0 +1,78 @@ +Text file for ipddp.c: + Appletalk-IP Decapsulation and Appletalk-IP Encapsulation + +This text file writen by Jay Schulist + +Introduction +------------ + +Appletalk-IP (IPDDP) is the method computers connected to Appletalk +networks can communicate via IP. Appletalk-IP is simply IP datagrams +inside Appletalk packets. + +Through this driver you can either allow your Linux box to communicate +IP over an Appletalk network or you can provide IP gatewaying functions +for you Appletalk users. + +You can currently Encapsulate or Decapsulate Appletalk-IP on LocalTalk, +EtherTalk and PPPTalk. The only limit on the protocol is that of what +the kernel Appletalk layer and drivers are available. + +Each mode requires its own user space software. + +Compiling Appletalk-IP Decapsulation/Encapsulation +================================================= + +Appletalk-IP Decapsulation needs to be compiled into your kernel. You +will need to turn on Appletalk-IP driver support. Then you will need to +select ONE of the two options; IP to Appletalk-IP Encapsulation support or +Appletalk-IP to IP Decapsulation support. If you compile the driver +staticly you will only be able to use the driver for the function you have +enabled in the kernel. If you compile the driver as a module you can +select what mode you want it to run in via a module loading param. +ipddp_mode=1 for Appletalk-IP Encapsulation and ipddp_mode=2 for +Appletalk-IP to IP Decapsulation. + +Basic instructions for user space tools +======================================= + +To enable Appletalk-IP Decapsulation/Encapsulation you will need the +proper tools. You can get the tools for Decapsulation from +http://spacs1.spacs.k12.wi.us/~jschlst/MacGate and for Encapsulation +from http://www.maths.unm.edu/~bradford/ltpc.html + +I will briefly describe the operation of the tools, but you will +need to consult the supporting documentation for each set of tools. + +Decapsulation - You will need to download a software package called +MacGate. In this distribution there will be a tool called MacRoute +which enabled you to add routes to the kernel for your Macs by hand. +Also the tool MacRegGateWay is included to register the +proper IP Gateway and IP addresses for your machine. Included in this +distribution is a patch to netatalk-1.4b2+asun2.0a17.2 (available from +ftp.u.washington.edu/pub/user-supported/asun/) this patch is optional +but it allows automatic adding and deleting of routes for Macs. (Handy +for locations with large Mac installations) + +Encapsulation - You will need to download a software daemon called ipddpd. +This software expects there to be and Appletalk-IP gateway on the network. +You will also need to add the proper routes to route your Linux box's IP +traffic out the ipddp interface. + +Common Uses of ipddp.c +---------------------- +Of course Appletalk-IP Decapsulation and Encapsulation, but specificly +Decapsulation is being used most for connecting LocalTalk networks to +IP networks. Although it has been used on EtherTalk networks to allow +Macs that are only able to tunnel IP over EtherTalk. + +Encapsulation has been used to allow a Linux box stuck on a LocalTalk +network to use IP. It should work equally well if you are stuck on an +EtherTalk only network. + +Further Assisatance +------------------- +You can contact me (Jay Schulist ) with any +questions reguarding Decapsulation or Encapsulation. Bradford W. Johnson + originally wrote the ipddp.c driver for IP +encapsulation in Appletalk. diff -u --recursive --new-file v2.1.74/linux/Makefile linux/Makefile --- v2.1.74/linux/Makefile Sun Dec 21 16:17:44 1997 +++ linux/Makefile Sun Dec 21 17:58:14 1997 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 1 -SUBLEVEL = 74 +SUBLEVEL = 75 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/) diff -u --recursive --new-file v2.1.74/linux/arch/alpha/config.in linux/arch/alpha/config.in --- v2.1.74/linux/arch/alpha/config.in Thu Dec 4 14:53:54 1997 +++ linux/arch/alpha/config.in Sun Dec 21 17:27:17 1997 @@ -193,3 +193,7 @@ fi bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ endmenu + +if [ "$CONFIG_TGA_CONSOLE" = "n" ]; then + define_bool CONFIG_VGA_CONSOLE y +fi diff -u --recursive --new-file v2.1.74/linux/arch/alpha/mm/fault.c linux/arch/alpha/mm/fault.c --- v2.1.74/linux/arch/alpha/mm/fault.c Thu Jul 17 10:06:03 1997 +++ linux/arch/alpha/mm/fault.c Sun Dec 21 15:29:54 1997 @@ -70,6 +70,20 @@ struct mm_struct *mm = current->mm; unsigned fixup; + /* As of EV6, a load into $31/$f31 is a prefetch, and never faults + (or is suppressed by the PALcode). Support that for older cpu's + by ignoring such an instruction. */ + if (cause == 0) { + /* No need for get_user.. we know the insn is there. */ + unsigned int insn = *(unsigned int *)regs->pc; + if ((insn >> 21 & 0x1f) == 0x1f && + /* ldq ldl ldt lds ldg ldf ldwu ldbu */ + (1ul << (insn >> 26) & 0x30f00001400ul)) { + regs->pc += 4; + return; + } + } + down(&mm->mmap_sem); vma = find_vma(mm, address); if (!vma) diff -u --recursive --new-file v2.1.74/linux/arch/i386/boot/compressed/Makefile linux/arch/i386/boot/compressed/Makefile --- v2.1.74/linux/arch/i386/boot/compressed/Makefile Thu Jun 26 12:33:36 1997 +++ linux/arch/i386/boot/compressed/Makefile Sun Dec 21 17:27:17 1997 @@ -57,4 +57,4 @@ rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk clean: - rm -f vmlinux bvmlinux + rm -f vmlinux bvmlinux _tmp_* diff -u --recursive --new-file v2.1.74/linux/arch/i386/boot/compressed/misc.c linux/arch/i386/boot/compressed/misc.c --- v2.1.74/linux/arch/i386/boot/compressed/misc.c Tue Dec 2 09:49:38 1997 +++ linux/arch/i386/boot/compressed/misc.c Sun Dec 21 17:27:17 1997 @@ -349,9 +349,9 @@ else setup_output_buffer_if_we_run_high(mv); makecrc(); - puts("Uncompressing Linux..."); + puts("Uncompressing Linux... "); gunzip(); - puts("done.\nNow booting the kernel\n"); + puts("Ok, booting the kernel.\n"); if (high_loaded) close_output_buffer_if_we_run_high(mv); return high_loaded; } diff -u --recursive --new-file v2.1.74/linux/arch/i386/config.in linux/arch/i386/config.in --- v2.1.74/linux/arch/i386/config.in Thu Dec 4 14:53:54 1997 +++ linux/arch/i386/config.in Sun Dec 21 17:27:18 1997 @@ -10,6 +10,16 @@ endmenu mainmenu_option next_comment +comment 'Processor type and features' +choice 'Processor family' \ + "386 CONFIG_M386 \ + 486/Cx486 CONFIG_M486 \ + Pentium/K5/5x86/6x86 CONFIG_M586 \ + PPro/K6/6x86MX CONFIG_M686" Pentium +bool 'Math emulation' CONFIG_MATH_EMULATION +endmenu + +mainmenu_option next_comment comment 'Loadable module support' bool 'Enable loadable module support' CONFIG_MODULES if [ "$CONFIG_MODULES" = "y" ]; then @@ -21,7 +31,6 @@ mainmenu_option next_comment comment 'General setup' -bool 'Kernel math emulation' CONFIG_MATH_EMULATION bool 'Networking support' CONFIG_NET bool 'PCI support' CONFIG_PCI if [ "$CONFIG_PCI" = "y" ]; then @@ -41,16 +50,11 @@ tristate 'Kernel support for JAVA binaries (obsolete)' CONFIG_BINFMT_JAVA fi -choice 'Processor type' \ - "386 CONFIG_M386 \ - 486 CONFIG_M486 \ - Pentium CONFIG_M586 \ - PPro CONFIG_M686" Pentium bool 'Video mode selection support' CONFIG_VIDEO_SELECT tristate 'Parallel port support' CONFIG_PARPORT if [ "$CONFIG_PARPORT" != "n" ]; then - dep_tristate ' PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT + dep_tristate ' PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT fi endmenu @@ -140,3 +144,5 @@ fi bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ endmenu + +define_bool CONFIG_VGA_CONSOLE y diff -u --recursive --new-file v2.1.74/linux/arch/i386/defconfig linux/arch/i386/defconfig --- v2.1.74/linux/arch/i386/defconfig Fri Dec 19 15:52:48 1997 +++ linux/arch/i386/defconfig Sun Dec 21 17:45:50 1997 @@ -8,6 +8,15 @@ # CONFIG_EXPERIMENTAL is not set # +# Processor type and features +# +# CONFIG_M386 is not set +# CONFIG_M486 is not set +CONFIG_M586=y +# CONFIG_M686 is not set +# CONFIG_MATH_EMULATION is not set + +# # Loadable module support # CONFIG_MODULES=y @@ -17,21 +26,16 @@ # # General setup # -# CONFIG_MATH_EMULATION is not set CONFIG_NET=y CONFIG_PCI=y CONFIG_PCI_BIOS=y -# CONFIG_PCI_DIRECT is not set +CONFIG_PCI_DIRECT=y # CONFIG_MCA is not set CONFIG_SYSVIPC=y CONFIG_SYSCTL=y CONFIG_BINFMT_AOUT=y CONFIG_BINFMT_ELF=y CONFIG_BINFMT_MISC=y -# CONFIG_M386 is not set -# CONFIG_M486 is not set -# CONFIG_M586 is not set -CONFIG_M686=y # CONFIG_VIDEO_SELECT is not set # CONFIG_PARPORT is not set @@ -79,6 +83,7 @@ # CONFIG_NETLINK is not set # CONFIG_FIREWALL is not set # CONFIG_NET_ALIAS is not set +# CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y # CONFIG_IP_MULTICAST is not set @@ -123,6 +128,7 @@ # CONFIG_SCSI_MULTI_LUN=y CONFIG_SCSI_CONSTANTS=y +# CONFIG_SCSI_LOGGING is not set # # SCSI low-level drivers @@ -185,8 +191,7 @@ # CONFIG_DLCI is not set # CONFIG_PPP is not set # CONFIG_SLIP is not set -# CONFIG_STRIP is not set -# CONFIG_WAVELAN is not set +# CONFIG_NET_RADIO is not set # CONFIG_TR is not set # CONFIG_WAN_DRIVERS is not set # CONFIG_LAPBETHER is not set @@ -228,6 +233,7 @@ # CONFIG_CODA_FS is not set # CONFIG_SMB_FS is not set # CONFIG_HPFS_FS is not set +# CONFIG_NTFS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_AFFS_FS is not set # CONFIG_ROMFS_FS is not set @@ -283,3 +289,4 @@ # # CONFIG_PROFILE is not set # CONFIG_MAGIC_SYSRQ is not set +CONFIG_VGA_CONSOLE=y diff -u --recursive --new-file v2.1.74/linux/arch/i386/kernel/Makefile linux/arch/i386/kernel/Makefile --- v2.1.74/linux/arch/i386/kernel/Makefile Tue May 13 22:41:00 1997 +++ linux/arch/i386/kernel/Makefile Sun Dec 21 17:27:17 1997 @@ -18,9 +18,13 @@ all: kernel.o head.o init_task.o O_TARGET := kernel.o -O_OBJS := process.o signal.o entry.o traps.o irq.o vm86.o bios32.o \ +O_OBJS := process.o signal.o entry.o traps.o irq.o vm86.o \ ptrace.o ioport.o ldt.o setup.o time.o sys_i386.o OX_OBJS := i386_ksyms.o + +ifdef CONFIG_PCI +O_OBJS += bios32.o +endif ifdef CONFIG_MCA O_OBJS += mca.o diff -u --recursive --new-file v2.1.74/linux/arch/i386/kernel/bios32.c linux/arch/i386/kernel/bios32.c --- v2.1.74/linux/arch/i386/kernel/bios32.c Tue Dec 2 09:49:39 1997 +++ linux/arch/i386/kernel/bios32.c Sun Dec 21 17:27:18 1997 @@ -1,7 +1,7 @@ /* * bios32.c - BIOS32, PCI BIOS functions. * - * $Id: bios32.c,v 1.14 1997/08/02 22:20:57 mj Exp $ + * $Id: bios32.c,v 1.17 1997/11/16 11:03:41 mj Exp $ * * Sponsored by * iX Multiuser Multitasking Magazine @@ -456,7 +456,7 @@ outb (0x00, 0xCFB); outb (0x00, 0xCF8); outb (0x00, 0xCFA); - if (inb (0xCF8) == 0x00 && inb (0xCFB) == 0x00) { + if (inb (0xCF8) == 0x00 && inb (0xCFA) == 0x00) { restore_flags(flags); printk("PCI: Using configuration type 2\n"); return &pci_direct_conf2; @@ -897,6 +897,8 @@ #else #ifdef CONFIG_PCI_DIRECT a = pci_check_direct(); +#else +#error "You need to set CONFIG_PCI_BIOS or CONFIG_PCI_DIRECT if you want PCI support." #endif #endif if (a) diff -u --recursive --new-file v2.1.74/linux/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S --- v2.1.74/linux/arch/i386/kernel/head.S Tue Dec 2 09:49:39 1997 +++ linux/arch/i386/kernel/head.S Sun Dec 21 17:27:18 1997 @@ -1,11 +1,10 @@ /* - * linux/arch/i386/head.S + * linux/arch/i386/head.S -- the 32-bit startup code. * * Copyright (C) 1991, 1992 Linus Torvalds - */ - -/* - * head.S contains the 32-bit startup code. + * + * Enhanced CPU detection and feature setting code by Mike Jagdis + * and Martin Mares, November 1997. */ .text @@ -20,6 +19,20 @@ #define CL_OFFSET 0x90022 /* + * References to members of the boot_cpu_data structure. + */ + +#define CPU_PARAMS SYMBOL_NAME(boot_cpu_data) +#define X86 CPU_PARAMS+0 +#define X86_VENDOR CPU_PARAMS+1 +#define X86_MODEL CPU_PARAMS+2 +#define X86_MASK CPU_PARAMS+3 +#define X86_HARD_MATH CPU_PARAMS+6 +#define X86_CPUID CPU_PARAMS+8 +#define X86_CAPABILITY CPU_PARAMS+12 +#define X86_VENDOR_ID CPU_PARAMS+16 + +/* * swapper_pg_dir is the main page directory, address 0x00101000 */ ENTRY(stext) @@ -45,15 +58,9 @@ * not yet offset 0xC0000000.. */ #define cr4_bits mmu_cr4_features-0xC0000000 -#ifdef GAS_KNOWS_CR4 movl %cr4,%eax # Turn on 4Mb pages orl cr4_bits,%eax movl %eax,%cr4 -#else - .byte 0x0f,0x20,0xe0 - orl cr4_bits,%eax - .byte 0x0f,0x22,0xe0 -#endif #endif /* * Setup paging (the tables are already set up, just switch them on) @@ -135,13 +142,69 @@ checkCPUtype: #endif + movl $-1,X86_CPUID # -1 for no CPUID initially + /* check if it is 486 or 386. */ /* * XXX - this does a lot of unnecessary setup. Alignment checks don't * apply at our cpl of 0 and the stack ought to be aligned already, and * we don't need to preserve eflags. */ - movl $3, SYMBOL_NAME(x86) + /* + * A Cyrix preserves flags in cases where other CPUs change + * them in undefined ways. We need to know this since we may + * need to enable the CPUID instruction at least. (Cyrix chips + * prior to M2 have CPUID disabled by default, the Cx486s + * didn't have it at all.) + */ + xor %ax,%ax + sahf + movb $5,%al + movb $2,%bl + div %bl + lahf + cmpb $2,%ah + jne ncyrix + + /* + * It behaves like a Cyrix so put "Cyrix" in the vendor id + * field. It may be overwritten later with the real thing + * if CPUID works. + */ + movl $0x69727943,X86_VENDOR_ID # low 4 chars + movl $0x00000078,X86_VENDOR_ID # next 4 chars + + /* + * N.B. The pattern of accesses to 0x22 and 0x23 is *important* + * so do not try and "optimise" it! For the same reason we + * do all this with interrupts off just to be sure. + */ +#define setCx86(reg, val) \ + movb reg,%al; \ + outb %al,$0x22; \ + movb val,%al; \ + outb %al,$0x23 + +#define getCx86(reg) \ + movb reg,%al; \ + outb %al,$0x22; \ + inb $0x23,%al + + getCx86($0xc3) # get CCR3 + movb %al,%cl # Save old value + movb %al,%bl + andb $0x0f,%bl # Enable all config registers (for CCR4 access) + orb $0x10,%bl + setCx86($0xc3,%bl) + + getCx86($0xe8) # CCR4 |= CPUID + orb $0x80,%al + movb %al,%bl + setCx86($0xe8,%bl) + + setCx86($0xc3,%cl) # Restore old CCR3 + +ncyrix: movl $3,X86 # at least 386 pushfl # push EFLAGS popl %eax # get EFLAGS movl %eax,%ecx # save original EFLAGS @@ -153,7 +216,8 @@ xorl %ecx,%eax # change in flags andl $0x40000,%eax # check if AC bit changed je is386 - movl $4,SYMBOL_NAME(x86) + + movl $4,X86 # at least 486 movl %ecx,%eax xorl $0x200000,%eax # check ID flag pushl %eax @@ -161,42 +225,74 @@ pushfl # 487SX we can't change it popl %eax xorl %ecx,%eax - andl $0x200000,%eax - je is486 -isnew: pushl %ecx # restore original EFLAGS + pushl %ecx # restore original EFLAGS popfl - incl SYMBOL_NAME(have_cpuid) # we have CPUID - /* get processor type */ - # LINUS WE HAVE A BUG HERE - MUST CHECK WITH - # CPUID#0 THAT CPUID#1 IS SUPPORTED... - movl $1, %eax # Use the CPUID instruction to - .byte 0x0f, 0xa2 # check the processor type - movb %al, %cl # save reg for future use - andb $0x0f,%ah # mask processor family - movb %ah,SYMBOL_NAME(x86) - andb $0xf0, %eax # mask model - shrb $4, %al - movb %al,SYMBOL_NAME(x86_model) - andb $0x0f, %cl # mask mask revision - movb %cl,SYMBOL_NAME(x86_mask) - movl %edx,SYMBOL_NAME(x86_capability) + andl $0x200000,%eax + je nocpuid + /* get vendor info */ - xorl %eax, %eax # call CPUID with 0 -> return vendor ID - .byte 0x0f, 0xa2 # CPUID - movl %ebx,SYMBOL_NAME(x86_vendor_id) # lo 4 chars - movl %edx,SYMBOL_NAME(x86_vendor_id)+4 # next 4 chars - movl %ecx,SYMBOL_NAME(x86_vendor_id)+8 # last 4 chars + xorl %eax,%eax # call CPUID with 0 -> return vendor ID + cpuid + movl %eax,X86_CPUID # save CPUID level + movl %ebx,X86_VENDOR_ID # lo 4 chars + movl %edx,X86_VENDOR_ID+4 # next 4 chars + movl %ecx,X86_VENDOR_ID+8 # last 4 chars + + orl %eax,%eax # do we have processor info as well? + je nocpuid + + movl $1,%eax # Use the CPUID instruction to get CPU type + cpuid + movb %al,%cl # save reg for future use + andb $0x0f,%ah # mask processor family + movb %ah,X86 + andb $0xf0,%al # mask model + shrb $4,%al + movb %al,X86_MODEL + andb $0x0f,%cl # mask mask revision + movb %cl,X86_MASK + movl %edx,X86_CAPABILITY + +nocpuid: + /* + * Even if we had CPUID Cyrix tries to look compatible with + * Intel so we have to go elsewhere for the nitty gritty. + */ + cmpl $0x69727943,X86_VENDOR_ID # "Cyri[x.*]"? + jne is486 # maybe ... + + movb $0xfe,X86_MODEL # Generic Cx486? + movb $0,X86_MASK + + getCx86($0xc3) # Test for DEVID by writing CCR3 + movb %al,%cl + movb %al,%bl + orb $0x80,%bl + setCx86($0xc3,%bl) + getCx86($0xc0) # dummy to change bus + getCx86($0xc3) + cmpb %al,%cl + je is486 # not writable == no DEVID + + setCx86($0xc3,%cl) # restore CCR3 + + getCx86($0xff) # get DEVID in preference to any CPUID + movb %al,X86_MASK + getCx86($0xfe) + movb %al,X86_MODEL + andb $0xf0,%al # Check for 6x86(L) + cmp $0x30,%al + jnz is486 + getCx86($0xe9) # CCR5: reset SLOP bit, so that the udelay loop + andb $0xfd,%al # works well on 6x86(L) CPU's. + movb %al,%bl + setCx86($0xe9,%bl) - movl %cr0,%eax # 486+ - andl $0x80000011,%eax # Save PG,PE,ET - orl $0x50022,%eax # set AM, WP, NE and MP - jmp 2f -is486: pushl %ecx # restore original EFLAGS - popfl - movl %cr0,%eax # 486 +is486: movl %cr0,%eax # 486 or better andl $0x80000011,%eax # Save PG,PE,ET orl $0x50022,%eax # set AM, WP, NE and MP jmp 2f + is386: pushl %ecx # restore original EFLAGS popfl movl %cr0,%eax # 386 @@ -208,17 +304,11 @@ movb ready,%al # First CPU if 0 orb %al,%al jz 4f # First CPU skip this stuff -#ifdef GAS_KNOWS_CR4 movl %cr4,%eax # Turn on 4Mb pages orl $16,%eax movl %eax,%cr4 -#else - .byte 0x0f,0x20,0xe0 - orl $16,%eax - .byte 0x0f,0x22,0xe0 -#endif - movl %cr3, %eax # Intel specification clarification says - movl %eax, %cr3 # to do this. Maybe it makes a difference. + movl %cr3,%eax # Intel specification clarification says + movl %eax,%cr3 # to do this. Maybe it makes a difference. # Who knows ? #endif 4: @@ -228,7 +318,7 @@ lgdt gdt_descr lidt idt_descr ljmp $(__KERNEL_CS),$1f -1: movl $(__KERNEL_DS),%eax# reload all the segment registers +1: movl $(__KERNEL_DS),%eax # reload all the segment registers mov %ax,%ds # after changing gdt. mov %ax,%es mov %ax,%fs @@ -258,7 +348,7 @@ * We depend on ET to be correct. This checks for 287/387. */ check_x87: - movb $0,SYMBOL_NAME(hard_math) + movb $0,X86_HARD_MATH clts fninit fstsw %ax @@ -269,7 +359,7 @@ movl %eax,%cr0 ret ALIGN -1: movb $1,SYMBOL_NAME(hard_math) +1: movb $1,X86_HARD_MATH .byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */ ret @@ -366,7 +456,7 @@ .long 0x00102007 .fill 767,4,0 .long 0x00102007 - .fill 127,4,0 + .fill 255,4,0 /* * The page tables are initialized to only 4MB here - the final page diff -u --recursive --new-file v2.1.74/linux/arch/i386/kernel/i386_ksyms.c linux/arch/i386/kernel/i386_ksyms.c --- v2.1.74/linux/arch/i386/kernel/i386_ksyms.c Sat Nov 29 11:25:09 1997 +++ linux/arch/i386/kernel/i386_ksyms.c Sun Dec 21 17:27:18 1997 @@ -27,10 +27,9 @@ #endif /* platform dependent support */ -EXPORT_SYMBOL(x86); +EXPORT_SYMBOL(boot_cpu_data); EXPORT_SYMBOL(EISA_bus); EXPORT_SYMBOL(MCA_bus); -EXPORT_SYMBOL(wp_works_ok); EXPORT_SYMBOL(__verify_write); EXPORT_SYMBOL(dump_thread); EXPORT_SYMBOL(dump_fpu); diff -u --recursive --new-file v2.1.74/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c --- v2.1.74/linux/arch/i386/kernel/irq.c Sun Sep 7 13:10:42 1997 +++ linux/arch/i386/kernel/irq.c Sun Dec 21 17:27:18 1997 @@ -15,6 +15,7 @@ * Naturally it's not a 1:1 relation, but there are similarities. */ +#include #include #include #include @@ -202,7 +203,7 @@ static void math_error_irq(int cpl, void *dev_id, struct pt_regs *regs) { outb(0,0xF0); - if (ignore_irq13 || !hard_math) + if (ignore_irq13 || !boot_cpu_data.hard_math) return; math_error(); } diff -u --recursive --new-file v2.1.74/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c --- v2.1.74/linux/arch/i386/kernel/process.c Fri Dec 19 15:52:48 1997 +++ linux/arch/i386/kernel/process.c Sun Dec 21 17:27:18 1997 @@ -75,7 +75,7 @@ static void hard_idle(void) { while (!need_resched) { - if (hlt_works_ok && !hlt_counter) { + if (boot_cpu_data.hlt_works_ok && !hlt_counter) { #ifdef CONFIG_APM /* If the APM BIOS is not enabled, or there is an error calling the idle routine, we @@ -114,8 +114,7 @@ /* endless idle loop with no priority at all */ current->priority = -100; current->counter = -100; - for (;;) - { + for (;;) { /* * We are locked at this point. So we can safely call * the APM bios knowing only one CPU at a time will do @@ -124,12 +123,9 @@ if (!start_idle) start_idle = jiffies; if (jiffies - start_idle > HARD_IDLE_TIMEOUT) - { hard_idle(); - } - else - { - if (hlt_works_ok && !hlt_counter && !need_resched) + else { + if (boot_cpu_data.hlt_works_ok && !hlt_counter && !need_resched) __asm__("hlt"); } run_task_queue(&tq_scheduler); @@ -154,7 +150,7 @@ current->priority = -100; while(1) { - if(cpu_data[smp_processor_id()].hlt_works_ok && + if(current_cpu_data.hlt_works_ok && !hlt_counter && !need_resched) __asm("hlt"); /* @@ -185,6 +181,7 @@ * controller to pulse the reset-line low. We try that for a while, * and if it doesn't work, we do some other stupid things. */ + static long no_idt[2] = {0, 0}; static int reboot_mode = 0; static int reboot_thru_bios = 0; @@ -226,7 +223,7 @@ { 0x0000000000000000ULL, /* Null descriptor */ 0x00009a000000ffffULL, /* 16-bit real-mode 64k code at 0x00000000 */ - 0x000092000100ffffULL /* 16-bit real-mode 64k data at 0x00000100 */ + 0x000092000100ffffULL /* 16-bit real-mode 64k data at 0x00000100 */ }; static struct @@ -260,16 +257,16 @@ { 0x66, 0x0f, 0x20, 0xc0, /* movl %cr0,%eax */ 0x66, 0x83, 0xe0, 0x11, /* andl $0x00000011,%eax */ - 0x66, 0x0d, 0x00, 0x00, 0x00, 0x60, /* orl $0x60000000,%eax */ + 0x66, 0x0d, 0x00, 0x00, 0x00, 0x60, /* orl $0x60000000,%eax */ 0x66, 0x0f, 0x22, 0xc0, /* movl %eax,%cr0 */ 0x66, 0x0f, 0x22, 0xd8, /* movl %eax,%cr3 */ 0x66, 0x0f, 0x20, 0xc3, /* movl %cr0,%ebx */ 0x66, 0x81, 0xe3, 0x00, 0x00, 0x00, 0x60, /* andl $0x60000000,%ebx */ - 0x74, 0x02, /* jz f */ - 0x0f, 0x08, /* invd */ - 0x24, 0x10, /* f: andb $0x10,al */ + 0x74, 0x02, /* jz f */ + 0x0f, 0x08, /* invd */ + 0x24, 0x10, /* f: andb $0x10,al */ 0x66, 0x0f, 0x22, 0xc0, /* movl %eax,%cr0 */ - 0xea, 0x00, 0x00, 0xff, 0xff /* ljmp $0xffff,$0x0000 */ + 0xea, 0x00, 0x00, 0xff, 0xff /* ljmp $0xffff,$0x0000 */ }; static inline void kb_wait(void) @@ -301,7 +298,7 @@ } } - cli (); + cli(); /* Write zero to CMOS register number 0x0f, which the BIOS POST routine will recognize as telling it to do a proper reboot. (Well @@ -325,7 +322,7 @@ /* Make sure the first page is mapped to the start of physical memory. It is normally not mapped, to trap kernel NULL pointer dereferences. */ - pg0 [0] = 7; + pg0[0] = 7; /* * Use `swapper_pg_dir' as our page directory. We bother with @@ -530,7 +527,7 @@ int fpvalid; if ((fpvalid = current->used_math) != 0) { - if (hard_math) { + if (boot_cpu_data.hard_math) { if (last_task_used_math == current) { __asm__("clts ; fsave %0; fwait": :"m" (*fpu)); } diff -u --recursive --new-file v2.1.74/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c --- v2.1.74/linux/arch/i386/kernel/setup.c Mon Nov 17 18:47:20 1997 +++ linux/arch/i386/kernel/setup.c Sun Dec 21 17:27:18 1997 @@ -2,6 +2,9 @@ * linux/arch/i386/kernel/setup.c * * Copyright (C) 1995 Linus Torvalds + * + * Enhanced CPU type detection by Mike Jagdis, Patrick St. Jean + * and Martin Mares, November 1997. */ /* @@ -34,22 +37,11 @@ #include /* - * Tell us the machine setup.. + * Machine setup.. */ -char hard_math = 0; /* set by kernel/head.S */ -char x86 = 0; /* set by kernel/head.S to 3..6 */ -char x86_model = 0; /* set by kernel/head.S */ -char x86_mask = 0; /* set by kernel/head.S */ -int x86_capability = 0; /* set by kernel/head.S */ -int fdiv_bug = 0; /* set if Pentium(TM) with FP bug */ -int pentium_f00f_bug = 0; /* set if Pentium(TM) with F00F bug */ -int have_cpuid = 0; /* set if CPUID instruction works */ - -char x86_vendor_id[13] = "unknown"; char ignore_irq13 = 0; /* set if exception 16 works */ -char wp_works_ok = -1; /* set if paging hardware honours WP */ -char hlt_works_ok = 1; /* set if the "hlt" instruction works */ +struct cpuinfo_x86 boot_cpu_data = { 0, 0, 0, 0, -1, 1, 0, 0, -1 }; /* * Bus types .. @@ -93,9 +85,7 @@ */ #define PARAM empty_zero_page #define EXT_MEM_K (*(unsigned short *) (PARAM+2)) -#ifndef STANDARD_MEMORY_BIOS_CALL #define ALT_MEM_K (*(unsigned long *) (PARAM+0x1e0)) -#endif #ifdef CONFIG_APM #define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+64)) #endif @@ -124,15 +114,12 @@ unsigned long * memory_start_p, unsigned long * memory_end_p)) { unsigned long memory_start, memory_end; - unsigned long memory_alt_end; char c = ' ', *to = command_line, *from = COMMAND_LINE; int len = 0; static unsigned char smptrap=0; - if(smptrap==1) - { + if (smptrap) return; - } smptrap=1; ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV); @@ -150,13 +137,12 @@ aux_device_present = AUX_DEVICE_INFO; memory_end = (1<<20) + (EXT_MEM_K<<10); #ifndef STANDARD_MEMORY_BIOS_CALL - memory_alt_end = (1<<20) + (ALT_MEM_K<<10); - if (memory_alt_end > memory_end) { - printk("Memory: sized by int13 0e801h\n"); - memory_end = memory_alt_end; + { + unsigned long memory_alt_end = (1<<20) + (ALT_MEM_K<<10); + /* printk(KERN_DEBUG "Memory sizing: %08x %08x\n", memory_end, memory_alt_end); */ + if (memory_alt_end > memory_end) + memory_end = memory_alt_end; } - else - printk("Memory: sized by int13 088h\n"); #endif memory_end &= PAGE_MASK; #ifdef CONFIG_BLK_DEV_RAM @@ -186,7 +172,7 @@ if (to != command_line) to--; if (!memcmp(from+4, "nopentium", 9)) { from += 9+4; - x86_capability &= ~8; + boot_cpu_data.x86_capability &= ~8; } else { memory_end = simple_strtoul(from+4, &from, 0); if ( *from == 'K' || *from == 'k' ) { @@ -232,79 +218,220 @@ request_region(0xf0,0x10,"fpu"); } -static const char * i486model(unsigned int nr) +/* + * Detection of CPU model. + */ + +extern inline void cpuid(int op, int *eax, int *ebx, int *ecx, int *edx) { - static const char *model[] = { - "0","DX","SX","DX/2","4","SX/2","6","DX/2-WB","DX/4","DX/4-WB", - "10","11","12","13","Am5x86-WT","Am5x86-WB" - }; - if (nr < sizeof(model)/sizeof(char *)) - return model[nr]; - return NULL; + __asm__("cpuid" + : "=a" (*eax), + "=b" (*ebx), + "=c" (*ecx), + "=d" (*edx) + : "a" (op) + : "cc"); } -static const char * i586model(unsigned int nr) +__initfunc(static int cyrix_model(struct cpuinfo_x86 *c)) { - static const char *model[] = { - "0", "Pentium 60/66","Pentium 75+","OverDrive PODP5V83", - "Pentium MMX" - }; - if (nr < sizeof(model)/sizeof(char *)) - return model[nr]; - return NULL; + int nr = c->x86_model; + char *buf = c->x86_model_id; + + /* Note that some of the possibilities this decoding allows + * have never actually been manufactured - but those that + * do actually exist are correctly decoded. + */ + if (nr < 0x20) { + strcpy(buf, "Cx486"); + if (!(nr & 0x10)) { + sprintf(buf+5, "%c%s%c", + (nr & 0x01) ? 'D' : 'S', + (nr & 0x04) ? "Rx" : "LC", + (nr & 0x02) ? '2' : '\000'); + } else if (!(nr & 0x08)) { + sprintf(buf+5, "S%s%c", + (nr & 0x01) ? "2" : "", + (nr & 0x02) ? 'e' : '\000'); + } else { + sprintf(buf+5, "DX%c", + nr == 0x1b ? '2' + : (nr == 0x1f ? '4' : '\000')); + } + } else if (nr >= 0x20 && nr <= 0x4f) { /* 5x86, 6x86 or Gx86 */ + char *s = ""; + if (nr >= 0x30 && nr < 0x40) { /* 6x86 */ + if (c->x86 == 5 && (c->x86_capability & (1 << 8))) + s = "L"; /* 6x86L */ + else if (c->x86 == 6) + s = "MX"; /* 6x86MX */ + } + sprintf(buf, "%cx86%s %cx Core/Bus Clock", + "??56G"[nr>>4], + s, + "12??43"[nr & 0x05]); + } else if (nr >= 0x50 && nr <= 0x5f) { /* Cyrix 6x86MX */ + sprintf(buf, "6x86MX %c%sx Core/Bus Clock", + "12233445"[nr & 0x07], + (nr && !(nr&1)) ? ".5" : ""); + } else if (nr >= 0xfd && c->cpuid_level < 0) { + /* Probably 0xfd (Cx486[SD]LC with no ID register) + * or 0xfe (Cx486 A step with no ID register). + */ + strcpy(buf, "Cx486"); + } else + return 0; /* Use CPUID if applicable */ + return 1; } -static const char * k5model(unsigned int nr) +__initfunc(static int amd_model(struct cpuinfo_x86 *c)) { - static const char *model[] = { - "SSA5 (PR-75, PR-90, PR-100)", "5k86 (PR-120, PR-133)", - "5k86 (PR-166)", "5k86 (PR-200)", "", "", - "K6(PR-133..PR-166)","K6(PR-133..PR-200)" - }; - if (nr < sizeof(model)/sizeof(char *)) - return model[nr]; - return NULL; + unsigned int n, dummy, *v; + + /* Actually we must have cpuid or we could never have + * figured out that this was AMD from the vendor info :-). + */ + + cpuid(0x80000000, &n, &dummy, &dummy, &dummy); + if (n < 4) + return 0; + v = (unsigned int *) c->x86_model_id; + cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]); + cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]); + cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]); + c->x86_model_id[48] = 0; + return 1; } -static const char * i686model(unsigned int nr) +__initfunc(void get_cpu_vendor(struct cpuinfo_x86 *c)) { - static const char *model[] = { - "PPro A-step", "Pentium Pro", "2", "Pentium II" - }; - if (nr < sizeof(model)/sizeof(char *)) - return model[nr]; - return NULL; + char *v = c->x86_vendor_id; + + if (!strcmp(v, "GenuineIntel")) + c->x86_vendor = X86_VENDOR_INTEL; + else if (!strcmp(v, "AuthenticAMD")) + c->x86_vendor = X86_VENDOR_AMD; + else if (!strncmp(v, "Cyrix", 5)) + c->x86_vendor = X86_VENDOR_CYRIX; + else if (!strcmp(v, "UMC UMC UMC ")) + c->x86_vendor = X86_VENDOR_UMC; + else if (!strcmp(v, "CentaurHauls")) + c->x86_vendor = X86_VENDOR_CENTAUR; + else if (!strcmp(v, "NexGenDriven")) + c->x86_vendor = X86_VENDOR_NEXGEN; + else + c->x86_vendor = X86_VENDOR_UNKNOWN; } -static const char * getmodel(int x86, int model) +struct cpu_model_info { + int vendor; + int x86; + char *model_names[16]; +}; + +static struct cpu_model_info cpu_models[] __initdata = { + { X86_VENDOR_INTEL, 4, + { "486 DX-25/33", "486 DX-50", "486 SX", "487 DX", "486 DX/2", "486 SL", "486 SX/2", + NULL, "486 DX/2-WB", "486 DX/4", "486 DX/4-WB", NULL, NULL, NULL, NULL }}, + { X86_VENDOR_INTEL, 5, + { "Pentium 60/66 A-step", "Pentium 60/66", "Pentium 75+", + "OverDrive PODP5V83", "Pentium MMX", NULL, NULL, + "Mobile Pentium 75+", "Mobile Pentium MMX", NULL, NULL, NULL, + NULL, NULL, NULL, NULL }}, + { X86_VENDOR_INTEL, 6, + { "Pentium Pro A-step", "Pentium Pro", NULL, "Pentium II", NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }}, + { X86_VENDOR_CYRIX, 4, + { NULL, NULL, NULL, NULL, "MediaGX", NULL, NULL, NULL, NULL, "5x86", + NULL, NULL, NULL, NULL, NULL, NULL }}, + { X86_VENDOR_CYRIX, 5, + { NULL, NULL, "6x86", NULL, "GXm", NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL }}, + { X86_VENDOR_CYRIX, 6, + { "6x86MX", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL }}, + { X86_VENDOR_AMD, 4, + { NULL, NULL, NULL, "DX/2", NULL, NULL, NULL, "DX/2-WB", "DX/4", + "DX/4-WB", NULL, NULL, NULL, NULL, "Am5x86-WT", "Am5x86-WB" }}, + { X86_VENDOR_AMD, 5, + { "K5/SSA5 (PR-75, PR-90, PR-100)", "K5 (PR-120, PR-133)", + "K5 (PR-166)", "K5 (PR-200)", NULL, NULL, + "K6 (166 - 266)", "K6 (166 - 300)", "K6 (model 8)", + "K6 (model 9)", NULL, NULL, NULL, NULL, NULL, NULL }}, + { X86_VENDOR_UMC, 4, + { NULL, "U5D", "U5S", NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL }}, + { X86_VENDOR_CENTAUR, 5, + { NULL, NULL, NULL, NULL, "C6", NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL }}, + { X86_VENDOR_NEXGEN, 5, + { "Nx586", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL }}, +}; + +__initfunc(void identify_cpu(struct cpuinfo_x86 *c)) { - const char *p = NULL; - static char nbuf[12]; - switch (x86) { - case 4: - p = i486model(model); - break; - case 5: - if(strcmp(x86_vendor_id, "AuthenticAMD") == 0){ - p = k5model(model); - } else { - p = i586model(model); + int i; + char *p = NULL; + + c->loops_per_sec = loops_per_sec; + + get_cpu_vendor(c); + + if (c->x86_vendor == X86_VENDOR_UNKNOWN && + c->cpuid_level < 0) + return; + + if ((c->x86_vendor == X86_VENDOR_AMD && amd_model(c)) || + (c->x86_vendor == X86_VENDOR_CYRIX && cyrix_model(c))) + return; + + if (c->x86_model < 16) + for (i=0; ix86_vendor && + cpu_models[i].x86 == c->x86) { + p = cpu_models[i].model_names[c->x86_model]; + break; } - break; - case 6: - p = i686model(model); - break; - } - if (p) - return p; + if (p) + strcpy(c->x86_model_id, p); + else + sprintf(c->x86_model_id, "%02x/%02x", c->x86_vendor, c->x86_model); +} + +static char *cpu_vendor_names[] __initdata = { + "Intel", "Cyrix", "AMD", "UMC", "NexGen", "Centaur" }; + +__initfunc(void print_cpu_info(struct cpuinfo_x86 *c)) +{ + char *vendor = NULL; + + if (c->x86_vendor < sizeof(cpu_vendor_names)/sizeof(char *)) + vendor = cpu_vendor_names[c->x86_vendor]; + else if (c->cpuid_level >= 0) + vendor = c->x86_vendor_id; + + if (vendor) + printk("%s ", vendor); + + if (!c->x86_model_id[0]) + printk("%d86", c->x86); + else + printk("%s", c->x86_model_id); - sprintf(nbuf, "%d", model); - return nbuf; + if (c->x86_mask) + printk(" stepping %02x", c->x86_mask); + + printk("\n"); } +/* + * Get CPU information for use by the procfs. + */ + int get_cpuinfo(char * buffer) { - int i, len = 0; + char *p = buffer; int sep_bug; static const char *x86_cap_flags[] = { "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", @@ -312,83 +439,63 @@ "16", "17", "18", "19", "20", "21", "22", "mmx", "24", "25", "26", "27", "28", "29", "30", "31" }; - -#ifdef __SMP__ - int n; + struct cpuinfo_x86 *c = cpu_data; + int i, n; -#define CD(X) (cpu_data[n].X) -/* SMP has the wrong name for loops_per_sec */ -#define loops_per_sec udelay_val -#define CPUN n - - for ( n = 0 ; n < 32 ; n++ ) { - if ( cpu_present_map & (1<x86 + '0', + c->x86_model_id[0] ? c->x86_model_id : "unknown", + c->x86_vendor_id[0] ? c->x86_vendor_id : "unknown"); + if (c->x86_mask) { + if (c->x86_vendor == X86_VENDOR_CYRIX) + p += sprintf(p, "stepping\t: %d rev %d\n", + c->x86_mask >> 4, + c->x86_mask & 0x0f); + else + p += sprintf(p, "stepping\t: %d\n", c->x86_mask); + } else + p += sprintf(p, "stepping\t: unknown\n"); + + sep_bug = c->x86_vendor == X86_VENDOR_INTEL && + c->x86 == 0x06 && + c->cpuid_level >= 0 && + (c->x86_capability & 0x800) && + c->x86_model < 3 && + c->x86_mask < 3; + + p += sprintf(p, "fdiv_bug\t: %s\n" + "hlt_bug\t\t: %s\n" + "sep_bug\t\t: %s\n" + "f00f_bug\t: %s\n" + "fpu\t\t: %s\n" + "fpu_exception\t: %s\n" + "cpuid level\t: %d\n" + "wp\t\t: %s\n" + "flags\t\t:", + c->fdiv_bug ? "yes" : "no", + c->hlt_works_ok ? "no" : "yes", + sep_bug ? "yes" : "no", + c->f00f_bug ? "yes" : "no", + c->hard_math ? "yes" : "no", + (c->hard_math && ignore_irq13) ? "yes" : "no", + c->cpuid_level, + c->wp_works_ok ? "yes" : "no"); + + for ( i = 0 ; i < 32 ; i++ ) + if ( c->x86_capability & (1 << i) ) + p += sprintf(p, " %s", x86_cap_flags[i]); + p += sprintf(p, "\nbogomips\t: %lu.%02lu\n\n", + (c->loops_per_sec+2500)/500000, + ((c->loops_per_sec+2500)/5000) % 100); + } + return p - buffer; } diff -u --recursive --new-file v2.1.74/linux/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c --- v2.1.74/linux/arch/i386/kernel/signal.c Fri Dec 19 15:52:48 1997 +++ linux/arch/i386/kernel/signal.c Sun Dec 21 17:27:18 1997 @@ -165,7 +165,7 @@ #ifndef CONFIG_MATH_EMULATION restore_i387_hard(buf); #else - if (hard_math) + if (boot_cpu_data.hard_math) restore_i387_hard(buf); else restore_i387_soft(¤t->tss.i387.soft, buf); @@ -325,7 +325,7 @@ #ifndef CONFIG_MATH_EMULATION return save_i387_hard(buf); #else - return hard_math ? save_i387_hard(buf) + return boot_cpu_data.hard_math ? save_i387_hard(buf) : save_i387_soft(¤t->tss.i387.soft, buf); #endif } diff -u --recursive --new-file v2.1.74/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c --- v2.1.74/linux/arch/i386/kernel/smp.c Thu Sep 11 09:02:23 1997 +++ linux/arch/i386/kernel/smp.c Sun Dec 21 17:27:18 1997 @@ -460,7 +460,7 @@ cpu_present_map=3; num_processors=2; printk("I/O APIC at 0xFEC00000.\n"); - printk("Bus#0 is "); + printk("Bus #0 is "); } switch(mpf->mpf_feature1) { @@ -558,7 +558,7 @@ __initfunc(unsigned long smp_alloc_memory(unsigned long mem_base)) { if (virt_to_phys((void *)mem_base) >= 0x9F000) - panic("smp_alloc_memory: Insufficient low memory for kernel trampoline 0x%lx.\n", mem_base); + panic("smp_alloc_memory: Insufficient low memory for kernel trampoline 0x%lx.", mem_base); trampoline_base = (void *)mem_base; return mem_base + PAGE_SIZE; } @@ -571,22 +571,17 @@ __initfunc(void smp_store_cpu_info(int id)) { struct cpuinfo_x86 *c=&cpu_data[id]; - c->hard_math=hard_math; /* Always assumed same currently */ - c->x86=x86; - c->x86_model=x86_model; - c->x86_mask=x86_mask; + + *c = boot_cpu_data; + identify_cpu(c); /* * Mask B, Pentium, but not Pentium MMX */ - if(x86_mask>=1 && x86_mask<=4 && x86==5 && (x86_model>=0&&x86_model<=3)) + if (c->x86_vendor == X86_VENDOR_INTEL && + c->x86 == 5 && + c->x86_mask >= 1 && c->x86_mask <= 4 && + c->x86_model <= 3) smp_b_stepping=1; /* Remember we have B step Pentia with bugs */ - c->x86_capability=x86_capability; - c->fdiv_bug=fdiv_bug; - c->wp_works_ok=wp_works_ok; /* Always assumed the same currently */ - c->hlt_works_ok=hlt_works_ok; - c->have_cpuid=have_cpuid; - c->udelay_val=loops_per_sec; - strcpy(c->x86_vendor_id, x86_vendor_id); } /* @@ -706,7 +701,7 @@ idle = task[cpucount]; if (!idle) - panic("No idle process for CPU %d\n", i); + panic("No idle process for CPU %d", i); idle->processor = i; cpu_logical_map[cpucount] = i; @@ -855,6 +850,9 @@ cpu_number_map[i] = cpucount; cpu_logical_map[cpucount] = i; #endif + printk("OK.\n"); + printk("CPU%d: ", i); + print_cpu_info(&cpu_data[i]); } else { @@ -901,28 +899,33 @@ */ smp_store_cpu_info(boot_cpu_id); /* Final full version of the data */ + printk("CPU%d: ", boot_cpu_id); + print_cpu_info(&cpu_data[boot_cpu_id]); cpu_present_map |= (1 << hard_smp_processor_id()); cpu_number_map[boot_cpu_id] = 0; active_kernel_processor=boot_cpu_id; /* - * If SMP should be disabled, then really disable it! + * If we don't conform to the Intel MPS standard, get out + * of here now! */ - if (!max_cpus && smp_found_config) + if (!smp_found_config) { - smp_found_config = 0; - printk("SMP mode deactivated, forcing use of dummy APIC emulation.\n"); + printk(KERN_NOTICE "SMP motherboard not detected. Using dummy APIC emulation.\n"); + return; } /* - * If we don't conform to the Intel MPS standard, get out - * of here now! + * If SMP should be disabled, then really disable it! */ - if (!smp_found_config) - return; + if (!max_cpus) + { + smp_found_config = 0; + printk("SMP mode deactivated, forcing use of dummy APIC emulation.\n"); + } /* * Map the local APIC into kernel space @@ -931,7 +934,7 @@ apic_reg = ioremap(apic_addr,4096); if(apic_reg == NULL) - panic("Unable to map local apic.\n"); + panic("Unable to map local apic."); #ifdef SMP_DEBUG { @@ -1049,7 +1052,7 @@ SMP_PRINTK(("Before bogomips.\n")); if(cpucount==0) { - printk("Error: only one processor found.\n"); + printk(KERN_ERR "Error: only one processor found.\n"); cpu_present_map=(1< #include diff -u --recursive --new-file v2.1.74/linux/arch/i386/lib/delay.c linux/arch/i386/lib/delay.c --- v2.1.74/linux/arch/i386/lib/delay.c Mon Dec 1 12:04:11 1997 +++ linux/arch/i386/lib/delay.c Sun Dec 21 17:27:18 1997 @@ -15,12 +15,6 @@ #include #endif -#ifdef __SMP__ -#define __udelay_val cpu_data[smp_processor_id()].udelay_val -#else -#define __udelay_val loops_per_sec -#endif - void __delay(unsigned long loops) { __asm__ __volatile__( @@ -34,7 +28,7 @@ { __asm__("mull %0" :"=d" (xloops) - :"a" (xloops),"0" (__udelay_val) + :"a" (xloops),"0" (current_cpu_data.loops_per_sec) :"ax"); __delay(xloops); } diff -u --recursive --new-file v2.1.74/linux/arch/i386/mm/fault.c linux/arch/i386/mm/fault.c --- v2.1.74/linux/arch/i386/mm/fault.c Wed Nov 26 16:24:01 1997 +++ linux/arch/i386/mm/fault.c Sun Dec 21 17:27:18 1997 @@ -76,8 +76,6 @@ asmlinkage void do_invalid_op (struct pt_regs *, unsigned long); -extern int pentium_f00f_bug; - /* * This routine handles page faults. It determines the address, * and the problem, and then passes it off to one of the appropriate @@ -180,7 +178,7 @@ /* * Pentium F0 0F C7 C8 bug workaround. */ - if (pentium_f00f_bug) { + if (boot_cpu_data.f00f_bug) { unsigned long nr; nr = (address - (unsigned long) idt) >> 3; @@ -209,10 +207,16 @@ * * First we check if it was the bootup rw-test, though.. */ - if (wp_works_ok < 0 && address == TASK_SIZE && (error_code & 1)) { - wp_works_ok = 1; - pg0[0] = pte_val(mk_pte(TASK_SIZE, PAGE_SHARED)); - flush_tlb(); + if (boot_cpu_data.wp_works_ok < 0 && + address == PAGE_OFFSET && (error_code & 1)) { + boot_cpu_data.wp_works_ok = 1; + pg0[0] = pte_val(mk_pte(PAGE_OFFSET, PAGE_KERNEL)); + local_flush_tlb(); + /* + * Beware: Black magic here. The printk is needed here to flush + * CPU state on certain buggy processors. + */ + printk("Ok"); goto out; } diff -u --recursive --new-file v2.1.74/linux/arch/i386/mm/init.c linux/arch/i386/mm/init.c --- v2.1.74/linux/arch/i386/mm/init.c Sun Sep 7 13:10:42 1997 +++ linux/arch/i386/mm/init.c Sun Dec 21 17:27:18 1997 @@ -131,14 +131,6 @@ #define X86_FEATURE_MCA 0x4000 /* Machine Check Architecture */ #define X86_FEATURE_CMOV 0x8000 /* Cmov/fcomi */ -#ifdef GAS_KNOWS_CR4 -#define read_cr4 "movl %%cr4,%%eax" -#define write_cr4 "movl %%eax,%%cr4" -#else -#define read_cr4 ".byte 0x0f,0x20,0xe0" -#define write_cr4 ".byte 0x0f,0x22,0xe0" -#endif - /* * Save the cr4 feature set we're using (ie * Pentium 4MB enable and PPro Global page @@ -150,9 +142,9 @@ static inline void set_in_cr4(unsigned long mask) { mmu_cr4_features |= mask; - __asm__(read_cr4 "\n\t" + __asm__("movl %%cr4,%%eax\n\t" "orl %0,%%eax\n\t" - write_cr4 + "movl %%eax,%%cr4\n" : : "irg" (mask) :"ax"); } @@ -178,9 +170,6 @@ * kernel. * It may also hold the MP configuration table when we are booting SMP. */ -#if 0 - memset((void *) 0, 0, PAGE_SIZE); -#endif #ifdef __SMP__ if (!smp_scan_config(0x0,0x400)) /* Scan the bottom 1K for a signature */ { @@ -199,9 +188,6 @@ */ /* smp_alloc_memory(8192); */ #endif -#ifdef TEST_VERIFY_AREA - wp_works_ok = 0; -#endif start_mem = PAGE_ALIGN(start_mem); address = PAGE_OFFSET; pg_dir = swapper_pg_dir; @@ -219,14 +205,14 @@ * virtual memory boundary, but that's OK as we won't * use that memory anyway. */ - if (x86_capability & X86_FEATURE_PSE) { + if (boot_cpu_data.x86_capability & X86_FEATURE_PSE) { unsigned long __pe; set_in_cr4(X86_CR4_PSE); - wp_works_ok = 1; + boot_cpu_data.wp_works_ok = 1; __pe = _KERNPG_TABLE + _PAGE_4M + __pa(address); /* Make it "global" too if supported */ - if (x86_capability & X86_FEATURE_PGE) { + if (boot_cpu_data.x86_capability & X86_FEATURE_PGE) { set_in_cr4(X86_CR4_PGE); __pe += _PAGE_GLOBAL; } @@ -235,6 +221,7 @@ address += 4*1024*1024; continue; } + /* * We're on a [34]86, use normal page tables. * pg_table is physical at this point @@ -247,6 +234,7 @@ pgd_val(*pg_dir) = _PAGE_TABLE | (unsigned long) pg_table; pg_dir++; + /* now change pg_table to kernel virtual addresses */ pg_table = (pte_t *) __va(pg_table); for (tmp = 0 ; tmp < PTRS_PER_PTE ; tmp++,pg_table++) { @@ -288,14 +276,14 @@ pg0[0] = old; local_flush_tlb(); current->mm->mmap->vm_start -= PAGE_SIZE; - if (wp_works_ok < 0) { - wp_works_ok = 0; + if (boot_cpu_data.wp_works_ok < 0) { + boot_cpu_data.wp_works_ok = 0; printk("No.\n"); #ifndef CONFIG_M386 panic("This kernel doesn't support CPU's with broken WP. Recompile it for a 386!"); #endif } else - printk("Ok.\n"); + printk(".\n"); } __initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem)) @@ -377,7 +365,7 @@ datapages << (PAGE_SHIFT-10), initpages << (PAGE_SHIFT-10)); - if (wp_works_ok < 0) + if (boot_cpu_data.wp_works_ok < 0) test_wp_bit(); } diff -u --recursive --new-file v2.1.74/linux/drivers/block/floppy.c linux/drivers/block/floppy.c --- v2.1.74/linux/drivers/block/floppy.c Thu Dec 4 14:53:54 1997 +++ linux/drivers/block/floppy.c Sun Dec 21 17:41:24 1997 @@ -3295,8 +3295,8 @@ return -EINVAL; /* permission checks */ - if (((cmd & 0x80) && !suser()) || - ((cmd & 0x40) && !IOCTL_ALLOWED)) + if (((cmd & 0x40) && !IOCTL_ALLOWED) || + ((cmd & 0x80) && !suser())) return -EPERM; /* copyin */ diff -u --recursive --new-file v2.1.74/linux/drivers/block/ide-disk.c linux/drivers/block/ide-disk.c --- v2.1.74/linux/drivers/block/ide-disk.c Fri Dec 19 15:52:56 1997 +++ linux/drivers/block/ide-disk.c Sun Dec 21 17:14:45 1997 @@ -467,7 +467,22 @@ drive->special.b.set_multmode = 1; } +static int proc_idedisk_read_cache + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + char *out = page; + int len; + + if (drive->id) + len = sprintf(out,"%i\n", drive->id->buf_size / 2); + else + len = sprintf(out,"(none)\n"); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + static ide_proc_entry_t idedisk_proc[] = { + { "cache", proc_idedisk_read_cache, NULL }, { "geometry", proc_ide_read_geometry, NULL }, { NULL, NULL, NULL } }; diff -u --recursive --new-file v2.1.74/linux/drivers/block/ide-floppy.c linux/drivers/block/ide-floppy.c --- v2.1.74/linux/drivers/block/ide-floppy.c Fri Dec 19 15:52:57 1997 +++ linux/drivers/block/ide-floppy.c Sun Dec 21 17:14:45 1997 @@ -1,5 +1,5 @@ /* - * linux/drivers/block/ide-floppy.c Version 0.8 Feb 21, 1997 + * linux/drivers/block/ide-floppy.c Version 0.8 Dec 7, 1997 * * Copyright (C) 1996, 1997 Gadi Oxman */ @@ -1364,6 +1364,11 @@ return 0; } +static ide_proc_entry_t idefloppy_proc[] = { + { "geometry", proc_ide_read_geometry, NULL }, + { NULL, NULL, NULL } +}; + int idefloppy_init (void); static ide_module_t idefloppy_module = { IDE_DRIVER_MODULE, @@ -1391,7 +1396,7 @@ NULL, /* pre_reset */ idefloppy_capacity, /* capacity */ NULL, /* special */ - NULL /* proc */ + idefloppy_proc /* proc */ }; /* diff -u --recursive --new-file v2.1.74/linux/drivers/block/ide-probe.c linux/drivers/block/ide-probe.c --- v2.1.74/linux/drivers/block/ide-probe.c Fri Dec 19 15:52:57 1997 +++ linux/drivers/block/ide-probe.c Sun Dec 21 17:14:45 1997 @@ -582,7 +582,7 @@ { struct gendisk *gd, **gdp; unsigned int unit, units, minors; - int *bs, *max_sect; + int *bs, *max_sect, *max_ra; /* figure out maximum drive number on the interface */ for (units = MAX_DRIVES; units > 0; --units) { @@ -595,15 +595,18 @@ gd->part = kmalloc (minors * sizeof(struct hd_struct), GFP_KERNEL); bs = kmalloc (minors*sizeof(int), GFP_KERNEL); max_sect = kmalloc (minors*sizeof(int), GFP_KERNEL); + max_ra = kmalloc (minors*sizeof(int), GFP_KERNEL); memset(gd->part, 0, minors * sizeof(struct hd_struct)); /* cdroms and msdos f/s are examples of non-1024 blocksizes */ blksize_size[hwif->major] = bs; max_sectors[hwif->major] = max_sect; + max_readahead[hwif->major] = max_ra; for (unit = 0; unit < minors; ++unit) { *bs++ = BLOCK_SIZE; - *max_sect++ = 244; + *max_sect++ = MAX_SECTORS; + *max_ra++ = MAX_READAHEAD; } for (unit = 0; unit < units; ++unit) @@ -673,6 +676,10 @@ } return hwif->present; } + + +int ideprobe_init(void); + static ide_module_t ideprobe_module = { IDE_PROBE_MODULE, diff -u --recursive --new-file v2.1.74/linux/drivers/block/ide-proc.c linux/drivers/block/ide-proc.c --- v2.1.74/linux/drivers/block/ide-proc.c Fri Dec 19 15:52:57 1997 +++ linux/drivers/block/ide-proc.c Sun Dec 21 17:14:45 1997 @@ -50,23 +50,6 @@ #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #endif -/* - * Standard exit stuff: - */ -#define PROC_IDE_READ_RETURN(page,start,off,count,eof,len) \ -{ \ - len -= off; \ - if (len < count) { \ - *eof = 1; \ - if (len <= 0) \ - return 0; \ - } else \ - len = count; \ - *start = page + off; \ - return len; \ -} - - #ifdef CONFIG_PCI static int ide_getxdigit(char c) @@ -129,8 +112,9 @@ cli(); /* ensure all PCI writes are done together */ while (((ide_hwgroup_t *)(hwif->hwgroup))->active || (hwif->mate && ((ide_hwgroup_t *)(hwif->mate->hwgroup))->active)) { sti(); - if (0 < (signed long)(timeout - jiffies)) { + if (0 < (signed long)(jiffies - timeout)) { printk("/proc/ide/%s/pci: channel(s) busy, cannot write\n", hwif->name); + restore_flags(flags); return -EBUSY; } cli(); @@ -311,6 +295,8 @@ (char *page, char **start, off_t off, int count, int *eof, void *data) { ide_drive_t *drive = (ide_drive_t *) data; + int major = HWIF(drive)->major; + int minor = drive->select.b.unit << PARTN_BITS; char *out = page; int len; @@ -321,6 +307,9 @@ out += sprintf(out,"nowerr %i\n", drive->bad_wstat == BAD_R_STAT); out += sprintf(out,"keepsettings %i\n", drive->keep_settings); out += sprintf(out,"nice %i/%i/%i\n", drive->nice0, drive->nice1, drive->nice2); + out += sprintf(out,"dsc_overlap %i\n", drive->dsc_overlap); + out += sprintf(out,"max_sectors %i\n", max_sectors[major][minor]); + out += sprintf(out,"readahead %i\n", max_readahead[major][minor] / 1024); len = out - page; PROC_IDE_READ_RETURN(page,start,off,count,eof,len); } @@ -329,9 +318,13 @@ (char *page, char **start, off_t off, int count, int *eof, void *data) { ide_drive_t *drive = (ide_drive_t *) data; + ide_driver_t *driver = (ide_driver_t *) drive->driver; int len; - len = sprintf(page,"%li\n", ((ide_driver_t *)drive->driver)->capacity(drive)); + if (!driver) + len = sprintf(page, "(none)\n"); + else + len = sprintf(page,"%li\n", ((ide_driver_t *)drive->driver)->capacity(drive)); PROC_IDE_READ_RETURN(page,start,off,count,eof,len); } @@ -399,7 +392,6 @@ static ide_proc_entry_t generic_drive_entries[] = { - { "capacity", proc_ide_read_capacity, NULL }, { "driver", proc_ide_read_driver, NULL }, { "identify", proc_ide_read_identify, NULL }, { "media", proc_ide_read_media, NULL }, diff -u --recursive --new-file v2.1.74/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c --- v2.1.74/linux/drivers/block/ide-tape.c Fri Dec 19 15:52:57 1997 +++ linux/drivers/block/ide-tape.c Sun Dec 21 17:14:45 1997 @@ -3321,6 +3321,9 @@ unsigned short mask,i; #endif /* IDETAPE_DEBUG_LOG */ + if (!id) + return 0; + *((unsigned short *) &gcw) = id->config; #if IDETAPE_DEBUG_LOG @@ -3421,10 +3424,7 @@ printk (KERN_ERR "ide-tape: Device type is not set to tape\n"); else if (!gcw.removable) printk (KERN_ERR "ide-tape: The removable flag is not set\n"); - else if (gcw.drq_type != 2) { - printk (KERN_ERR "ide-tape: Sorry, DRQ types other than Accelerated DRQ\n"); - printk (KERN_ERR "ide-tape: are still not supported by the driver\n"); - } else if (gcw.packet_size != 0) { + else if (gcw.packet_size != 0) { printk (KERN_ERR "ide-tape: Packet size is not 12 bytes long\n"); if (gcw.packet_size == 1) printk (KERN_ERR "ide-tape: Sorry, padding to 16 bytes is still not supported\n"); @@ -3605,6 +3605,88 @@ return 0; } +static int proc_idetape_read_buffer + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + idetape_tape_t *tape = drive->driver_data; + char *out = page; + int len; + + len = sprintf(out,"%d\n", tape->capabilities.buffer_size / 2); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_idetape_read_name + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + idetape_tape_t *tape = drive->driver_data; + char *out = page; + int len; + + len = sprintf(out,"%s\n", tape->name); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_idetape_read_pipeline + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + idetape_tape_t *tape = drive->driver_data; + char *out = page; + int len; + + len = sprintf(out,"%d\n", tape->max_stages * tape->stage_size / 1024); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_idetape_read_speed + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + idetape_tape_t *tape = drive->driver_data; + char *out = page; + int len; + + len = sprintf(out,"%d\n", tape->capabilities.speed); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_idetape_read_stage + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + idetape_tape_t *tape = drive->driver_data; + char *out = page; + int len; + + len = sprintf(out,"%d\n", tape->stage_size / 1024); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_idetape_read_tdsc + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + idetape_tape_t *tape = drive->driver_data; + char *out = page; + int len; + + len = sprintf(out,"%lu\n", tape->best_dsc_rw_frequency * 1000 / HZ); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static ide_proc_entry_t idetape_proc[] = { + { "buffer", proc_idetape_read_buffer, NULL }, + { "name", proc_idetape_read_name, NULL }, + { "pipeline", proc_idetape_read_pipeline, NULL }, + { "speed", proc_idetape_read_speed, NULL }, + { "stage", proc_idetape_read_stage, NULL }, + { "tdsc", proc_idetape_read_tdsc, NULL }, + { NULL, NULL, NULL } +}; + int idetape_init (void); static ide_module_t idetape_module = { @@ -3633,7 +3715,7 @@ idetape_pre_reset, /* pre_reset */ NULL, /* capacity */ NULL, /* special */ - NULL /* proc */ + idetape_proc /* proc */ }; /* diff -u --recursive --new-file v2.1.74/linux/drivers/block/ide.c linux/drivers/block/ide.c --- v2.1.74/linux/drivers/block/ide.c Fri Dec 19 15:52:57 1997 +++ linux/drivers/block/ide.c Sun Dec 21 17:14:45 1997 @@ -1640,6 +1640,7 @@ unregister_blkdev(hwif->major, hwif->name); kfree(blksize_size[hwif->major]); kfree(max_sectors[hwif->major]); + kfree(max_readahead[hwif->major]); blk_dev[hwif->major].request_fn = NULL; blk_dev[hwif->major].data = NULL; blk_dev[hwif->major].queue = NULL; @@ -2598,6 +2599,11 @@ return NULL; } +static ide_proc_entry_t generic_subdriver_entries[] = { + { "capacity", proc_ide_read_capacity, NULL }, + { NULL, NULL, NULL } +}; + int ide_register_subdriver (ide_drive_t *drive, ide_driver_t *driver, int version) { unsigned long flags; @@ -2619,6 +2625,7 @@ drive->nice1 = 1; } drive->revalidate = 1; + ide_add_proc_entries(drive, generic_subdriver_entries); ide_add_proc_entries(drive, driver->proc); return 0; } @@ -2634,6 +2641,7 @@ return 1; } ide_remove_proc_entries(drive, DRIVER(drive)->proc); + ide_remove_proc_entries(drive, generic_subdriver_entries); drive->driver = NULL; restore_flags(flags); return 0; diff -u --recursive --new-file v2.1.74/linux/drivers/block/ide.h linux/drivers/block/ide.h --- v2.1.74/linux/drivers/block/ide.h Fri Dec 19 15:52:57 1997 +++ linux/drivers/block/ide.h Sun Dec 21 17:58:46 1997 @@ -373,7 +373,24 @@ void proc_ide_init(void); void ide_add_proc_entries(ide_drive_t *drive, ide_proc_entry_t *p); void ide_remove_proc_entries(ide_drive_t *drive, ide_proc_entry_t *p); +read_proc_t proc_ide_read_capacity; read_proc_t proc_ide_read_geometry; + +/* + * Standard exit stuff: + */ +#define PROC_IDE_READ_RETURN(page,start,off,count,eof,len) \ +{ \ + len -= off; \ + if (len < count) { \ + *eof = 1; \ + if (len <= 0) \ + return 0; \ + } else \ + len = count; \ + *start = page + off; \ + return len; \ +} /* * Subdrivers support. diff -u --recursive --new-file v2.1.74/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c --- v2.1.74/linux/drivers/block/ll_rw_blk.c Fri Dec 19 15:52:57 1997 +++ linux/drivers/block/ll_rw_blk.c Sun Dec 21 17:14:45 1997 @@ -95,7 +95,7 @@ static inline int get_max_sectors(kdev_t dev) { if (!max_sectors[MAJOR(dev)]) - return 244; /* 254? */ + return MAX_SECTORS; return max_sectors[MAJOR(dev)][MINOR(dev)]; } @@ -684,6 +684,7 @@ dev->plug.rq_status = RQ_INACTIVE; dev->plug.cmd = -1; dev->plug.next = NULL; + dev->plug_tq.sync = 0; dev->plug_tq.routine = &unplug_device; dev->plug_tq.data = dev; } diff -u --recursive --new-file v2.1.74/linux/drivers/block/swim3.c linux/drivers/block/swim3.c --- v2.1.74/linux/drivers/block/swim3.c Wed Sep 24 20:05:46 1997 +++ linux/drivers/block/swim3.c Sun Dec 21 17:41:24 1997 @@ -741,8 +741,8 @@ struct floppy_state *fs; int err; - if (((cmd & 0x80) && !suser()) - || ((cmd & 0x40) && !(filp && (filp->f_mode & IOCTL_MODE_BIT)))) + if (((cmd & 0x40) && !(filp && (filp->f_mode & IOCTL_MODE_BIT))) || + ((cmd & 0x80) && !suser())) return -EPERM; fs = &floppy_states[0]; diff -u --recursive --new-file v2.1.74/linux/drivers/char/Makefile linux/drivers/char/Makefile --- v2.1.74/linux/drivers/char/Makefile Tue Dec 2 16:45:18 1997 +++ linux/drivers/char/Makefile Sun Dec 21 17:27:17 1997 @@ -373,10 +373,9 @@ ifdef CONFIG_VT ifdef CONFIG_TGA_CONSOLE L_OBJS += tga.o - else - ifndef CONFIG_SUN_CONSOLE - L_OBJS += vga.o vesa_blank.o - endif + endif + ifdef CONFIG_VGA_CONSOLE + L_OBJS += vga.o vesa_blank.o endif endif diff -u --recursive --new-file v2.1.74/linux/drivers/char/apm_bios.c linux/drivers/char/apm_bios.c --- v2.1.74/linux/drivers/char/apm_bios.c Sat Nov 29 11:25:09 1997 +++ linux/drivers/char/apm_bios.c Sun Dec 21 17:41:24 1997 @@ -964,6 +964,13 @@ as->event_tail = as->event_head = 0; as->suspends_pending = as->standbys_pending = 0; as->suspends_read = as->standbys_read = 0; + /* + * XXX - this is a tiny bit broken, when we consider BSD + * process accounting. If the device is opened by root, we + * instantly flag that we used superuser privs. Who knows, + * we might close the device immediately without doing a + * privileged operation -- cevans + */ as->suser = suser(); as->next = user_list; user_list = as; diff -u --recursive --new-file v2.1.74/linux/drivers/char/bttv.c linux/drivers/char/bttv.c --- v2.1.74/linux/drivers/char/bttv.c Tue Dec 2 16:45:18 1997 +++ linux/drivers/char/bttv.c Sun Dec 21 17:41:24 1997 @@ -378,7 +378,7 @@ */ case 1: btwrite(BT848_COLOR_FMT_RGB8, BT848_COLOR_FMT); - btand(~0x10, BT848_CAP_CTL); // Dithering looks much better in this mode + btand(~0x10, BT848_CAP_CTL); /* Dithering looks much better in this mode */ break; case 2: btwrite(BT848_COLOR_FMT_RGB16, BT848_COLOR_FMT); @@ -721,7 +721,9 @@ * Cliprect -> risc table. * * FIXME: This is generating wrong code when we have some kinds of - * rectangle lists. I don't currently understand why. + * rectangle lists. If you generate overlapped rectangles then it + * gets a bit confused. Since we add the frame buffer clip rectangles + * we need to fix this. Better yet to rewrite this function. */ static void write_risc_data(struct bttv *btv, struct video_clip *vp, int count) @@ -818,6 +820,11 @@ while ((cur=cur->next)); } + /* + * Fixme - we have to handle overlapped rectangles + * here, but the overlap might be partial + */ + /* add rect to second (x-sorted) list if rect.y == y */ if ((cur=first.next)) { @@ -1068,7 +1075,7 @@ /* Only channel 0 has a tuner */ if(v.tuner!=0 || lastchan) return -EINVAL; - if(v.mode!=VIDEO_MODE_PAL||v.mode!=VIDEO_MODE_NTSC) + if(v.mode!=VIDEO_MODE_PAL&&v.mode!=VIDEO_MODE_NTSC) return -EOPNOTSUPP; btv->win.norm = v.mode; bt848_set_size(btv); @@ -1262,6 +1269,9 @@ vp=btv->audio_dev; vp.flags&=~(VIDEO_AUDIO_MUTE|VIDEO_AUDIO_MUTABLE); vp.flags|=VIDEO_AUDIO_MUTABLE; + strcpy(vp.name,"TV"); + if(copy_to_user(&v,arg,sizeof(v))) + return -EFAULT; return 0; } case VIDIOCSAUDIO: diff -u --recursive --new-file v2.1.74/linux/drivers/char/mem.c linux/drivers/char/mem.c --- v2.1.74/linux/drivers/char/mem.c Mon Dec 1 12:04:12 1997 +++ linux/drivers/char/mem.c Sun Dec 21 17:27:18 1997 @@ -129,7 +129,7 @@ * The surround logic should disable caching for the high device * addresses anyway, but right now this seems still needed. */ - if (x86 > 3 && offset >= __pa(high_memory)) + if (boot_cpu_data.x86 > 3 && offset >= __pa(high_memory)) pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; #endif #ifdef __powerpc__ diff -u --recursive --new-file v2.1.74/linux/drivers/char/pc_keyb.h linux/drivers/char/pc_keyb.h --- v2.1.74/linux/drivers/char/pc_keyb.h Fri Dec 19 15:52:58 1997 +++ linux/drivers/char/pc_keyb.h Sun Dec 21 17:27:18 1997 @@ -10,7 +10,7 @@ * Configuration Switches */ -#define KBD_REPORT_ERR /* Report keyboard errors */ +#undef KBD_REPORT_ERR /* Report keyboard errors */ #define KBD_REPORT_UNKN /* Report unknown scan codes */ #define KBD_REPORT_TIMEOUTS /* Report keyboard timeouts */ #undef KBD_IS_FOCUS_9000 /* We have the brain-damaged FOCUS-9000 keyboard */ @@ -87,7 +87,7 @@ * Controller Mode Register Bits */ -#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generage IRQ1 */ +#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */ #define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */ #define KBD_MODE_SYS 0x04 /* The system flag (?) */ #define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */ diff -u --recursive --new-file v2.1.74/linux/drivers/char/pms.c linux/drivers/char/pms.c --- v2.1.74/linux/drivers/char/pms.c Sat Nov 29 11:25:09 1997 +++ linux/drivers/char/pms.c Sun Dec 21 17:41:24 1997 @@ -1045,9 +1045,15 @@ #ifdef MODULE +MODULE_PARM(io_port,"i"); +MODULE_PARM(mem_base,"i"); + int init_module(void) { printk(KERN_INFO "Mediavision Pro Movie Studio driver 0.01\n"); + + data_port = io_port +1; + if(init_mediavision()) { printk(KERN_INFO "Board not found.\n"); diff -u --recursive --new-file v2.1.74/linux/drivers/char/random.c linux/drivers/char/random.c --- v2.1.74/linux/drivers/char/random.c Sat Oct 25 02:44:15 1997 +++ linux/drivers/char/random.c Sun Dec 21 17:27:18 1997 @@ -585,7 +585,7 @@ begin_benchmark(&timer_benchmark); #endif #if defined (__i386__) - if (x86_capability & 16) { + if (boot_cpu_data.x86_capability & 16) { unsigned long low, high; __asm__(".byte 0x0f,0x31" :"=a" (low), "=d" (high)); diff -u --recursive --new-file v2.1.74/linux/drivers/char/sysrq.c linux/drivers/char/sysrq.c --- v2.1.74/linux/drivers/char/sysrq.c Mon Aug 4 16:25:37 1997 +++ linux/drivers/char/sysrq.c Sun Dec 21 17:27:17 1997 @@ -1,6 +1,6 @@ /* -*- linux-c -*- * - * $Id: sysrq.c,v 1.4 1997/07/17 11:54:15 mj Exp $ + * $Id: sysrq.c,v 1.7 1997/11/06 15:57:09 mj Exp $ * * Linux Magic System Request Key Hacks * @@ -31,10 +31,6 @@ extern int console_loglevel; extern struct vfsmount *vfsmntlist; -#ifdef __sparc__ -extern void halt_now(void); -#endif - /* Send a signal to all user processes */ static void send_sig_all(int sig, int even_init) @@ -60,6 +56,9 @@ { int orig_log_level = console_loglevel; + if (!key) + return; + console_loglevel = 7; printk(KERN_INFO "SysRq: "); switch (key) { @@ -69,7 +68,7 @@ printk("Keyboard mode set to XLATE\n"); } break; - case 'a': /* A -- SAK */ + case 'k': /* K -- SAK */ printk("SAK\n"); if (tty) do_SAK(tty); @@ -79,12 +78,6 @@ printk("Resetting\n"); machine_restart(NULL); break; -#ifdef __sparc__ - case 'h': /* H -- halt immediately */ - printk("Halting\n"); - halt_now(); - break; -#endif #ifdef CONFIG_APM case 'o': /* O -- power off */ printk("Power off\n"); @@ -123,7 +116,7 @@ send_sig_all(SIGTERM, 0); orig_log_level = 8; /* We probably have killed syslogd */ break; - case 'k': /* K -- kill all user processes */ + case 'i': /* I -- kill all user processes */ printk("Kill All Tasks\n"); send_sig_all(SIGKILL, 0); orig_log_level = 8; @@ -134,14 +127,16 @@ orig_log_level = 8; break; default: /* Unknown: help */ - printk("unRaw sAk Boot " -#ifdef __sparc__ - "Halt " -#endif + if (kbd) + printk("unRaw "); + if (tty) + printk("saK "); + printk("Boot " #ifdef CONFIG_APM "Off " #endif - "Sync Unmount showPc showTasks showMem loglevel0-8 tErm Kill killalL\n"); + "Sync Unmount showPc showTasks showMem loglevel0-8 tErm kIll killalL\n"); + /* Don't use 'A' as it's handled specially on the Sparc */ } console_loglevel = orig_log_level; diff -u --recursive --new-file v2.1.74/linux/drivers/char/tga.c linux/drivers/char/tga.c --- v2.1.74/linux/drivers/char/tga.c Thu Dec 4 14:53:55 1997 +++ linux/drivers/char/tga.c Sun Dec 21 17:02:16 1997 @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -446,14 +447,21 @@ /* * See if we have a TGA card. + * Just a placeholder at the moment, because of the strange + * way the TGA card is initialized. This has to be enabled when + * the kernel initializes PCI devices before the console. */ -__initfunc(int con_is_present()) +__initfunc(int con_is_present(void)) { +#if 0 + unsigned char pci_bus, pci_devfn; int status; status = pcibios_find_device (PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA, 0, &pci_bus, &pci_devfn); return (status == PCIBIOS_DEVICE_NOT_FOUND) ? 0 : 1; +#endif + return 1; } /* diff -u --recursive --new-file v2.1.74/linux/drivers/char/wdt.c linux/drivers/char/wdt.c --- v2.1.74/linux/drivers/char/wdt.c Wed Dec 10 11:12:43 1997 +++ linux/drivers/char/wdt.c Sun Dec 21 17:41:24 1997 @@ -1,7 +1,7 @@ /* * Industrial Computer Source WDT500/501 driver for Linux 2.1.x * - * (c) Copyright 1996 Alan Cox , All Rights Reserved. + * (c) Copyright 1996-1997 Alan Cox , All Rights Reserved. * http://www.cymru.net * * This program is free software; you can redistribute it and/or @@ -15,7 +15,7 @@ * * (c) Copyright 1995 Alan Cox * - * Release 0.06. + * Release 0.07. * * Fixes * Dave Gregorich : Modularisation and minor bugs @@ -368,7 +368,7 @@ #ifdef CONFIG_WDT_501 misc_deregister(&temp_miscdev); #endif - notifier_chain_unregister(&boot_notifier_list, &wdt_notifier); + notifier_chain_unregister(&reboot_notifier_list, &wdt_notifier); release_region(io,8); free_irq(irq, NULL); } @@ -377,7 +377,7 @@ __initfunc(int wdt_init(void)) { - printk("WDT500/501-P driver at %X(Interrupt %d)\n", io,irq); + printk("WDT500/501-P driver 0.07 at %X (Interrupt %d)\n", io,irq); if(request_irq(irq, wdt_interrupt, SA_INTERRUPT, "wdt501p", NULL)) { printk("IRQ %d is not free.\n", irq); @@ -387,8 +387,8 @@ #ifdef CONFIG_WDT_501 misc_register(&temp_miscdev); #endif - request_region(io, 8, "wdt501"); - notifier_chain_register(&boot_notifier_list, &wdt_notifier); + request_region(io, 8, "wdt501p"); + notifier_chain_register(&reboot_notifier_list, &wdt_notifier); return 0; } diff -u --recursive --new-file v2.1.74/linux/drivers/net/Config.in linux/drivers/net/Config.in --- v2.1.74/linux/drivers/net/Config.in Fri Dec 19 15:52:58 1997 +++ linux/drivers/net/Config.in Sun Dec 21 17:41:24 1997 @@ -136,7 +136,11 @@ bool 'Dayna firmware support' CONFIG_COPS_DAYNA bool 'Tangent firmware support' CONFIG_COPS_TANGENT fi - dep_tristate 'IP-over-DDP driver support' CONFIG_IPDDP $CONFIG_ATALK + tristate 'Appletalk-IP driver support' CONFIG_IPDDP + if [ "$CONFIG_IPDDP" != "n" ]; then + bool 'IP to Appletalk-IP Encapsulation support' CONFIG_IPDDP_ENCAP + bool 'Appletalk-IP to IP Decapsulation support' CONFIG_IPDDP_DECAP + fi fi fi @@ -156,8 +160,11 @@ bool ' Six bit SLIP encapsulation' CONFIG_SLIP_MODE_SLIP6 fi -tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP -tristate 'AT&T WaveLAN & DEC RoamAbout DS support' CONFIG_WAVELAN +bool 'Wireless LAN (non-hamradio)' CONFIG_NET_RADIO +if [ "$CONFIG_NET_RADIO" = "y" ]; then + tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP + tristate 'AT&T WaveLAN & DEC RoamAbout DS support' CONFIG_WAVELAN +fi bool 'Token Ring driver support' CONFIG_TR if [ "$CONFIG_TR" = "y" ]; then diff -u --recursive --new-file v2.1.74/linux/drivers/net/hamradio/baycom_par.c linux/drivers/net/hamradio/baycom_par.c --- v2.1.74/linux/drivers/net/hamradio/baycom_par.c Thu Dec 4 14:53:55 1997 +++ linux/drivers/net/hamradio/baycom_par.c Sun Dec 21 17:41:24 1997 @@ -502,7 +502,7 @@ return 0; case HDLCDRVCTL_SETMODE: - if (!suser() || dev->start) + if (dev->start || !suser()) return -EACCES; hi->data.modename[sizeof(hi->data.modename)-1] = '\0'; return baycom_setmode(bc, hi->data.modename); diff -u --recursive --new-file v2.1.74/linux/drivers/net/hamradio/baycom_ser_fdx.c linux/drivers/net/hamradio/baycom_ser_fdx.c --- v2.1.74/linux/drivers/net/hamradio/baycom_ser_fdx.c Thu Dec 4 14:53:55 1997 +++ linux/drivers/net/hamradio/baycom_ser_fdx.c Sun Dec 21 17:41:24 1997 @@ -587,7 +587,7 @@ return 0; case HDLCDRVCTL_SETMODE: - if (!suser() || dev->start) + if (dev->start || !suser()) return -EACCES; hi->data.modename[sizeof(hi->data.modename)-1] = '\0'; return baycom_setmode(bc, hi->data.modename); diff -u --recursive --new-file v2.1.74/linux/drivers/net/hamradio/baycom_ser_hdx.c linux/drivers/net/hamradio/baycom_ser_hdx.c --- v2.1.74/linux/drivers/net/hamradio/baycom_ser_hdx.c Thu Dec 4 14:53:55 1997 +++ linux/drivers/net/hamradio/baycom_ser_hdx.c Sun Dec 21 17:41:24 1997 @@ -626,7 +626,7 @@ return 0; case HDLCDRVCTL_SETMODE: - if (!suser() || dev->start) + if (dev->start || !suser()) return -EACCES; hi->data.modename[sizeof(hi->data.modename)-1] = '\0'; return baycom_setmode(bc, hi->data.modename); diff -u --recursive --new-file v2.1.74/linux/drivers/net/hamradio/soundmodem/sm.c linux/drivers/net/hamradio/soundmodem/sm.c --- v2.1.74/linux/drivers/net/hamradio/soundmodem/sm.c Thu Dec 4 14:53:55 1997 +++ linux/drivers/net/hamradio/soundmodem/sm.c Sun Dec 21 17:41:24 1997 @@ -592,7 +592,7 @@ return 0; case HDLCDRVCTL_SETMODE: - if (!suser() || dev->start) + if (dev->start || !suser()) return -EACCES; hi->data.modename[sizeof(hi->data.modename)-1] = '\0'; return sethw(dev, sm, hi->data.modename); diff -u --recursive --new-file v2.1.74/linux/drivers/net/ipddp.c linux/drivers/net/ipddp.c --- v2.1.74/linux/drivers/net/ipddp.c Fri Dec 19 15:52:59 1997 +++ linux/drivers/net/ipddp.c Sun Dec 21 17:41:24 1997 @@ -1,9 +1,10 @@ /* - * ipddp.c: IP-over-DDP driver for Linux + * ipddp.c: IP to Appletalk-IP Encapsulation driver for Linux + * Appletalk-IP to IP Decapsulation driver for Linux * * Authors: - * - Original code by: Bradford W. Johnson - * - Moved to driver by: Jay Schulist + * - DDP-IP Encap by: Bradford W. Johnson + * - DDP-IP Decap by: Jay Schulist * * Derived from: * - Almost all code already existed in net/appletalk/ddp.c I just @@ -12,6 +13,8 @@ * - skeleton.c: A network driver outline for linux. * Written 1993-94 by Donald Becker. * - dummy.c: A dummy net driver. By Nick Holloway. + * - MacGate: A user space Daemon for Appletalk-IP Decap for + * Linux by Jay Schulist * * Copyright 1993 United States Government as represented by the * Director, National Security Agency. @@ -21,7 +24,7 @@ */ static const char *version = -"ipddp.c:v0.01 8/28/97 Bradford W. Johnson \n"; + "ipddp.c:v0.01 8/28/97 Bradford W. Johnson \n"; #include #ifdef MODULE @@ -53,16 +56,24 @@ #include #include #include +#include #include "ipddp.h" /* Our stuff */ +static struct ipddp_route *ipddp_route_list = NULL; + /* * The name of the card. Is used for messages and in the requests for * io regions, irqs and dma channels */ - static const char *cardname = "ipddp"; +#ifdef CONFIG_IPDDP_ENCAP +static int ipddp_mode = IPDDP_ENCAP; +#else +static int ipddp_mode = IPDDP_DECAP; +#endif + /* Use 0 for production, 1 for verification, 2 for debug, 3 for verbose debug */ #ifndef IPDDP_DEBUG #define IPDDP_DEBUG 1 @@ -73,8 +84,11 @@ static int ipddp_xmit(struct sk_buff *skb, struct device *dev); static struct net_device_stats *ipddp_get_stats(struct device *dev); static int ipddp_rebuild_header(struct sk_buff *skb); -static int ipddp_header(struct sk_buff *skb, struct device *dev, +static int ipddp_hard_header(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len); +static int ipddp_create(struct ipddp_route *new_rt); +static int ipddp_delete(struct ipddp_route *rt); +static struct ipddp_route* ipddp_find_route(struct ipddp_route *rt); static int ipddp_ioctl(struct device *dev, struct ifreq *ifr, int cmd); @@ -103,6 +117,17 @@ if (ipddp_debug && version_printed++ == 0) printk("%s", version); + /* Let the user now what mode we are in */ + if(ipddp_mode == IPDDP_ENCAP) + printk("%s: Appletalk-IP Encapsulation mode by Bradford W. Johnson \n", + dev->name); + if(ipddp_mode == IPDDP_DECAP) + printk("%s: Appletalk-IP Decapsulation mode by Jay Schulist \n", + dev->name); + + /* Fill in the device structure with ethernet-generic values. */ + ether_setup(dev); + /* Initalize the device structure. */ dev->hard_start_xmit = ipddp_xmit; @@ -115,7 +140,7 @@ dev->stop = ipddp_close; dev->get_stats = ipddp_get_stats; dev->do_ioctl = ipddp_ioctl; - dev->hard_header = ipddp_header; /* see ip_output.c */ + dev->hard_header = ipddp_hard_header; /* see ip_output.c */ dev->rebuild_header = ipddp_rebuild_header; dev->type = ARPHRD_IPDDP; /* IP over DDP tunnel */ @@ -129,9 +154,6 @@ */ dev->hard_header_len = 14+8+sizeof(struct ddpehdr)+1; - /* Fill in the device structure with ethernet-generic values. */ - ether_setup(dev); - return 0; } @@ -141,16 +163,13 @@ static int ipddp_xmit(struct sk_buff *skb, struct device *dev) { /* Retrieve the saved address hint */ - struct at_addr *a=(struct at_addr *)skb->data; + struct at_addr *at = (struct at_addr *)skb->data; skb_pull(skb,4); ((struct net_device_stats *) dev->priv)->tx_packets++; ((struct net_device_stats *) dev->priv)->tx_bytes+=skb->len; - if(ipddp_debug>1) - printk("ipddp_xmit: Headroom %d\n",skb_headroom(skb)); - - if(aarp_send_ddp(skb->dev,skb,a,NULL) < 0) + if(aarp_send_ddp(skb->dev, skb, at, NULL) < 0) dev_kfree_skb(skb,FREE_WRITE); return 0; @@ -165,7 +184,8 @@ } /* - * Now the packet really wants to go out. + * Now the packet really wants to go out. On entry skb->data points to the + * ddpehdr we reserved earlier. skb->h.raw will be the higher level header. */ static int ipddp_rebuild_header(struct sk_buff *skb) { @@ -175,106 +195,171 @@ struct ipddp_route *rt; struct at_addr *our_addr; - /* - * On entry skb->data points to the ddpehdr we reserved earlier. - * skb->h.raw will be the higher level header. - */ - - /* - * We created this earlier. + /* + * Find appropriate route to use, based only on IP number. */ - - ddp = (struct ddpehdr *) (skb->data+4); - - /* find appropriate route */ - - for(rt=ipddp_route_head;rt;rt=rt->next) + for(rt = ipddp_route_list; rt != NULL; rt = rt->next) { if(rt->ip == paddr) break; } - if(!rt) { - printk("ipddp unreachable dst %08lx\n",ntohl(paddr)); + if(rt == NULL) + { + printk("%s: unreachable dst %s\n", cardname, in_ntoa(paddr)); return -ENETUNREACH; } our_addr = atalk_find_dev_addr(rt->dev); - /* fill in ddpehdr */ + if(ipddp_mode == IPDDP_DECAP) + /* + * Pull off the excess room that should not be there. + * This is the case for Localtalk, this may not hold + * true for Ethertalk, etc. + */ + skb_pull(skb, 31-(sizeof(struct ddpehdr)+1)); + + /* Create the Extended DDP header */ + ddp = (struct ddpehdr *) (skb->data+4); ddp->deh_len = skb->len; ddp->deh_hops = 1; ddp->deh_pad = 0; ddp->deh_sum = 0; - ddp->deh_dnet = rt->at.s_net; /* FIXME more hops?? */ - ddp->deh_snet = our_addr->s_net; + + /* + * For Localtalk we need aarp_send_ddp to strip the + * Ext DDP header and place a Shrt DDP header on it. + */ + if(rt->dev->type == ARPHRD_LOCALTLK) + { + ddp->deh_dnet = 0; /* FIXME more hops?? */ + ddp->deh_snet = 0; + } + else + { + ddp->deh_dnet = rt->at.s_net; /* FIXME more hops?? */ + ddp->deh_snet = our_addr->s_net; + } ddp->deh_dnode = rt->at.s_node; ddp->deh_snode = our_addr->s_node; ddp->deh_dport = 72; ddp->deh_sport = 72; - *((__u8 *)(ddp+1)) = 22; /* ddp type = IP */ + *((__u8 *)(ddp+1)) = 22; /* ddp type = IP */ + *((__u16 *)ddp)=ntohs(*((__u16 *)ddp)); /* fix up length field */ - /* fix up length field */ - *((__u16 *)ddp)=ntohs(*((__u16 *)ddp)); - - /* set skb->dev to appropriate device */ - skb->dev = rt->dev; - - /* skb->raddr = (unsigned long) at */ + /* Hide it at the start of the buffer, we pull it out in ipddp_xmit */ at = rt->at; - /* Hide it at the start of the buffer */ memcpy(skb->data,(void *)&at,sizeof(at)); - skb->arp = 1; /* so the actual device doesn't try to arp it... */ + + skb->dev = rt->dev; /* set skb->dev to appropriate device */ + skb->arp = 1; /* so the actual device doesn't try to arp it... */ skb->protocol = htons(ETH_P_ATALK); /* Protocol has changed */ return 0; } -static int ipddp_header(struct sk_buff *skb, struct device *dev, +static int ipddp_hard_header(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len) { - if(ipddp_debug>=2) - printk("%s: ipddp_header\n", cardname); - /* Push down the header space and the type byte */ skb_push(skb, sizeof(struct ddpehdr)+1+4); return 0; } +/* + * Create a routing entry. We first verify that the + * record does not already exist. If it does we return -EEXIST + */ +static int ipddp_create(struct ipddp_route *new_rt) +{ + struct ipddp_route *rt =(struct ipddp_route*) kmalloc(sizeof(*rt), GFP_KERNEL); + struct ipddp_route *test; + + if(rt == NULL) + return -ENOMEM; + + rt->ip = new_rt->ip; + rt->at = new_rt->at; + rt->next = NULL; + rt->dev = atrtr_get_dev(&rt->at); + if(rt->dev == NULL) + return (-ENETUNREACH); + + test = ipddp_find_route(rt); + if(test != NULL) + return (-EEXIST); + + rt->next = ipddp_route_list; + ipddp_route_list = rt; + + return 0; +} + +/* + * Delete a route, we only delete a FULL match. + * If route does not exist we return -ENOENT. + */ +static int ipddp_delete(struct ipddp_route *rt) +{ + struct ipddp_route **r = &ipddp_route_list; + struct ipddp_route *tmp; + + while((tmp = *r) != NULL) + { + if(tmp->ip == rt->ip + && tmp->at.s_net == rt->at.s_net + && tmp->at.s_node == rt->at.s_node) + { + *r = tmp->next; + kfree_s(tmp, sizeof(struct ipddp_route)); + return 0; + } + r = &tmp->next; + } + + return (-ENOENT); +} + +/* + * Find a routing entry, we only return a FULL match + */ +static struct ipddp_route* ipddp_find_route(struct ipddp_route *rt) +{ + struct ipddp_route *f; + + for(f = ipddp_route_list; f != NULL; f = f->next) + { + if(f->ip == rt->ip + && f->at.s_net == rt->at.s_net + && f->at.s_node == rt->at.s_node) + return (f); + } + + return (NULL); +} + static int ipddp_ioctl(struct device *dev, struct ifreq *ifr, int cmd) { - struct ipddp_route *urt = (struct ipddp_route *)ifr->ifr_data; + struct ipddp_route *rt = (struct ipddp_route *)ifr->ifr_data; if(!suser()) return -EPERM; - /* for now we only have one route at a time */ - switch(cmd) { - case SIOCADDIPDDPRT: - if(copy_from_user(&ipddp_route_test,urt,sizeof(struct ipddp_route))) - return -EFAULT; - ipddp_route_test.dev = atrtr_get_dev(&ipddp_route_test.at); - if (dev==NULL) - return -ENETUNREACH; - ipddp_route_test.next = NULL; - printk("%s: Added route through %s\n", - ipddp_route_test.dev->name, cardname); - ipddp_route_head = &ipddp_route_test; - return 0; + case SIOCADDIPDDPRT: + return (ipddp_create(rt)); case SIOCFINDIPDDPRT: - if(copy_to_user(urt,&ipddp_route_test,sizeof(struct ipddp_route))) + if(copy_to_user(rt, ipddp_find_route(rt), sizeof(struct ipddp_route))) return -EFAULT; return 0; case SIOCDELIPDDPRT: - ipddp_route_test.dev = NULL; - ipddp_route_head = NULL; - return 0; + return (ipddp_delete(rt)); default: return -EINVAL; @@ -291,9 +376,17 @@ 0, 0, 0, NULL, ipddp_init }; +MODULE_PARM(ipddp_mode, "i"); + int init_module(void) { - if (register_netdev(&dev_ipddp) != 0) + int err; + + err=dev_alloc_name(&dev_ipddp, "ipddp%d"); + if(err < 0) + return err; + + if(register_netdev(&dev_ipddp) != 0) return -EIO; return 0; diff -u --recursive --new-file v2.1.74/linux/drivers/net/ipddp.h linux/drivers/net/ipddp.h --- v2.1.74/linux/drivers/net/ipddp.h Tue Sep 23 16:48:48 1997 +++ linux/drivers/net/ipddp.h Sun Dec 21 17:41:24 1997 @@ -7,9 +7,10 @@ #ifdef __KERNEL__ -#define SIOCADDIPDDPRT SIOCDEVPRIVATE -#define SIOCDELIPDDPRT SIOCDEVPRIVATE+1 -#define SIOCFINDIPDDPRT SIOCDEVPRIVATE+2 +#define SIOCADDIPDDPRT (SIOCDEVPRIVATE) +#define SIOCDELIPDDPRT (SIOCDEVPRIVATE+1) +#define SIOCFINDIPDDPRT (SIOCDEVPRIVATE+2) +#define SIOCPRINTIPDDPRT (SIOCDEVPRIVATE+3) struct ipddp_route { @@ -20,8 +21,8 @@ struct ipddp_route *next; }; -static struct ipddp_route *ipddp_route_head; -static struct ipddp_route ipddp_route_test; +#define IPDDP_ENCAP 1 +#define IPDDP_DECAP 2 #endif /* __KERNEL__ */ #endif /* __LINUX_IPDDP_H */ diff -u --recursive --new-file v2.1.74/linux/drivers/pci/Makefile linux/drivers/pci/Makefile --- v2.1.74/linux/drivers/pci/Makefile Tue Aug 15 05:07:02 1995 +++ linux/drivers/pci/Makefile Sun Dec 21 17:27:18 1997 @@ -12,4 +12,8 @@ L_OBJS := pci.o L_TARGET := pci.a +ifeq ($(CONFIG_PCI_OPTIMIZE),y) +L_OBJS += quirks.o +endif + include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.1.74/linux/drivers/pci/pci.c linux/drivers/pci/pci.c --- v2.1.74/linux/drivers/pci/pci.c Fri Dec 19 15:52:59 1997 +++ linux/drivers/pci/pci.c Sun Dec 21 17:27:18 1997 @@ -1,10 +1,10 @@ /* - * $Id: pci.c,v 1.44 1997/09/03 05:08:22 richard Exp $ + * $Id: pci.c,v 1.51 1997/12/03 06:18:11 davem Exp $ * * PCI services that are built on top of the BIOS32 service. * - * Copyright 1993, 1994, 1995 Drew Eckhardt, Frederic Potter, - * David Mosberger-Tang + * Copyright 1993, 1994, 1995, 1997 Drew Eckhardt, Frederic Potter, + * David Mosberger-Tang, Martin Mares */ #include #include @@ -20,20 +20,10 @@ struct pci_bus pci_root; struct pci_dev *pci_devices = 0; -/* - * The bridge_id field is an offset of an item into the array - * BRIDGE_MAPPING_TYPE. 0xff indicates that the device is not a PCI - * bridge, or that we don't know for the moment how to configure it. - * I'm trying to do my best so that the kernel stays small. Different - * chipset can have same optimization structure. i486 and pentium - * chipsets from the same manufacturer usually have the same - * structure. - */ -#define DEVICE(vid,did,name) \ - {PCI_VENDOR_ID_##vid, PCI_DEVICE_ID_##did, (name), 0xff} +#undef DEBUG -#define BRIDGE(vid,did,name,bridge) \ - {PCI_VENDOR_ID_##vid, PCI_DEVICE_ID_##did, (name), (bridge)} +#define DEVICE(vid,did,name) \ + {PCI_VENDOR_ID_##vid, PCI_DEVICE_ID_##did, (name)} /* * Sorted in ascending order by vendor and device. @@ -87,7 +77,7 @@ DEVICE( TSENG, TSENG_ET6000, "ET6000"), DEVICE( WEITEK, WEITEK_P9000, "P9000"), DEVICE( WEITEK, WEITEK_P9100, "P9100"), - BRIDGE( DEC, DEC_BRD, "DC21050", 0x00), + DEVICE( DEC, DEC_BRD, "DC21050"), DEVICE( DEC, DEC_TULIP, "DC21040"), DEVICE( DEC, DEC_TGA, "TGA"), DEVICE( DEC, DEC_TULIP_FAST, "DC21140"), @@ -180,10 +170,10 @@ DEVICE( N9, N9_I128, "Imagine 128"), DEVICE( N9, N9_I128_2, "Imagine 128v2"), DEVICE( UMC, UMC_UM8673F, "UM8673F"), - BRIDGE( UMC, UMC_UM8891A, "UM8891A", 0x01), + DEVICE( UMC, UMC_UM8891A, "UM8891A"), DEVICE( UMC, UMC_UM8886BF, "UM8886BF"), DEVICE( UMC, UMC_UM8886A, "UM8886A"), - BRIDGE( UMC, UMC_UM8881F, "UM8881F", 0x02), + DEVICE( UMC, UMC_UM8881F, "UM8881F"), DEVICE( UMC, UMC_UM8886F, "UM8886F"), DEVICE( UMC, UMC_UM9017F, "UM9017F"), DEVICE( UMC, UMC_UM8886N, "UM8886N"), @@ -206,7 +196,7 @@ DEVICE( OLICOM, OLICOM_OC6151, "OC-6151/6152"), DEVICE( SUN, SUN_EBUS, "EBUS"), DEVICE( SUN, SUN_HAPPYMEAL, "Happy Meal"), - BRIDGE( SUN, SUN_PBM, "PCI Bus Module", 0x02), + DEVICE( SUN, SUN_PBM, "PCI Bus Module"), DEVICE( CMD, CMD_640, "640 (buggy)"), DEVICE( CMD, CMD_643, "643"), DEVICE( CMD, CMD_646, "646"), @@ -382,10 +372,10 @@ DEVICE( S3, S3_ViRGE_DXGX, "ViRGE/DX or /GX"), DEVICE( S3, S3_ViRGE_GX2, "ViRGE/GX2"), DEVICE( INTEL, INTEL_82375, "82375EB"), - BRIDGE( INTEL, INTEL_82424, "82424ZX Saturn", 0x00), + DEVICE( INTEL, INTEL_82424, "82424ZX Saturn"), DEVICE( INTEL, INTEL_82378, "82378IB"), DEVICE( INTEL, INTEL_82430, "82430ZX Aries"), - BRIDGE( INTEL, INTEL_82434, "82434LX Mercury/Neptune", 0x00), + DEVICE( INTEL, INTEL_82434, "82434LX Mercury/Neptune"), DEVICE( INTEL, INTEL_82092AA_0,"82092AA PCMCIA bridge"), DEVICE( INTEL, INTEL_82092AA_1,"82092AA EIDE"), DEVICE( INTEL, INTEL_7116, "SAA7116"), @@ -436,81 +426,6 @@ }; -#ifdef CONFIG_PCI_OPTIMIZE - -/* - * An item of this structure has the following meaning: - * for each optimization, the register address, the mask - * and value to write to turn it on. - * There are 5 optimizations for the moment: - * Cache L2 write back best than write through - * Posted Write for CPU to PCI enable - * Posted Write for CPU to MEMORY enable - * Posted Write for PCI to MEMORY enable - * PCI Burst enable - * - * Half of the bios I've meet don't allow you to turn that on, and you - * can gain more than 15% on graphic accesses using those - * optimizations... - */ -struct optimization_type { - const char *type; - const char *off; - const char *on; -} bridge_optimization[] = { - {"Cache L2", "write through", "write back"}, - {"CPU-PCI posted write", "off", "on"}, - {"CPU-Memory posted write", "off", "on"}, - {"PCI-Memory posted write", "off", "on"}, - {"PCI burst", "off", "on"} -}; - -#define NUM_OPTIMIZATIONS \ - (sizeof(bridge_optimization) / sizeof(bridge_optimization[0])) - -struct bridge_mapping_type { - unsigned char addr; /* config space address */ - unsigned char mask; - unsigned char value; -} bridge_mapping[] = { - /* - * Intel Neptune/Mercury/Saturn: - * If the internal cache is write back, - * the L2 cache must be write through! - * I've to check out how to control that - * for the moment, we won't touch the cache - */ - {0x0 ,0x02 ,0x02 }, - {0x53 ,0x02 ,0x02 }, - {0x53 ,0x01 ,0x01 }, - {0x54 ,0x01 ,0x01 }, - {0x54 ,0x02 ,0x02 }, - - /* - * UMC 8891A Pentium chipset: - * Why did you think UMC was cheaper ?? - */ - {0x50 ,0x10 ,0x00 }, - {0x51 ,0x40 ,0x40 }, - {0x0 ,0x0 ,0x0 }, - {0x0 ,0x0 ,0x0 }, - {0x0 ,0x0 ,0x0 }, - - /* - * UMC UM8881F - * This is a dummy entry for my tests. - * I have this chipset and no docs.... - */ - {0x0 ,0x1 ,0x1 }, - {0x0 ,0x2 ,0x0 }, - {0x0 ,0x0 ,0x0 }, - {0x0 ,0x0 ,0x0 }, - {0x0 ,0x0 ,0x0 } -}; - -#endif /* CONFIG_PCI_OPTIMIZE */ - - /* * device_info[] is sorted so we can use binary search */ @@ -784,52 +699,6 @@ /* - * Turn on/off PCI bridge optimization. This should allow benchmarking. - */ -__initfunc(static void burst_bridge(unsigned char bus, unsigned char devfn, - unsigned char pos, int turn_on)) -{ -#ifdef CONFIG_PCI_OPTIMIZE - struct bridge_mapping_type *bmap; - unsigned char val; - int i; - - pos *= NUM_OPTIMIZATIONS; - printk("PCI bridge optimization.\n"); - for (i = 0; i < NUM_OPTIMIZATIONS; i++) { - printk(" %s: ", bridge_optimization[i].type); - bmap = &bridge_mapping[pos + i]; - if (!bmap->addr) { - printk("Not supported."); - } else { - pcibios_read_config_byte(bus, devfn, bmap->addr, &val); - if ((val & bmap->mask) == bmap->value) { - printk("%s.", bridge_optimization[i].on); - if (!turn_on) { - pcibios_write_config_byte(bus, devfn, - bmap->addr, - (val | bmap->mask) - - bmap->value); - printk("Changed! Now %s.", bridge_optimization[i].off); - } - } else { - printk("%s.", bridge_optimization[i].off); - if (turn_on) { - pcibios_write_config_byte(bus, devfn, - bmap->addr, - (val & (0xff - bmap->mask)) - + bmap->value); - printk("Changed! Now %s.", bridge_optimization[i].on); - } - } - } - printk("\n"); - } -#endif /* CONFIG_PCI_OPTIMIZE */ -} - - -/* * Convert some of the configuration space registers of the device at * address (bus,devfn) into a string (possibly several lines each). * The configuration string is stored starting at buf[len]. If the @@ -1010,42 +879,35 @@ { void *mem; -#ifdef DEBUG - printk("...pci_malloc(size=%ld,mem=%p)", size, (void *)*mem_startp); -#endif mem = (void*) *mem_startp; *mem_startp += (size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); memset(mem, 0, size); return mem; } - unsigned int pci_scan_bus(struct pci_bus *bus, unsigned long *mem_startp) { - unsigned int devfn, l, max; - unsigned char cmd, tmp, irq, hdr_type = 0; + unsigned int devfn, l, max, class; + unsigned char cmd, irq, tmp, hdr_type = 0; struct pci_dev_info *info; struct pci_dev *dev; struct pci_bus *child; int reg; #ifdef DEBUG - printk("...pci_scan_bus(busno=%d,mem=%p)\n", bus->number, - (void *)*mem_startp); + printk("pci_scan_bus for bus %d\n", bus->number); #endif max = bus->secondary; for (devfn = 0; devfn < 0xff; ++devfn) { if (PCI_FUNC(devfn) == 0) { - pcibios_read_config_byte(bus->number, devfn, - PCI_HEADER_TYPE, &hdr_type); + pcibios_read_config_byte(bus->number, devfn, PCI_HEADER_TYPE, &hdr_type); } else if (!(hdr_type & 0x80)) { /* not a multi-function device */ continue; } - pcibios_read_config_dword(bus->number, devfn, PCI_VENDOR_ID, - &l); + pcibios_read_config_dword(bus->number, devfn, PCI_VENDOR_ID, &l); /* some broken boards return 0 if a slot is empty: */ if (l == 0xffffffff || l == 0x00000000) { hdr_type = 0; @@ -1054,14 +916,6 @@ dev = pci_malloc(sizeof(*dev), mem_startp); dev->bus = bus; - /* - * Put it into the simple chain of devices on this - * bus. It is used to find devices once everything is - * set up. - */ - dev->next = pci_devices; - pci_devices = dev; - dev->devfn = devfn; dev->vendor = l & 0xffff; dev->device = (l >> 16) & 0xffff; @@ -1075,47 +929,62 @@ if (!info) { printk("PCI: Warning: Unknown PCI device (%x:%x). Please read include/linux/pci.h\n", dev->vendor, dev->device); - } else { - /* Some BIOS' are lazy. Let's do their job: */ - if (info->bridge_type != 0xff) { - burst_bridge(bus->number, devfn, - info->bridge_type, 1); - } } /* non-destructively determine if device can be a master: */ - pcibios_read_config_byte(bus->number, devfn, PCI_COMMAND, - &cmd); - pcibios_write_config_byte(bus->number, devfn, PCI_COMMAND, - cmd | PCI_COMMAND_MASTER); - pcibios_read_config_byte(bus->number, devfn, PCI_COMMAND, - &tmp); + pcibios_read_config_byte(bus->number, devfn, PCI_COMMAND, &cmd); + pcibios_write_config_byte(bus->number, devfn, PCI_COMMAND, cmd | PCI_COMMAND_MASTER); + pcibios_read_config_byte(bus->number, devfn, PCI_COMMAND, &tmp); dev->master = ((tmp & PCI_COMMAND_MASTER) != 0); - pcibios_write_config_byte(bus->number, devfn, PCI_COMMAND, - cmd); + pcibios_write_config_byte(bus->number, devfn, PCI_COMMAND, cmd); + + pcibios_read_config_dword(bus->number, devfn, PCI_CLASS_REVISION, &class); + class >>= 8; /* upper 3 bytes */ + dev->class = class; + + switch (hdr_type & 0x7f) { /* header type */ + case 0: /* standard header */ + if (class >> 8 == PCI_CLASS_BRIDGE_PCI) + goto bad; + /* read irq level (may be changed during pcibios_fixup()): */ + pcibios_read_config_byte(bus->number, dev->devfn, PCI_INTERRUPT_LINE, &irq); + dev->irq = irq; + /* + * read base address registers, again pcibios_fixup() can + * tweak these + */ + for (reg = 0; reg < 6; reg++) { + pcibios_read_config_dword(bus->number, devfn, PCI_BASE_ADDRESS_0 + (reg << 2), &l); + dev->base_address[reg] = (l == 0xffffffff) ? 0 : l; + } + break; + case 1: /* bridge header */ + if (class >> 8 != PCI_CLASS_BRIDGE_PCI) + goto bad; + for (reg = 0; reg < 2; reg++) { + pcibios_read_config_dword(bus->number, devfn, PCI_BASE_ADDRESS_0 + (reg << 2), &l); + dev->base_address[reg] = (l == 0xffffffff) ? 0 : l; + } + break; + default: /* unknown header */ + bad: + printk(KERN_ERR "PCI: %02x:%02x [%04x/%04x/%06x] has unknown header type %02x, ignoring.\n", + bus->number, dev->devfn, dev->vendor, dev->device, class, hdr_type); + continue; + } - /* read irq level (may be changed during pcibios_fixup()): */ - pcibios_read_config_byte(bus->number, devfn, - PCI_INTERRUPT_LINE, &irq); - dev->irq = irq; +#ifdef DEBUG + printk("PCI: %02x:%02x [%04x/%04x]\n", + bus->number, dev->devfn, dev->vendor, dev->device); +#endif - /* read base address registers, again pcibios_fixup() can - * tweak these + /* + * Put it into the global PCI device chain. It's used to + * find devices once everything is set up. */ - for (reg = 0; reg < 6; reg++) { - pcibios_read_config_dword(bus->number, devfn, - PCI_BASE_ADDRESS_0 + (reg << 2), &l); - if (l == 0xffffffff) - dev->base_address[reg] = 0; - else - dev->base_address[reg] = l; - } + dev->next = pci_devices; + pci_devices = dev; - /* check to see if this device is a PCI-PCI bridge: */ - pcibios_read_config_dword(bus->number, devfn, - PCI_CLASS_REVISION, &l); - l = l >> 8; /* upper 3 bytes */ - dev->class = l; /* * Now insert it into the list of devices held * by the parent bus. @@ -1123,7 +992,10 @@ dev->sibling = bus->devices; bus->devices = dev; - if (dev->class >> 8 == PCI_CLASS_BRIDGE_PCI) { + /* + * If it's a bridge, scan the bus behind it. + */ + if (class >> 8 == PCI_CLASS_BRIDGE_PCI) { unsigned int buses; unsigned short cr; @@ -1131,7 +1003,7 @@ * Insert it into the tree of buses. */ child = pci_malloc(sizeof(*child), mem_startp); - child->next = bus->children; + child->next = bus->children; bus->children = child; child->self = dev; child->parent = bus; @@ -1147,20 +1019,16 @@ * Clear all status bits and turn off memory, * I/O and master enables. */ - pcibios_read_config_word(bus->number, devfn, - PCI_COMMAND, &cr); - pcibios_write_config_word(bus->number, devfn, - PCI_COMMAND, 0x0000); - pcibios_write_config_word(bus->number, devfn, - PCI_STATUS, 0xffff); + pcibios_read_config_word(bus->number, devfn, PCI_COMMAND, &cr); + pcibios_write_config_word(bus->number, devfn, PCI_COMMAND, 0x0000); + pcibios_write_config_word(bus->number, devfn, PCI_STATUS, 0xffff); /* * Read the existing primary/secondary/subordinate bus * number configuration to determine if the PCI bridge * has already been configured by the system. If so, * do not modify the configuration, merely note it. */ - pcibios_read_config_dword(bus->number, devfn, 0x18, - &buses); + pcibios_read_config_dword(bus->number, devfn, 0x18, &buses); if ((buses & 0xFFFFFF) != 0) { child->primary = buses & 0xFF; @@ -1179,8 +1047,7 @@ (((unsigned int)(child->primary) << 0) | ((unsigned int)(child->secondary) << 8) | ((unsigned int)(child->subordinate) << 16)); - pcibios_write_config_dword(bus->number, devfn, 0x18, - buses); + pcibios_write_config_dword(bus->number, devfn, 0x18, buses); /* * Now we can scan all subordinate buses: */ @@ -1192,11 +1059,9 @@ child->subordinate = max; buses = (buses & 0xff00ffff) | ((unsigned int)(child->subordinate) << 16); - pcibios_write_config_dword(bus->number, devfn, 0x18, - buses); + pcibios_write_config_dword(bus->number, devfn, 0x18, buses); } - pcibios_write_config_word(bus->number, devfn, - PCI_COMMAND, cr); + pcibios_write_config_word(bus->number, devfn, PCI_COMMAND, cr); } } /* @@ -1206,6 +1071,9 @@ * * Return how far we've got finding sub-buses. */ +#ifdef DEBUG + printk("PCI: pci_scan_bus returning with max=%02x\n", max); +#endif return max; } @@ -1236,5 +1104,10 @@ } } #endif + +#ifdef CONFIG_PCI_OPTIMIZE + pci_quirks_init(); +#endif + return mem_start; } diff -u --recursive --new-file v2.1.74/linux/drivers/pci/quirks.c linux/drivers/pci/quirks.c --- v2.1.74/linux/drivers/pci/quirks.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/pci/quirks.c Sun Dec 21 17:27:18 1997 @@ -0,0 +1,158 @@ +/* + * $Id: quirks.c,v 1.2 1997/09/20 21:43:34 davem Exp $ + * + * PCI Chipset-Specific Quirks + * + * Extracted from pci.c and rewritten by Martin Mares + * + * This is the right place for all special fixups for on-board + * devices not depending on system architecture -- for example + * bus bridges. The only thing implemented in this release is + * the bridge optimization, but others might appear later. + */ + +#include +#include +#include +#include +#include +#include + +#undef DEBUG + +/* + * The PCI Bridge Optimization -- Some BIOS'es are too lazy + * and are unable to turn on several features which can burst + * system performance. + */ + +/* + * An item of this structure has the following meaning: + * for each optimization, the register address, the mask + * and value to write to turn it on. + */ +struct optimization_type { + const char *type; + const char *off; + const char *on; +} bridge_optimization[] __initdata = { + {"Cache L2", "write through", "write back"}, + {"CPU-PCI posted write", "off", "on"}, + {"CPU-Memory posted write", "off", "on"}, + {"PCI-Memory posted write", "off", "on"}, + {"PCI burst", "off", "on"} +}; + +#define NUM_OPTIMIZATIONS \ + (sizeof(bridge_optimization) / sizeof(bridge_optimization[0])) + +struct bridge_mapping_type { + unsigned char addr; /* config space address */ + unsigned char mask; + unsigned char value; +} bridge_mapping[] = { + /* + * Intel Neptune/Mercury/Saturn: + * If the internal cache is write back, + * the L2 cache must be write through! + * I've to check out how to control that + * for the moment, we won't touch the cache + */ + {0x0 ,0x02 ,0x02 }, + {0x53 ,0x02 ,0x02 }, + {0x53 ,0x01 ,0x01 }, + {0x54 ,0x01 ,0x01 }, + {0x54 ,0x02 ,0x02 }, + + /* + * UMC 8891A Pentium chipset: + * Why did you think UMC was cheaper ?? + */ + {0x50 ,0x10 ,0x00 }, + {0x51 ,0x40 ,0x40 }, + {0x0 ,0x0 ,0x0 }, + {0x0 ,0x0 ,0x0 }, + {0x0 ,0x0 ,0x0 }, +}; + +__initfunc(static void quirk_bridge(struct pci_dev *dev, int pos)) +{ + struct bridge_mapping_type *bmap; + unsigned char val; + int i; + + pos *= NUM_OPTIMIZATIONS; + for (i = 0; i < NUM_OPTIMIZATIONS; i++) { + printk(" %s: ", bridge_optimization[i].type); + bmap = &bridge_mapping[pos + i]; + if (!bmap->addr) { + printk("Not supported."); + } else { + pcibios_read_config_byte(dev->bus->number, dev->devfn, bmap->addr, &val); + if ((val & bmap->mask) == bmap->value) + printk("%s.", bridge_optimization[i].on); + else { + printk("%s.", bridge_optimization[i].off); + pcibios_write_config_byte(dev->bus->number, dev->devfn, + bmap->addr, + (val & (0xff - bmap->mask)) + + bmap->value); + printk("Changed! Now %s.", bridge_optimization[i].on); + } + } + printk("\n"); + } +} + +/* + * Table of quirk handler functions + */ + +#define Q_BRIDGE 0 + +struct quirk_type { + void (*handler)(struct pci_dev *, int); + char *name; +}; + +static struct quirk_type quirk_types[] __initdata = { + { quirk_bridge, "Bridge optimization" }, +}; + +/* + * Mapping from PCI vendor/device ID pairs to quirk function types and arguments + */ + +struct quirk_info { + unsigned short vendor, device; + unsigned short quirk, arg; +}; + +static struct quirk_info quirk_list[] __initdata = { + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_BRD, Q_BRIDGE, 0x00 }, + { PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8891A, Q_BRIDGE, 0x01 }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82424, Q_BRIDGE, 0x00 }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82434, Q_BRIDGE, 0x00 } +}; + +__initfunc(void pci_quirks_init(void)) +{ + struct pci_dev *d; + int i; + +#ifdef DEBUG + printk("PCI: pci_quirks_init\n"); +#endif + for(d=pci_devices; d; d=d->next) { + for(i=0; ivendor == d->vendor && q->device == d->device) { + struct quirk_type *t = quirk_types + q->quirk; + printk("PCI: %02x:%02x [%04x/%04x]: %s (%02x)\n", + d->bus->number, d->devfn, d->vendor, d->device, + t->name, q->arg); + t->handler(d, q->arg); + } + } + } +} diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/53c7,8xx.h linux/drivers/scsi/53c7,8xx.h --- v2.1.74/linux/drivers/scsi/53c7,8xx.h Mon Aug 4 16:25:38 1997 +++ linux/drivers/scsi/53c7,8xx.h Sun Dec 21 17:59:19 1997 @@ -66,66 +66,22 @@ #define NCR53c7xx_release NULL #endif -#ifdef LINUX_1_2 -#define NCR53c7xx {NULL, NULL, "NCR53c{7,8}xx (rel 17)", NCR53c7xx_detect,\ - NULL, /* info */ NULL, /* command, deprecated */ NULL, \ - NCR53c7xx_queue_command, NCR53c7xx_abort, NCR53c7xx_reset, \ - NULL /* slave attach */, scsicam_bios_param, /* can queue */ 24, \ - /* id */ 7, 127 /* old SG_ALL */, /* cmd per lun */ 3, \ - /* present */ 0, /* unchecked isa dma */ 0, DISABLE_CLUSTERING} -#else -#define NCR53c7xx {NULL, NULL, NULL, NULL, \ - "NCR53c{7,8}xx (rel 17)", NCR53c7xx_detect,\ - NULL, /* info */ NULL, /* command, deprecated */ NULL, \ - NCR53c7xx_queue_command, NCR53c7xx_abort, NCR53c7xx_reset, \ - NULL /* slave attach */, scsicam_bios_param, /* can queue */ 24, \ - /* id */ 7, 127 /* old SG_ALL */, /* cmd per lun */ 3, \ - /* present */ 0, /* unchecked isa dma */ 0, DISABLE_CLUSTERING} -#endif +#define NCR53c7xx { \ + name: "NCR53c{7,8}xx (rel 17)", \ + detect: NCR53c7xx_detect, \ + queuecommand: NCR53c7xx_queue_command, \ + abort: NCR53c7xx_abort, \ + reset: NCR53c7xx_reset, \ + bios_param: scsicam_bios_param, \ + can_queue: 24, \ + this_id: 7, \ + sg_tablesize: 127, \ + cmd_per_lun: 3, \ + use_clustering: DISABLE_CLUSTERING} #endif /* defined(HOSTS_C) || defined(MODULE) */ #ifndef HOSTS_C -#ifdef LINUX_1_2 -/* - * Change virtual addresses to physical addresses and vv. - * These are trivial on the 1:1 Linux/i386 mapping (but if we ever - * make the kernel segment mapped at 0, we need to do translation - * on the i386 as well) - */ -extern inline unsigned long virt_to_phys(volatile void * address) -{ - return (unsigned long) address; -} - -extern inline void * phys_to_virt(unsigned long address) -{ - return (void *) address; -} - -/* - * IO bus memory addresses are also 1:1 with the physical address - */ -#define virt_to_bus virt_to_phys -#define bus_to_virt phys_to_virt - -/* - * readX/writeX() are used to access memory mapped devices. On some - * architectures the memory mapped IO stuff needs to be accessed - * differently. On the x86 architecture, we just read/write the - * memory location directly. - */ -#define readb(addr) (*(volatile unsigned char *) (addr)) -#define readw(addr) (*(volatile unsigned short *) (addr)) -#define readl(addr) (*(volatile unsigned int *) (addr)) - -#define writeb(b,addr) ((*(volatile unsigned char *) (addr)) = (b)) -#define writew(b,addr) ((*(volatile unsigned short *) (addr)) = (b)) -#define writel(b,addr) ((*(volatile unsigned int *) (addr)) = (b)) - -#define mb() - -#endif /* def LINUX_1_2 */ /* Register addresses, ordered numerically */ diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/AM53C974.c linux/drivers/scsi/AM53C974.c --- v2.1.74/linux/drivers/scsi/AM53C974.c Wed Apr 23 19:01:21 1997 +++ linux/drivers/scsi/AM53C974.c Sun Dec 21 17:04:48 1997 @@ -2543,52 +2543,54 @@ * Inputs : cmd -- which command within the command block was responsible for the reset * * Returns : status (SCSI_ABORT_SUCCESS) +* +* FIXME(eric) the reset_flags are ignored. **************************************************************************/ -int AM53C974_reset(Scsi_Cmnd *cmd) +int AM53C974_reset(Scsi_Cmnd *cmd, unsigned int reset_flags) { -AM53C974_local_declare(); -int i; -struct Scsi_Host *instance = cmd->host; -struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata; -AM53C974_setio(instance); - -cli(); -DEB(printk("AM53C974_reset called; ")); - -printk("AM53C974_reset called\n"); -AM53C974_print(instance); -AM53C974_keywait(); - + AM53C974_local_declare(); + int i; + struct Scsi_Host *instance = cmd->host; + struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata; + AM53C974_setio(instance); + + cli(); + DEB(printk("AM53C974_reset called; ")); + + printk("AM53C974_reset called\n"); + AM53C974_print(instance); + AM53C974_keywait(); + /* do hard reset */ -AM53C974_write_8(CMDREG, CMDREG_RDEV); -AM53C974_write_8(CMDREG, CMDREG_NOP); -hostdata->msgout[0] = NOP; -for (i = 0; i < 8; i++) { - hostdata->busy[i] = 0; - hostdata->sync_per[i] = DEF_STP; - hostdata->sync_off[i] = 0; - hostdata->sync_neg[i] = 0; } -hostdata->last_message[0] = NOP; -hostdata->sel_cmd = NULL; -hostdata->connected = NULL; -hostdata->issue_queue = NULL; -hostdata->disconnected_queue = NULL; -hostdata->in_reset = 0; -hostdata->aborted = 0; -hostdata->selecting = 0; -hostdata->disconnecting = 0; -hostdata->dma_busy = 0; - + AM53C974_write_8(CMDREG, CMDREG_RDEV); + AM53C974_write_8(CMDREG, CMDREG_NOP); + hostdata->msgout[0] = NOP; + for (i = 0; i < 8; i++) { + hostdata->busy[i] = 0; + hostdata->sync_per[i] = DEF_STP; + hostdata->sync_off[i] = 0; + hostdata->sync_neg[i] = 0; } + hostdata->last_message[0] = NOP; + hostdata->sel_cmd = NULL; + hostdata->connected = NULL; + hostdata->issue_queue = NULL; + hostdata->disconnected_queue = NULL; + hostdata->in_reset = 0; + hostdata->aborted = 0; + hostdata->selecting = 0; + hostdata->disconnecting = 0; + hostdata->dma_busy = 0; + /* reset bus */ -AM53C974_write_8(CNTLREG1, CNTLREG1_DISR | instance->this_id); /* disable interrupt upon SCSI RESET */ -AM53C974_write_8(CMDREG, CMDREG_RBUS); /* reset SCSI bus */ -udelay(40); -AM53C974_config_after_reset(instance); - -sti(); -cmd->result = DID_RESET << 16; -cmd->scsi_done(cmd); -return SCSI_ABORT_SUCCESS; + AM53C974_write_8(CNTLREG1, CNTLREG1_DISR | instance->this_id); /* disable interrupt upon SCSI RESET */ + AM53C974_write_8(CMDREG, CMDREG_RBUS); /* reset SCSI bus */ + udelay(40); + AM53C974_config_after_reset(instance); + + sti(); + cmd->result = DID_RESET << 16; + cmd->scsi_done(cmd); + return SCSI_ABORT_SUCCESS; } diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/AM53C974.h linux/drivers/scsi/AM53C974.h --- v2.1.74/linux/drivers/scsi/AM53C974.h Thu Jun 12 15:29:27 1997 +++ linux/drivers/scsi/AM53C974.h Sun Dec 21 17:04:48 1997 @@ -52,29 +52,22 @@ extern struct proc_dir_entry proc_scsi_am53c974; -#define AM53C974 { \ - NULL, /* pointer to next in list */ \ - NULL, /* struct module *module */ \ - &proc_scsi_am53c974, /* struct proc_dir_entry *proc_dir */ \ - NULL, /* int (*proc_info)(char *, char **, off_t, int, int, int); */ \ - "AM53C974", /* name */ \ - AM53C974_detect, /* int (* detect)(struct SHT *) */ \ - AM53C974_release, /* int (*release)(struct Scsi_Host *) */ \ - AM53C974_info, /* const char *(* info)(struct Scsi_Host *) */ \ - AM53C974_command, /* int (* command)(Scsi_Cmnd *) */ \ - AM53C974_queue_command, /* int (* queuecommand)(Scsi_Cmnd *, \ - void (*done)(Scsi_Cmnd *)) */ \ - AM53C974_abort, /* int (* abort)(Scsi_Cmnd *) */ \ - AM53C974_reset, /* int (* reset)(Scsi_Cmnd *) */ \ - NULL, /* int (* slave_attach)(int, int) */ \ - scsicam_bios_param, /* int (* bios_param)(Disk *, int, int[]) */ \ - 12, /* can_queue */ \ - -1, /* this_id */ \ - SG_ALL, /* sg_tablesize */ \ - 1, /* cmd_per_lun */ \ - 0, /* present, i.e. how many adapters of this kind */ \ - 0, /* unchecked_isa_dma */ \ - DISABLE_CLUSTERING /* use_clustering */ \ +#define AM53C974 { \ + proc_dir: &proc_scsi_am53c974, \ + name: "AM53C974", \ + detect: AM53C974_detect, \ + release: AM53C974_release, \ + info: AM53C974_info, \ + command: AM53C974_command, \ + queuecommand: AM53C974_queue_command, \ + abort: AM53C974_abort, \ + reset: AM53C974_reset, \ + bios_param: scsicam_bios_param, \ + can_queue: 12, \ + this_id: -1, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: 1, \ + use_clustering: DISABLE_CLUSTERING \ } void AM53C974_setup(char *str, int *ints); @@ -85,7 +78,7 @@ int AM53C974_command(Scsi_Cmnd *SCpnt); int AM53C974_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)); int AM53C974_abort(Scsi_Cmnd *cmd); -int AM53C974_reset (Scsi_Cmnd *cmd); +int AM53C974_reset (Scsi_Cmnd *cmd, unsigned int); #endif /* AM53C974_H */ diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/BusLogic.h linux/drivers/scsi/BusLogic.h --- v2.1.74/linux/drivers/scsi/BusLogic.h Mon Dec 1 12:04:13 1997 +++ linux/drivers/scsi/BusLogic.h Sun Dec 21 17:04:48 1997 @@ -27,9 +27,6 @@ */ -#include - - /* Define types for some of the structures that interface with the rest of the Linux Kernel and SCSI Subsystem. @@ -67,27 +64,18 @@ */ #define BUSLOGIC \ - { NULL, /* Next */ \ - NULL, /* Usage Count Pointer */ \ - &BusLogic_ProcDirectoryEntry, /* /proc Directory Entry */ \ - BusLogic_ProcDirectoryInfo, /* /proc Info Function */ \ - "BusLogic", /* Driver Name */ \ - BusLogic_DetectHostAdapter, /* Detect Host Adapter */ \ - BusLogic_ReleaseHostAdapter, /* Release Host Adapter */ \ - BusLogic_DriverInfo, /* Driver Info Function */ \ - NULL, /* Command Function */ \ - BusLogic_QueueCommand, /* Queue Command Function */ \ - BusLogic_AbortCommand, /* Abort Command Function */ \ - BusLogic_ResetCommand, /* Reset Command Function */ \ - NULL, /* Slave Attach Function */ \ - BusLogic_BIOSDiskParameters, /* BIOS Disk Parameters */ \ - 0, /* Can Queue */ \ - 0, /* This ID */ \ - 0, /* Scatter/Gather Table Size */ \ - 0, /* SCSI Commands per LUN */ \ - 0, /* Present */ \ - 1, /* Default Unchecked ISA DMA */ \ - ENABLE_CLUSTERING } /* Enable Clustering */ + { proc_dir: &BusLogic_ProcDirectoryEntry, /* /proc Directory Entry */ \ + proc_info: BusLogic_ProcDirectoryInfo, /* /proc Info Function */ \ + name: "BusLogic", /* Driver Name */ \ + detect: BusLogic_DetectHostAdapter, /* Detect Host Adapter */ \ + release: BusLogic_ReleaseHostAdapter, /* Release Host Adapter */ \ + info: BusLogic_DriverInfo, /* Driver Info Function */ \ + queuecommand: BusLogic_QueueCommand, /* Queue Command Function */ \ + abort: BusLogic_AbortCommand, /* Abort Command Function */ \ + reset: BusLogic_ResetCommand, /* Reset Command Function */ \ + bios_param: BusLogic_BIOSDiskParameters, /* BIOS Disk Parameters */ \ + unchecked_isa_dma: 1, /* Default Unchecked ISA DMA */ \ + use_clustering: ENABLE_CLUSTERING } /* Enable Clustering */ /* diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in --- v2.1.74/linux/drivers/scsi/Config.in Mon Dec 1 12:04:13 1997 +++ linux/drivers/scsi/Config.in Sun Dec 21 17:04:48 1997 @@ -13,6 +13,7 @@ bool 'Probe all LUNs on each SCSI device' CONFIG_SCSI_MULTI_LUN bool 'Verbose SCSI error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS +bool 'SCSI logging facility' CONFIG_SCSI_LOGGING mainmenu_option next_comment comment 'SCSI low-level drivers' @@ -113,7 +114,17 @@ int ' maximum number of queued commands' CONFIG_SCSI_U14_34F_MAX_TAGS 8 fi dep_tristate 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR $CONFIG_SCSI -#dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG $CONFIG_SCSI +# +# Note - this is a very special 'host' adapter that simulates the presence of some disks. +# It can come in very handy for troubleshooting. Anyone else is welcome to use it - all +# you do is hack it to simulate the condition you want to test for, and then use it. +# +# The actual configuration in any kernel release could change at any time as I hack it to +# simulate various conditions that I am testing. +# +if [ "`whoami`" = "eric" ]; then + dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG $CONFIG_SCSI +fi if [ "$CONFIG_PPC" = "y" ]; then dep_tristate 'MESH (Power Mac internal SCSI) support' CONFIG_SCSI_MESH $CONFIG_SCSI if [ "$CONFIG_SCSI_MESH" != "n" ]; then diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile --- v2.1.74/linux/drivers/scsi/Makefile Wed Nov 12 13:34:26 1997 +++ linux/drivers/scsi/Makefile Sun Dec 21 17:04:48 1997 @@ -37,7 +37,7 @@ SCSI=scsi.o ifeq ($(CONFIG_MODULES),y) O_TARGET := scsi_n_syms.o - O_OBJS := scsi.o + O_OBJS := scsi.o scsi_error.o scsi_obsolete.o scsi_queue.o OX_OBJS := scsi_syms.o SCSI := $(O_TARGET) endif @@ -490,8 +490,10 @@ rm fake.c scsi_mod.o: $(MIX_OBJS) hosts.o scsi.o scsi_ioctl.o constants.o \ - scsicam.o scsi_proc.o - $(LD) $(LD_RFLAG) -r -o $@ $(MIX_OBJS) hosts.o scsi.o scsi_ioctl.o constants.o scsicam.o scsi_proc.o + scsicam.o scsi_proc.o scsi_error.o scsi_obsolete.o scsi_queue.o + $(LD) $(LD_RFLAG) -r -o $@ $(MIX_OBJS) hosts.o scsi.o scsi_ioctl.o \ + constants.o scsicam.o scsi_proc.o \ + scsi_error.o scsi_obsolete.o scsi_queue.o \ sr_mod.o: sr.o sr_ioctl.o sr_vendor.o $(LD) $(LD_RFLAG) -r -o $@ sr.o sr_ioctl.o sr_vendor.o diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/NCR53c406a.h linux/drivers/scsi/NCR53c406a.h --- v2.1.74/linux/drivers/scsi/NCR53c406a.h Fri Dec 27 02:03:23 1996 +++ linux/drivers/scsi/NCR53c406a.h Sun Dec 21 17:04:48 1997 @@ -26,27 +26,21 @@ * Use SG_NONE if DMA mode is enabled! */ #define NCR53c406a { \ - NULL /* next */, \ - NULL /* usage count */, \ - &proc_scsi_NCR53c406a /* proc_dir */, \ - NULL /* proc_info */, \ - "NCR53c406a" /* name */, \ - NCR53c406a_detect /* detect */, \ - NULL /* release */, \ - NCR53c406a_info /* info */, \ - NCR53c406a_command /* command */, \ - NCR53c406a_queue /* queuecommand */, \ - NCR53c406a_abort /* abort */, \ - NCR53c406a_reset /* reset */, \ - NULL /* slave_attach */, \ - NCR53c406a_biosparm /* biosparm */, \ - 1 /* can_queue */, \ - 7 /* SCSI ID of the chip */, \ - 32 /*SG_ALL*/ /*SG_NONE*/, \ - 1 /* commands per lun */, \ - 0 /* number of boards in system */, \ - 1 /* unchecked_isa_dma */, \ - ENABLE_CLUSTERING \ + proc_dir: &proc_scsi_NCR53c406a /* proc_dir */, \ + name: "NCR53c406a" /* name */, \ + detect: NCR53c406a_detect /* detect */, \ + info: NCR53c406a_info /* info */, \ + command: NCR53c406a_command /* command */, \ + queuecommand: NCR53c406a_queue /* queuecommand */, \ + abort: NCR53c406a_abort /* abort */, \ + reset: NCR53c406a_reset /* reset */, \ + bios_param: NCR53c406a_biosparm /* biosparm */, \ + can_queue: 1 /* can_queue */, \ + this_id: 7 /* SCSI ID of the chip */, \ + sg_tablesize: 32 /*SG_ALL*/ /*SG_NONE*/, \ + cmd_per_lun: 1 /* commands per lun */, \ + unchecked_isa_dma: 1 /* unchecked_isa_dma */, \ + use_clustering: ENABLE_CLUSTERING \ } extern struct proc_dir_entry proc_scsi_NCR53c406a; diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/a2091.h linux/drivers/scsi/a2091.h --- v2.1.74/linux/drivers/scsi/a2091.h Thu Jun 12 15:29:27 1997 +++ linux/drivers/scsi/a2091.h Sun Dec 21 17:04:48 1997 @@ -33,27 +33,18 @@ extern struct proc_dir_entry proc_scsi_a2091; -#define A2091_SCSI { /* next */ NULL, \ - /* module */ NULL, \ - /* proc_dir_entry */ &proc_scsi_a2091, \ - /* proc_info */ NULL, \ - /* name */ "Commodore A2091/A590 SCSI", \ - /* detect */ a2091_detect, \ - /* release */ a2091_release, \ - /* info */ NULL, \ - /* command */ NULL, \ - /* queuecommand */ wd33c93_queuecommand, \ - /* abort */ wd33c93_abort, \ - /* reset */ wd33c93_reset, \ - /* slave_attach */ NULL, \ - /* bios_param */ NULL, \ - /* can_queue */ CAN_QUEUE, \ - /* this_id */ 7, \ - /* sg_tablesize */ SG_ALL, \ - /* cmd_per_lun */ CMD_PER_LUN, \ - /* present */ 0, \ - /* unchecked_isa_dma */ 0, \ - /* use_clustering */ DISABLE_CLUSTERING } +#define A2091_SCSI { proc_dir: &proc_scsi_a2091, \ + name: "Commodore A2091/A590 SCSI", \ + detect: a2091_detect, \ + release: a2091_release, \ + queuecommand: wd33c93_queuecommand, \ + abort: wd33c93_abort, \ + reset: wd33c93_reset, \ + can_queue: CAN_QUEUE, \ + this_id: 7, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: CMD_PER_LUN, \ + use_clustering: DISABLE_CLUSTERING } #else /* diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/a3000.h linux/drivers/scsi/a3000.h --- v2.1.74/linux/drivers/scsi/a3000.h Thu Jun 12 15:29:27 1997 +++ linux/drivers/scsi/a3000.h Sun Dec 21 17:04:48 1997 @@ -33,27 +33,19 @@ extern struct proc_dir_entry proc_scsi_a3000; -#define A3000_SCSI { /* next */ NULL, \ - /* module */ NULL, \ - /* proc_dir_entry */ &proc_scsi_a3000, \ - /* proc_info */ NULL, \ - /* name */ "Amiga 3000 built-in SCSI", \ - /* detect */ a3000_detect, \ - /* release */ a3000_release, \ - /* info */ NULL, \ - /* command */ NULL, \ - /* queuecommand */ wd33c93_queuecommand, \ - /* abort */ wd33c93_abort, \ - /* reset */ wd33c93_reset, \ - /* slave_attach */ NULL, \ - /* bios_param */ NULL, \ - /* can_queue */ CAN_QUEUE, \ - /* this_id */ 7, \ - /* sg_tablesize */ SG_ALL, \ - /* cmd_per_lun */ CMD_PER_LUN, \ - /* present */ 0, \ - /* unchecked_isa_dma */ 0, \ - /* use_clustering */ ENABLE_CLUSTERING } +#define A3000_SCSI { proc_dir: &proc_scsi_a3000, \ + proc_info: NULL, \ + name: "Amiga 3000 built-in SCSI", \ + detect: a3000_detect, \ + release: a3000_release, \ + queuecommand: wd33c93_queuecommand, \ + abort: wd33c93_abort, \ + reset: wd33c93_reset, \ + can_queue: CAN_QUEUE, \ + this_id: 7, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: CMD_PER_LUN, \ + use_clustering: ENABLE_CLUSTERING } #else /* diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/advansys.c linux/drivers/scsi/advansys.c --- v2.1.74/linux/drivers/scsi/advansys.c Thu May 29 21:53:08 1997 +++ linux/drivers/scsi/advansys.c Sun Dec 21 17:04:48 1997 @@ -2815,7 +2815,7 @@ int leftlen; char *curbuf; off_t advoffset; - Scsi_Device *scd; + Scsi_Device *scd = NULL; ASC_DBG(1, "advansys_proc_info: begin\n"); @@ -2889,8 +2889,7 @@ * Display target driver information for each device attached * to the board. */ - for (scd = scsi_devices; scd; scd = scd->next) { - if (scd->host == shp) { + for (scd = scd->host->host_queue; scd; scd = scd->next) { cp = boardp->prtbuf; /* * Note: If proc_print_scsidevice() writes more than @@ -2907,7 +2906,6 @@ } advoffset += cplen; curbuf += cnt; - } } /* @@ -3409,15 +3407,20 @@ shp->select_queue_depths = advansys_select_queue_depths; #ifdef MODULE - /* - * Following v1.3.89, 'cmd_per_lun' is no longer needed - * and should be set to zero. But because of a bug introduced - * in v1.3.89 if the driver is compiled as a module and - * 'cmd_per_lun' is zero, the Mid-Level SCSI function - * 'allocate_device' will panic. To allow the driver to - * work as a module in these kernels set 'cmd_per_lun' to 1. - */ - shp->cmd_per_lun = 1; + /* + * FIXME(eric) - this is completely bogus. We need to + * figure out what exactly the real problem is and deal + * with it. + */ + /* + * Following v1.3.89, 'cmd_per_lun' is no longer needed + * and should be set to zero. But because of a bug introduced + * in v1.3.89 if the driver is compiled as a module and + * 'cmd_per_lun' is zero, the Mid-Level SCSI function + * 'scsi_allocate_device' will panic. To allow the driver to + * work as a module in these kernels set 'cmd_per_lun' to 1. + */ + shp->cmd_per_lun = 1; #else /* MODULE */ shp->cmd_per_lun = 0; #endif /* MODULE */ @@ -6677,10 +6680,16 @@ (unsigned) s->next, s->extra_bytes, s->host_busy, s->host_no, (unsigned) s->last_reset); +#ifdef ERIC_neverdef /* { */ + /* + * This information is private to the mid-layer scsi and the + * the low-level drivers shouldn't even be aware that it is there. + */ printk( " host_wait %x, host_queue %x, hostt %x, block %x,\n", (unsigned) s->host_wait, (unsigned) s->host_queue, (unsigned) s->hostt, (unsigned) s->block); +#endif /* ERIC_neverdef */ /* } */ printk( " wish_block %d, base %x, io_port %d, n_io_port %d, irq %d, dma_channel %d,\n", diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/advansys.h linux/drivers/scsi/advansys.h --- v2.1.74/linux/drivers/scsi/advansys.h Thu Jul 17 10:06:05 1997 +++ linux/drivers/scsi/advansys.h Sun Dec 21 17:59:19 1997 @@ -40,18 +40,10 @@ int advansys_command(Scsi_Cmnd *); int advansys_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *)); int advansys_abort(Scsi_Cmnd *); -#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,89) -int advansys_reset(Scsi_Cmnd *); -#else /* version >= v1.3.89 */ int advansys_reset(Scsi_Cmnd *, unsigned int); -#endif /* version >= v1.3.89 */ -#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,0) -int advansys_biosparam(Disk *, int, int[]); -#else /* version >= v1.3.0 */ int advansys_biosparam(Disk *, kdev_t, int[]); extern struct proc_dir_entry proc_scsi_advansys; int advansys_proc_info(char *, char **, off_t, int, int, int); -#endif /* version >= v1.3.0 */ /* init/main.c setup function */ void advansys_setup(char *, int *); @@ -59,88 +51,31 @@ /* * AdvanSys Host Driver Scsi_Host_Template (struct SHT) from hosts.h. */ -#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,0) #define ADVANSYS { \ - NULL, /* struct SHT *next */ \ - NULL, /* int *usage_count */ \ - "advansys", /* char *name */ \ - advansys_detect, /* int (*detect)(struct SHT *) */ \ - advansys_release, /* int (*release)(struct Scsi_Host *) */ \ - advansys_info, /* const char *(*info)(struct Scsi_Host *) */ \ - advansys_command, /* int (*command)(Scsi_Cmnd *) */ \ - advansys_queuecommand, \ - /* int (*queuecommand)(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)) */ \ - advansys_abort, /* int (*abort)(Scsi_Cmnd *) */ \ - advansys_reset, /* int (*reset)(Scsi_Cmnd *) */ \ - NULL, /* int (*slave_attach)(int, int) */ \ - advansys_biosparam, /* int (* bios_param)(Disk *, int, int []) */ \ - /* \ - * The following fields are set per adapter in advansys_detect(). \ - */ \ - 0, /* int can_queue */ \ - 0, /* int this_id */ \ - 0, /* short unsigned int sg_tablesize */ \ - 0, /* short cmd_per_lun */ \ - 0, /* unsigned char present */ \ - /* \ - * Because the driver may control an ISA adapter 'unchecked_isa_dma' \ - * must be set. The flag will be cleared in advansys_detect for non-ISA \ - * adapters. Refer to the comment in scsi_module.c for more information. \ - */ \ - 1, /* unsigned unchecked_isa_dma:1 */ \ - /* \ - * All adapters controlled by this driver are capable of large \ - * scatter-gather lists. According to the mid-level SCSI documentation \ - * this obviates any performance gain provided by setting \ - * 'use_clustering'. But empirically while CPU utilization is increased \ - * by enabling clustering, I/O throughput increases as well. \ - */ \ - ENABLE_CLUSTERING, /* unsigned use_clustering:1 */ \ + proc_dir: &proc_scsi_advansys, \ + proc_info: advansys_proc_info, \ + name: "advansys", \ + detect: advansys_detect, \ + release: advansys_release, \ + info: advansys_info, \ + command: advansys_command, \ + queuecommand: advansys_queuecommand, \ + abort: advansys_abort, \ + reset: advansys_reset, \ + bios_param: advansys_biosparam, \ + /* \ + * Because the driver may control an ISA adapter 'unchecked_isa_dma' \ + * must be set. The flag will be cleared in advansys_detect for non-ISA \ + * adapters. Refer to the comment in scsi_module.c for more information. \ + */ \ + unchecked_isa_dma: 1, \ + /* \ + * All adapters controlled by this driver are capable of large \ + * scatter-gather lists. According to the mid-level SCSI documentation \ + * this obviates any performance gain provided by setting \ + * 'use_clustering'. But empirically while CPU utilization is increased \ + * by enabling clustering, I/O throughput increases as well. \ + */ \ + use_clustering: ENABLE_CLUSTERING, \ } -#else /* version >= v1.3.0 */ -#define ADVANSYS { \ - NULL, /* struct SHT *next */ \ - NULL, \ - /* version < v2.1.23 long *usage_count */ \ - /* version >= v2.1.23 struct module * */ \ - &proc_scsi_advansys, /* struct proc_dir_entry *proc_dir */ \ - advansys_proc_info, \ - /* int (*proc_info)(char *, char **, off_t, int, int, int) */ \ - "advansys", /* const char *name */ \ - advansys_detect, /* int (*detect)(struct SHT *) */ \ - advansys_release, /* int (*release)(struct Scsi_Host *) */ \ - advansys_info, /* const char *(*info)(struct Scsi_Host *) */ \ - advansys_command, /* int (*command)(Scsi_Cmnd *) */ \ - advansys_queuecommand, \ - /* int (*queuecommand)(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)) */ \ - advansys_abort, /* int (*abort)(Scsi_Cmnd *) */ \ - advansys_reset, \ - /* version < v1.3.89 int (*reset)(Scsi_Cmnd *) */ \ - /* version >= v1.3.89 int (*reset)(Scsi_Cmnd *, unsigned int) */ \ - NULL, /* int (*slave_attach)(int, int) */ \ - advansys_biosparam, /* int (* bios_param)(Disk *, kdev_t, int []) */ \ - /* \ - * The following fields are set per adapter in advansys_detect(). \ - */ \ - 0, /* int can_queue */ \ - 0, /* int this_id */ \ - 0, /* short unsigned int sg_tablesize */ \ - 0, /* short cmd_per_lun */ \ - 0, /* unsigned char present */ \ - /* \ - * Because the driver may control an ISA adapter 'unchecked_isa_dma' \ - * must be set. The flag will be cleared in advansys_detect for non-ISA \ - * adapters. Refer to the comment in scsi_module.c for more information. \ - */ \ - 1, /* unsigned unchecked_isa_dma:1 */ \ - /* \ - * All adapters controlled by this driver are capable of large \ - * scatter-gather lists. According to the mid-level SCSI documentation \ - * this obviates any performance gain provided by setting \ - * 'use_clustering'. But empirically while CPU utilization is increased \ - * by enabling clustering, I/O throughput increases as well. \ - */ \ - ENABLE_CLUSTERING, /* unsigned use_clustering:1 */ \ -} -#endif /* version >= v1.3.0 */ #endif /* _ADVANSYS_H */ diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/aha152x.h linux/drivers/scsi/aha152x.h --- v2.1.74/linux/drivers/scsi/aha152x.h Mon Jul 7 16:03:00 1997 +++ linux/drivers/scsi/aha152x.h Sun Dec 21 17:59:22 1997 @@ -28,27 +28,23 @@ extern struct proc_dir_entry proc_scsi_aha152x; /* Initial value of Scsi_Host entry */ -#define AHA152X { /* next */ 0, \ - /* module */ 0, \ - /* proc_dir */ &proc_scsi_aha152x, \ - /* proc_info */ aha152x_proc_info, \ - /* name */ AHA152X_REVID, \ - /* detect */ aha152x_detect, \ - /* release */ 0, \ - /* info */ 0, \ - /* command */ aha152x_command, \ - /* queuecommand */ aha152x_queue, \ - /* abort */ aha152x_abort, \ - /* reset */ aha152x_reset, \ - /* slave_attach */ 0, \ - /* bios_param */ aha152x_biosparam, \ - /* can_queue */ 1, \ - /* this_id */ 7, \ - /* sg_tablesize */ SG_ALL, \ - /* cmd_per_lun */ 1, \ - /* present */ 0, \ - /* unchecked_isa_dma */ 0, \ - /* use_clustering */ DISABLE_CLUSTERING } +#define AHA152X { proc_dir: &proc_scsi_aha152x, \ + proc_info: aha152x_proc_info, \ + name: AHA152X_REVID, \ + detect: aha152x_detect, \ + command: aha152x_command, \ + queuecommand: aha152x_queue, \ + abort: aha152x_abort, \ + reset: aha152x_reset, \ + slave_attach: 0, \ + bios_param: aha152x_biosparam, \ + can_queue: 1, \ + this_id: 7, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: 1, \ + present: 0, \ + unchecked_isa_dma: 0, \ + use_clustering: DISABLE_CLUSTERING } #endif diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/aha1542.c linux/drivers/scsi/aha1542.c --- v2.1.74/linux/drivers/scsi/aha1542.c Tue Mar 4 10:25:24 1997 +++ linux/drivers/scsi/aha1542.c Sun Dec 21 17:04:48 1997 @@ -1117,12 +1117,264 @@ return 0; } -/* The abort command does not leave the device in a clean state where - it is available to be used again. Until this gets worked out, we will - leave it commented out. */ - int aha1542_abort(Scsi_Cmnd * SCpnt) { + + /* + * The abort command does not leave the device in a clean state where + * it is available to be used again. Until this gets worked out, we + * will leave it commented out. + */ + + printk("aha1542.c: Unable to abort command for target %d\n", + SCpnt->target); + return FAILED; +} + +/* + * This is a device reset. This is handled by sending a special command + * to the device. + */ +int aha1542_dev_reset(Scsi_Cmnd * SCpnt) +{ + unsigned long flags; + struct mailbox * mb; + unchar target = SCpnt->target; + unchar lun = SCpnt->lun; + int mbo; + struct ccb *ccb; + unchar ahacmd = CMD_START_SCSI; + + ccb = HOSTDATA(SCpnt->host)->ccb; + mb = HOSTDATA(SCpnt->host)->mb; + + save_flags(flags); + cli(); + mbo = HOSTDATA(SCpnt->host)->aha1542_last_mbo_used + 1; + if (mbo >= AHA1542_MAILBOXES) mbo = 0; + + do{ + if(mb[mbo].status == 0 && HOSTDATA(SCpnt->host)->SCint[mbo] == NULL) + break; + mbo++; + if (mbo >= AHA1542_MAILBOXES) mbo = 0; + } while (mbo != HOSTDATA(SCpnt->host)->aha1542_last_mbo_used); + + if(mb[mbo].status || HOSTDATA(SCpnt->host)->SCint[mbo]) + panic("Unable to find empty mailbox for aha1542.\n"); + + HOSTDATA(SCpnt->host)->SCint[mbo] = SCpnt; /* This will effectively + prevent someone else from + screwing with this cdb. */ + + HOSTDATA(SCpnt->host)->aha1542_last_mbo_used = mbo; + restore_flags(flags); + + any2scsi(mb[mbo].ccbptr, SCSI_PA(&ccb[mbo])); /* This gets trashed for some reason*/ + + memset(&ccb[mbo], 0, sizeof(struct ccb)); + + ccb[mbo].op = 0x81; /* BUS DEVICE RESET */ + + ccb[mbo].idlun = (target&7)<<5 | (lun & 7); /*SCSI Target Id*/ + + ccb[mbo].linkptr[0] = ccb[mbo].linkptr[1] = ccb[mbo].linkptr[2] = 0; + ccb[mbo].commlinkid = 0; + + /* + * Now tell the 1542 to flush all pending commands for this + * target + */ + aha1542_out(SCpnt->host->io_port, &ahacmd, 1); + + printk("aha1542.c: Trying device reset for target %d\n", SCpnt->target); + + return SUCCESS; + + +#ifdef ERIC_neverdef + /* + * With the 1542 we apparently never get an interrupt to + * acknowledge a device reset being sent. Then again, Leonard + * says we are doing this wrong in the first place... + * + * Take a wait and see attitude. If we get spurious interrupts, + * then the device reset is doing something sane and useful, and + * we will wait for the interrupt to post completion. + */ + printk("Sent BUS DEVICE RESET to target %d\n", SCpnt->target); + + /* + * Free the command block for all commands running on this + * target... + */ + for(i=0; i< AHA1542_MAILBOXES; i++) + { + if(HOSTDATA(SCpnt->host)->SCint[i] && + HOSTDATA(SCpnt->host)->SCint[i]->target == SCpnt->target) + { + Scsi_Cmnd * SCtmp; + SCtmp = HOSTDATA(SCpnt->host)->SCint[i]; + if (SCtmp->host_scribble) + { + scsi_free(SCtmp->host_scribble, 512); + } + + HOSTDATA(SCpnt->host)->SCint[i] = NULL; + HOSTDATA(SCpnt->host)->mb[i].status = 0; + } + } + return SUCCESS; + + return FAILED; +#endif /* ERIC_neverdef */ +} + +int aha1542_bus_reset(Scsi_Cmnd * SCpnt) +{ + int i; + + /* + * This does a scsi reset for all devices on the bus. + * In principle, we could also reset the 1542 - should + * we do this? Try this first, and we can add that later + * if it turns out to be useful. + */ + outb(SCRST, CONTROL(SCpnt->host->io_port)); + + /* + * Wait for the thing to settle down a bit. Unfortunately + * this is going to basically lock up the machine while we + * wait for this to complete. To be 100% correct, we need to + * check for timeout, and if we are doing something like this + * we are pretty desperate anyways. + */ + scsi_sleep(4*HZ); + + WAIT(STATUS(SCpnt->host->io_port), + STATMASK, INIT|IDLE, STST|DIAGF|INVDCMD|DF|CDF); + + /* + * Now try to pick up the pieces. For all pending commands, + * free any internal data structures, and basically clear things + * out. We do not try and restart any commands or anything - + * the strategy handler takes care of that crap. + */ + printk("Sent BUS RESET to scsi host %d\n", SCpnt->host->host_no); + + for(i=0; i< AHA1542_MAILBOXES; i++) + { + if(HOSTDATA(SCpnt->host)->SCint[i] != NULL) + { + Scsi_Cmnd * SCtmp; + SCtmp = HOSTDATA(SCpnt->host)->SCint[i]; + + + if( SCtmp->device->soft_reset ) + { + /* + * If this device implements the soft reset option, + * then it is still holding onto the command, and + * may yet complete it. In this case, we don't + * flush the data. + */ + continue; + } + + if (SCtmp->host_scribble) + { + scsi_free(SCtmp->host_scribble, 512); + } + + HOSTDATA(SCpnt->host)->SCint[i] = NULL; + HOSTDATA(SCpnt->host)->mb[i].status = 0; + } + } + + return SUCCESS; + +fail: + return FAILED; +} + +int aha1542_host_reset(Scsi_Cmnd * SCpnt) +{ + int i; + + /* + * This does a scsi reset for all devices on the bus. + * In principle, we could also reset the 1542 - should + * we do this? Try this first, and we can add that later + * if it turns out to be useful. + */ + outb(HRST | SCRST, CONTROL(SCpnt->host->io_port)); + + /* + * Wait for the thing to settle down a bit. Unfortunately + * this is going to basically lock up the machine while we + * wait for this to complete. To be 100% correct, we need to + * check for timeout, and if we are doing something like this + * we are pretty desperate anyways. + */ + scsi_sleep(4*HZ); + + WAIT(STATUS(SCpnt->host->io_port), + STATMASK, INIT|IDLE, STST|DIAGF|INVDCMD|DF|CDF); + + /* + * We need to do this too before the 1542 can interact with + * us again. + */ + setup_mailboxes(SCpnt->host->io_port, SCpnt->host); + + /* + * Now try to pick up the pieces. For all pending commands, + * free any internal data structures, and basically clear things + * out. We do not try and restart any commands or anything - + * the strategy handler takes care of that crap. + */ + printk("Sent BUS RESET to scsi host %d\n", SCpnt->host->host_no); + + for(i=0; i< AHA1542_MAILBOXES; i++) + { + if(HOSTDATA(SCpnt->host)->SCint[i] != NULL) + { + Scsi_Cmnd * SCtmp; + SCtmp = HOSTDATA(SCpnt->host)->SCint[i]; + + if( SCtmp->device->soft_reset ) + { + /* + * If this device implements the soft reset option, + * then it is still holding onto the command, and + * may yet complete it. In this case, we don't + * flush the data. + */ + continue; + } + + if (SCtmp->host_scribble) + { + scsi_free(SCtmp->host_scribble, 512); + } + + HOSTDATA(SCpnt->host)->SCint[i] = NULL; + HOSTDATA(SCpnt->host)->mb[i].status = 0; + } + } + + return SUCCESS; + +fail: + return FAILED; +} + +/* + * These are the old error handling routines. They are only temporarily + * here while we play with the new error handling code. + */ +int aha1542_old_abort(Scsi_Cmnd * SCpnt) +{ #if 0 unchar ahacmd = CMD_START_SCSI; unsigned long flags; @@ -1194,7 +1446,7 @@ For a first go, we assume that the 1542 notifies us with all of the pending commands (it does implement soft reset, after all). */ -int aha1542_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags) +int aha1542_old_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags) { unchar ahacmd = CMD_START_SCSI; int i; diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/aha1542.h linux/drivers/scsi/aha1542.h --- v2.1.74/linux/drivers/scsi/aha1542.h Thu Jun 12 15:29:27 1997 +++ linux/drivers/scsi/aha1542.h Sun Dec 21 17:04:48 1997 @@ -133,8 +133,12 @@ int aha1542_detect(Scsi_Host_Template *); int aha1542_command(Scsi_Cmnd *); int aha1542_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); -int aha1542_abort(Scsi_Cmnd *); -int aha1542_reset(Scsi_Cmnd *, unsigned int); +int aha1542_abort(Scsi_Cmnd * SCpnt); +int aha1542_bus_reset(Scsi_Cmnd * SCpnt); +int aha1542_dev_reset(Scsi_Cmnd * SCpnt); +int aha1542_host_reset(Scsi_Cmnd * SCpnt); +extern int aha1542_old_abort(Scsi_Cmnd * SCpnt); +int aha1542_old_reset(Scsi_Cmnd *, unsigned int); int aha1542_biosparam(Disk *, kdev_t, int*); #define AHA1542_MAILBOXES 8 @@ -147,25 +151,24 @@ extern struct proc_dir_entry proc_scsi_aha1542; -#define AHA1542 { NULL, NULL, \ - &proc_scsi_aha1542,/* proc_dir_entry */ \ - NULL, \ - "Adaptec 1542", \ - aha1542_detect, \ - NULL, \ - NULL, \ - aha1542_command, \ - aha1542_queuecommand, \ - aha1542_abort, \ - aha1542_reset, \ - NULL, \ - aha1542_biosparam, \ - AHA1542_MAILBOXES, \ - 7, \ - AHA1542_SCATTER, \ - AHA1542_CMDLUN, \ - 0, \ - 1, \ - ENABLE_CLUSTERING} +#define AHA1542 { proc_dir: &proc_scsi_aha1542, \ + name: "Adaptec 1542", \ + detect: aha1542_detect, \ + command: aha1542_command, \ + queuecommand: aha1542_queuecommand, \ + abort: aha1542_old_abort, \ + reset: aha1542_old_reset, \ + eh_abort_handler: aha1542_abort, \ + eh_device_reset_handler: aha1542_dev_reset, \ + eh_bus_reset_handler: aha1542_bus_reset, \ + eh_host_reset_handler: aha1542_host_reset, \ + bios_param: aha1542_biosparam, \ + can_queue: AHA1542_MAILBOXES, \ + this_id: 7, \ + sg_tablesize: AHA1542_SCATTER, \ + cmd_per_lun: AHA1542_CMDLUN, \ + unchecked_isa_dma: 1, \ + use_clustering: ENABLE_CLUSTERING, \ + use_new_eh_code: 1} #endif diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/aha1740.h linux/drivers/scsi/aha1740.h --- v2.1.74/linux/drivers/scsi/aha1740.h Thu Mar 27 14:40:05 1997 +++ linux/drivers/scsi/aha1740.h Sun Dec 21 17:04:48 1997 @@ -172,25 +172,19 @@ extern struct proc_dir_entry proc_scsi_aha1740; -#define AHA1740 {NULL, NULL, \ - &proc_scsi_aha1740, \ - aha1740_proc_info, \ - "Adaptec 174x (EISA)", \ - aha1740_detect, \ - NULL, \ - NULL, \ - aha1740_command, \ - aha1740_queuecommand, \ - aha1740_abort, \ - aha1740_reset, \ - NULL, \ - aha1740_biosparam, \ - AHA1740_ECBS, \ - 7, \ - AHA1740_SCATTER, \ - AHA1740_CMDLUN, \ - 0, \ - 0, \ - ENABLE_CLUSTERING} +#define AHA1740 { proc_dir: &proc_scsi_aha1740, \ + proc_info: aha1740_proc_info, \ + name: "Adaptec 174x (EISA)", \ + detect: aha1740_detect, \ + command: aha1740_command, \ + queuecommand: aha1740_queuecommand, \ + abort: aha1740_abort, \ + reset: aha1740_reset, \ + bios_param: aha1740_biosparam, \ + can_queue: AHA1740_ECBS, \ + this_id: 7, \ + sg_tablesize: AHA1740_SCATTER, \ + cmd_per_lun: AHA1740_CMDLUN, \ + use_clustering: ENABLE_CLUSTERING} #endif diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/aic7xxx.h linux/drivers/scsi/aic7xxx.h --- v2.1.74/linux/drivers/scsi/aic7xxx.h Mon Aug 4 16:25:38 1997 +++ linux/drivers/scsi/aic7xxx.h Sun Dec 21 17:04:48 1997 @@ -30,33 +30,26 @@ * to do with card config are filled in after the card is detected. */ #define AIC7XXX { \ - NULL, \ - NULL, \ - NULL, \ - aic7xxx_proc_info, \ - NULL, \ - aic7xxx_detect, \ - NULL, \ - aic7xxx_info, \ - NULL, \ - aic7xxx_queue, \ - NULL, \ - aic7xxx_reset, \ - NULL, \ - aic7xxx_biosparam, \ - -1, /* max simultaneous cmds */\ - -1, /* scsi id of host adapter */\ - 0, /* max scatter-gather cmds */\ - 2, /* cmds per lun (linked cmds) */\ - 0, /* number of 7xxx's present */\ - 0, /* no memory DMA restrictions */\ - ENABLE_CLUSTERING \ + proc_info: aic7xxx_proc_info, \ + detect: aic7xxx_detect, \ + info: aic7xxx_info, \ + queuecommand: aic7xxx_queue, \ + abort: aic7xxx_abort, \ + reset: aic7xxx_reset, \ + bios_param: aic7xxx_biosparam, \ + can_queue: -1, /* max simultaneous cmds */\ + this_id: -1, /* scsi id of host adapter */\ + sg_tablesize: SG_ALL, /* max scatter-gather cmds */\ + cmd_per_lun: 2, /* cmds per lun (linked cmds) */\ + use_clustering: ENABLE_CLUSTERING, \ + use_new_eh_code: 0 /* Enable new error code */ \ } extern int aic7xxx_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *)); extern int aic7xxx_biosparam(Disk *, kdev_t, int[]); extern int aic7xxx_detect(Scsi_Host_Template *); extern int aic7xxx_command(Scsi_Cmnd *); +extern int aic7xxx_abort(Scsi_Cmnd *); extern int aic7xxx_reset(Scsi_Cmnd *, unsigned int); extern const char *aic7xxx_info(struct Scsi_Host *); diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/amiga7xx.h linux/drivers/scsi/amiga7xx.h --- v2.1.74/linux/drivers/scsi/amiga7xx.h Thu Jun 12 15:29:37 1997 +++ linux/drivers/scsi/amiga7xx.h Sun Dec 21 17:04:48 1997 @@ -27,26 +27,16 @@ extern struct proc_dir_entry proc_scsi_amiga7xx; -#define AMIGA7XX_SCSI {/* next */ NULL, \ - /* usage_count */ NULL, \ - /* proc_dir_entry */ NULL, \ - /* proc_info */ NULL, \ - /* name */ "Amiga NCR53c710 SCSI", \ - /* detect */ amiga7xx_detect, \ - /* release */ NULL, \ - /* info */ NULL, \ - /* command */ NULL, \ - /* queuecommand */ NCR53c7xx_queue_command, \ - /* abort */ NCR53c7xx_abort, \ - /* reset */ NCR53c7xx_reset, \ - /* slave_attach */ NULL, \ - /* bios_param */ scsicam_bios_param, \ - /* can_queue */ 24, \ - /* this_id */ 7, \ - /* sg_tablesize */ 127, \ - /* cmd_per_lun */ 3, \ - /* present */ 0, \ - /* unchecked_isa_dma */ 0, \ - /* use_clustering */ DISABLE_CLUSTERING } +#define AMIGA7XX_SCSI {name: "Amiga NCR53c710 SCSI", \ + detect: amiga7xx_detect, \ + queuecommand: NCR53c7xx_queue_command, \ + abort: NCR53c7xx_abort, \ + reset: NCR53c7xx_reset, \ + bios_param: scsicam_bios_param, \ + can_queue: 24, \ + this_id: 7, \ + sg_tablesize: 127, \ + cmd_per_lun: 3, \ + use_clustering: DISABLE_CLUSTERING } #endif #endif /* AMIGA7XX_H */ diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/atari_scsi.h linux/drivers/scsi/atari_scsi.h --- v2.1.74/linux/drivers/scsi/atari_scsi.h Fri Dec 20 01:20:02 1996 +++ linux/drivers/scsi/atari_scsi.h Sun Dec 21 17:04:48 1997 @@ -53,25 +53,19 @@ #if defined (HOSTS_C) || defined (MODULE) -#define ATARI_SCSI { NULL, NULL, NULL, \ - atari_scsi_proc_info, \ - "Atari native SCSI", \ - atari_scsi_detect, \ - atari_scsi_release, \ - atari_scsi_info, \ - /* command */ NULL, \ - atari_scsi_queue_command, \ - atari_scsi_abort, \ - atari_scsi_reset, \ - /* slave_attach */ NULL, \ - /* bios_param */ NULL, \ - /* can queue */ 0, /* initialized at run-time */ \ - /* host_id */ 0, /* initialized at run-time */ \ - /* scatter gather */ 0, /* initialized at run-time */ \ - /* cmd per lun */ 0, /* initialized at run-time */ \ - /* present */ 0, \ - /* unchecked ISA DMA */ 0, \ - /* use_clustering */ DISABLE_CLUSTERING } +#define ATARI_SCSI { proc_info: atari_scsi_proc_info, \ + name: "Atari native SCSI", \ + detect: atari_scsi_detect, \ + release: atari_scsi_release, \ + info: atari_scsi_info, \ + queuecommand: atari_scsi_queue_command, \ + abort: atari_scsi_abort, \ + reset: atari_scsi_reset, \ + can_queue: 0, /* initialized at run-time */ \ + this_id: 0, /* initialized at run-time */ \ + sg_tablesize: 0, /* initialized at run-time */ \ + cmd_per_lun: 0, /* initialized at run-time */ \ + use_clustering: DISABLE_CLUSTERING } #endif diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/constants.c linux/drivers/scsi/constants.c --- v2.1.74/linux/drivers/scsi/constants.c Tue Jun 4 02:56:48 1996 +++ linux/drivers/scsi/constants.c Sun Dec 21 17:04:48 1997 @@ -3,11 +3,6 @@ * etc. */ -/* - * Don't import our own symbols, as this would severely mess up our - * symbol tables. - */ -#define _SCSI_SYMS_VER_ #define __NO_VERSION__ #include diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/dc390.h linux/drivers/scsi/dc390.h --- v2.1.74/linux/drivers/scsi/dc390.h Mon Jul 7 16:02:51 1997 +++ linux/drivers/scsi/dc390.h Sun Dec 21 17:59:19 1997 @@ -63,29 +63,22 @@ #ifdef VERSION_2_0_0 -#define DC390_T { \ - NULL, /* *next */ \ - NULL, /* *usage_count */ \ - &proc_scsi_tmscsim, /* *proc_dir */ \ - tmscsim_proc_info, /* (*proc_info)() */ \ - "Tekram DC390(T) V1.10 Dec-05-1996", /* *name */ \ - DC390_detect, \ - DC390_release, /* (*release)() */ \ - NULL, /* *(*info)() */ \ - NULL, /* (*command)() */ \ - DC390_queue_command, \ - DC390_abort, \ - DC390_reset, \ - NULL, /* slave attach */\ - DC390_bios_param, \ - 10,/* can queue(-1) */ \ - 7, /* id(-1) */ \ - SG_ALL, \ - 2, /* cmd per lun(2) */ \ - 0, /* present */ \ - 0, /* unchecked isa dma */ \ - DISABLE_CLUSTERING \ - } +#define DC390_T { \ + proc_dir: &proc_scsi_tmscsim, \ + proc_info: tmscsim_proc_info + name: "Tekram DC390(T) V1.10 Dec-05-1996",\ + detect: DC390_detect, \ + release: DC390_release, \ + queuecommand: DC390_queue_command, \ + abort: DC390_abort, \ + reset: DC390_reset, \ + bios_param: DC390_bios_param, \ + can_queue: 10, \ + this_id: 7, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: 2, \ + use_clustering: DISABLE_CLUSTERING \ + } #endif diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/dtc.h linux/drivers/scsi/dtc.h --- v2.1.74/linux/drivers/scsi/dtc.h Mon Mar 17 14:54:29 1997 +++ linux/drivers/scsi/dtc.h Sun Dec 21 17:04:48 1997 @@ -57,13 +57,18 @@ #if defined(HOSTS_C) || defined(MODULE) -#define DTC3x80 {NULL, NULL, NULL, NULL, \ - "DTC 3180/3280 ", dtc_detect, NULL, \ - NULL, \ - NULL, dtc_queue_command, dtc_abort, dtc_reset, NULL, \ - dtc_biosparam, \ - /* can queue */ CAN_QUEUE, /* id */ 7, SG_ALL, \ - /* cmd per lun */ CMD_PER_LUN , 0, 0, DISABLE_CLUSTERING} +#define DTC3x80 { \ + name: "DTC 3180/3280 ", \ + detect: dtc_detect, \ + queuecommand: dtc_queue_command, \ + abort: dtc_abort, \ + reset: dtc_reset, \ + bios_param: dtc_biosparam, \ + can_queue: CAN_QUEUE, \ + this_id: 7, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: CMD_PER_LUN , \ + use_clustering: DISABLE_CLUSTERING} #endif diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/eata.h linux/drivers/scsi/eata.h --- v2.1.74/linux/drivers/scsi/eata.h Tue Sep 23 16:48:48 1997 +++ linux/drivers/scsi/eata.h Sun Dec 21 17:04:48 1997 @@ -15,27 +15,16 @@ #define EATA_VERSION "3.11.00" -#define EATA { \ - NULL, /* Ptr for modules */ \ - NULL, /* usage count for modules */ \ - NULL, \ - NULL, \ - "EATA/DMA 2.0x rev. " EATA_VERSION " ", \ - eata2x_detect, \ - eata2x_release, \ - NULL, \ - NULL, \ - eata2x_queuecommand, \ - eata2x_abort, \ - eata2x_reset, \ - NULL, \ - scsicam_bios_param, \ - 0, /* can_queue, reset by detect */ \ - 7, /* this_id, reset by detect */ \ - 0, /* sg_tablesize, reset by detect */ \ - 0, /* cmd_per_lun, reset by detect */ \ - 0, /* number of boards present */ \ - 1, /* unchecked isa dma, reset by detect */ \ - ENABLE_CLUSTERING \ +#define EATA { \ + name: "EATA/DMA 2.0x rev. " EATA_VERSION " ", \ + detect: eata2x_detect, \ + release: eata2x_release, \ + queuecommand: eata2x_queuecommand, \ + abort: eata2x_abort, \ + reset: eata2x_reset, \ + bios_param: scsicam_bios_param, \ + this_id: 7, /* this_id, reset by detect */ \ + unchecked_isa_dma: 1, /* unchecked isa dma, reset by detect */\ + use_clustering: ENABLE_CLUSTERING \ } #endif diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/eata_dma.h linux/drivers/scsi/eata_dma.h --- v2.1.74/linux/drivers/scsi/eata_dma.h Thu Jun 12 15:29:27 1997 +++ linux/drivers/scsi/eata_dma.h Sun Dec 21 17:04:48 1997 @@ -84,26 +84,17 @@ #include -#define EATA_DMA { \ - NULL, NULL, \ - NULL, /* proc_dir_entry */ \ - eata_proc_info, /* procinfo */ \ - "EATA (Extended Attachment) HBA driver", \ - eata_detect, \ - eata_release, \ - NULL, NULL, \ - eata_queue, \ - eata_abort, \ - eata_reset, \ - NULL, /* Slave attach */ \ - scsicam_bios_param, \ - 0, /* Canqueue */ \ - 0, /* this_id */ \ - 0, /* sg_tablesize */ \ - 0, /* cmd_per_lun */ \ - 0, /* present */ \ - 1, /* True if ISA */ \ - ENABLE_CLUSTERING } +#define EATA_DMA { \ + proc_info: eata_proc_info, /* procinfo */ \ + name: "EATA (Extended Attachment) HBA driver", \ + detect: eata_detect, \ + release: eata_release, \ + queuecommand: eata_queue, \ + abort: eata_abort, \ + reset: eata_reset, \ + bios_param: scsicam_bios_param, \ + unchecked_isa_dma: 1, /* True if ISA */ \ + use_clustering: ENABLE_CLUSTERING } #endif /* _EATA_DMA_H */ diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/eata_dma_proc.c linux/drivers/scsi/eata_dma_proc.c --- v2.1.74/linux/drivers/scsi/eata_dma_proc.c Thu Aug 1 05:43:04 1996 +++ linux/drivers/scsi/eata_dma_proc.c Sun Dec 21 17:04:49 1997 @@ -439,15 +439,12 @@ goto stop_output; } -#if 0 - scd = scsi_devices; - - size = sprintf(buffer+len,"Attached devices: %s\n", (scd)?"":"none"); + size = sprintf(buffer+len,"Attached devices: %s\n", + (HBA_ptr->host_queue)?"":"none"); len += size; pos = begin + len; - while (scd) { - if (scd->host == HBA_ptr) { + for(scd = HBA_ptr->host_queue; scd; scd = scd->next) { proc_print_scsidevice(scd, buffer, &size, len); len += size; pos = begin + len; @@ -458,10 +455,7 @@ } if (pos > offset + length) goto stop_output; - } - scd = scd->next; } -#endif stop_output: DBG(DBG_PROC, printk("2pos: %ld offset: %ld len: %d\n", pos, offset, len)); diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/eata_pio.h linux/drivers/scsi/eata_pio.h --- v2.1.74/linux/drivers/scsi/eata_pio.h Mon Jul 7 16:03:00 1997 +++ linux/drivers/scsi/eata_pio.h Sun Dec 21 17:59:22 1997 @@ -74,26 +74,17 @@ #endif -#define EATA_PIO { \ - NULL, NULL, \ - NULL, /* proc_dir_entry */ \ - eata_pio_proc_info, /* procinfo */ \ - "EATA (Extended Attachment) PIO driver", \ - eata_pio_detect, \ - eata_pio_release, \ - NULL, NULL, \ - eata_pio_queue, \ - eata_pio_abort, \ - eata_pio_reset, \ - NULL, /* Slave attach */ \ - scsicam_bios_param, \ - 0, /* Canqueue */ \ - 0, /* this_id */ \ - 0, /* sg_tablesize */ \ - 0, /* cmd_per_lun */ \ - 0, /* present */ \ - 1, /* True if ISA */ \ - ENABLE_CLUSTERING } +#define EATA_PIO { \ + proc_info: eata_pio_proc_info, /* procinfo */ \ + name: "EATA (Extended Attachment) PIO driver", \ + detect: eata_pio_detect, \ + release: eata_pio_release, \ + queuecommand: eata_pio_queue, \ + abort: eata_pio_abort, \ + reset: eata_pio_reset, \ + bios_param: scsicam_bios_param, \ + unchecked_isa_dma: 1, /* True if ISA */ \ + use_clustering: ENABLE_CLUSTERING } #endif /* _EATA_PIO_H */ diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/eata_pio_proc.c linux/drivers/scsi/eata_pio_proc.c --- v2.1.74/linux/drivers/scsi/eata_pio_proc.c Mon Apr 7 11:35:29 1997 +++ linux/drivers/scsi/eata_pio_proc.c Sun Dec 21 17:04:49 1997 @@ -81,14 +81,12 @@ if (pos > offset + length) goto stop_output; - scd = scsi_devices; - - size = sprintf(buffer+len,"Attached devices: %s\n", (scd)?"":"none"); + size = sprintf(buffer+len,"Attached devices: %s\n", + (HBA_ptr->host_queue)?"":"none"); len += size; pos = begin + len; - while (scd) { - if (scd->host == HBA_ptr) { + for(scd = HBA_ptr->host_queue; scd; scd = scd->next) { proc_print_scsidevice(scd, buffer, &size, len); len += size; pos = begin + len; @@ -99,8 +97,6 @@ } if (pos > offset + length) goto stop_output; - } - scd = scd->next; } stop_output: diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/esp.c linux/drivers/scsi/esp.c --- v2.1.74/linux/drivers/scsi/esp.c Sun Sep 7 13:10:43 1997 +++ linux/drivers/scsi/esp.c Sun Dec 21 17:04:49 1997 @@ -1163,7 +1163,7 @@ copy_info(&info, "Target #\tconfig3\t\tSync Capabilities\tDisconnect\tWide\n"); for(i = 0; i < 15; i++) { if(esp->targets_present & (1 << i)) { - Scsi_Device *SDptr = scsi_devices; + Scsi_Device *SDptr = esp->ehost->host_queue; while((SDptr->host != esp->ehost) && (SDptr->id != i) && diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/esp.h linux/drivers/scsi/esp.h --- v2.1.74/linux/drivers/scsi/esp.h Mon Jul 7 16:02:51 1997 +++ linux/drivers/scsi/esp.h Sun Dec 21 17:59:19 1997 @@ -395,28 +395,21 @@ extern struct proc_dir_entry proc_scsi_esp; -#define SCSI_SPARC_ESP { \ -/* struct SHT *next */ NULL, \ -/* struct module *module */ NULL, \ -/* struct proc_dir_entry *proc_dir */ &proc_scsi_esp, \ -/* int (*proc_info)(char *, char **, off_t, int, int, int) */ &esp_proc_info, \ -/* const char *name */ "Sun ESP 100/100a/200", \ -/* int detect(struct SHT *) */ esp_detect, \ -/* int release(struct Scsi_Host *) */ NULL, \ -/* const char *info(struct Scsi_Host *) */ esp_info, \ -/* int command(Scsi_Cmnd *) */ esp_command, \ -/* int queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)) */ esp_queue, \ -/* int abort(Scsi_Cmnd *) */ esp_abort, \ -/* int reset(Scsi_Cmnd *, int) */ esp_reset, \ -/* int slave_attach(int, int) */ NULL, \ -/* int bios_param(Disk *, kdev_t, int[]) */ NULL, \ -/* int can_queue */ 7, \ -/* int this_id */ 7, \ -/* short unsigned int sg_tablesize */ SG_ALL, \ -/* short cmd_per_lun */ 1, \ -/* unsigned char present */ 0, \ -/* unsigned unchecked_isa_dma:1 */ 0, \ -/* unsigned use_clustering:1 */ DISABLE_CLUSTERING, } +#define SCSI_SPARC_ESP { \ + proc_dir: &proc_scsi_esp, \ + proc_info: &esp_proc_info, \ + name: "Sun ESP 100/100a/200", \ + detect: esp_detect, \ + info: esp_info, \ + command: esp_command, \ + queuecommand: esp_queue, \ + abort: esp_abort, \ + reset: esp_reset, \ + can_queue: 7, \ + this_id: 7, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: 1, \ + use_clustering: DISABLE_CLUSTERING, } /* For our interrupt engine. */ #define for_each_esp(esp) \ diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/fdomain.h linux/drivers/scsi/fdomain.h --- v2.1.74/linux/drivers/scsi/fdomain.h Fri Dec 27 02:03:23 1996 +++ linux/drivers/scsi/fdomain.h Sun Dec 21 17:04:49 1997 @@ -37,25 +37,17 @@ extern struct proc_dir_entry proc_scsi_fdomain; -#define FDOMAIN_16X0 { NULL, \ - NULL, \ - NULL, \ - fdomain_16x0_proc_info, \ - NULL, \ - fdomain_16x0_detect, \ - NULL, \ - fdomain_16x0_info, \ - fdomain_16x0_command, \ - fdomain_16x0_queue, \ - fdomain_16x0_abort, \ - fdomain_16x0_reset, \ - NULL, \ - fdomain_16x0_biosparam, \ - 1, \ - 6, \ - 64, \ - 1, \ - 0, \ - 0, \ - DISABLE_CLUSTERING } +#define FDOMAIN_16X0 { proc_info: fdomain_16x0_proc_info, \ + detect: fdomain_16x0_detect, \ + info: fdomain_16x0_info, \ + command: fdomain_16x0_command, \ + queuecommand: fdomain_16x0_queue, \ + abort: fdomain_16x0_abort, \ + reset: fdomain_16x0_reset, \ + bios_param: fdomain_16x0_biosparam, \ + can_queue: 1, \ + this_id: 6, \ + sg_tablesize: 64, \ + cmd_per_lun: 1, \ + use_clustering: DISABLE_CLUSTERING } #endif diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/g_NCR5380.c linux/drivers/scsi/g_NCR5380.c --- v2.1.74/linux/drivers/scsi/g_NCR5380.c Sun Sep 7 13:10:43 1997 +++ linux/drivers/scsi/g_NCR5380.c Sun Dec 21 17:04:49 1997 @@ -658,8 +658,7 @@ PRINTP(" %d pending writes" ANDP hostdata->pendingw); if (hostdata->pendingr || hostdata->pendingw) PRINTP("\n"); - for (dev = scsi_devices; dev; dev=dev->next) { - if (dev->host == scsi_ptr) { + for (dev = scsi_ptr->host_queue; dev; dev=dev->next) { unsigned long br = hostdata->bytes_read[dev->id]; unsigned long bw = hostdata->bytes_write[dev->id]; long tr = hostdata->time_read[dev->id] / HZ; @@ -687,7 +686,6 @@ if (tw) PRINTP(" @ %5ld bps" ANDP bw / tw); PRINTP("\n"); - } } #endif diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/g_NCR5380.h linux/drivers/scsi/g_NCR5380.h --- v2.1.74/linux/drivers/scsi/g_NCR5380.h Mon Jul 7 16:02:51 1997 +++ linux/drivers/scsi/g_NCR5380.h Sun Dec 21 17:59:19 1997 @@ -72,16 +72,21 @@ #if defined(HOSTS_C) || defined(MODULE) -#define GENERIC_NCR5380 {NULL, NULL, NULL, \ - generic_NCR5380_proc_info, \ - "Generic NCR5380/NCR53C400 Scsi Driver", \ - generic_NCR5380_detect, generic_NCR5380_release_resources, \ - (void *)generic_NCR5380_info, NULL, \ - generic_NCR5380_queue_command, generic_NCR5380_abort, \ - generic_NCR5380_reset, NULL, \ - NCR5380_BIOSPARAM, \ - /* can queue */ CAN_QUEUE, /* id */ 7, SG_ALL, \ - /* cmd per lun */ CMD_PER_LUN , 0, 0, DISABLE_CLUSTERING} +#define GENERIC_NCR5380 { \ + proc_info: generic_NCR5380_proc_info, \ + name: "Generic NCR5380/NCR53C400 Scsi Driver", \ + detect: generic_NCR5380_detect, \ + release: generic_NCR5380_release_resources, \ + info: (void *)generic_NCR5380_info, \ + queuecommand: generic_NCR5380_queue_command, \ + abort: generic_NCR5380_abort, \ + reset: generic_NCR5380_reset, \ + bios_param: NCR5380_BIOSPARAM, \ + can_queue: CAN_QUEUE, \ + this_id: 7, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: CMD_PER_LUN , \ + use_clustering: DISABLE_CLUSTERING} #endif diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/gdth.h linux/drivers/scsi/gdth.h --- v2.1.74/linux/drivers/scsi/gdth.h Wed Nov 12 13:34:26 1997 +++ linux/drivers/scsi/gdth.h Sun Dec 21 17:59:19 1997 @@ -670,51 +670,26 @@ const char *gdth_info(struct Scsi_Host *); -#if LINUX_VERSION_CODE >= 0x010300 int gdth_bios_param(Disk *,kdev_t,int *); extern struct proc_dir_entry proc_scsi_gdth; int gdth_proc_info(char *,char **,off_t,int,int,int); -#define GDTH { NULL, NULL, \ - &proc_scsi_gdth, \ - gdth_proc_info, \ - "GDT SCSI Disk Array Controller", \ - gdth_detect, \ - gdth_release, \ - gdth_info, \ - gdth_command, \ - gdth_queuecommand, \ - gdth_abort, \ - gdth_reset, \ - NULL, \ - gdth_bios_param, \ - GDTH_MAXCMDS, \ - -1, \ - GDTH_MAXSG, \ - GDTH_MAXC_P_L, \ - 0, \ - 1, \ - ENABLE_CLUSTERING} -#else -int gdth_bios_param(Disk *,int,int *); -#define GDTH { NULL, NULL, \ - "GDT SCSI Disk Array Controller", \ - gdth_detect, \ - gdth_release, \ - gdth_info, \ - gdth_command, \ - gdth_queuecommand, \ - gdth_abort, \ - gdth_reset, \ - NULL, \ - gdth_bios_param, \ - GDTH_MAXCMDS, \ - -1, \ - GDTH_MAXSG, \ - GDTH_MAXC_P_L, \ - 0, \ - 1, \ - ENABLE_CLUSTERING} -#endif +#define GDTH { proc_dir: &proc_scsi_gdth, \ + proc_info: gdth_proc_info, \ + name: "GDT SCSI Disk Array Controller", \ + detect: gdth_detect, \ + release: gdth_release, \ + info: gdth_info, \ + command: gdth_command, \ + queuecommand: gdth_queuecommand, \ + abort: gdth_abort, \ + reset: gdth_reset, \ + bios_param: gdth_bios_param, \ + can_queue: GDTH_MAXCMDS, \ + this_id: -1, \ + sg_tablesize: GDTH_MAXSG, \ + cmd_per_lun: GDTH_MAXC_P_L, \ + unchecked_isa_dma: 1, \ + use_clustering: ENABLE_CLUSTERING} #endif diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/gvp11.h linux/drivers/scsi/gvp11.h --- v2.1.74/linux/drivers/scsi/gvp11.h Thu Jun 12 15:29:27 1997 +++ linux/drivers/scsi/gvp11.h Sun Dec 21 17:04:49 1997 @@ -34,27 +34,18 @@ extern struct proc_dir_entry proc_scsi_gvp11; -#define GVP11_SCSI { /* next */ NULL, \ - /* module */ NULL, \ - /* proc_dir_entry */ &proc_scsi_gvp11, \ - /* proc_info */ NULL, \ - /* name */ "GVP Series II SCSI", \ - /* detect */ gvp11_detect, \ - /* release */ gvp11_release, \ - /* info */ NULL, \ - /* command */ NULL, \ - /* queuecommand */ wd33c93_queuecommand, \ - /* abort */ wd33c93_abort, \ - /* reset */ wd33c93_reset, \ - /* slave_attach */ NULL, \ - /* bios_param */ NULL, \ - /* can_queue */ CAN_QUEUE, \ - /* this_id */ 7, \ - /* sg_tablesize */ SG_ALL, \ - /* cmd_per_lun */ CMD_PER_LUN, \ - /* present */ 0, \ - /* unchecked_isa_dma */ 0, \ - /* use_clustering */ DISABLE_CLUSTERING } +#define GVP11_SCSI { proc_dir: &proc_scsi_gvp11, \ + name: "GVP Series II SCSI", \ + detect: gvp11_detect, \ + release: gvp11_release, \ + queuecommand: wd33c93_queuecommand, \ + abort: wd33c93_abort, \ + reset: wd33c93_reset, \ + can_queue: CAN_QUEUE, \ + this_id: 7, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: CMD_PER_LUN, \ + use_clustering: DISABLE_CLUSTERING } #else /* diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c --- v2.1.74/linux/drivers/scsi/hosts.c Wed Nov 12 13:34:26 1997 +++ linux/drivers/scsi/hosts.c Sun Dec 21 17:04:49 1997 @@ -16,11 +16,6 @@ * hosts currently present in the system. */ -/* - * Don't import our own symbols, as this would severely mess up our - * symbol tables. - */ -#define _SCSI_SYMS_VER_ #define __NO_VERSION__ #include @@ -32,6 +27,10 @@ #include #include +#define __KERNEL_SYSCALLS__ + +#include + #include "scsi.h" #ifndef NULL @@ -433,7 +432,9 @@ struct Scsi_Host * retval, *shpnt; retval = (struct Scsi_Host *)scsi_init_malloc(sizeof(struct Scsi_Host) + j, (tpnt->unchecked_isa_dma && j ? GFP_DMA : 0) | GFP_ATOMIC); + atomic_set(&retval->host_active,0); retval->host_busy = 0; + retval->host_failed = 0; retval->block = NULL; retval->wish_block = 0; if(j > 0xffff) panic("Too many extra bytes requested\n"); @@ -456,6 +457,16 @@ retval->io_port = 0; retval->hostt = tpnt; retval->next = NULL; + retval->in_recovery = 0; + retval->ehandler = NULL; /* Initial value until the thing starts up. */ + retval->eh_notify = NULL; /* Who we notify when we exit. */ + + /* + * Initialize the fields used for mid-level queueing. + */ + retval->pending_commands = NULL; + retval->host_busy = FALSE; + #ifdef DEBUG printk("Register %x %x: %d\n", (int)retval, (int)retval->hostt, j); #endif @@ -542,6 +553,25 @@ name = shpnt->hostt->name; printk ("scsi%d : %s\n", /* And print a little message */ shpnt->host_no, name); + + /* + * Now start the error recovery thread for the host. + */ + if( shpnt->hostt->use_new_eh_code ) + { + struct semaphore sem = MUTEX_LOCKED; + + shpnt->eh_notify = &sem; + + kernel_thread((int (*)(void *))scsi_error_handler, + (void *) shpnt, 0); + /* + * Now wait for the kernel error thread to initialize itself + * as it might be needed when we scan the bus. + */ + down (&sem); + shpnt->eh_notify = NULL; + } } printk ("scsi : %d host%s.\n", next_scsi_host, diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/hosts.h linux/drivers/scsi/hosts.h --- v2.1.74/linux/drivers/scsi/hosts.h Mon Jul 7 16:02:51 1997 +++ linux/drivers/scsi/hosts.h Sun Dec 21 17:59:19 1997 @@ -127,10 +127,34 @@ * # and exit result when the command is complete. * Host number is the POSITION IN THE hosts array of THIS * host adapter. + * + * The done() function must only be called after QueueCommand() + * has returned. */ int (* queuecommand)(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); /* + * This is an error handling strategy routine. You don't need to + * define one of these if you don't want to - there is a default + * routine that is present that should work in most cases. For those + * driver authors that have the inclination and ability to write their + * own strategy routine, this is where it is specified. Note - the + * strategy routine is *ALWAYS* run in the context of the kernel eh + * thread. Thus you are guaranteed to *NOT* be in an interrupt handler + * when you execute this, and you are also guaranteed to *NOT* have any + * other commands being queued while you are in the strategy routine. + * When you return from this function, operations return to normal. + * + * See scsi_error.c scsi_unjam_host for additional comments about what + * this function should and should not be attempting to do. + */ + int (*eh_strategy_handler)(struct Scsi_Host *); + int (*eh_abort_handler)(Scsi_Cmnd *); + int (*eh_device_reset_handler)(Scsi_Cmnd *); + int (*eh_bus_reset_handler)(Scsi_Cmnd *); + int (*eh_host_reset_handler)(Scsi_Cmnd *); + + /* * Since the mid level driver handles time outs, etc, we want to * be able to abort the current command. Abort returns 0 if the * abortion was successful. The field SCpnt->abort reason @@ -143,6 +167,9 @@ * * Note that the scsi driver should "clean up" after itself, * resetting the bus, etc. if necessary. + * + * NOTE - this interface is depreciated, and will go away. Use + * the eh_ routines instead. */ int (* abort)(Scsi_Cmnd *); @@ -155,6 +182,9 @@ * the first place. Some hosts do not implement a reset function, * and these hosts must call scsi_request_sense(SCpnt) to keep * the command alive. + * + * NOTE - this interface is depreciated, and will go away. Use + * the eh_ routines instead. */ int (* reset)(Scsi_Cmnd *, unsigned int); @@ -227,6 +257,13 @@ */ unsigned use_clustering:1; + /* + * True if this driver uses the new error handling code. This flag is + * really only temporary until all of the other drivers get converted + * to use the new error handling code. + */ + unsigned use_new_eh_code:1; + } Scsi_Host_Template; /* @@ -240,14 +277,40 @@ struct Scsi_Host { - struct Scsi_Host * next; +/* private: */ + /* + * This information is private to the scsi mid-layer. Wrapping it in a + * struct private is a way of marking it in a sort of C++ type of way. + */ + struct Scsi_Host * next; + Scsi_Device * host_queue; + /* + * List of commands that have been rejected because either the host + * or the device was busy. These need to be retried relatively quickly, + * but we need to hold onto it for a short period until the host/device + * is available. + */ + Scsi_Cmnd * pending_commands; + + struct task_struct * ehandler; /* Error recovery thread. */ + struct semaphore * eh_wait; /* The error recovery thread waits on + this. */ + struct semaphore * eh_notify; /* wait for eh to begin */ + struct semaphore * eh_action; /* Wait for specific actions on the + host. */ + unsigned int eh_active:1; /* Indicates the eh thread is awake and active if + this is true. */ + struct wait_queue * host_wait; + Scsi_Host_Template * hostt; + atomic_t host_active; /* commands checked out */ + volatile unsigned short host_busy; /* commands actually active on low-level */ + volatile unsigned short host_failed; /* commands that failed. */ + +/* public: */ unsigned short extra_bytes; - volatile unsigned char host_busy; - char host_no; /* Used for IOCTL_GET_IDLUN, /proc/scsi et al. */ + unsigned short host_no; /* Used for IOCTL_GET_IDLUN, /proc/scsi et al. */ unsigned long last_reset; - struct wait_queue *host_wait; - Scsi_Cmnd *host_queue; - Scsi_Host_Template * hostt; + /* * These three parameters can be used to allow for wide scsi, @@ -292,12 +355,19 @@ int can_queue; short cmd_per_lun; short unsigned int sg_tablesize; + + unsigned in_recovery:1; unsigned unchecked_isa_dma:1; unsigned use_clustering:1; /* * True if this host was loaded as a loadable module */ unsigned loaded_as_module:1; + + /* + * Host has rejected a command because it was busy. + */ + unsigned host_blocked:1; void (*select_queue_depths)(struct Scsi_Host *, Scsi_Device *); diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/ibmmca.h linux/drivers/scsi/ibmmca.h --- v2.1.74/linux/drivers/scsi/ibmmca.h Mon Oct 20 10:36:52 1997 +++ linux/drivers/scsi/ibmmca.h Sun Dec 21 17:04:49 1997 @@ -19,28 +19,22 @@ extern struct proc_dir_entry proc_scsi_ibmmca; /*initialization for Scsi_host_template type */ -#define IBMMCA { \ - NULL, /*next*/ \ - NULL, /*usage_count*/ \ - &proc_scsi_ibmmca, /*proc_dir*/ \ - ibmmca_proc_info, /*proc info fn*/ \ - "IBMMCA", /*name*/ \ - ibmmca_detect, /*detect fn*/ \ - ibmmca_release, /*release fn*/ \ - NULL, /*info fn*/ \ - ibmmca_command, /*command fn*/ \ - ibmmca_queuecommand, /*queuecommand fn*/ \ - ibmmca_abort, /*abort fn*/ \ - ibmmca_reset, /*reset fn*/ \ - NULL, /*slave_attach fn*/ \ - ibmmca_biosparam, /*bios fn*/ \ - 16, /*can_queue*/ \ - 7, /*set by detect*/ \ - 16, /*sg_tablesize*/ \ - 1, /*cmd_per_lun*/ \ - 0, /*present*/ \ - 0, /*unchecked_isa_dma*/ \ - ENABLE_CLUSTERING /*use_clustering*/ \ +#define IBMMCA { \ + proc_dir: &proc_scsi_ibmmca, /*proc_dir*/ \ + proc_info: ibmmca_proc_info, /*proc info fn*/ \ + name: "IBMMCA", /*name*/ \ + detect: ibmmca_detect, /*detect fn*/ \ + release: ibmmca_release, /*release fn*/ \ + command: ibmmca_command, /*command fn*/ \ + queuecommand: ibmmca_queuecommand, /*queuecommand fn*/ \ + abort: ibmmca_abort, /*abort fn*/ \ + reset: ibmmca_reset, /*reset fn*/ \ + bios_param: ibmmca_biosparam, /*bios fn*/ \ + can_queue: 16, /*can_queue*/ \ + this_id: 7, /*set by detect*/ \ + sg_tablesize: 16, /*sg_tablesize*/ \ + cmd_per_lun: 1, /*cmd_per_lun*/ \ + use_clustering: ENABLE_CLUSTERING /*use_clustering*/ \ } #endif /* _IBMMCA_H */ diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/ide-scsi.h linux/drivers/scsi/ide-scsi.h --- v2.1.74/linux/drivers/scsi/ide-scsi.h Fri Dec 19 15:52:59 1997 +++ linux/drivers/scsi/ide-scsi.h Sun Dec 21 17:09:12 1997 @@ -15,28 +15,20 @@ extern int idescsi_reset (Scsi_Cmnd *cmd, unsigned int resetflags); extern int idescsi_bios (Disk *disk, kdev_t dev, int *parm); -#define IDESCSI \ -{ NULL, /* next */ \ - NULL, /* module */ \ - NULL, /* proc_dir */ \ - NULL, /* proc_info */ \ - "idescsi", /* name */ \ - idescsi_detect, /* detect */ \ - idescsi_release, /* release */ \ - idescsi_info, /* info */ \ - NULL, /* command */ \ - idescsi_queue, /* queuecommand */ \ - idescsi_abort, /* abort */ \ - idescsi_reset, /* reset */ \ - NULL, /* slave_attach */ \ - idescsi_bios, /* bios_param */ \ - 10, /* can_queue */ \ - -1, /* this_id */ \ - 256, /* sg_tablesize */ \ - 5, /* cmd_per_lun */ \ - 0, /* present */ \ - 0, /* isa_dma */ \ - DISABLE_CLUSTERING /* clustering */ \ +#define IDESCSI { \ + name: "idescsi", /* name */ \ + detect: idescsi_detect, /* detect */ \ + release: idescsi_release, /* release */ \ + info: idescsi_info, /* info */ \ + queuecommand: idescsi_queue, /* queuecommand */ \ + abort: idescsi_abort, /* abort */ \ + reset: idescsi_reset, /* reset */ \ + bios_param: idescsi_bios, /* bios_param */ \ + can_queue: 10, /* can_queue */ \ + this_id: -1, /* this_id */ \ + sg_tablesize: 256, /* sg_tablesize */ \ + cmd_per_lun: 5, /* cmd_per_lun */ \ + use_clustering: DISABLE_CLUSTERING /* clustering */ \ } #endif /* IDESCSI_H */ diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/in2000.h linux/drivers/scsi/in2000.h --- v2.1.74/linux/drivers/scsi/in2000.h Sun Jul 20 20:41:58 1997 +++ linux/drivers/scsi/in2000.h Sun Dec 21 17:59:19 1997 @@ -392,28 +392,21 @@ #define IN2000_CPL 2 #define IN2000_HOST_ID 7 -#define IN2000 { NULL, /* link 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 */ \ - in2000_detect, /* returns number of in2000's found */ \ - NULL, /* optional unload function for modules */ \ - NULL, /* optional misc info function */ \ - NULL, /* send scsi command, wait for completion */ \ - in2000_queuecommand, /* queue scsi command, don't wait */ \ - in2000_abort, /* abort current command */ \ - in2000_reset, /* reset scsi bus */ \ - NULL, /* slave_attach - unused */ \ - in2000_biosparam, /* figures out BIOS parameters for lilo, etc */ \ - IN2000_CAN_Q, /* max commands we can queue up */ \ - IN2000_HOST_ID, /* host-adapter scsi id */ \ - IN2000_SG, /* scatter-gather table size */ \ - IN2000_CPL, /* commands per lun */ \ - 0, /* board counter */ \ - 0, /* unchecked dma */ \ - DISABLE_CLUSTERING \ - } +#define IN2000 { proc_dir: &proc_scsi_in2000, /* pointer to /proc/scsi directory entry */ \ + proc_info: in2000_proc_info, /* pointer to proc info function */ \ + name: "Always IN2000", /* device name */ \ + detect: in2000_detect, /* returns number of in2000's found */ \ + queuecommand: in2000_queuecommand, /* queue scsi command, don't wait */ \ + abort: in2000_abort, /* abort current command */ \ + reset: in2000_reset, /* reset scsi bus */ \ + bios_param: in2000_biosparam, /* figures out BIOS parameters for lilo, etc */ \ + can_queue: IN2000_CAN_Q, /* max commands we can queue up */ \ + this_id: IN2000_HOST_ID, /* host-adapter scsi id */ \ + sg_tablesize: IN2000_SG, /* scatter-gather table size */ \ + cmd_per_lun: IN2000_CPL, /* commands per lun */ \ + use_clustering: DISABLE_CLUSTERING, \ + use_new_eh_code: 0 /* Enable new error code */ \ + } #endif /* IN2000_H */ diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/mac53c94.h linux/drivers/scsi/mac53c94.h --- v2.1.74/linux/drivers/scsi/mac53c94.h Mon Aug 18 18:19:46 1997 +++ linux/drivers/scsi/mac53c94.h Sun Dec 21 17:04:49 1997 @@ -16,27 +16,18 @@ int mac53c94_reset(Scsi_Cmnd *, unsigned int); #define SCSI_MAC53C94 { \ - NULL, /* next */ \ - NULL, /* usage_count */ \ - &proc_scsi_mac53c94, /* proc_dir */ \ - NULL, /* proc_info */ \ - "53C94", /* name */ \ - mac53c94_detect, /* detect */ \ - NULL, /* release */ \ - NULL, /* info */ \ - mac53c94_command, /* command */ \ - mac53c94_queue, /* queuecommand */ \ - mac53c94_abort, /* abort */ \ - mac53c94_reset, /* reset */ \ - NULL, /* slave_attach */ \ - NULL, /* bios_param */ \ - 1, /* can_queue */ \ - 7, /* this_id */ \ - SG_ALL, /* sg_tablesize */ \ - 1, /* cmd_per_lun */ \ - 0, /* present */ \ - 0, /* unchecked_isa_dma */ \ - DISABLE_CLUSTERING, /* use_clustering */ \ + proc_dir: &proc_scsi_mac53c94, \ + name: "53C94", \ + detect: mac53c94_detect, \ + command: mac53c94_command, \ + queuecommand: mac53c94_queue, \ + abort: mac53c94_abort, \ + reset: mac53c94_reset, \ + can_queue: 1, \ + this_id: 7, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: 1, \ + use_clustering: DISABLE_CLUSTERING, \ } /* diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/mesh.h linux/drivers/scsi/mesh.h --- v2.1.74/linux/drivers/scsi/mesh.h Mon Aug 18 18:19:46 1997 +++ linux/drivers/scsi/mesh.h Sun Dec 21 17:04:49 1997 @@ -16,27 +16,18 @@ int mesh_reset(Scsi_Cmnd *, unsigned int); #define SCSI_MESH { \ - NULL, /* next */ \ - NULL, /* usage_count */ \ - &proc_scsi_mesh, /* proc_dir */ \ - NULL, /* proc_info */ \ - "MESH", /* name */ \ - mesh_detect, /* detect */ \ - NULL, /* release */ \ - NULL, /* info */ \ - mesh_command, /* command */ \ - mesh_queue, /* queuecommand */ \ - mesh_abort, /* abort */ \ - mesh_reset, /* reset */ \ - NULL, /* slave_attach */ \ - NULL, /* bios_param */ \ - 20, /* can_queue */ \ - 7, /* this_id */ \ - SG_ALL, /* sg_tablesize */ \ - 2, /* cmd_per_lun */ \ - 0, /* present */ \ - 0, /* unchecked_isa_dma */ \ - DISABLE_CLUSTERING, /* use_clustering */ \ + proc_dir: &proc_scsi_mesh, \ + name: "MESH", \ + detect: mesh_detect, \ + command: mesh_command, \ + queuecommand: mesh_queue, \ + abort: mesh_abort, \ + reset: mesh_reset, \ + can_queue: 20, \ + this_id: 7, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: 2, \ + use_clustering: DISABLE_CLUSTERING, \ } /* diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/ncr53c8xx.h linux/drivers/scsi/ncr53c8xx.h --- v2.1.74/linux/drivers/scsi/ncr53c8xx.h Wed Sep 24 20:05:47 1997 +++ linux/drivers/scsi/ncr53c8xx.h Sun Dec 21 17:59:19 1997 @@ -290,28 +290,19 @@ #define ncr53c8xx_release NULL #endif -#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0) - -#define NCR53C8XX {NULL,NULL,NULL,NULL,SCSI_NCR_DRIVER_NAME, ncr53c8xx_detect,\ - ncr53c8xx_release, /* info */ NULL, /* command, deprecated */ NULL, \ - ncr53c8xx_queue_command, ncr53c8xx_abort, ncr53c8xx_reset, \ - NULL /* slave attach */, scsicam_bios_param, /* can queue */ SCSI_NCR_CAN_QUEUE,\ - /* id */ 7, SCSI_NCR_SG_TABLESIZE /* SG */, /* cmd per lun */ SCSI_NCR_CMD_PER_LUN, \ - /* present */ 0, /* unchecked isa dma */ 0, DISABLE_CLUSTERING} - - -#else - - -#define NCR53C8XX {NULL, NULL, SCSI_NCR_DRIVER_NAME, ncr53c8xx_detect,\ - ncr53c8xx_release, /* info */ NULL, /* command, deprecated */ NULL, \ - ncr53c8xx_queue_command, ncr53c8xx_abort, ncr53c8xx_reset, \ - NULL /* slave attach */, scsicam_bios_param, /* can queue */ SCSI_NCR_CAN_QUEUE,\ - /* id */ 7, SCSI_NCR_SG_TABLESIZE /* SG */, /* cmd per lun */ SCSI_NCR_CMD_PER_LUN, \ - /* present */ 0, /* unchecked isa dma */ 0, DISABLE_CLUSTERING} - -#endif /* LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0) */ - +#define NCR53C8XX { name: SCSI_NCR_DRIVER_NAME, \ + detect: ncr53c8xx_detect, \ + release: ncr53c8xx_release, \ + queuecommand: ncr53c8xx_queue_command,\ + abort: ncr53c8xx_abort, \ + reset: ncr53c8xx_reset, \ + bios_param: scsicam_bios_param, \ + can_queue: SCSI_NCR_CAN_QUEUE, \ + this_id: 7, \ + sg_tablesize: SCSI_NCR_SG_TABLESIZE, \ + cmd_per_lun: SCSI_NCR_CMD_PER_LUN, \ + use_clustering: DISABLE_CLUSTERING} + #endif /* defined(HOSTS_C) || defined(MODULE) */ diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/pas16.h linux/drivers/scsi/pas16.h --- v2.1.74/linux/drivers/scsi/pas16.h Mon Apr 29 07:14:19 1996 +++ linux/drivers/scsi/pas16.h Sun Dec 21 17:04:49 1997 @@ -142,13 +142,18 @@ #if defined(HOSTS_C) || defined(MODULE) -#define MV_PAS16 {NULL, NULL, NULL, NULL, \ - "Pro Audio Spectrum-16 SCSI", \ - pas16_detect, NULL, NULL, \ - NULL, pas16_queue_command, pas16_abort, pas16_reset, NULL, \ - pas16_biosparam, \ - /* can queue */ CAN_QUEUE, /* id */ 7, SG_ALL, \ - /* cmd per lun */ CMD_PER_LUN , 0, 0, DISABLE_CLUSTERING} +#define MV_PAS16 { \ + name: "Pro Audio Spectrum-16 SCSI", \ + detect: pas16_detect, \ + queuecommand: pas16_queue_command, \ + abort: pas16_abort, \ + reset: pas16_reset, \ + bios_param: pas16_biosparam, \ + can_queue: CAN_QUEUE, \ + this_id: 7, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: CMD_PER_LUN , \ + use_clustering: DISABLE_CLUSTERING} #endif #ifndef HOSTS_C diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/pci2000.h linux/drivers/scsi/pci2000.h --- v2.1.74/linux/drivers/scsi/pci2000.h Wed Nov 12 13:34:26 1997 +++ linux/drivers/scsi/pci2000.h Sun Dec 21 17:04:49 1997 @@ -202,25 +202,18 @@ extern struct proc_dir_entry Proc_Scsi_Pci2000; -#define PCI2000 { NULL, NULL, \ - &Proc_Scsi_Pci2000,/* proc_dir_entry */ \ - NULL, \ - "PCI-2000 SCSI Intelligent Disk Controller",\ - Pci2000_Detect, \ - NULL, \ - NULL, \ - Pci2000_Command, \ - Pci2000_QueueCommand, \ - Pci2000_Abort, \ - Pci2000_Reset, \ - NULL, \ - Pci2000_BiosParam, \ - 16, \ - -1, \ - 16, \ - 1, \ - 0, \ - 0, \ - DISABLE_CLUSTERING } +#define PCI2000 { proc_dir: &Proc_Scsi_Pci2000,/* proc_dir_entry */ \ + name: "PCI-2000 SCSI Intelligent Disk Controller",\ + detect: Pci2000_Detect, \ + command: Pci2000_Command, \ + queuecommand: Pci2000_QueueCommand, \ + abort: Pci2000_Abort, \ + reset: Pci2000_Reset, \ + biosparam: Pci2000_BiosParam, \ + can_queue: 16, \ + this_id: -1, \ + sg_tablesize: 16, \ + cmd_per_lun: 1, \ + use_clustering: DISABLE_CLUSTERING } #endif diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/pci2220i.h linux/drivers/scsi/pci2220i.h --- v2.1.74/linux/drivers/scsi/pci2220i.h Wed Nov 12 13:34:26 1997 +++ linux/drivers/scsi/pci2220i.h Sun Dec 21 17:04:49 1997 @@ -321,25 +321,18 @@ extern struct proc_dir_entry Proc_Scsi_Pci2220i; -#define PCI2220I { NULL, NULL, \ - &Proc_Scsi_Pci2220i,/* proc_dir_entry */ \ - NULL, \ - "PCI-2220I EIDE Disk Controller", \ - Pci2220i_Detect, \ - NULL, \ - NULL, \ - Pci2220i_Command, \ - Pci2220i_QueueCommand, \ - Pci2220i_Abort, \ - Pci2220i_Reset, \ - NULL, \ - Pci2220i_BiosParam, \ - 1, \ - -1, \ - SG_NONE, \ - 1, \ - 0, \ - 0, \ - DISABLE_CLUSTERING } +#define PCI2220I { proc_dir: &Proc_Scsi_Pci2220i,/* proc_dir_entry */ \ + name: "PCI-2220I EIDE Disk Controller",\ + detect: Pci2220i_Detect, \ + command: Pci2220i_Command, \ + queuecommand: Pci2220i_QueueCommand, \ + abort: Pci2220i_Abort, \ + reset: Pci2220i_Reset, \ + biosparam: Pci2220i_BiosParam, \ + can_queue: 1, \ + this_id: -1, \ + sg_tablesize: SG_NONE, \ + cmd_per_lun: 1, \ + use_clustering: DISABLE_CLUSTERING } #endif diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/ppa.h linux/drivers/scsi/ppa.h --- v2.1.74/linux/drivers/scsi/ppa.h Wed Nov 26 16:24:02 1997 +++ linux/drivers/scsi/ppa.h Sun Dec 21 17:59:22 1997 @@ -152,26 +152,19 @@ int ppa_proc_info(char *, char **, off_t, int, int, int); int ppa_biosparam(Disk *, kdev_t, int *); -#define PPA { /* next */ 0, \ - /* usage_count */ 0, \ - /* proc_dir */ &proc_scsi_ppa, \ - /* proc_info */ ppa_proc_info, \ - /* name */ "Iomega parport ZIP drive", \ - /* detect */ ppa_detect, \ - /* release */ ppa_release, \ - /* info */ 0, \ - /* command */ ppa_command, \ - /* queuecommand */ ppa_queuecommand, \ - /* abort */ ppa_abort, \ - /* reset */ ppa_reset, \ - /* slave_attach */ 0, \ - /* bios_param */ ppa_biosparam, \ - /* can_queue */ 0, \ - /* this_id */ -1, \ - /* sg_tablesize */ SG_ALL, \ - /* cmd_per_lun */ 1, \ - /* present */ 0, \ - /* unchecked_isa_dma */ 0, \ - /* use_clustering */ ENABLE_CLUSTERING \ +#define PPA { proc_dir: &proc_scsi_ppa, \ + proc_info: ppa_proc_info, \ + name: "Iomega parport ZIP drive", \ + detect: ppa_detect, \ + release: ppa_release, \ + command: ppa_command, \ + queuecommand: ppa_queuecommand, \ + abort: ppa_abort, \ + reset: ppa_reset, \ + bios_param: ppa_biosparam, \ + this_id: -1, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: 1, \ + use_clustering: ENABLE_CLUSTERING \ } #endif /* _PPA_H */ diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/psi240i.h linux/drivers/scsi/psi240i.h --- v2.1.74/linux/drivers/scsi/psi240i.h Wed Nov 12 13:34:26 1997 +++ linux/drivers/scsi/psi240i.h Sun Dec 21 17:04:49 1997 @@ -320,25 +320,18 @@ extern struct proc_dir_entry Proc_Scsi_Psi240i; -#define PSI240I { NULL, NULL, \ - &Proc_Scsi_Psi240i,/* proc_dir_entry */ \ - NULL, \ - "PSI-240I EIDE Disk Controller", \ - Psi240i_Detect, \ - NULL, \ - NULL, \ - Psi240i_Command, \ - Psi240i_QueueCommand, \ - Psi240i_Abort, \ - Psi240i_Reset, \ - NULL, \ - Psi240i_BiosParam, \ - 1, \ - -1, \ - SG_NONE, \ - 1, \ - 0, \ - 0, \ - DISABLE_CLUSTERING } +#define PSI240I { proc_dir: &Proc_Scsi_Psi240i,/* proc_dir_entry */ \ + name: "PSI-240I EIDE Disk Controller",\ + detect: Psi240i_Detect, \ + command: Psi240i_Command, \ + queuecommand: Psi240i_QueueCommand, \ + abort: Psi240i_Abort, \ + reset: Psi240i_Reset, \ + biosparam: Psi240i_BiosParam, \ + can_queue: 1, \ + this_id: -1, \ + sg_tablesize: SG_NONE, \ + cmd_per_lun: 1, \ + use_clustering: DISABLE_CLUSTERING } #endif diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/qlogicfas.h linux/drivers/scsi/qlogicfas.h --- v2.1.74/linux/drivers/scsi/qlogicfas.h Fri Dec 27 02:03:24 1996 +++ linux/drivers/scsi/qlogicfas.h Sun Dec 21 17:04:49 1997 @@ -14,27 +14,18 @@ #endif #define QLOGICFAS { \ - NULL, \ - NULL, \ - NULL, \ - NULL, \ - NULL, \ - qlogicfas_detect, \ - NULL, \ - qlogicfas_info, \ - qlogicfas_command, \ - qlogicfas_queuecommand, \ - qlogicfas_abort, \ - qlogicfas_reset, \ - NULL, \ - qlogicfas_biosparam, \ - 0, \ - -1, \ - SG_ALL, \ - 1, \ - 0, \ - 0, \ - DISABLE_CLUSTERING \ + detect: qlogicfas_detect, \ + info: qlogicfas_info, \ + command: qlogicfas_command, \ + queuecommand: qlogicfas_queuecommand, \ + abort: qlogicfas_abort, \ + reset: qlogicfas_reset, \ + bios_param: qlogicfas_biosparam, \ + can_queue: 0, \ + this_id: -1, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: 1, \ + use_clustering: DISABLE_CLUSTERING \ } #endif /* _QLOGICFAS_H */ diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/qlogicisp.h linux/drivers/scsi/qlogicisp.h --- v2.1.74/linux/drivers/scsi/qlogicisp.h Sun Jan 26 02:07:18 1997 +++ linux/drivers/scsi/qlogicisp.h Sun Dec 21 17:04:49 1997 @@ -73,27 +73,20 @@ extern struct proc_dir_entry proc_scsi_isp1020; #define QLOGICISP { \ - /* next */ NULL, \ - /* module */ NULL, \ - /* proc dir */ NULL, \ - /* procfs info */ NULL, \ - /* name */ NULL, \ - /* detect */ isp1020_detect, \ - /* release */ isp1020_release, \ - /* info */ isp1020_info, \ - /* command */ NULL, \ - /* queuecommand */ isp1020_queuecommand, \ - /* abort */ isp1020_abort, \ - /* reset */ isp1020_reset, \ - /* slave_attach */ NULL, \ - /* bios_param */ isp1020_biosparam, \ - /* can_queue */ QLOGICISP_REQ_QUEUE_LEN, \ - /* this_id */ -1, \ - /* sg_tablesize */ QLOGICISP_MAX_SG(QLOGICISP_REQ_QUEUE_LEN), \ - /* cmd_per_lun */ 1, \ - /* present */ 0, \ - /* unchecked_isa_dma */ 0, \ - /* use_clustering */ DISABLE_CLUSTERING \ + detect: isp1020_detect, \ + release: isp1020_release, \ + info: isp1020_info, \ + queuecommand: isp1020_queuecommand, \ + abort: isp1020_abort, \ + reset: isp1020_reset, \ + bios_param: isp1020_biosparam, \ + can_queue: QLOGICISP_REQ_QUEUE_LEN, \ + this_id: -1, \ + sg_tablesize: QLOGICISP_MAX_SG(QLOGICISP_REQ_QUEUE_LEN), \ + cmd_per_lun: 1, \ + present: 0, \ + unchecked_isa_dma: 0, \ + use_clustering: DISABLE_CLUSTERING \ } #endif /* _QLOGICISP_H */ diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/qlogicpti.h linux/drivers/scsi/qlogicpti.h --- v2.1.74/linux/drivers/scsi/qlogicpti.h Thu May 29 21:53:08 1997 +++ linux/drivers/scsi/qlogicpti.h Sun Dec 21 17:04:49 1997 @@ -718,28 +718,18 @@ #define HCCTRL_B1ENAB 0x0008 /* Breakpoint 1 enable */ #define HCCTRL_B0ENAB 0x0004 /* Breakpoint 0 enable */ -#define QLOGICPTI { \ - /* next */ NULL, \ - /* module */ NULL, \ - /* proc dir */ NULL, \ - /* procfs info */ NULL, \ - /* name */ NULL, \ - /* detect */ qlogicpti_detect, \ - /* release */ qlogicpti_release, \ - /* info */ qlogicpti_info, \ - /* command */ NULL, \ - /* queuecommand */ qlogicpti_queuecommand, \ - /* abort */ qlogicpti_abort, \ - /* reset */ qlogicpti_reset, \ - /* slave_attach */ NULL, \ - /* bios_param */ NULL, \ - /* can_queue */ QLOGICISP_REQ_QUEUE_LEN, \ - /* this_id */ 7, \ - /* sg_tablesize */ QLOGICISP_MAX_SG(QLOGICISP_REQ_QUEUE_LEN), \ - /* cmd_per_lun */ 1, \ - /* present */ 0, \ - /* unchecked_isa_dma */ 0, \ - /* use_clustering */ DISABLE_CLUSTERING \ +#define QLOGICPTI { \ + detect: qlogicpti_detect, \ + release: qlogicpti_release, \ + info: qlogicpti_info, \ + queuecommand: qlogicpti_queuecommand, \ + abort: qlogicpti_abort, \ + reset: qlogicpti_reset, \ + can_queue: QLOGICISP_REQ_QUEUE_LEN, \ + this_id: 7, \ + sg_tablesize: QLOGICISP_MAX_SG(QLOGICISP_REQ_QUEUE_LEN), \ + cmd_per_lun: 1, \ + use_clustering: DISABLE_CLUSTERING \ } /* For our interrupt engine. */ diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c --- v2.1.74/linux/drivers/scsi/scsi.c Mon Nov 3 13:04:26 1997 +++ linux/drivers/scsi/scsi.c Sun Dec 21 17:49:49 1997 @@ -12,9 +12,8 @@ * Rik Faith * Tommy Thorn * Thomas Wuensche - * Andrea Arcangeli * - * Modified by Eric Youngdale eric@aib.com to + * Modified by Eric Youngdale eric@andante.jic.com or ericy@gnu.ai.mit.edu to * add scatter-gather, multiple outstanding request, and other * enhancements. * @@ -30,12 +29,6 @@ * Leonard N. Zubkoff */ -/* - * Don't import our own symbols, as this would severely mess up our - * symbol tables. - */ -#define _SCSI_SYMS_VER_ - #include #include @@ -51,6 +44,10 @@ #include #include +#define __KERNEL_SYSCALLS__ + +#include + #include #include #include @@ -69,10 +66,9 @@ static const char RCSid[] = "$Header: /vger/u4/cvs/linux/drivers/scsi/scsi.c,v 1.38 1997/01/19 23:07:18 davem Exp $"; */ - -/* Command groups 3 and 4 are reserved and should never be used. */ -const unsigned char scsi_command_size[8] = { 6, 10, 10, 12, 12, 12, 10, 10 }; - +/* + * Definitions and constants. + */ #define INTERNAL_ERROR (panic ("Internal error in file %s, line %d.\n", __FILE__, __LINE__)) /* @@ -90,27 +86,71 @@ # error You lose. #endif -static void scsi_done (Scsi_Cmnd *SCpnt); -int update_timeout (Scsi_Cmnd *, int); -static void print_inquiry(unsigned char *data); -static void scsi_times_out (Scsi_Cmnd * SCpnt); -static int scan_scsis_single (int channel,int dev,int lun,int * max_scsi_dev , - int * sparse_lun, Scsi_Device ** SDpnt, Scsi_Cmnd * SCpnt, - struct Scsi_Host *shpnt, char * scsi_result); -void scsi_build_commandblocks(Scsi_Device * SDpnt); +#define MAX_SCSI_DEVICE_CODE 10 + +#ifdef DEBUG + #define SCSI_TIMEOUT (5*HZ) +#else + #define SCSI_TIMEOUT (2*HZ) +#endif + +#define MIN_RESET_DELAY (2*HZ) + +/* Do not call reset on error if we just did a reset within 15 sec. */ +#define MIN_RESET_PERIOD (15*HZ) + +/* The following devices are known not to tolerate a lun != 0 scan for + * one reason or another. Some will respond to all luns, others will + * lock up. + */ + +#define BLIST_NOLUN 0x01 +#define BLIST_FORCELUN 0x02 +#define BLIST_BORKEN 0x04 +#define BLIST_KEY 0x08 +#define BLIST_SINGLELUN 0x10 +#define BLIST_NOTQ 0x20 +#define BLIST_SPARSELUN 0x40 +/* + * Data declarations. + */ +unsigned long scsi_pid = 0; +Scsi_Cmnd * last_cmnd = NULL; +/* Command groups 3 and 4 are reserved and should never be used. */ +const unsigned char scsi_command_size[8] = { 6, 10, 10, 12, + 12, 12, 10, 10 }; +static unsigned long serial_number = 0; +static Scsi_Cmnd * scsi_bh_queue_head = NULL; static FreeSectorBitmap * dma_malloc_freelist = NULL; -static int scsi_need_isa_bounce_buffers; -static unsigned int dma_sectors = 0; -unsigned int dma_free_sectors = 0; -unsigned int need_isa_buffer = 0; -static unsigned char ** dma_malloc_pages = NULL; +static int need_isa_bounce_buffers; +static unsigned int dma_sectors = 0; +unsigned int scsi_dma_free_sectors = 0; +unsigned int scsi_need_isa_buffer = 0; +static unsigned char ** dma_malloc_pages = NULL; + +/* + * Note - the initial logging level can be set here to log events at boot time. + * After the system is up, you may enable logging via the /proc interface. + */ +unsigned int scsi_logging_level = 0; -static int time_start; -static int time_elapsed; static volatile struct Scsi_Host * host_active = NULL; -#define SCSI_BLOCK(HOST) ((HOST->block && host_active && HOST != host_active) \ - || (HOST->can_queue && HOST->host_busy >= HOST->can_queue)) + +#if CONFIG_PROC_FS +/* + * This is the pointer to the /proc/scsi code. + * It is only initialized to !=0 if the scsi code is present + */ +struct proc_dir_entry proc_scsi_scsi = { + PROC_SCSI_SCSI, 4, "scsi", + S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0, 0, + NULL, + NULL, NULL, + NULL, NULL, NULL +}; +#endif + const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] = { @@ -126,92 +166,49 @@ "Communications " }; - -/* - * global variables : - * scsi_devices an array of these specifying the address for each - * (host, id, LUN) +/* + * Function prototypes. */ - -Scsi_Device * scsi_devices = NULL; - -/* Process ID of SCSI commands */ -unsigned long scsi_pid = 0; - -static unsigned long serial_number = 0; - -static unsigned char generic_sense[6] = {REQUEST_SENSE, 0,0,0, 255, 0}; static void resize_dma_pool(void); +static void print_inquiry(unsigned char *data); +extern void scsi_times_out (Scsi_Cmnd * SCpnt); +static int scan_scsis_single (int channel,int dev,int lun,int * max_scsi_dev , + int * sparse_lun, Scsi_Device ** SDpnt, Scsi_Cmnd * SCpnt, + struct Scsi_Host *shpnt, char * scsi_result); +void scsi_build_commandblocks(Scsi_Device * SDpnt); -/* This variable is merely a hook so that we can debug the kernel with gdb. */ -Scsi_Cmnd * last_cmnd = NULL; - -/* This is the pointer to the /proc/scsi code. - * It is only initialized to !=0 if the scsi code is present +/* + * These are the interface to the old error handling code. It should go away + * someday soon. */ +extern void scsi_old_done (Scsi_Cmnd *SCpnt); +extern void scsi_old_times_out (Scsi_Cmnd * SCpnt); + #if CONFIG_PROC_FS extern int (* dispatch_scsi_info_ptr)(int ino, char *buffer, char **start, off_t offset, int length, int inout); extern int dispatch_scsi_info(int ino, char *buffer, char **start, off_t offset, int length, int inout); - -struct proc_dir_entry proc_scsi_scsi = { - PROC_SCSI_SCSI, 4, "scsi", - S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0, 0, - NULL, - NULL, NULL, - NULL, NULL, NULL -}; #endif +#define SCSI_BLOCK(DEVICE, HOST) \ + ((HOST->block && host_active && HOST != host_active) \ + || ((HOST)->can_queue && HOST->host_busy >= HOST->can_queue) \ + || ((HOST)->host_blocked) \ + || ((DEVICE) != NULL && (DEVICE)->device_blocked) ) + +static void scsi_dump_status(int level); + + /* * This is the number of clock ticks we should wait before we time out * and abort the command. This is for where the scsi.c module generates * the command, not where it originates from a higher level, in which * case the timeout is specified there. * - * ABORT_TIMEOUT and RESET_TIMEOUT are the timeouts for RESET and ABORT - * respectively. */ -#ifdef DEBUG_TIMEOUT -static void scsi_dump_status(void); -#endif - - -#ifdef DEBUG - #define SCSI_TIMEOUT (5*HZ) -#else - #define SCSI_TIMEOUT (2*HZ) -#endif - -#ifdef DEBUG - #define SENSE_TIMEOUT SCSI_TIMEOUT - #define ABORT_TIMEOUT SCSI_TIMEOUT - #define RESET_TIMEOUT SCSI_TIMEOUT -#else - #define SENSE_TIMEOUT (5*HZ/10) - #define RESET_TIMEOUT (5*HZ/10) - #define ABORT_TIMEOUT (5*HZ/10) -#endif - -#define MIN_RESET_DELAY (2*HZ) - -/* Do not call reset on error if we just did a reset within 15 sec. */ -#define MIN_RESET_PERIOD (15*HZ) - -/* The following devices are known not to tolerate a lun != 0 scan for - * one reason or another. Some will respond to all luns, others will - * lock up. - */ -#define BLIST_NOLUN 0x01 -#define BLIST_FORCELUN 0x02 -#define BLIST_BORKEN 0x04 -#define BLIST_KEY 0x08 -#define BLIST_SINGLELUN 0x10 -#define BLIST_NOTQ 0x20 -#define BLIST_SPARSELUN 0x40 struct dev_info{ const char * vendor; @@ -284,6 +281,7 @@ {"PIONEER","CD-ROM DRM-604X","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"EMULEX","MD21/S2 ESDI","*", BLIST_SINGLELUN}, {"CANON","IPUBJD","*", BLIST_SPARSELUN}, +{"nCipher","Fastness Crypto","*", BLIST_FORCELUN}, {"MATSHITA","PD","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"YAMAHA","CDR100","1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ {"YAMAHA","CDR102","1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ @@ -311,7 +309,31 @@ return 0; } -void scsi_make_blocked_list(void) { +/* + * Function: scsi_make_blocked_list + * + * Purpose: Build linked list of hosts that require blocking. + * + * Arguments: None. + * + * Returns: Nothing + * + * Notes: Blocking is sort of a hack that is used to prevent more than one + * host adapter from being active at one time. This is used in cases + * where the ISA bus becomes unreliable if you have more than one + * host adapter really pumping data through. + * + * We spent a lot of time examining the problem, and I *believe* that + * the problem is bus related as opposed to being a driver bug. + * + * The blocked list is used as part of the synchronization object + * that we use to ensure that only one host is active at one time. + * I (ERY) would like to make this go away someday, but this would + * require that we have a recursive mutex object. + */ +void +scsi_make_blocked_list(void) +{ int block_count = 0, index; unsigned long flags; struct Scsi_Host * sh[128], * shpnt; @@ -373,22 +395,31 @@ static void scan_scsis_done (Scsi_Cmnd * SCpnt) { -#ifdef DEBUG - printk ("scan_scsis_done(%p, %06x)\n", SCpnt->host, SCpnt->result); -#endif + SCSI_LOG_MLCOMPLETE(1,printk ("scan_scsis_done(%p, %06x)\n", SCpnt->host, SCpnt->result)); SCpnt->request.rq_status = RQ_SCSI_DONE; if (SCpnt->request.sem != NULL) up(SCpnt->request.sem); } +void scsi_logging_setup(char *str, int *ints) +{ + if (ints[0] != 1) { + printk("scsi_logging_setup : usage scsi_logging_level=n " + "(n should be 0 or non-zero)\n"); + } else { + scsi_logging_level = (ints[1])? ~0 : 0; + } +} + #ifdef CONFIG_SCSI_MULTI_LUN static int max_scsi_luns = 8; #else static int max_scsi_luns = 1; #endif -void scsi_luns_setup(char *str, int *ints) { +void scsi_luns_setup(char *str, int *ints) +{ if (ints[0] != 1) printk("scsi_luns_setup : usage max_scsi_luns=n (n should be between 1 and 8)\n"); else @@ -402,37 +433,72 @@ * lun address of all sequential devices to the tape driver, all random * devices to the disk driver. */ -static void scan_scsis (struct Scsi_Host *shpnt, unchar hardcoded, - unchar hchannel, unchar hid, unchar hlun) -{ - int dev, lun, channel; - unsigned char scsi_result0[256]; - unsigned char *scsi_result; - Scsi_Device *SDpnt; - int max_dev_lun, sparse_lun; - Scsi_Cmnd *SCpnt; +static void scan_scsis (struct Scsi_Host *shpnt, + unchar hardcoded, + unchar hchannel, + unchar hid, + unchar hlun) +{ + int channel; + int dev; + int lun; + int max_dev_lun; + Scsi_Cmnd * SCpnt; + unsigned char * scsi_result; + unsigned char scsi_result0[256]; + Scsi_Device * SDpnt; + Scsi_Device * SDtail; + int sparse_lun; SCpnt = (Scsi_Cmnd *) scsi_init_malloc (sizeof (Scsi_Cmnd), GFP_ATOMIC | GFP_DMA); - SDpnt = (Scsi_Device *) scsi_init_malloc (sizeof (Scsi_Device), GFP_ATOMIC); memset (SCpnt, 0, sizeof (Scsi_Cmnd)); + SDpnt = (Scsi_Device *) scsi_init_malloc (sizeof (Scsi_Device), GFP_ATOMIC); + memset (SDpnt, 0, sizeof (Scsi_Device)); + /* Make sure we have something that is valid for DMA purposes */ scsi_result = ( ( !shpnt->unchecked_isa_dma ) ? &scsi_result0[0] : scsi_init_malloc (512, GFP_DMA)); - if (scsi_result == NULL) { - printk ("Unable to obtain scsi_result buffer\n"); - goto leave; + if (scsi_result == NULL) + { + printk ("Unable to obtain scsi_result buffer\n"); + goto leave; } - /* We must chain ourself in the host_queue, so commands can time out */ - if(shpnt->host_queue) - shpnt->host_queue->prev = SCpnt; - SCpnt->next = shpnt->host_queue; - SCpnt->prev = NULL; - shpnt->host_queue = SCpnt; + /* + * We must chain ourself in the host_queue, so commands can time out + */ + SCpnt->next = NULL; + SDpnt->device_queue = SCpnt; + SDpnt->host = shpnt; + SDpnt->online = TRUE; + + /* + * Next, hook the device to the host in question. + */ + SDpnt->prev = NULL; + SDpnt->next = NULL; + if( shpnt->host_queue != NULL ) + { + SDtail = shpnt->host_queue; + while( SDtail->next != NULL ) + SDtail = SDtail->next; + + SDtail->next = SDpnt; + SDpnt->prev = SDtail; + } + else + { + shpnt->host_queue = SDpnt; + } + /* + * We need to increment the counter for this one device so we can track when + * things are quiet. + */ + atomic_inc(&shpnt->host_active); if (hardcoded == 1) { Scsi_Device *oldSDpnt=SDpnt; @@ -490,20 +556,30 @@ } /* for channel ends */ } /* if/else hardcoded */ + /* + * We need to decrement the counter for this one device + * so we know when everything is quiet. + */ + atomic_dec(&shpnt->host_active); + leave: {/* Unchain SCpnt from host_queue */ - Scsi_Cmnd *prev, *next, *hqptr; - for(hqptr = shpnt->host_queue; hqptr != SCpnt; hqptr = hqptr->next) ; - if(hqptr) { - prev = hqptr->prev; - next = hqptr->next; - if(prev) - prev->next = next; - else - shpnt->host_queue = next; - if(next) next->prev = prev; - } + Scsi_Device *prev, *next; + Scsi_Device * dqptr; + + for(dqptr = shpnt->host_queue; dqptr != SDpnt; dqptr = dqptr->next) + continue; + if(dqptr) + { + prev = dqptr->prev; + next = dqptr->next; + if(prev) + prev->next = next; + else + shpnt->host_queue = next; + if(next) next->prev = prev; + } } /* Last device block does not exist. Free memory. */ @@ -515,8 +591,25 @@ /* If we allocated a buffer so we could do DMA, free it now */ if (scsi_result != &scsi_result0[0] && scsi_result != NULL) - scsi_init_free (scsi_result, 512); + { + scsi_init_free (scsi_result, 512); + } + + { + Scsi_Device * sdev; + Scsi_Cmnd * scmd; + SCSI_LOG_SCAN_BUS(4,printk("Host status for host %p:\n", shpnt)); + for(sdev = shpnt->host_queue; sdev; sdev = sdev->next) + { + SCSI_LOG_SCAN_BUS(4,printk("Device %d %p: ", sdev->id, sdev)); + for(scmd=sdev->device_queue; scmd; scmd = scmd->next) + { + SCSI_LOG_SCAN_BUS(4,printk("%p ", scmd)); + } + SCSI_LOG_SCAN_BUS(4,printk("\n", scmd)); + } + } } /* @@ -533,16 +626,11 @@ Scsi_Device * SDtail, *SDpnt=*SDpnt2; int bflags, type=-1; - SDtail = scsi_devices; - if (scsi_devices) - while (SDtail->next) - SDtail = SDtail->next; - - memset (SDpnt, 0, sizeof (Scsi_Device)); SDpnt->host = shpnt; SDpnt->id = dev; SDpnt->lun = lun; SDpnt->channel = channel; + SDpnt->online = TRUE; /* Some low level driver could use device->type (DB) */ SDpnt->type = -1; @@ -572,14 +660,14 @@ (void *) scsi_result, 256, scan_scsis_done, SCSI_TIMEOUT + 4 * HZ, 5); down (&sem); + SCpnt->request.sem = NULL; } -#if defined(DEBUG) || defined(DEBUG_INIT) - printk ("scsi: scan_scsis_single id %d lun %d. Return code 0x%08x\n", - dev, lun, SCpnt->result); - print_driverbyte(SCpnt->result); print_hostbyte(SCpnt->result); - printk("\n"); -#endif + SCSI_LOG_SCAN_BUS(3, printk ("scsi: scan_scsis_single id %d lun %d. Return code 0x%08x\n", + dev, lun, SCpnt->result)); + SCSI_LOG_SCAN_BUS(3,print_driverbyte(SCpnt->result)); + SCSI_LOG_SCAN_BUS(3,print_hostbyte(SCpnt->result)); + SCSI_LOG_SCAN_BUS(3,printk("\n")); if (SCpnt->result) { if (((driver_byte (SCpnt->result) & DRIVER_SENSE) || @@ -594,9 +682,7 @@ return 0; } -#if defined (DEBUG) || defined(DEBUG_INIT) - printk ("scsi: performing INQUIRY\n"); -#endif + SCSI_LOG_SCAN_BUS(3,printk ("scsi: performing INQUIRY\n")); /* * Build an INQUIRY command block. */ @@ -615,12 +701,11 @@ (void *) scsi_result, 256, scan_scsis_done, SCSI_TIMEOUT, 3); down (&sem); + SCpnt->request.sem = NULL; } -#if defined(DEBUG) || defined(DEBUG_INIT) - printk ("scsi: INQUIRY %s with code 0x%x\n", - SCpnt->result ? "failed" : "successful", SCpnt->result); -#endif + SCSI_LOG_SCAN_BUS(3,printk ("scsi: INQUIRY %s with code 0x%x\n", + SCpnt->result ? "failed" : "successful", SCpnt->result)); if (SCpnt->result) return 0; /* assume no peripheral if any sort of error */ @@ -649,6 +734,7 @@ memcpy (SDpnt->rev, scsi_result + 32, 4); SDpnt->removable = (0x80 & scsi_result[1]) >> 7; + SDpnt->online = TRUE; SDpnt->lockable = SDpnt->removable; SDpnt->changed = 0; SDpnt->access_count = 0; @@ -675,6 +761,8 @@ printk ("scsi: unknown type %d\n", type); } + SDpnt->device_blocked = FALSE; + SDpnt->device_busy = 0; SDpnt->single_lun = 0; SDpnt->soft_reset = (scsi_result[7] & 1) && ((scsi_result[3] & 7) == 2); @@ -728,6 +816,13 @@ SDpnt->borken = 0; /* + * If we want to only allow I/O to one of the luns attached to this device + * at a time, then we set this flag. + */ + if (bflags & BLIST_SINGLELUN) + SDpnt->single_lun = 1; + + /* * These devices need this "key" to unlock the devices so we can use it */ if ((bflags & BLIST_KEY) != 0) { @@ -748,33 +843,61 @@ (void *) scsi_result, 0x2a, scan_scsis_done, SCSI_TIMEOUT, 3); down (&sem); + SCpnt->request.sem = NULL; } } - /* Add this device to the linked list at the end */ - if (SDtail) - SDtail->next = SDpnt; - else - scsi_devices = SDpnt; - SDtail = SDpnt; + /* + * Detach the command from the device. It was just a temporary to be used while + * scanning the bus - the real ones will be allocated later. + */ + SDpnt->device_queue = NULL; + + /* + * This device was already hooked up to the host in question, + * so at this point we just let go of it and it should be fine. We do need to + * allocate a new one and attach it to the host so that we can further scan the bus. + */ SDpnt = (Scsi_Device *) scsi_init_malloc (sizeof (Scsi_Device), GFP_ATOMIC); *SDpnt2=SDpnt; if (!SDpnt) - printk ("scsi: scan_scsis_single: Cannot malloc\n"); + { + printk ("scsi: scan_scsis_single: Cannot malloc\n"); + return 0; + } + memset (SDpnt, 0, sizeof (Scsi_Device)); /* - * Some scsi devices cannot be polled for lun != 0 due to firmware bugs + * And hook up our command block to the new device we will be testing + * for. */ - if (bflags & BLIST_NOLUN) - return 0; /* break; */ + SDpnt->device_queue = SCpnt; + SDpnt->online = TRUE; /* - * If we want to only allow I/O to one of the luns attached to this device - * at a time, then we set this flag. + * Since we just found one device, there had damn well better be one in the list + * already. */ - if (bflags & BLIST_SINGLELUN) - SDpnt->single_lun = 1; + if( shpnt->host_queue == NULL ) + panic("scan_scsis_single: Host queue == NULL\n"); + + SDtail = shpnt->host_queue; + while (SDtail->next) + { + SDtail = SDtail->next; + } + + /* Add this device to the linked list at the end */ + SDtail->next = SDpnt; + SDpnt->prev = SDtail; + SDpnt->next = NULL; + + /* + * Some scsi devices cannot be polled for lun != 0 due to firmware bugs + */ + if (bflags & BLIST_NOLUN) + return 0; /* break; */ /* * If this device is known to support sparse multiple units, override the @@ -816,68 +939,6 @@ #define IN_RESET2 4 #define IN_RESET3 8 -/* - * This is our time out function, called when the timer expires for a - * given host adapter. It will attempt to abort the currently executing - * command, that failing perform a kernel panic. - */ - -static void scsi_times_out (Scsi_Cmnd * SCpnt) -{ - - switch (SCpnt->internal_timeout & (IN_ABORT | IN_RESET | IN_RESET2 | IN_RESET3)) - { - case NORMAL_TIMEOUT: - { -#ifdef DEBUG_TIMEOUT - scsi_dump_status(); -#endif - } - - if (!scsi_abort (SCpnt, DID_TIME_OUT)) - return; - case IN_ABORT: - printk("SCSI host %d abort (pid %ld) timed out - resetting\n", - SCpnt->host->host_no, SCpnt->pid); - if (!scsi_reset (SCpnt, SCSI_RESET_ASYNCHRONOUS)) - return; - case IN_RESET: - case (IN_ABORT | IN_RESET): - /* This might be controversial, but if there is a bus hang, - * you might conceivably want the machine up and running - * esp if you have an ide disk. - */ - printk("SCSI host %d channel %d reset (pid %ld) timed out - " - "trying harder\n", - SCpnt->host->host_no, SCpnt->channel, SCpnt->pid); - SCpnt->internal_timeout &= ~IN_RESET; - SCpnt->internal_timeout |= IN_RESET2; - scsi_reset (SCpnt, - SCSI_RESET_ASYNCHRONOUS | SCSI_RESET_SUGGEST_BUS_RESET); - return; - case (IN_ABORT | IN_RESET | IN_RESET2): - /* Obviously the bus reset didn't work. - * Let's try even harder and call for an HBA reset. - * Maybe the HBA itself crashed and this will shake it loose. - */ - printk("SCSI host %d reset (pid %ld) timed out - trying to shake it loose\n", - SCpnt->host->host_no, SCpnt->pid); - SCpnt->internal_timeout &= ~(IN_RESET | IN_RESET2); - SCpnt->internal_timeout |= IN_RESET3; - scsi_reset (SCpnt, - SCSI_RESET_ASYNCHRONOUS | SCSI_RESET_SUGGEST_HOST_RESET); - return; - - default: - printk("SCSI host %d reset (pid %ld) timed out again -\n", - SCpnt->host->host_no, SCpnt->pid); - printk("probably an unrecoverable SCSI bus or device hang.\n"); - return; - - } - -} - /* This function takes a quick look at a request, and decides if it * can be queued now, or if there would be a stall while waiting for @@ -886,7 +947,7 @@ * of the calling code to ensure that this is the case. */ -Scsi_Cmnd * request_queueable (struct request * req, Scsi_Device * device) +Scsi_Cmnd * scsi_request_queueable (struct request * req, Scsi_Device * device) { Scsi_Cmnd * SCpnt = NULL; int tablesize; @@ -894,10 +955,10 @@ struct buffer_head * bh, *bhp; if (!device) - panic ("No device passed to request_queueable().\n"); + panic ("No device passed to scsi_request_queueable().\n"); if (req && req->rq_status == RQ_INACTIVE) - panic("Inactive in request_queueable"); + panic("Inactive in scsi_request_queueable"); /* * Look for a free command block. If we have been instructed not to queue @@ -909,10 +970,10 @@ SCpnt = device->device_queue; while(SCpnt){ if(SCpnt->request.rq_status == RQ_INACTIVE) break; - SCpnt = SCpnt->device_next; + SCpnt = SCpnt->next; } } else { - SCpnt = device->host->host_queue; + SCpnt = device->device_queue; while(SCpnt){ if(SCpnt->channel == device->channel && SCpnt->target == device->id) { @@ -939,7 +1000,7 @@ if (!SCpnt) return NULL; - if (SCSI_BLOCK(device->host)) return NULL; + if (SCSI_BLOCK(device, device->host)) return NULL; if (req) { memcpy(&SCpnt->request, req, sizeof(struct request)); @@ -979,6 +1040,9 @@ * either */ } + atomic_inc(&SCpnt->host->host_active); + SCSI_LOG_MLQUEUE(5, printk("Activating command for device %d (%d)\n", SCpnt->target, + atomic_read(&SCpnt->host->host_active))); SCpnt->use_sg = 0; /* Reset the scatter-gather flag */ SCpnt->old_use_sg = 0; SCpnt->transfersize = 0; @@ -991,6 +1055,8 @@ SCpnt->channel = device->channel; SCpnt->lun = device->lun; SCpnt->target = device->id; + SCpnt->state = SCSI_STATE_INITIALIZING; + SCpnt->owner = SCSI_OWNER_HIGHLEVEL; return SCpnt; } @@ -1001,11 +1067,11 @@ * not to return a descriptor if the host is unable to accept any more * commands for the time being. We need to keep in mind that there is no * guarantee that the host remain not busy. Keep in mind the - * request_queueable function also knows the internal allocation scheme + * scsi_request_queueable function also knows the internal allocation scheme * of the packets for each device */ -Scsi_Cmnd * allocate_device (struct request ** reqp, Scsi_Device * device, +Scsi_Cmnd * scsi_allocate_device (struct request ** reqp, Scsi_Device * device, int wait) { kdev_t dev; @@ -1019,7 +1085,7 @@ Scsi_Cmnd * found = NULL; if (!device) - panic ("No device passed to allocate_device().\n"); + panic ("No device passed to scsi_allocate_device().\n"); if (reqp) req = *reqp; @@ -1032,7 +1098,7 @@ host = device->host; - if (in_interrupt() && SCSI_BLOCK(host)) return NULL; + if (in_interrupt() && SCSI_BLOCK(device, host)) return NULL; while (1==1){ if (!device->single_lun) { @@ -1040,10 +1106,10 @@ while(SCpnt){ SCwait = SCpnt; if(SCpnt->request.rq_status == RQ_INACTIVE) break; - SCpnt = SCpnt->device_next; + SCpnt = SCpnt->next; } } else { - SCpnt = device->host->host_queue; + SCpnt = device->device_queue; while(SCpnt){ if(SCpnt->channel == device->channel && SCpnt->target == device->id) { @@ -1080,7 +1146,6 @@ } if (!SCpnt || SCpnt->request.rq_status != RQ_INACTIVE) /* Might have changed */ { -#if 1 /* NEW CODE */ if (wait && SCwait && SCwait->request.rq_status != RQ_INACTIVE){ sleep_on(&device->device_wait); restore_flags(flags); @@ -1091,21 +1156,9 @@ printk("Attempt to allocate device channel %d," " target %d, lun %d\n", device->channel, device->id, device->lun); - panic("No device found in allocate_device\n"); + panic("No device found in scsi_allocate_device\n"); } } -#else /* ORIGINAL CODE */ - restore_flags(flags); - if(!wait) return NULL; - if (!SCwait) { - printk("Attempt to allocate device channel %d, target" - " %d, lun %d\n", device->channel, device->id, - device->lun); - panic("No device found in allocate_device\n"); - } - SCSI_SLEEP(&device->device_wait, - (SCwait->request.rq_status != RQ_INACTIVE)); -#endif } else { if (req) { memcpy(&SCpnt->request, req, sizeof(struct request)); @@ -1146,7 +1199,11 @@ SCpnt->request.sem = NULL; /* And no one is waiting for this * to complete */ } + atomic_inc(&SCpnt->host->host_active); restore_flags(flags); + SCSI_LOG_MLQUEUE(5, printk("Activating command for device %d (%d)\n", + SCpnt->target, + atomic_read(&SCpnt->host->host_active))); break; } } @@ -1160,25 +1217,74 @@ /* Since not everyone seems to set the device info correctly * before Scsi_Cmnd gets send out to scsi_do_command, we do it here. + * FIXME(eric) This doesn't make any sense. */ SCpnt->channel = device->channel; SCpnt->lun = device->lun; SCpnt->target = device->id; + SCpnt->state = SCSI_STATE_INITIALIZING; + SCpnt->owner = SCSI_OWNER_HIGHLEVEL; return SCpnt; } /* + * Function: scsi_release_command + * + * Purpose: Release a command block. + * + * Arguments: SCpnt - command block we are releasing. + * + * Notes: The command block can no longer be used by the caller once + * this funciton is called. This is in effect the inverse + * of scsi_allocate_device/scsi_request_queueable. + */ +void +scsi_release_command(Scsi_Cmnd * SCpnt) +{ + SCpnt->request.rq_status = RQ_INACTIVE; + SCpnt->state = SCSI_STATE_UNUSED; + SCpnt->owner = SCSI_OWNER_NOBODY; + atomic_dec(&SCpnt->host->host_active); + + SCSI_LOG_MLQUEUE(5, printk("Deactivating command for device %d (active=%d, failed=%d)\n", + SCpnt->target, + atomic_read(&SCpnt->host->host_active), + SCpnt->host->host_failed)); + if( SCpnt->host->host_failed != 0 ) + { + SCSI_LOG_ERROR_RECOVERY(5, printk("Error handler thread %d %d\n", + SCpnt->host->in_recovery, + SCpnt->host->eh_active)); + } + + /* + * If the host is having troubles, then look to see if this was the last + * command that might have failed. If so, wake up the error handler. + */ + if( SCpnt->host->in_recovery + && !SCpnt->host->eh_active + && atomic_read(&SCpnt->host->host_active) == SCpnt->host->host_failed ) + { + SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler thread (%d)\n", + atomic_read(&SCpnt->host->eh_wait->count))); + up(SCpnt->host->eh_wait); + } +} + +/* * This is inline because we have stack problemes if we recurse to deeply. */ -inline void internal_cmnd (Scsi_Cmnd * SCpnt) +inline int internal_cmnd (Scsi_Cmnd * SCpnt) { - unsigned long flags, timeout; - struct Scsi_Host * host; #ifdef DEBUG_DELAY unsigned long clock; #endif + unsigned long flags; + struct Scsi_Host * host; + int rtn = 0; + unsigned long timeout; #if DEBUG unsigned long *ret = 0; @@ -1219,25 +1325,31 @@ } restore_flags(flags); - update_timeout(SCpnt, SCpnt->timeout_per_command); + if( host->hostt->use_new_eh_code ) + { + scsi_add_timer(SCpnt, SCpnt->timeout_per_command, scsi_times_out); + } + else + { + scsi_add_timer(SCpnt, SCpnt->timeout_per_command, + scsi_old_times_out); + } /* * We will use a queued command if possible, otherwise we will emulate the * queuing and calling of completion function ourselves. */ -#ifdef DEBUG - printk("internal_cmnd (host = %d, channel = %d, target = %d, " + SCSI_LOG_MLQUEUE(3,printk("internal_cmnd (host = %d, channel = %d, target = %d, " "command = %p, buffer = %p, \nbufflen = %d, done = %p)\n", SCpnt->host->host_no, SCpnt->channel, SCpnt->target, SCpnt->cmnd, - SCpnt->buffer, SCpnt->bufflen, SCpnt->done); -#endif + SCpnt->buffer, SCpnt->bufflen, SCpnt->done)); + SCpnt->state = SCSI_STATE_QUEUED; + SCpnt->owner = SCSI_OWNER_LOWLEVEL; if (host->can_queue) { -#ifdef DEBUG - printk("queuecommand : routine at %p\n", - host->hostt->queuecommand); -#endif + SCSI_LOG_MLQUEUE(3,printk("queuecommand : routine at %p\n", + host->hostt->queuecommand)); /* This locking tries to prevent all sorts of races between * queuecommand and the interrupt code. In effect, * we are only allowed to be in queuecommand once at @@ -1250,7 +1362,23 @@ if(!in_interrupt() && SCpnt->host->irq) disable_irq(SCpnt->host->irq); - host->hostt->queuecommand (SCpnt, scsi_done); + /* + * Use the old error handling code if we haven't converted the driver + * to use the new one yet. Note - only the new queuecommand variant + * passes a meaningful return value. + */ + if( host->hostt->use_new_eh_code ) + { + rtn = host->hostt->queuecommand (SCpnt, scsi_done); + if( rtn != 0 ) + { + scsi_mlqueue_insert(SCpnt, SCSI_MLQUEUE_HOST_BUSY); + } + } + else + { + host->hostt->queuecommand (SCpnt, scsi_old_done); + } if(!in_interrupt() && SCpnt->host->irq) enable_irq(SCpnt->host->irq); @@ -1259,9 +1387,7 @@ { int temp; -#ifdef DEBUG - printk("command() : routine at %p\n", host->hostt->command); -#endif + SCSI_LOG_MLQUEUE(3,printk("command() : routine at %p\n", host->hostt->command)); temp = host->hostt->command (SCpnt); SCpnt->result = temp; #ifdef DEBUG_DELAY @@ -1270,39 +1396,19 @@ printk("done(host = %d, result = %04x) : routine at %p\n", host->host_no, temp, host->hostt->command); #endif - scsi_done(SCpnt); + if( host->hostt->use_new_eh_code ) + { + scsi_done(SCpnt); + } + else + { + scsi_old_done(SCpnt); + } } -#ifdef DEBUG - printk("leaving internal_cmnd()\n"); -#endif + SCSI_LOG_MLQUEUE(3,printk("leaving internal_cmnd()\n")); + return rtn; } -static void scsi_request_sense (Scsi_Cmnd * SCpnt) -{ - unsigned long flags; - - save_flags(flags); - cli(); - SCpnt->flags |= WAS_SENSE | ASKED_FOR_SENSE; - update_timeout(SCpnt, SENSE_TIMEOUT); - restore_flags(flags); - - - memcpy ((void *) SCpnt->cmnd , (void *) generic_sense, - sizeof(generic_sense)); - - SCpnt->cmnd[1] = SCpnt->lun << 5; - SCpnt->cmnd[4] = sizeof(SCpnt->sense_buffer); - - SCpnt->request_buffer = &SCpnt->sense_buffer; - SCpnt->request_bufflen = sizeof(SCpnt->sense_buffer); - SCpnt->use_sg = 0; - SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); - internal_cmnd (SCpnt); -} - - - /* * scsi_do_cmd sends all the commands out to the low-level driver. It * handles the specifics required for each low level driver - ie queued @@ -1316,8 +1422,11 @@ { unsigned long flags; struct Scsi_Host * host = SCpnt->host; + Scsi_Device * device = SCpnt->device; -#ifdef DEBUG + SCpnt->owner = SCSI_OWNER_MIDLEVEL; + +SCSI_LOG_MLQUEUE(4, { int i; int target = SCpnt->target; @@ -1329,8 +1438,7 @@ for (i = 0; i < 10; ++i) printk ("%02x ", ((unsigned char *) cmnd)[i]); printk("\n"); - } -#endif + }); if (!host) { @@ -1350,15 +1458,16 @@ cli(); SCpnt->pid = scsi_pid++; - while (SCSI_BLOCK(host)) { + while (SCSI_BLOCK((Scsi_Device *) NULL, host)) { restore_flags(flags); - SCSI_SLEEP(&host->host_wait, SCSI_BLOCK(host)); + SCSI_SLEEP(&host->host_wait, SCSI_BLOCK((Scsi_Device *) NULL, host)); cli(); } if (host->block) host_active = host; host->host_busy++; + device->device_busy++; restore_flags(flags); /* @@ -1369,12 +1478,6 @@ */ memcpy ((void *) SCpnt->data_cmnd , (const void *) cmnd, 12); -#if 0 - SCpnt->host = host; - SCpnt->channel = channel; - SCpnt->target = target; - SCpnt->lun = (SCpnt->data_cmnd[1] >> 5); -#endif SCpnt->reset_chain = NULL; SCpnt->serial_number = 0; SCpnt->bufflen = bufflen; @@ -1403,967 +1506,321 @@ SCpnt->abort_reason = 0; internal_cmnd (SCpnt); -#ifdef DEBUG - printk ("Leaving scsi_do_cmd()\n"); -#endif -} - -static int check_sense (Scsi_Cmnd * SCpnt) -{ - /* If there is no sense information, request it. If we have already - * requested it, there is no point in asking again - the firmware must - * be confused. - */ - if (((SCpnt->sense_buffer[0] & 0x70) >> 4) != 7) { - if(!(SCpnt->flags & ASKED_FOR_SENSE)) - return SUGGEST_SENSE; - else - return SUGGEST_RETRY; - } - - SCpnt->flags &= ~ASKED_FOR_SENSE; - -#ifdef DEBUG_INIT - printk("scsi%d, channel%d : ", SCpnt->host->host_no, SCpnt->channel); - print_sense("", SCpnt); - printk("\n"); -#endif - if (SCpnt->sense_buffer[2] & 0xe0) - return SUGGEST_ABORT; - - switch (SCpnt->sense_buffer[2] & 0xf) - { - case NO_SENSE: - return 0; - case RECOVERED_ERROR: - return SUGGEST_IS_OK; - - case ABORTED_COMMAND: - return SUGGEST_RETRY; - case NOT_READY: - case UNIT_ATTENTION: - /* - * If we are expecting a CC/UA because of a bus reset that we - * performed, treat this just as a retry. Otherwise this is - * information that we should pass up to the upper-level driver - * so that we can deal with it there. - */ - if( SCpnt->device->expecting_cc_ua ) - { - SCpnt->device->expecting_cc_ua = 0; - return SUGGEST_RETRY; - } - return SUGGEST_ABORT; - - /* these three are not supported */ - case COPY_ABORTED: - case VOLUME_OVERFLOW: - case MISCOMPARE: - - case MEDIUM_ERROR: - return SUGGEST_REMAP; - case BLANK_CHECK: - case DATA_PROTECT: - case HARDWARE_ERROR: - case ILLEGAL_REQUEST: - default: - return SUGGEST_ABORT; - } + SCSI_LOG_MLQUEUE(3,printk ("Leaving scsi_do_cmd()\n")); } /* This function is the mid-level interrupt routine, which decides how * to handle error conditions. Each invocation of this function must * do one and *only* one of the following: * - * (1) Call last_cmnd[host].done. This is done for fatal errors and - * normal completion, and indicates that the handling for this - * request is complete. - * (2) Call internal_cmnd to requeue the command. This will result in - * scsi_done being called again when the retry is complete. - * (3) Call scsi_request_sense. This asks the host adapter/drive for - * more information about the error condition. When the information - * is available, scsi_done will be called again. - * (4) Call reset(). This is sort of a last resort, and the idea is that - * this may kick things loose and get the drive working again. reset() - * automatically calls scsi_request_sense, and thus scsi_done will be - * called again once the reset is complete. - * - * If none of the above actions are taken, the drive in question - * will hang. If more than one of the above actions are taken by - * scsi_done, then unpredictable behavior will result. - */ -static void scsi_done (Scsi_Cmnd * SCpnt) -{ - int status=0; - int exit=0; - int checked; - int oldto; - struct Scsi_Host * host = SCpnt->host; - int result = SCpnt->result; - SCpnt->serial_number = 0; - oldto = update_timeout(SCpnt, 0); - -#ifdef DEBUG_TIMEOUT - if(result) printk("Non-zero result in scsi_done %x %d:%d\n", - result, SCpnt->target, SCpnt->lun); -#endif - - /* If we requested an abort, (and we got it) then fix up the return - * status to say why - */ - if(host_byte(result) == DID_ABORT && SCpnt->abort_reason) - SCpnt->result = result = (result & 0xff00ffff) | - (SCpnt->abort_reason << 16); - - -#define FINISHED 0 -#define MAYREDO 1 -#define REDO 3 -#define PENDING 4 - -#ifdef DEBUG - printk("In scsi_done(host = %d, result = %06x)\n", host->host_no, result); -#endif - - if(SCpnt->flags & WAS_SENSE) - { - SCpnt->use_sg = SCpnt->old_use_sg; - SCpnt->cmd_len = SCpnt->old_cmd_len; - } - - switch (host_byte(result)) - { - case DID_OK: - if (status_byte(result) && (SCpnt->flags & WAS_SENSE)) - /* Failed to obtain sense information */ - { - SCpnt->flags &= ~WAS_SENSE; -#if 0 /* This cannot possibly be correct. */ - SCpnt->internal_timeout &= ~SENSE_TIMEOUT; -#endif - - if (!(SCpnt->flags & WAS_RESET)) - { - printk("scsi%d : channel %d target %d lun %d request sense" - " failed, performing reset.\n", - SCpnt->host->host_no, SCpnt->channel, SCpnt->target, - SCpnt->lun); - scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS); - return; - } - else - { - exit = (DRIVER_HARD | SUGGEST_ABORT); - status = FINISHED; - } - } - else switch(msg_byte(result)) - { - case COMMAND_COMPLETE: - switch (status_byte(result)) - { - case GOOD: - if (SCpnt->flags & WAS_SENSE) - { -#ifdef DEBUG - printk ("In scsi_done, GOOD status, COMMAND COMPLETE, " - "parsing sense information.\n"); -#endif - SCpnt->flags &= ~WAS_SENSE; -#if 0 /* This cannot possibly be correct. */ - SCpnt->internal_timeout &= ~SENSE_TIMEOUT; -#endif - - switch (checked = check_sense(SCpnt)) - { - case SUGGEST_SENSE: - case 0: -#ifdef DEBUG - printk("NO SENSE. status = REDO\n"); -#endif - update_timeout(SCpnt, oldto); - status = REDO; - break; - case SUGGEST_IS_OK: - break; - case SUGGEST_REMAP: -#ifdef DEBUG - printk("SENSE SUGGEST REMAP - status = FINISHED\n"); -#endif - status = FINISHED; - exit = DRIVER_SENSE | SUGGEST_ABORT; - break; - case SUGGEST_RETRY: -#ifdef DEBUG - printk("SENSE SUGGEST RETRY - status = MAYREDO\n"); -#endif - status = MAYREDO; - exit = DRIVER_SENSE | SUGGEST_RETRY; - break; - case SUGGEST_ABORT: -#ifdef DEBUG - printk("SENSE SUGGEST ABORT - status = FINISHED"); -#endif - status = FINISHED; - exit = DRIVER_SENSE | SUGGEST_ABORT; - break; - default: - printk ("Internal error %s %d \n", __FILE__, - __LINE__); - } - } /* end WAS_SENSE */ - else - { -#ifdef DEBUG - printk("COMMAND COMPLETE message returned, " - "status = FINISHED. \n"); -#endif - exit = DRIVER_OK; - status = FINISHED; - } - break; - - case CHECK_CONDITION: - case COMMAND_TERMINATED: - switch (check_sense(SCpnt)) - { - case 0: - update_timeout(SCpnt, oldto); - status = REDO; - break; - case SUGGEST_REMAP: - status = FINISHED; - exit = DRIVER_SENSE | SUGGEST_ABORT; - break; - case SUGGEST_RETRY: - status = MAYREDO; - exit = DRIVER_SENSE | SUGGEST_RETRY; - break; - case SUGGEST_ABORT: - status = FINISHED; - exit = DRIVER_SENSE | SUGGEST_ABORT; - break; - case SUGGEST_SENSE: - scsi_request_sense (SCpnt); - status = PENDING; - break; - } - break; - - case CONDITION_GOOD: - case INTERMEDIATE_GOOD: - case INTERMEDIATE_C_GOOD: - break; - - case BUSY: - case QUEUE_FULL: - update_timeout(SCpnt, oldto); - status = REDO; - break; - - case RESERVATION_CONFLICT: - printk("scsi%d, channel %d : RESERVATION CONFLICT performing" - " reset.\n", SCpnt->host->host_no, SCpnt->channel); - scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS); - return; -#if 0 - exit = DRIVER_SOFT | SUGGEST_ABORT; - status = MAYREDO; - break; -#endif - default: - printk ("Internal error %s %d \n" - "status byte = %d \n", __FILE__, - __LINE__, status_byte(result)); - - } - break; - default: - panic("scsi: unsupported message byte %d received\n", - msg_byte(result)); - } - break; - case DID_TIME_OUT: -#ifdef DEBUG - printk("Host returned DID_TIME_OUT - "); -#endif - - if (SCpnt->flags & WAS_TIMEDOUT) - { -#ifdef DEBUG - printk("Aborting\n"); -#endif - /* - Allow TEST_UNIT_READY and INQUIRY commands to timeout early - without causing resets. All other commands should be retried. - */ - if (SCpnt->cmnd[0] != TEST_UNIT_READY && - SCpnt->cmnd[0] != INQUIRY) - status = MAYREDO; - exit = (DRIVER_TIMEOUT | SUGGEST_ABORT); - } - else - { -#ifdef DEBUG - printk ("Retrying.\n"); -#endif - SCpnt->flags |= WAS_TIMEDOUT; - SCpnt->internal_timeout &= ~IN_ABORT; - status = REDO; - } - break; - case DID_BUS_BUSY: - case DID_PARITY: - status = REDO; - break; - case DID_NO_CONNECT: -#ifdef DEBUG - printk("Couldn't connect.\n"); -#endif - exit = (DRIVER_HARD | SUGGEST_ABORT); - break; - case DID_ERROR: - status = MAYREDO; - exit = (DRIVER_HARD | SUGGEST_ABORT); - break; - case DID_BAD_TARGET: - case DID_ABORT: - exit = (DRIVER_INVALID | SUGGEST_ABORT); - break; - case DID_RESET: - if (SCpnt->flags & IS_RESETTING) - { - SCpnt->flags &= ~IS_RESETTING; - status = REDO; - break; - } - - if(msg_byte(result) == GOOD && - status_byte(result) == CHECK_CONDITION) { - switch (check_sense(SCpnt)) { - case 0: - update_timeout(SCpnt, oldto); - status = REDO; - break; - case SUGGEST_REMAP: - case SUGGEST_RETRY: - status = MAYREDO; - exit = DRIVER_SENSE | SUGGEST_RETRY; - break; - case SUGGEST_ABORT: - status = FINISHED; - exit = DRIVER_SENSE | SUGGEST_ABORT; - break; - case SUGGEST_SENSE: - scsi_request_sense (SCpnt); - status = PENDING; - break; - } - } else { - status=REDO; - exit = SUGGEST_RETRY; - } - break; - default : - exit = (DRIVER_ERROR | SUGGEST_DIE); - } - - switch (status) - { - case FINISHED: - case PENDING: - break; - case MAYREDO: -#ifdef DEBUG - printk("In MAYREDO, allowing %d retries, have %d\n", - SCpnt->allowed, SCpnt->retries); -#endif - if ((++SCpnt->retries) < SCpnt->allowed) - { - if ((SCpnt->retries >= (SCpnt->allowed >> 1)) - && !(SCpnt->host->last_reset > 0 && - jiffies < SCpnt->host->last_reset + MIN_RESET_PERIOD) - && !(SCpnt->flags & WAS_RESET)) - { - printk("scsi%d channel %d : resetting for second half of retries.\n", - SCpnt->host->host_no, SCpnt->channel); - scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS); - break; - } - - } - else - { - status = FINISHED; - break; - } - /* fall through to REDO */ - - case REDO: - - if (SCpnt->flags & WAS_SENSE) - scsi_request_sense(SCpnt); - else - { - memcpy ((void *) SCpnt->cmnd, - (void*) SCpnt->data_cmnd, - sizeof(SCpnt->data_cmnd)); - SCpnt->request_buffer = SCpnt->buffer; - SCpnt->request_bufflen = SCpnt->bufflen; - SCpnt->use_sg = SCpnt->old_use_sg; - SCpnt->cmd_len = SCpnt->old_cmd_len; - internal_cmnd (SCpnt); - } - break; - default: - INTERNAL_ERROR; - } - - if (status == FINISHED) { -#ifdef DEBUG - printk("Calling done function - at address %p\n", SCpnt->done); -#endif - host->host_busy--; /* Indicate that we are free */ - - if (host->block && host->host_busy == 0) { - host_active = NULL; - - /* For block devices "wake_up" is done in end_scsi_request */ - if (MAJOR(SCpnt->request.rq_dev) != SCSI_DISK_MAJOR && - MAJOR(SCpnt->request.rq_dev) != SCSI_CDROM_MAJOR) { - struct Scsi_Host * next; - - for (next = host->block; next != host; next = next->block) - wake_up(&next->host_wait); - } - - } - - wake_up(&host->host_wait); - SCpnt->result = result | ((exit & 0xff) << 24); - SCpnt->use_sg = SCpnt->old_use_sg; - SCpnt->cmd_len = SCpnt->old_cmd_len; - SCpnt->done (SCpnt); - } - -#undef FINISHED -#undef REDO -#undef MAYREDO -#undef PENDING -} - -/* - * The scsi_abort function interfaces with the abort() function of the host - * we are aborting, and causes the current command to not complete. The - * caller should deal with any error messages or status returned on the - * next call. + * 1) Insert command in BH queue. + * 2) Activate error handler for host. * - * This will not be called reentrantly for a given host. - */ - -/* - * Since we're nice guys and specified that abort() and reset() - * can be non-reentrant. The internal_timeout flags are used for - * this. + * FIXME(eric) - I am concerned about stack overflow (still). An interrupt could + * come while we are processing the bottom queue, which would cause another command + * to be stuffed onto the bottom queue, and it would in turn be processed as that + * interrupt handler is returning. Given a sufficiently steady rate of returning + * commands, this could cause the stack to overflow. I am not sure what is the most + * appropriate solution here - we should probably keep a depth count, and not process + * any commands while we still have a bottom handler active higher in the stack. + * + * There is currently code in the bottom half handler to monitor recursion in the bottom + * handler and report if it ever happens. If this becomes a problem, it won't be hard to + * engineer something to deal with it so that only the outer layer ever does any real + * processing. */ - - -int scsi_abort (Scsi_Cmnd * SCpnt, int why) +void +scsi_done (Scsi_Cmnd * SCpnt) { - int oldto; - unsigned long flags; - struct Scsi_Host * host = SCpnt->host; - - while(1) - { - save_flags(flags); - cli(); - - /* - * Protect against races here. If the command is done, or we are - * on a different command forget it. - */ - if (SCpnt->serial_number != SCpnt->serial_number_at_timeout) { - restore_flags(flags); - return 0; - } - - if (SCpnt->internal_timeout & IN_ABORT) - { - restore_flags(flags); - while (SCpnt->internal_timeout & IN_ABORT) - barrier(); - } - else - { - SCpnt->internal_timeout |= IN_ABORT; - oldto = update_timeout(SCpnt, ABORT_TIMEOUT); + unsigned long flags; + Scsi_Cmnd * SCswap; - if ((SCpnt->flags & IS_RESETTING) && SCpnt->device->soft_reset) { - /* OK, this command must have died when we did the - * reset. The device itself must have lied. - */ - printk("Stale command on %d %d:%d appears to have died when" - " the bus was reset\n", - SCpnt->channel, SCpnt->target, SCpnt->lun); - } + /* + * We don't have to worry about this one timing out any more. + */ + scsi_delete_timer(SCpnt); - restore_flags(flags); - if (!host->host_busy) { - SCpnt->internal_timeout &= ~IN_ABORT; - update_timeout(SCpnt, oldto); - return 0; - } - printk("scsi : aborting command due to timeout : pid %lu, scsi%d," - " channel %d, id %d, lun %d ", - SCpnt->pid, SCpnt->host->host_no, (int) SCpnt->channel, - (int) SCpnt->target, (int) SCpnt->lun); - print_command (SCpnt->cmnd); - if (SCpnt->serial_number != SCpnt->serial_number_at_timeout) - return 0; - SCpnt->abort_reason = why; - switch(host->hostt->abort(SCpnt)) { - /* We do not know how to abort. Try waiting another - * time increment and see if this helps. Set the - * WAS_TIMEDOUT flag set so we do not try this twice - */ - case SCSI_ABORT_BUSY: /* Tough call - returning 1 from - * this is too severe - */ - case SCSI_ABORT_SNOOZE: - if(why == DID_TIME_OUT) { - save_flags(flags); - cli(); - SCpnt->internal_timeout &= ~IN_ABORT; - if(SCpnt->flags & WAS_TIMEDOUT) { - restore_flags(flags); - return 1; /* Indicate we cannot handle this. - * We drop down into the reset handler - * and try again - */ - } else { - SCpnt->flags |= WAS_TIMEDOUT; - oldto = SCpnt->timeout_per_command; - update_timeout(SCpnt, oldto); - } - restore_flags(flags); - } - return 0; - case SCSI_ABORT_PENDING: - if(why != DID_TIME_OUT) { - save_flags(flags); - cli(); - update_timeout(SCpnt, oldto); - restore_flags(flags); - } - return 0; - case SCSI_ABORT_SUCCESS: - /* We should have already aborted this one. No - * need to adjust timeout - */ - SCpnt->internal_timeout &= ~IN_ABORT; - return 0; - case SCSI_ABORT_NOT_RUNNING: - SCpnt->internal_timeout &= ~IN_ABORT; - update_timeout(SCpnt, 0); - return 0; - case SCSI_ABORT_ERROR: - default: - SCpnt->internal_timeout &= ~IN_ABORT; - return 1; - } - } + /* + * First, see whether this command already timed out. If so, we ignore + * the response. We treat it as if the command never finished. + */ + if( SCpnt->state == SCSI_STATE_TIMEOUT ) + { + SCSI_LOG_MLCOMPLETE(1,printk("Ignoring completion of %p due to timeout status", SCpnt)); + return; } -} + SCpnt->state = SCSI_STATE_BHQUEUE; + SCpnt->owner = SCSI_OWNER_BH_HANDLER; + SCpnt->bh_next = NULL; -/* Mark a single SCSI Device as having been reset. */ + /* + * Next, put this command in the BH queue. All processing of the command + * past this point will take place with interrupts turned on. + * We start by atomicly swapping the pointer into the queue head slot. + * If it was NULL before, then everything is fine, and we are done + * (this is the normal case). If it was not NULL, then we block interrupts, + * and link them together. + */ + + SCswap = (Scsi_Cmnd *) xchg(&scsi_bh_queue_head, SCpnt); + if( SCswap != NULL ) + { + /* + * If we assume that the interrupt handler doesn't dawdle, then it is safe to + * say that we should come in here extremely rarely. Under very heavy load, + * the requests might not be removed from the list fast enough so that we + * *do* end up stacking them, and that would be bad. + */ + save_flags(flags); + cli(); + + /* + * See if the pointer is NULL - it might have been serviced already + */ + if( scsi_bh_queue_head == NULL ) + { + scsi_bh_queue_head = SCswap; + } + else + { + SCswap->bh_next = scsi_bh_queue_head; + scsi_bh_queue_head = SCswap; + } + + restore_flags(flags); + } -static inline void scsi_mark_device_reset(Scsi_Device *Device) -{ - Device->was_reset = 1; - Device->expecting_cc_ua = 1; + /* + * Mark the bottom half handler to be run. + */ + mark_bh(SCSI_BH); } +/* + * Procedure: scsi_bottom_half_handler + * + * Purpose: Called after we have finished processing interrupts, it + * performs post-interrupt handling for commands that may + * have completed. + * + * Notes: This is called with all interrupts enabled. This should reduce + * interrupt latency, stack depth, and reentrancy of the low-level + * drivers. + */ +void scsi_bottom_half_handler(void) +{ + Scsi_Cmnd * SCpnt; + Scsi_Cmnd * SCnext; + static atomic_t recursion_depth; -/* Mark all SCSI Devices on a specific Host as having been reset. */ - -void scsi_mark_host_reset(struct Scsi_Host *Host) -{ - Scsi_Cmnd *SCpnt; - for (SCpnt = Host->host_queue; SCpnt; SCpnt = SCpnt->next) - scsi_mark_device_reset(SCpnt->device); -} + + while(1==1) + { + /* + * If the counter is > 0, that means that there is another interrupt handler + * out there somewhere processing commands. We don't want to get these guys + * nested as this can lead to stack overflow problems, and there isn't any + * real sense in it anyways. + */ + if( atomic_read(&recursion_depth) > 0 ) + { + printk("SCSI bottom half recursion depth = %d \n", atomic_read(&recursion_depth)); + SCSI_LOG_MLCOMPLETE(1,printk("SCSI bottom half recursion depth = %d \n", + atomic_read(&recursion_depth))); + break; + } + + /* + * This is an atomic operation - swap the pointer with a NULL pointer + * We will process everything we find in the list here. + */ + SCpnt = xchg(&scsi_bh_queue_head, NULL); + + if( SCpnt == NULL ) + { + return; + } + + atomic_inc(&recursion_depth); + + SCnext = SCpnt->bh_next; + + for(; SCpnt; SCpnt = SCnext) + { + SCnext = SCpnt->bh_next; + + switch( scsi_decide_disposition(SCpnt) ) + { + case SUCCESS: + /* + * Add to BH queue. + */ + SCSI_LOG_MLCOMPLETE(3,printk("Command finished %d %d 0x%x\n", SCpnt->host->host_busy, + SCpnt->host->host_failed, + SCpnt->result)); + + scsi_finish_command(SCpnt); + break; + case NEEDS_RETRY: + /* + * We only come in here if we want to retry a command. The + * test to see whether the command should be retried should be + * keeping track of the number of tries, so we don't end up looping, + * of course. + */ + SCSI_LOG_MLCOMPLETE(3,printk("Command needs retry %d %d 0x%x\n", SCpnt->host->host_busy, + SCpnt->host->host_failed, SCpnt->result)); + + scsi_retry_command(SCpnt); + break; + case ADD_TO_MLQUEUE: + /* + * This typically happens for a QUEUE_FULL message - + * typically only when the queue depth is only + * approximate for a given device. Adding a command + * to the queue for the device will prevent further commands + * from being sent to the device, so we shouldn't end up + * with tons of things being sent down that shouldn't be. + */ + scsi_mlqueue_insert(SCpnt, SCSI_MLQUEUE_DEVICE_BUSY); + break; + default: + /* + * Here we have a fatal error of some sort. Turn it over to + * the error handler. + */ + SCSI_LOG_MLCOMPLETE(3,printk("Command failed %p %x active=%d busy=%d failed=%d\n", + SCpnt, SCpnt->result, + atomic_read(&SCpnt->host->host_active), + SCpnt->host->host_busy, + SCpnt->host->host_failed)); + + /* + * Dump the sense information too. + */ + if ((status_byte (SCpnt->result) & CHECK_CONDITION) != 0) + { + SCSI_LOG_MLCOMPLETE(3,print_sense("bh",SCpnt)); + } + + + if( SCpnt->host->eh_wait != NULL ) + { + SCpnt->host->host_failed++; + SCpnt->owner = SCSI_OWNER_ERROR_HANDLER; + SCpnt->state = SCSI_STATE_FAILED; + SCpnt->host->in_recovery = 1; + /* + * If the host is having troubles, then look to see if this was the last + * command that might have failed. If so, wake up the error handler. + */ + if( atomic_read(&SCpnt->host->host_active) == SCpnt->host->host_failed ) + { + SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler thread (%d)\n", + atomic_read(&SCpnt->host->eh_wait->count))); + up(SCpnt->host->eh_wait); + } + } + else + { + /* + * We only get here if the error recovery thread has died. + */ + scsi_finish_command(SCpnt); + } + } + } /* for(; SCpnt...) */ + atomic_dec(&recursion_depth); -/* Mark all SCSI Devices on a specific Host Bus as having been reset. */ + } /* while(1==1) */ -void scsi_mark_bus_reset(struct Scsi_Host *Host, int channel) -{ - Scsi_Cmnd *SCpnt; - for (SCpnt = Host->host_queue; SCpnt; SCpnt = SCpnt->next) - if (SCpnt->channel == channel) - scsi_mark_device_reset(SCpnt->device); } - -int scsi_reset (Scsi_Cmnd * SCpnt, unsigned int reset_flags) -{ - int temp; - unsigned long flags; - Scsi_Cmnd * SCpnt1; - struct Scsi_Host * host = SCpnt->host; - - printk("SCSI bus is being reset for host %d channel %d.\n", - host->host_no, SCpnt->channel); - -#if 0 - /* - * First of all, we need to make a recommendation to the low-level - * driver as to whether a BUS_DEVICE_RESET should be performed, - * or whether we should do a full BUS_RESET. There is no simple - * algorithm here - we basically use a series of heuristics - * to determine what we should do. - */ - SCpnt->host->suggest_bus_reset = FALSE; - - /* - * First see if all of the active devices on the bus have - * been jammed up so that we are attempting resets. If so, - * then suggest a bus reset. Forcing a bus reset could - * result in some race conditions, but no more than - * you would usually get with timeouts. We will cross - * that bridge when we come to it. - * - * This is actually a pretty bad idea, since a sequence of - * commands will often timeout together and this will cause a - * Bus Device Reset followed immediately by a SCSI Bus Reset. - * If all of the active devices really are jammed up, the - * Bus Device Reset will quickly timeout and scsi_times_out - * will follow up with a SCSI Bus Reset anyway. - */ - SCpnt1 = host->host_queue; - while(SCpnt1) { - if( SCpnt1->request.rq_status != RQ_INACTIVE - && (SCpnt1->flags & (WAS_RESET | IS_RESETTING)) == 0 ) - break; - SCpnt1 = SCpnt1->next; - } - if( SCpnt1 == NULL ) { - reset_flags |= SCSI_RESET_SUGGEST_BUS_RESET; - } - - /* - * If the code that called us is suggesting a hard reset, then - * definitely request it. This usually occurs because a - * BUS_DEVICE_RESET times out. - * - * Passing reset_flags along takes care of this automatically. - */ - if( reset_flags & SCSI_RESET_SUGGEST_BUS_RESET ) { - SCpnt->host->suggest_bus_reset = TRUE; - } -#endif - - while (1) { - save_flags(flags); - cli(); - - /* - * Protect against races here. If the command is done, or we are - * on a different command forget it. - */ - if (reset_flags & SCSI_RESET_ASYNCHRONOUS) - if (SCpnt->serial_number != SCpnt->serial_number_at_timeout) { - restore_flags(flags); - return 0; - } - - if (SCpnt->internal_timeout & IN_RESET) - { - restore_flags(flags); - while (SCpnt->internal_timeout & IN_RESET) - barrier(); - } - else - { - SCpnt->internal_timeout |= IN_RESET; - update_timeout(SCpnt, RESET_TIMEOUT); - - if (host->host_busy) - { - restore_flags(flags); - SCpnt1 = host->host_queue; - while(SCpnt1) { - if (SCpnt1->request.rq_status != RQ_INACTIVE) { -#if 0 - if (!(SCpnt1->flags & IS_RESETTING) && - !(SCpnt1->internal_timeout & IN_ABORT)) - scsi_abort(SCpnt1, DID_RESET); -#endif - SCpnt1->flags |= (WAS_RESET | IS_RESETTING); - } - SCpnt1 = SCpnt1->next; - } - - host->last_reset = jiffies; - temp = host->hostt->reset(SCpnt, reset_flags); - /* - This test allows the driver to introduce an additional bus - settle time delay by setting last_reset up to 20 seconds in - the future. In the normal case where the driver does not - modify last_reset, it must be assumed that the actual bus - reset occurred immediately prior to the return to this code, - and so last_reset must be updated to the current time, so - that the delay in internal_cmnd will guarantee at least a - MIN_RESET_DELAY bus settle time. - */ - if (host->last_reset - jiffies > 20UL * HZ) - host->last_reset = jiffies; - } - else - { - if (!host->block) host->host_busy++; - restore_flags(flags); - host->last_reset = jiffies; - SCpnt->flags |= (WAS_RESET | IS_RESETTING); - temp = host->hostt->reset(SCpnt, reset_flags); - if ((host->last_reset < jiffies) || - (host->last_reset > (jiffies + 20 * HZ))) - host->last_reset = jiffies; - if (!host->block) host->host_busy--; - } - -#ifdef DEBUG - printk("scsi reset function returned %d\n", temp); -#endif - - /* - * Now figure out what we need to do, based upon - * what the low level driver said that it did. - * If the result is SCSI_RESET_SUCCESS, SCSI_RESET_PENDING, - * or SCSI_RESET_WAKEUP, then the low level driver did a - * bus device reset or bus reset, so we should go through - * and mark one or all of the devices on that bus - * as having been reset. - */ - switch(temp & SCSI_RESET_ACTION) { - case SCSI_RESET_SUCCESS: - if (temp & SCSI_RESET_HOST_RESET) - scsi_mark_host_reset(host); - else if (temp & SCSI_RESET_BUS_RESET) - scsi_mark_bus_reset(host, SCpnt->channel); - else scsi_mark_device_reset(SCpnt->device); - save_flags(flags); - cli(); - SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3); - restore_flags(flags); - return 0; - case SCSI_RESET_PENDING: - if (temp & SCSI_RESET_HOST_RESET) - scsi_mark_host_reset(host); - else if (temp & SCSI_RESET_BUS_RESET) - scsi_mark_bus_reset(host, SCpnt->channel); - else scsi_mark_device_reset(SCpnt->device); - case SCSI_RESET_NOT_RUNNING: - return 0; - case SCSI_RESET_PUNT: - SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3); - scsi_request_sense (SCpnt); - return 0; - case SCSI_RESET_WAKEUP: - if (temp & SCSI_RESET_HOST_RESET) - scsi_mark_host_reset(host); - else if (temp & SCSI_RESET_BUS_RESET) - scsi_mark_bus_reset(host, SCpnt->channel); - else scsi_mark_device_reset(SCpnt->device); - SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3); - scsi_request_sense (SCpnt); - /* - * If a bus reset was performed, we - * need to wake up each and every command - * that was active on the bus or if it was a HBA - * reset all active commands on all channels - */ - if( temp & SCSI_RESET_HOST_RESET ) - { - SCpnt1 = host->host_queue; - while(SCpnt1) { - if (SCpnt1->request.rq_status != RQ_INACTIVE - && SCpnt1 != SCpnt) - scsi_request_sense (SCpnt1); - SCpnt1 = SCpnt1->next; - } - } else if( temp & SCSI_RESET_BUS_RESET ) { - SCpnt1 = host->host_queue; - while(SCpnt1) { - if(SCpnt1->request.rq_status != RQ_INACTIVE - && SCpnt1 != SCpnt - && SCpnt1->channel == SCpnt->channel) - scsi_request_sense (SCpnt); - SCpnt1 = SCpnt1->next; - } - } - return 0; - case SCSI_RESET_SNOOZE: - /* In this case, we set the timeout field to 0 - * so that this command does not time out any more, - * and we return 1 so that we get a message on the - * screen. - */ - save_flags(flags); - cli(); - SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3); - update_timeout(SCpnt, 0); - restore_flags(flags); - /* If you snooze, you lose... */ - case SCSI_RESET_ERROR: - default: - return 1; - } - - return temp; - } - } -} - - -static void scsi_main_timeout(void) +/* + * Function: scsi_retry_command + * + * Purpose: Send a command back to the low level to be retried. + * + * Notes: This command is always executed in the context of the + * bottom half handler, or the error handler thread. Low + * level drivers should not become re-entrant as a result of + * this. + */ +int +scsi_retry_command(Scsi_Cmnd * SCpnt) { - /* - * We must not enter update_timeout with a timeout condition still pending. - */ - - int timed_out; - unsigned long flags; - struct Scsi_Host * host; - Scsi_Cmnd * SCpnt = NULL; - - save_flags(flags); - cli(); - - update_timeout(NULL, 0); - - /* - * Find all timers such that they have 0 or negative (shouldn't happen) - * time remaining on them. - */ - timed_out = 0; - for (host = scsi_hostlist; host; host = host->next) { - for (SCpnt = host->host_queue; SCpnt; SCpnt = SCpnt->next) - if (SCpnt->timeout == -1) - { - SCpnt->timeout = 0; - SCpnt->serial_number_at_timeout = SCpnt->serial_number; - ++timed_out; - } - } - if (timed_out > 0) { - for (host = scsi_hostlist; host; host = host->next) { - for (SCpnt = host->host_queue; SCpnt; SCpnt = SCpnt->next) - if (SCpnt->serial_number_at_timeout > 0 && - SCpnt->serial_number_at_timeout == SCpnt->serial_number) - { - restore_flags(flags); - scsi_times_out(SCpnt); - SCpnt->serial_number_at_timeout = 0; - cli(); - } - } - } - restore_flags(flags); + memcpy ((void *) SCpnt->cmnd, (void*) SCpnt->data_cmnd, + sizeof(SCpnt->data_cmnd)); + SCpnt->request_buffer = SCpnt->buffer; + SCpnt->request_bufflen = SCpnt->bufflen; + SCpnt->use_sg = SCpnt->old_use_sg; + SCpnt->cmd_len = SCpnt->old_cmd_len; + return internal_cmnd (SCpnt); } /* - * The strategy is to cause the timer code to call scsi_times_out() - * when the soonest timeout is pending. - * The arguments are used when we are queueing a new command, because - * we do not want to subtract the time used from this time, but when we - * set the timer, we want to take this value into account. + * Function: scsi_finish_command + * + * Purpose: Pass command off to upper layer for finishing of I/O + * request, waking processes that are waiting on results, + * etc. */ - -int update_timeout(Scsi_Cmnd * SCset, int timeout) +void +scsi_finish_command(Scsi_Cmnd * SCpnt) { - unsigned int least, used; - unsigned int oldto; - unsigned long flags; struct Scsi_Host * host; - Scsi_Cmnd * SCpnt = NULL; + Scsi_Device * device; - save_flags(flags); - cli(); - - oldto = 0; + host = SCpnt->host; + device = SCpnt->device; - /* - * This routine can be a performance bottleneck under high loads, since - * it is called twice per SCSI operation: once when internal_cmnd is - * called, and again when scsi_done completes the command. To limit - * the load this routine can cause, we shortcut processing if no clock - * ticks have occurred since the last time it was called. - */ - - if (jiffies == time_start && timer_table[SCSI_TIMER].expires > 0) { - if(SCset){ - oldto = SCset->timeout; - SCset->timeout = timeout; - if (timeout > 0 && - jiffies + timeout < timer_table[SCSI_TIMER].expires) - timer_table[SCSI_TIMER].expires = jiffies + timeout; - } - restore_flags(flags); - return oldto; + host->host_busy--; /* Indicate that we are free */ + device->device_busy--; /* Decrement device usage counter. */ + + if (host->block && host->host_busy == 0) + { + host_active = NULL; + + /* For block devices "wake_up" is done in end_scsi_request */ + if (MAJOR(SCpnt->request.rq_dev) != SCSI_DISK_MAJOR && + MAJOR(SCpnt->request.rq_dev) != SCSI_CDROM_MAJOR) { + struct Scsi_Host * next; + + for (next = host->block; next != host; next = next->block) + wake_up(&next->host_wait); + } + } - - /* - * Figure out how much time has passed since the last time the timeouts - * were updated - */ - used = (time_start) ? (jiffies - time_start) : 0; - + /* - * Find out what is due to timeout soonest, and adjust all timeouts for - * the amount of time that has passed since the last time we called - * update_timeout. + * Now try and drain the mid-level queue if any commands have been + * inserted. Check to see whether the queue even has anything in + * it first, as otherwise this is useless overhead. */ - - oldto = 0; - - if(SCset){ - oldto = SCset->timeout - used; - SCset->timeout = timeout; + if( SCpnt->host->pending_commands != NULL ) + { + scsi_mlqueue_finish(SCpnt->host, SCpnt->device); } - least = 0xffffffff; - - for(host = scsi_hostlist; host; host = host->next) - for(SCpnt = host->host_queue; SCpnt; SCpnt = SCpnt->next) - if (SCpnt->timeout > 0) { - if (SCpnt != SCset) - SCpnt->timeout -= used; - if(SCpnt->timeout <= 0) SCpnt->timeout = -1; - if(SCpnt->timeout > 0 && SCpnt->timeout < least) - least = SCpnt->timeout; - } - + wake_up(&host->host_wait); + /* - * If something is due to timeout again, then we will set the next timeout - * interrupt to occur. Otherwise, timeouts are disabled. + * If we have valid sense information, then some kind of recovery + * must have taken place. Make a note of this. */ - - if (least != 0xffffffff) - { - time_start = jiffies; - timer_table[SCSI_TIMER].expires = (time_elapsed = least) + jiffies; - timer_active |= 1 << SCSI_TIMER; - } - else + if( scsi_sense_valid(SCpnt) ) { - timer_table[SCSI_TIMER].expires = time_start = time_elapsed = 0; - timer_active &= ~(1 << SCSI_TIMER); + SCpnt->result |= (DRIVER_SENSE << 24); } - restore_flags(flags); - return oldto; + + SCSI_LOG_MLCOMPLETE(3,printk("Notifying upper driver of completion for device %d %x\n", + SCpnt->device->id, SCpnt->result)); + + SCpnt->owner = SCSI_OWNER_HIGHLEVEL; + SCpnt->state = SCSI_STATE_FINISHED; + SCpnt->done (SCpnt); } #ifdef CONFIG_MODULES @@ -2389,9 +1846,10 @@ if ((dma_malloc_freelist[i] & (mask << j)) == 0){ dma_malloc_freelist[i] |= (mask << j); restore_flags(flags); - dma_free_sectors -= nbits; + scsi_dma_free_sectors -= nbits; #ifdef DEBUG - printk("SMalloc: %d %p\n",len, dma_malloc_pages[i] + (j << 9)); + SCSI_LOG_MLQUEUE(3,printk("SMalloc: %d %p [From:%p]\n",len, dma_malloc_pages[i] + (j << 9))); + printk("SMalloc: %d %p [From:%p]\n",len, dma_malloc_pages[i] + (j << 9)); #endif return (void *) ((unsigned long) dma_malloc_pages[i] + (j << 9)); } @@ -2414,6 +1872,7 @@ ret = __builtin_return_address(0); #endif printk("scsi_free %p %d\n",obj, len); + SCSI_LOG_MLQUEUE(3,printk("SFree: %p %d\n",obj, len)); #endif for (page = 0; page < dma_sectors / SECTORS_PER_PAGE; page++) { @@ -2439,7 +1898,7 @@ #endif panic("scsi_free:Trying to free unused memory"); } - dma_free_sectors += nbits; + scsi_dma_free_sectors += nbits; dma_malloc_freelist[page] &= ~(mask << sector); restore_flags(flags); return 0; @@ -2508,37 +1967,37 @@ scsi_init_malloc(sizeof(Scsi_Cmnd), GFP_ATOMIC | (host->unchecked_isa_dma ? GFP_DMA : 0)); - SCpnt->host = host; - SCpnt->device = SDpnt; - SCpnt->target = SDpnt->id; - SCpnt->lun = SDpnt->lun; - SCpnt->channel = SDpnt->channel; - SCpnt->request.rq_status = RQ_INACTIVE; - SCpnt->use_sg = 0; - SCpnt->old_use_sg = 0; - SCpnt->old_cmd_len = 0; - SCpnt->timeout = 0; - SCpnt->underflow = 0; - SCpnt->transfersize = 0; - SCpnt->serial_number = 0; - SCpnt->serial_number_at_timeout = 0; - SCpnt->host_scribble = NULL; - if(host->host_queue) - host->host_queue->prev = SCpnt; - SCpnt->next = host->host_queue; - SCpnt->prev = NULL; - host->host_queue = SCpnt; - SCpnt->device_next = SDpnt->device_queue; - SDpnt->device_queue = SCpnt; + memset(&SCpnt->eh_timeout, 0, sizeof(SCpnt->eh_timeout)); + SCpnt->host = host; + SCpnt->device = SDpnt; + SCpnt->target = SDpnt->id; + SCpnt->lun = SDpnt->lun; + SCpnt->channel = SDpnt->channel; + SCpnt->request.rq_status = RQ_INACTIVE; + SCpnt->host_wait = FALSE; + SCpnt->device_wait = FALSE; + SCpnt->use_sg = 0; + SCpnt->old_use_sg = 0; + SCpnt->old_cmd_len = 0; + SCpnt->underflow = 0; + SCpnt->transfersize = 0; + SCpnt->serial_number = 0; + SCpnt->serial_number_at_timeout = 0; + SCpnt->host_scribble = NULL; + SCpnt->next = SDpnt->device_queue; + SDpnt->device_queue = SCpnt; + SCpnt->state = SCSI_STATE_UNUSED; + SCpnt->owner = SCSI_OWNER_NOBODY; } SDpnt->has_cmdblocks = 1; } +#ifndef MODULE /* { */ /* * scsi_dev_init() is our initialization routine, which in turn calls host * initialization, bus scanning, and sd/st initialization routines. + * This is only used at boot time. */ - __initfunc(int scsi_dev_init(void)) { Scsi_Device * SDpnt; @@ -2556,9 +2015,6 @@ /* Init a few things so we can "malloc" memory. */ scsi_loadable_module_flag = 0; - timer_table[SCSI_TIMER].fn = scsi_main_timeout; - timer_table[SCSI_TIMER].expires = 0; - /* Register the /proc/scsi/scsi entry */ #if CONFIG_PROC_FS proc_scsi_register(0, &proc_scsi_scsi); @@ -2567,12 +2023,17 @@ /* initialize all hosts */ scsi_init(); - scsi_devices = (Scsi_Device *) NULL; + /* + * This is where the processing takes place for most everything + * when commands are completed. Until we do this, we will not be able + * to queue any commands. + */ + init_bh(SCSI_BH, scsi_bottom_half_handler); for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) { scan_scsis(shpnt,0,0,0,0); /* scan for scsi devices */ if (shpnt->select_queue_depths != NULL) - (shpnt->select_queue_depths)(shpnt, scsi_devices); + (shpnt->select_queue_depths)(shpnt, shpnt->host_queue); } printk("scsi : detected "); @@ -2585,14 +2046,17 @@ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) if(sdtpnt->init && sdtpnt->dev_noticed) (*sdtpnt->init)(); - for (SDpnt=scsi_devices; SDpnt; SDpnt = SDpnt->next) { - SDpnt->scsi_request_fn = NULL; - for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) - if(sdtpnt->attach) (*sdtpnt->attach)(SDpnt); - if(SDpnt->attached) scsi_build_commandblocks(SDpnt); + for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) + { + for(SDpnt = shpnt->host_queue; SDpnt; SDpnt = SDpnt->next) + { + /* SDpnt->scsi_request_fn = NULL; */ + for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) + if(sdtpnt->attach) (*sdtpnt->attach)(SDpnt); + if(SDpnt->attached) scsi_build_commandblocks(SDpnt); + } } - - + /* * This should build the DMA pool. */ @@ -2610,6 +2074,7 @@ return 0; } +#endif /* MODULE */ /* } */ static void print_inquiry(unsigned char *data) { @@ -2662,7 +2127,7 @@ { Scsi_Cmnd *SCpnt; struct Scsi_Device_Template *SDTpnt; - Scsi_Device *scd, *scd_h = NULL; + Scsi_Device *scd; struct Scsi_Host *HBA_ptr; char *p; int host, channel, id, lun; @@ -2670,37 +2135,41 @@ off_t begin = 0; off_t pos = 0; - scd = scsi_devices; - HBA_ptr = scsi_hostlist; - if(inout == 0) { - size = sprintf(buffer+len,"Attached devices: %s\n", (scd)?"":"none"); + /* + * First, see if there are any attached devices or not. + */ + for (HBA_ptr = scsi_hostlist; HBA_ptr; HBA_ptr = HBA_ptr->next) + { + if( HBA_ptr->host_queue != NULL ) + { + break; + } + } + size = sprintf(buffer+len,"Attached devices: %s\n", (HBA_ptr)?"":"none"); len += size; pos = begin + len; - while (HBA_ptr) { + for (HBA_ptr = scsi_hostlist; HBA_ptr; HBA_ptr = HBA_ptr->next) + { #if 0 size += sprintf(buffer+len,"scsi%2d: %s\n", (int) HBA_ptr->host_no, HBA_ptr->hostt->procname); len += size; pos = begin + len; #endif - scd = scsi_devices; - while (scd) { - if (scd->host == HBA_ptr) { - proc_print_scsidevice(scd, buffer, &size, len); - len += size; - pos = begin + len; - - if (pos < offset) { - len = 0; - begin = pos; - } - if (pos > offset + length) - goto stop_output; - } - scd = scd->next; + for(scd = HBA_ptr->host_queue; scd; scd = scd->next) + { + proc_print_scsidevice(scd, buffer, &size, len); + len += size; + pos = begin + len; + + if (pos < offset) { + len = 0; + begin = pos; + } + if (pos > offset + length) + goto stop_output; } - HBA_ptr = HBA_ptr->next; } stop_output: @@ -2711,8 +2180,124 @@ return (len); } - if(!buffer || length < 25 || strncmp("scsi", buffer, 4)) - return(-EINVAL); + if(!buffer || length < 11 || strncmp("scsi", buffer, 4)) + return(-EINVAL); + + /* + * Usage: echo "scsi dump #N" > /proc/scsi/scsi + * to dump status of all scsi commands. The number is used to specify the level + * of detail in the dump. + */ + if(!strncmp("dump", buffer + 5, 4)) + { + unsigned int level; + + p = buffer + 10; + + if( *p == '\0' ) + return (-EINVAL); + + level = simple_strtoul(p, NULL, 0); + scsi_dump_status(level); + } + /* + * Usage: echo "scsi log token #N" > /proc/scsi/scsi + * where token is one of [error,scan,mlqueue,mlcomplete,llqueue, + * llcomplete,hlqueue,hlcomplete] + */ +#if CONFIG_SCSI_LOGGING /* { */ + + if(!strncmp("log", buffer + 5, 3)) + { + char * token; + unsigned int level; + + p = buffer + 9; + token = p; + while(*p != ' ' && *p != '\t' && *p != '\0') + { + p++; + } + + if( *p == '\0' ) + { + if( strncmp(token, "all", 3) == 0 ) + { + /* + * Turn on absolutely everything. + */ + scsi_logging_level = ~0; + } + else if( strncmp(token, "none", 4) == 0 ) + { + /* + * Turn off absolutely everything. + */ + scsi_logging_level = 0; + } + else + { + return (-EINVAL); + } + } + else + { + *p++ = '\0'; + + level = simple_strtoul(p, NULL, 0); + + /* + * Now figure out what to do with it. + */ + if( strcmp(token, "error") == 0 ) + { + SCSI_SET_ERROR_RECOVERY_LOGGING(level); + } + else if( strcmp(token, "timeout") == 0 ) + { + SCSI_SET_TIMEOUT_LOGGING(level); + } + else if( strcmp(token, "scan") == 0 ) + { + SCSI_SET_SCAN_BUS_LOGGING(level); + } + else if( strcmp(token, "mlqueue") == 0 ) + { + SCSI_SET_MLQUEUE_LOGGING(level); + } + else if( strcmp(token, "mlcomplete") == 0 ) + { + SCSI_SET_MLCOMPLETE_LOGGING(level); + } + else if( strcmp(token, "llqueue") == 0 ) + { + SCSI_SET_LLQUEUE_LOGGING(level); + } + else if( strcmp(token, "llcomplete") == 0 ) + { + SCSI_SET_LLCOMPLETE_LOGGING(level); + } + else if( strcmp(token, "hlqueue") == 0 ) + { + SCSI_SET_HLQUEUE_LOGGING(level); + } + else if( strcmp(token, "hlcomplete") == 0 ) + { + SCSI_SET_HLCOMPLETE_LOGGING(level); + } + else if( strcmp(token, "ioctl") == 0 ) + { + SCSI_SET_IOCTL_LOGGING(level); + } + else + { + return (-EINVAL); + } + } + + printk("scsi logging level set to 0x%8.8x\n", scsi_logging_level); + } +#endif /* CONFIG_SCSI_LOGGING */ /* } */ /* * Usage: echo "scsi add-single-device 0 1 2 3" >/proc/scsi/scsi @@ -2736,20 +2321,29 @@ printk("scsi singledevice %d %d %d %d\n", host, channel, id, lun); - while(scd && (scd->host->host_no != host - || scd->channel != channel - || scd->id != id - || scd->lun != lun)) { - scd = scd->next; - } - if(scd) - return(-ENOSYS); /* We do not yet support unplugging */ - while(HBA_ptr && HBA_ptr->host_no != host) - HBA_ptr = HBA_ptr->next; - + for(HBA_ptr = scsi_hostlist; HBA_ptr; HBA_ptr = HBA_ptr->next) + { + if( HBA_ptr->host_no == host ) + { + break; + } + } if(!HBA_ptr) return(-ENXIO); + for(scd = HBA_ptr->host_queue; scd; scd = scd->next) + { + if((scd->channel == channel + && scd->id == id + && scd->lun == lun)) + { + break; + } + } + + if(scd) + return(-ENOSYS); /* We do not yet support unplugging */ + scan_scsis (HBA_ptr, 1, channel, id, lun); return(length); @@ -2774,15 +2368,25 @@ id = simple_strtoul(p+1, &p, 0); lun = simple_strtoul(p+1, &p, 0); - while(scd != NULL) { - if(scd->host->host_no == host - && scd->channel == channel - && scd->id == id - && scd->lun == lun){ + + for(HBA_ptr = scsi_hostlist; HBA_ptr; HBA_ptr = HBA_ptr->next) + { + if( HBA_ptr->host_no == host ) + { + break; + } + } + if(!HBA_ptr) + return(-ENODEV); + + for(scd = HBA_ptr->host_queue; scd; scd = scd->next) + { + if((scd->channel == channel + && scd->id == id + && scd->lun == lun)) + { break; } - scd_h = scd; - scd = scd->next; } if(scd == NULL) @@ -2802,24 +2406,23 @@ * Nobody is using this device any more. * Free all of the command structures. */ - for(SCpnt=scd->host->host_queue; SCpnt; SCpnt = SCpnt->next){ - if(SCpnt->device == scd) { - if(SCpnt->prev != NULL) - SCpnt->prev->next = SCpnt->next; - if(SCpnt->next != NULL) - SCpnt->next->prev = SCpnt->prev; - if(SCpnt == scd->host->host_queue) - scd->host->host_queue = SCpnt->next; - scsi_init_free((char *) SCpnt, sizeof(*SCpnt)); - } + for(SCpnt=scd->device_queue; SCpnt; SCpnt = SCpnt->next) + { + scd->device_queue = SCpnt->next; + scsi_init_free((char *) SCpnt, sizeof(*SCpnt)); } /* Now we can remove the device structure */ - if(scd_h != NULL) { - scd_h->next = scd->next; - } else if (scsi_devices == scd) { - /* We had a hit on the first entry of the device list */ - scsi_devices = scd->next; + if( scd->next != NULL ) + scd->next->prev = scd->prev; + + if( scd->prev != NULL ) + scd->prev->next = scd->next; + + if( HBA_ptr->host_queue == scd ) + { + HBA_ptr->host_queue = scd->next; } + scsi_init_free((char *) scd, sizeof(Scsi_Device)); } else { return(-EBUSY); @@ -2847,13 +2450,13 @@ unsigned int new_need_isa_buffer = 0; unsigned char ** new_dma_malloc_pages = NULL; - if( !scsi_devices ) + if( !scsi_hostlist ) { /* * Free up the DMA pool. */ - if( dma_free_sectors != dma_sectors ) - panic("SCSI DMA pool memory leak %d %d\n",dma_free_sectors,dma_sectors); + if( scsi_dma_free_sectors != dma_sectors ) + panic("SCSI DMA pool memory leak %d %d\n",scsi_dma_free_sectors,dma_sectors); for(i=0; i < dma_sectors / SECTORS_PER_PAGE; i++) scsi_init_free(dma_malloc_pages[i], PAGE_SIZE); @@ -2866,7 +2469,7 @@ (dma_sectors / SECTORS_PER_PAGE)*sizeof(*dma_malloc_freelist)); dma_malloc_freelist = NULL; dma_sectors = 0; - dma_free_sectors = 0; + scsi_dma_free_sectors = 0; return; } /* Next, check to see if we need to extend the DMA buffer pool */ @@ -2874,52 +2477,54 @@ new_dma_sectors = 2*SECTORS_PER_PAGE; /* Base value we use */ if (__pa(high_memory)-1 > ISA_DMA_THRESHOLD) - scsi_need_isa_bounce_buffers = 1; + need_isa_bounce_buffers = 1; else - scsi_need_isa_bounce_buffers = 0; + need_isa_bounce_buffers = 0; if (scsi_devicelist) for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next) new_dma_sectors += SECTORS_PER_PAGE; /* Increment for each host */ - for (SDpnt=scsi_devices; SDpnt; SDpnt = SDpnt->next) { - host = SDpnt->host; - - /* - * sd and sr drivers allocate scatterlists. - * sr drivers may allocate for each command 1x2048 or 2x1024 extra - * buffers for 2k sector size and 1k fs. - * sg driver allocates buffers < 4k. - * st driver does not need buffers from the dma pool. - * estimate 4k buffer/command for devices of unknown type (should panic). - */ - if (SDpnt->type == TYPE_WORM || SDpnt->type == TYPE_ROM || - SDpnt->type == TYPE_DISK || SDpnt->type == TYPE_MOD) { - new_dma_sectors += ((host->sg_tablesize * - sizeof(struct scatterlist) + 511) >> 9) * - SDpnt->queue_depth; - if (SDpnt->type == TYPE_WORM || SDpnt->type == TYPE_ROM) - new_dma_sectors += (2048 >> 9) * SDpnt->queue_depth; - } - else if (SDpnt->type == TYPE_SCANNER || - SDpnt->type == TYPE_PROCESSOR || - SDpnt->type == TYPE_MEDIUM_CHANGER) { - new_dma_sectors += (4096 >> 9) * SDpnt->queue_depth; - } - else { - if (SDpnt->type != TYPE_TAPE) { - printk("resize_dma_pool: unknown device type %d\n", SDpnt->type); - new_dma_sectors += (4096 >> 9) * SDpnt->queue_depth; - } + for (host = scsi_hostlist; host; host = host->next) + { + for (SDpnt=host->host_queue; SDpnt; SDpnt = SDpnt->next) + { + /* + * sd and sr drivers allocate scatterlists. + * sr drivers may allocate for each command 1x2048 or 2x1024 extra + * buffers for 2k sector size and 1k fs. + * sg driver allocates buffers < 4k. + * st driver does not need buffers from the dma pool. + * estimate 4k buffer/command for devices of unknown type (should panic). + */ + if (SDpnt->type == TYPE_WORM || SDpnt->type == TYPE_ROM || + SDpnt->type == TYPE_DISK || SDpnt->type == TYPE_MOD) { + new_dma_sectors += ((host->sg_tablesize * + sizeof(struct scatterlist) + 511) >> 9) * + SDpnt->queue_depth; + if (SDpnt->type == TYPE_WORM || SDpnt->type == TYPE_ROM) + new_dma_sectors += (2048 >> 9) * SDpnt->queue_depth; + } + else if (SDpnt->type == TYPE_SCANNER || + SDpnt->type == TYPE_PROCESSOR || + SDpnt->type == TYPE_MEDIUM_CHANGER) { + new_dma_sectors += (4096 >> 9) * SDpnt->queue_depth; + } + else { + if (SDpnt->type != TYPE_TAPE) { + printk("resize_dma_pool: unknown device type %d\n", SDpnt->type); + new_dma_sectors += (4096 >> 9) * SDpnt->queue_depth; + } + } + + if(host->unchecked_isa_dma && + need_isa_bounce_buffers && + SDpnt->type != TYPE_TAPE) { + new_dma_sectors += (PAGE_SIZE >> 9) * host->sg_tablesize * + SDpnt->queue_depth; + new_need_isa_buffer++; + } } - - if(host->unchecked_isa_dma && - scsi_need_isa_bounce_buffers && - SDpnt->type != TYPE_TAPE) { - new_dma_sectors += (PAGE_SIZE >> 9) * host->sg_tablesize * - SDpnt->queue_depth; - new_need_isa_buffer++; - } } #ifdef DEBUG_INIT @@ -2977,16 +2582,16 @@ scsi_init_free((char *) dma_malloc_pages, size); } - dma_free_sectors += new_dma_sectors - dma_sectors; + scsi_dma_free_sectors += new_dma_sectors - dma_sectors; dma_malloc_pages = new_dma_malloc_pages; dma_sectors = new_dma_sectors; - need_isa_buffer = new_need_isa_buffer; + scsi_need_isa_buffer = new_need_isa_buffer; restore_flags(flags); #ifdef DEBUG_INIT - printk("resize_dma_pool: dma free sectors = %d\n", dma_free_sectors); + printk("resize_dma_pool: dma free sectors = %d\n", scsi_dma_free_sectors); printk("resize_dma_pool: dma sectors = %d\n", dma_sectors); - printk("resize_dma_pool: need isa buffers = %d\n", need_isa_buffer); + printk("resize_dma_pool: need isa buffers = %d\n", scsi_need_isa_buffer); #endif } @@ -3010,13 +2615,16 @@ pcount = next_scsi_host; if ((tpnt->present = tpnt->detect(tpnt))) { - if(pcount == next_scsi_host) { - if(tpnt->present > 1) { + if(pcount == next_scsi_host) + { + if(tpnt->present > 1) + { printk("Failure to register low-level scsi driver"); scsi_unregister_host(tpnt); return 1; } - /* The low-level driver failed to register a driver. We + /* + * The low-level driver failed to register a driver. We * can do this now. */ scsi_register(tpnt,0); @@ -3029,16 +2637,46 @@ build_proc_dir_entries(tpnt); #endif + + /* + * Add the kernel threads for each host adapter that will + * handle error correction. + */ + for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next) + { + if( shpnt->hostt == tpnt && shpnt->hostt->use_new_eh_code ) + { + struct semaphore sem = MUTEX_LOCKED; + + shpnt->eh_notify = &sem; + kernel_thread((int (*)(void *))scsi_error_handler, + (void *) shpnt, 0); + + /* + * Now wait for the kernel error thread to initialize itself + * as it might be needed when we scan the bus. + */ + down (&sem); + shpnt->eh_notify = NULL; + } + } + for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next) + { if(shpnt->hostt == tpnt) { if(tpnt->info) + { name = tpnt->info(shpnt); + } else + { name = tpnt->name; + } printk ("scsi%d : %s\n", /* And print a little message */ shpnt->host_no, name); } + } printk ("scsi : %d host%s.\n", next_scsi_host, (next_scsi_host == 1) ? "" : "s"); @@ -3048,26 +2686,36 @@ /* The next step is to call scan_scsis here. This generates the * Scsi_Devices entries */ - for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next) - if(shpnt->hostt == tpnt) { - scan_scsis(shpnt,0,0,0,0); - if (shpnt->select_queue_depths != NULL) - (shpnt->select_queue_depths)(shpnt, scsi_devices); + { + if(shpnt->hostt == tpnt) + { + scan_scsis(shpnt,0,0,0,0); + if (shpnt->select_queue_depths != NULL) + { + (shpnt->select_queue_depths)(shpnt, shpnt->host_queue); + } } + } for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) + { if(sdtpnt->init && sdtpnt->dev_noticed) (*sdtpnt->init)(); + } - /* Next we create the Scsi_Cmnd structures for this host */ - - for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next) - if(SDpnt->host->hostt == tpnt) - { - for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) - if(sdtpnt->attach) (*sdtpnt->attach)(SDpnt); - if(SDpnt->attached) scsi_build_commandblocks(SDpnt); - } + /* + * Next we create the Scsi_Cmnd structures for this host + */ + for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) + { + for(SDpnt = shpnt->host_queue; SDpnt; SDpnt = SDpnt->next) + if(SDpnt->host->hostt == tpnt) + { + for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) + if(sdtpnt->attach) (*sdtpnt->attach)(SDpnt); + if(SDpnt->attached) scsi_build_commandblocks(SDpnt); + } + } /* * Now that we have all of the devices, resize the DMA pool, @@ -3077,8 +2725,12 @@ /* This does any final handling that is required. */ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) + { if(sdtpnt->finish && sdtpnt->nr_dev) + { (*sdtpnt->finish)(); + } + } } #if defined(USE_STATIC_SCSI_MEMORY) @@ -3095,90 +2747,187 @@ /* * Similarly, this entry point should be called by a loadable module if it * is trying to remove a low level scsi driver from the system. + * + * Note - there is a fatal flaw in the deregister module function. + * There is no way to return a code that says 'I cannot be unloaded now'. + * The system relies entirely upon usage counts that are maintained, + * and the assumption is that if the usage count is 0, then the module + * can be unloaded. */ static void scsi_unregister_host(Scsi_Host_Template * tpnt) { - Scsi_Host_Template * SHT, *SHTp; - Scsi_Device *sdpnt, * sdppnt, * sdpnt1; - Scsi_Cmnd * SCpnt; - unsigned long flags; + unsigned long flags; + int online_status; + int pcount; + Scsi_Cmnd * SCpnt; + Scsi_Device * SDpnt; + Scsi_Device * SDpnt1; struct Scsi_Device_Template * sdtpnt; - struct Scsi_Host * shpnt, *sh1; - int pcount; + struct Scsi_Host * sh1; + struct Scsi_Host * shpnt; + Scsi_Host_Template * SHT; + Scsi_Host_Template * SHTp; + + /* + * First verify that this host adapter is completely free with no pending + * commands + */ + for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) + { + for(SDpnt = shpnt->host_queue; SDpnt; + SDpnt = SDpnt->next) + { + if(SDpnt->host->hostt == tpnt + && SDpnt->host->hostt->module + && SDpnt->host->hostt->module->usecount) return; + /* + * FIXME(eric) - We need to find a way to notify the + * low level driver that we are shutting down - via the + * special device entry that still needs to get added. + * + * Is detach interface below good enough for this? + */ + } + } - /* First verify that this host adapter is completely free with no pending - * commands */ + /* + * FIXME(eric) put a spinlock on this. We force all of the devices offline + * to help prevent race conditions where other hosts/processors could try and + * get in and queue a command. + */ + for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) + { + for(SDpnt = shpnt->host_queue; SDpnt; + SDpnt = SDpnt->next) + { + if(SDpnt->host->hostt == tpnt ) + SDpnt->online = FALSE; - for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt->next) - if(sdpnt->host->hostt == tpnt && sdpnt->host->hostt->module - && sdpnt->host->hostt->module->usecount) return; + } + } for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) { - if (shpnt->hostt != tpnt) continue; - for(SCpnt = shpnt->host_queue; SCpnt; SCpnt = SCpnt->next) - { - save_flags(flags); - cli(); - if(SCpnt->request.rq_status != RQ_INACTIVE) { - restore_flags(flags); - for(SCpnt = shpnt->host_queue; SCpnt; SCpnt = SCpnt->next) - if(SCpnt->request.rq_status == RQ_SCSI_DISCONNECTING) - SCpnt->request.rq_status = RQ_INACTIVE; - printk("Device busy???\n"); - return; + if (shpnt->hostt != tpnt) + { + continue; + } + + for(SDpnt = shpnt->host_queue; SDpnt; + SDpnt = SDpnt->next) + { + /* + * Loop over all of the commands associated with the device. If any of + * them are busy, then set the state back to inactive and bail. + */ + for(SCpnt = SDpnt->device_queue; SCpnt; + SCpnt = SCpnt->next) + { + online_status = SDpnt->online; + SDpnt->online = FALSE; + save_flags(flags); + cli(); + if(SCpnt->request.rq_status != RQ_INACTIVE) + { + restore_flags(flags); + printk("SCSI device not inactive - state=%d, id=%d\n", + SCpnt->request.rq_status, SCpnt->target); + for(SDpnt1 = shpnt->host_queue; SDpnt1; + SDpnt1 = SDpnt1->next) + { + for(SCpnt = SDpnt1->device_queue; SCpnt; + SCpnt = SCpnt->next) + if(SCpnt->request.rq_status == RQ_SCSI_DISCONNECTING) + SCpnt->request.rq_status = RQ_INACTIVE; + } + SDpnt->online = online_status; + printk("Device busy???\n"); + return; + } + /* + * No, this device is really free. Mark it as such, and + * continue on. + */ + SCpnt->state = SCSI_STATE_DISCONNECTING; + SCpnt->request.rq_status = RQ_SCSI_DISCONNECTING; /* Mark as busy */ + restore_flags(flags); } - SCpnt->request.rq_status = RQ_SCSI_DISCONNECTING; /* Mark as busy */ - restore_flags(flags); - } + } } /* Next we detach the high level drivers from the Scsi_Device structures */ - for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt->next) - if(sdpnt->host->hostt == tpnt) - { + for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) + { + if(shpnt->hostt != tpnt) + { + continue; + } + + for(SDpnt = shpnt->host_queue; SDpnt; + SDpnt = SDpnt->next) + { for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) - if(sdtpnt->detach) (*sdtpnt->detach)(sdpnt); - /* If something still attached, punt */ - if (sdpnt->attached) { - printk("Attached usage count = %d\n", sdpnt->attached); - return; - } + if(sdtpnt->detach) (*sdtpnt->detach)(SDpnt); + + /* If something still attached, punt */ + if (SDpnt->attached) + { + printk("Attached usage count = %d\n", SDpnt->attached); + return; + } } + } + + /* + * Next, kill the kernel error recovery thread for this host. + */ + for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) + { + if( shpnt->hostt == tpnt + && shpnt->hostt->use_new_eh_code + && shpnt->ehandler != NULL ) + { + struct semaphore sem = MUTEX_LOCKED; + + shpnt->eh_notify = &sem; + send_sig(SIGKILL, shpnt->ehandler, 1); + down(&sem); + shpnt->eh_notify = NULL; + } + } /* Next we free up the Scsi_Cmnd structures for this host */ - for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt->next) - if(sdpnt->host->hostt == tpnt) - while (sdpnt->host->host_queue) { - SCpnt = sdpnt->host->host_queue->next; - scsi_init_free((char *) sdpnt->host->host_queue, sizeof(Scsi_Cmnd)); - sdpnt->host->host_queue = SCpnt; - if (SCpnt) SCpnt->prev = NULL; - sdpnt->has_cmdblocks = 0; - } + for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) + { + if(shpnt->hostt != tpnt) + { + continue; + } + + for(SDpnt = shpnt->host_queue; SDpnt; + SDpnt = shpnt->host_queue) + { + while (SDpnt->device_queue) + { + SCpnt = SDpnt->device_queue->next; + scsi_init_free((char *) SDpnt->device_queue, sizeof(Scsi_Cmnd)); + SDpnt->device_queue = SCpnt; + } + SDpnt->has_cmdblocks = 0; - /* Next free up the Scsi_Device structures for this host */ + /* Next free up the Scsi_Device structures for this host */ + shpnt->host_queue = SDpnt->next; + scsi_init_free((char *) SDpnt, sizeof (Scsi_Device)); - sdppnt = NULL; - for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt1) - { - sdpnt1 = sdpnt->next; - if (sdpnt->host->hostt == tpnt) { - if (sdppnt) - sdppnt->next = sdpnt->next; - else - scsi_devices = sdpnt->next; - scsi_init_free((char *) sdpnt, sizeof (Scsi_Device)); - } else - sdppnt = sdpnt; + } } /* Next we go through and remove the instances of the individual hosts * that were detected */ - shpnt = scsi_hostlist; - while(shpnt) { + for(shpnt = scsi_hostlist; shpnt; shpnt = sh1) + { sh1 = shpnt->next; if(shpnt->hostt == tpnt) { if(shpnt->loaded_as_module) { @@ -3204,7 +2953,6 @@ tpnt->present--; } } - shpnt = sh1; } /* @@ -3212,7 +2960,7 @@ * to completely nuke the DMA pool. The resize operation will * do the right thing and free everything. */ - if( !scsi_devices ) + if( !scsi_hosts ) resize_dma_pool(); printk ("scsi : %d host%s.\n", next_scsi_host, @@ -3256,7 +3004,8 @@ */ static int scsi_register_device_module(struct Scsi_Device_Template * tpnt) { - Scsi_Device * SDpnt; + Scsi_Device * SDpnt; + struct Scsi_Host * shpnt; if (tpnt->next) return 1; @@ -3265,8 +3014,14 @@ * First scan the devices that we know about, and see if we notice them. */ - for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next) - if(tpnt->detect) SDpnt->attached += (*tpnt->detect)(SDpnt); + for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) + { + for(SDpnt = shpnt->host_queue; SDpnt; + SDpnt = SDpnt->next) + { + if(tpnt->detect) SDpnt->attached += (*tpnt->detect)(SDpnt); + } + } /* * If any of the devices would match this driver, then perform the @@ -3278,15 +3033,22 @@ /* * Now actually connect the devices to the new driver. */ - for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next) + for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) { - if(tpnt->attach) (*tpnt->attach)(SDpnt); - /* - * If this driver attached to the device, and we no longer - * have anything attached, release the scsi command blocks. - */ - if(SDpnt->attached && SDpnt->has_cmdblocks == 0) - scsi_build_commandblocks(SDpnt); + for(SDpnt = shpnt->host_queue; SDpnt; + SDpnt = SDpnt->next) + { + if(tpnt->attach) (*tpnt->attach)(SDpnt); + /* + * If this driver attached to the device, and don't have any + * command blocks for this device, allocate some. + */ + if(SDpnt->attached && SDpnt->has_cmdblocks == 0) + { + SDpnt->online = TRUE; + scsi_build_commandblocks(SDpnt); + } + } } /* @@ -3302,6 +3064,7 @@ { Scsi_Device * SDpnt; Scsi_Cmnd * SCpnt; + struct Scsi_Host * shpnt; struct Scsi_Device_Template * spnt; struct Scsi_Device_Template * prev_spnt; @@ -3314,30 +3077,30 @@ * Next, detach the devices from the driver. */ - for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next) + for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) { - if(tpnt->detach) (*tpnt->detach)(SDpnt); - if(SDpnt->attached == 0) - { - /* - * Nobody is using this device any more. Free all of the - * command structures. - */ - for(SCpnt = SDpnt->host->host_queue; SCpnt; SCpnt = SCpnt->next) + for(SDpnt = shpnt->host_queue; SDpnt; + SDpnt = SDpnt->next) + { + if(tpnt->detach) (*tpnt->detach)(SDpnt); + if(SDpnt->attached == 0) { - if(SCpnt->device == SDpnt) - { - if(SCpnt->prev != NULL) - SCpnt->prev->next = SCpnt->next; - if(SCpnt->next != NULL) - SCpnt->next->prev = SCpnt->prev; - if(SCpnt == SDpnt->host->host_queue) - SDpnt->host->host_queue = SCpnt->next; + SDpnt->online = FALSE; + + /* + * Nobody is using this device any more. Free all of the + * command structures. + */ + for(SCpnt = SDpnt->device_queue; SCpnt; + SCpnt = SCpnt->next) + { + if(SCpnt == SDpnt->device_queue) + SDpnt->device_queue = SCpnt->next; scsi_init_free((char *) SCpnt, sizeof(*SCpnt)); - } + } + SDpnt->has_cmdblocks = 0; } - SDpnt->has_cmdblocks = 0; - } + } } /* * Extract the template from the linked list. @@ -3365,26 +3128,27 @@ int scsi_register_module(int module_type, void * ptr) { - switch(module_type){ + switch(module_type) + { case MODULE_SCSI_HA: return scsi_register_host((Scsi_Host_Template *) ptr); - + /* Load upper level device handler of some kind */ case MODULE_SCSI_DEV: #ifdef CONFIG_KERNELD if (scsi_hosts == NULL) - request_module("scsi_hostadapter"); + request_module("scsi_hostadapter"); #endif return scsi_register_device_module((struct Scsi_Device_Template *) ptr); /* The rest of these are not yet implemented */ - + /* Load constants.o */ case MODULE_SCSI_CONST: - + /* Load specialized ioctl handler for some device. Intended for * cdroms that have non-SCSI2 audio command sets. */ case MODULE_SCSI_IOCTL: - + default: return 1; } @@ -3392,7 +3156,8 @@ void scsi_unregister_module(int module_type, void * ptr) { - switch(module_type) { + switch(module_type) + { case MODULE_SCSI_HA: scsi_unregister_host((Scsi_Host_Template *) ptr); break; @@ -3410,65 +3175,123 @@ #endif /* CONFIG_MODULES */ -#ifdef DEBUG_TIMEOUT +/* + * Function: scsi_dump_status + * + * Purpose: Brain dump of scsi system, used for problem solving. + * + * Arguments: level - used to indicate level of detail. + * + * Notes: The level isn't used at all yet, but we need to find some way + * of sensibly logging varying degrees of information. A quick one-line + * display of each command, plus the status would be most useful. + * + * This does depend upon CONFIG_SCSI_LOGGING - I do want some way of turning + * it all off if the user wants a lean and mean kernel. It would probably + * also be useful to allow the user to specify one single host to be dumped. + * A second argument to the function would be useful for that purpose. + * + * FIXME - some formatting of the output into tables would be very handy. + */ static void -scsi_dump_status(void) +scsi_dump_status(int level) { +#if CONFIG_PROC_FS +#if CONFIG_SCSI_LOGGING /* { */ int i; struct Scsi_Host * shpnt; Scsi_Cmnd * SCpnt; - printk("Dump of scsi parameters:\n"); + Scsi_Device * SDpnt; + printk("Dump of scsi host parameters:\n"); i = 0; for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) - for(SCpnt=shpnt->host_queue; SCpnt; SCpnt = SCpnt->next) + { + printk(" %d %d %d : %d %p\n", + shpnt->host_failed, + shpnt->host_busy, + shpnt->host_active, + shpnt->host_blocked, + shpnt->pending_commands); + + } + + printk("\n\n"); + printk("Dump of scsi command parameters:\n"); + for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) + { + printk("h:c:t:l (dev sect nsect cnumsec sg) (ret all flg) (to/cmd to ito) cmd snse result\n"); + for(SDpnt=shpnt->host_queue; SDpnt; SDpnt = SDpnt->next) { - /* (0) 0:0:0:0 (802 123434 8 8 0) (3 3 2) (%d %d %d) %d %x */ - printk("(%d) %d:%d:%d:%d (%s %ld %ld %ld %d) (%d %d %x) (%d %d %d) %x %x %x\n", - i++, SCpnt->host->host_no, - SCpnt->channel, - SCpnt->target, - SCpnt->lun, - kdevname(SCpnt->request.rq_dev), - SCpnt->request.sector, - SCpnt->request.nr_sectors, - SCpnt->request.current_nr_sectors, - SCpnt->use_sg, - SCpnt->retries, - SCpnt->allowed, - SCpnt->flags, - SCpnt->timeout_per_command, - SCpnt->timeout, - SCpnt->internal_timeout, - SCpnt->cmnd[0], - SCpnt->sense_buffer[2], - SCpnt->result); - } - printk("wait_for_request = %p\n", wait_for_request); - /* Now dump the request lists for each block device */ - printk("Dump of pending block device requests\n"); - for(i=0; idevice_queue; SCpnt; SCpnt = SCpnt->next) + { + /* (0) h:c:t:l (dev sect nsect cnumsec sg) (ret all flg) (to/cmd to ito) cmd snse result %d %x */ + printk("(%3d) %2d:%1d:%2d:%2d (%6s %4ld %4ld %4ld %4x %1d) (%1d %1d 0x%2x) (%4d %4d %4d) 0x%2.2x 0x%2.2x 0x%8.8x\n", + i++, + + SCpnt->host->host_no, + SCpnt->channel, + SCpnt->target, + SCpnt->lun, + + kdevname(SCpnt->request.rq_dev), + SCpnt->request.sector, + SCpnt->request.nr_sectors, + SCpnt->request.current_nr_sectors, + SCpnt->request.rq_status, + SCpnt->use_sg, + + SCpnt->retries, + SCpnt->allowed, + SCpnt->flags, + + SCpnt->timeout_per_command, + SCpnt->timeout, + SCpnt->internal_timeout, + + SCpnt->cmnd[0], + SCpnt->sense_buffer[2], + SCpnt->result); + } + } + } + + for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) + { + for(SDpnt=shpnt->host_queue; SDpnt; SDpnt = SDpnt->next) { - struct request * req; - printk("%d: ", i); - req = blk_dev[i].current_request; - while(req) { - printk("(%s %d %ld %ld %ld) ", - kdevname(req->rq_dev), - req->cmd, - req->sector, - req->nr_sectors, - req->current_nr_sectors); - req = req->next; - } - printk("\n"); - } + /* Now dump the request lists for each block device */ + printk("Dump of pending block device requests\n"); + for(i=0; irq_dev), + req->cmd, + req->sector, + req->nr_sectors, + req->current_nr_sectors); + req = req->next; + } + printk("\n"); + } + } + } + } + printk("wait_for_request = %p\n", wait_for_request); +#endif /* CONFIG_SCSI_LOGGING */ /* } */ +#endif /* CONFIG_PROC_FS */ } -#endif #ifdef MODULE -int init_module(void) { +int init_module(void) +{ unsigned long size; /* @@ -3478,8 +3301,6 @@ dispatch_scsi_info_ptr = dispatch_scsi_info; #endif - timer_table[SCSI_TIMER].fn = scsi_main_timeout; - timer_table[SCSI_TIMER].expires = 0; scsi_loadable_module_flag = 1; /* Register the /proc/scsi/scsi entry */ @@ -3489,7 +3310,7 @@ dma_sectors = PAGE_SIZE / SECTOR_SIZE; - dma_free_sectors= dma_sectors; + scsi_dma_free_sectors= dma_sectors; /* * Set up a minimal DMA buffer list - this will be used during scan_scsis * in some cases. @@ -3505,12 +3326,20 @@ scsi_init_malloc((dma_sectors / SECTORS_PER_PAGE)*sizeof(*dma_malloc_pages), GFP_ATOMIC); dma_malloc_pages[0] = (unsigned char *) scsi_init_malloc(PAGE_SIZE, GFP_ATOMIC | GFP_DMA); + + /* + * This is where the processing takes place for most everything + * when commands are completed. + */ + init_bh(SCSI_BH, scsi_bottom_half_handler); + return 0; } void cleanup_module( void) { - timer_active &= ~(1 << SCSI_TIMER); + remove_bh(SCSI_BH); + #if CONFIG_PROC_FS proc_scsi_unregister(0, PROC_SCSI_SCSI); @@ -3523,8 +3352,6 @@ */ resize_dma_pool(); - timer_table[SCSI_TIMER].fn = NULL; - timer_table[SCSI_TIMER].expires = 0; } #endif /* MODULE */ diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h --- v2.1.74/linux/drivers/scsi/scsi.h Wed Nov 12 13:34:26 1997 +++ linux/drivers/scsi/scsi.h Sun Dec 21 17:59:19 1997 @@ -24,6 +24,7 @@ #include #include +#include #include /* @@ -39,9 +40,57 @@ #define MAX_SCSI_DEVICE_CODE 10 extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE]; -extern void scsi_make_blocked_list(void); -extern volatile int in_scan_scsis; -extern const unsigned char scsi_command_size[8]; +/* + * Use these to separate status msg and our bytes + * + * These are set by: + * + * status byte = set from target device + * msg_byte = return status from host adapter itself. + * host_byte = set by low-level driver to indicate status. + * driver_byte = set by mid-level. + */ +#define status_byte(result) (((result) >> 1) & 0x1f) +#define msg_byte(result) (((result) >> 8) & 0xff) +#define host_byte(result) (((result) >> 16) & 0xff) +#define driver_byte(result) (((result) >> 24) & 0xff) +#define suggestion(result) (driver_byte(result) & SUGGEST_MASK) + +#define sense_class(sense) (((sense) >> 4) & 0x7) +#define sense_error(sense) ((sense) & 0xf) +#define sense_valid(sense) ((sense) & 0x80); + +#define NEEDS_RETRY 0x2001 +#define SUCCESS 0x2002 +#define FAILED 0x2003 +#define QUEUED 0x2004 +#define SOFT_ERROR 0x2005 +#define ADD_TO_MLQUEUE 0x2006 + +/* + * These are the values that scsi_cmd->state can take. + */ +#define SCSI_STATE_TIMEOUT 0x1000 +#define SCSI_STATE_FINISHED 0x1001 +#define SCSI_STATE_FAILED 0x1002 +#define SCSI_STATE_QUEUED 0x1003 +#define SCSI_STATE_UNUSED 0x1006 +#define SCSI_STATE_DISCONNECTING 0x1008 +#define SCSI_STATE_INITIALIZING 0x1009 +#define SCSI_STATE_BHQUEUE 0x100a +#define SCSI_STATE_MLQUEUE 0x100b + +/* + * These are the values that the owner field can take. + * They are used as an indication of who the command belongs to. + */ +#define SCSI_OWNER_HIGHLEVEL 0x100 +#define SCSI_OWNER_MIDLEVEL 0x101 +#define SCSI_OWNER_LOWLEVEL 0x102 +#define SCSI_OWNER_ERROR_HANDLER 0x103 +#define SCSI_OWNER_BH_HANDLER 0x104 +#define SCSI_OWNER_NOBODY 0x105 + #define COMMAND_SIZE(opcode) scsi_command_size[((opcode) >> 5) & 7] #define IDENTIFY_BASE 0x80 @@ -50,6 +99,126 @@ ((lun) & 0x07)) +/* + * This defines the scsi logging feature. It is a means by which the + * user can select how much information they get about various goings on, + * and it can be really useful for fault tracing. The logging word is divided + * into 8 nibbles, each of which describes a loglevel. The division of things + * is somewhat arbitrary, and the division of the word could be changed if it + * were really needed for any reason. The numbers below are the only place where these + * are specified. For a first go-around, 3 bits is more than enough, since this + * gives 8 levels of logging (really 7, since 0 is always off). Cutting to 2 bits + * might be wise at some point. + */ + +#define SCSI_LOG_ERROR_SHIFT 0 +#define SCSI_LOG_TIMEOUT_SHIFT 3 +#define SCSI_LOG_SCAN_SHIFT 6 +#define SCSI_LOG_MLQUEUE_SHIFT 9 +#define SCSI_LOG_MLCOMPLETE_SHIFT 12 +#define SCSI_LOG_LLQUEUE_SHIFT 15 +#define SCSI_LOG_LLCOMPLETE_SHIFT 18 +#define SCSI_LOG_HLQUEUE_SHIFT 21 +#define SCSI_LOG_HLCOMPLETE_SHIFT 24 +#define SCSI_LOG_IOCTL_SHIFT 27 + +#define SCSI_LOG_ERROR_BITS 3 +#define SCSI_LOG_TIMEOUT_BITS 3 +#define SCSI_LOG_SCAN_BITS 3 +#define SCSI_LOG_MLQUEUE_BITS 3 +#define SCSI_LOG_MLCOMPLETE_BITS 3 +#define SCSI_LOG_LLQUEUE_BITS 3 +#define SCSI_LOG_LLCOMPLETE_BITS 3 +#define SCSI_LOG_HLQUEUE_BITS 3 +#define SCSI_LOG_HLCOMPLETE_BITS 3 +#define SCSI_LOG_IOCTL_BITS 3 + +#if CONFIG_SCSI_LOGGING + +#define SCSI_CHECK_LOGGING(SHIFT, BITS, LEVEL, CMD) \ +{ \ + unsigned int mask; \ + \ + mask = (1 << (BITS)) - 1; \ + if( ((scsi_logging_level >> (SHIFT)) & mask) > (LEVEL) ) \ + { \ + (CMD); \ + } \ +} + +#define SCSI_SET_LOGGING(SHIFT, BITS, LEVEL) \ +{ \ + unsigned int mask; \ + \ + mask = ((1 << (BITS)) - 1) << SHIFT; \ + scsi_logging_level = ((scsi_logging_level & ~mask) \ + | ((LEVEL << SHIFT) & mask)); \ +} + + + +#else + +/* + * With no logging enabled, stub these out so they don't do anything. + */ +#define SCSI_SET_LOGGING(SHIFT, BITS, LEVEL) + +#define SCSI_CHECK_LOGGING(SHIFT, BITS, LEVEL, CMD) +#endif + +/* + * These are the macros that are actually used throughout the code to + * log events. If logging isn't enabled, they are no-ops and will be + * completely absent from the user's code. + * + * The 'set' versions of the macros are really intended to only be called + * from the /proc filesystem, and in production kernels this will be about + * all that is ever used. It could be useful in a debugging environment to + * bump the logging level when certain strange events are detected, however. + */ +#define SCSI_LOG_ERROR_RECOVERY(LEVEL,CMD) \ + SCSI_CHECK_LOGGING(SCSI_LOG_ERROR_SHIFT, SCSI_LOG_ERROR_BITS, LEVEL,CMD); +#define SCSI_LOG_TIMEOUT(LEVEL,CMD) \ + SCSI_CHECK_LOGGING(SCSI_LOG_TIMEOUT_SHIFT, SCSI_LOG_TIMEOUT_BITS, LEVEL,CMD); +#define SCSI_LOG_SCAN_BUS(LEVEL,CMD) \ + SCSI_CHECK_LOGGING(SCSI_LOG_SCAN_SHIFT, SCSI_LOG_SCAN_BITS, LEVEL,CMD); +#define SCSI_LOG_MLQUEUE(LEVEL,CMD) \ + SCSI_CHECK_LOGGING(SCSI_LOG_MLQUEUE_SHIFT, SCSI_LOG_MLQUEUE_BITS, LEVEL,CMD); +#define SCSI_LOG_MLCOMPLETE(LEVEL,CMD) \ + SCSI_CHECK_LOGGING(SCSI_LOG_MLCOMPLETE_SHIFT, SCSI_LOG_MLCOMPLETE_BITS, LEVEL,CMD); +#define SCSI_LOG_LLQUEUE(LEVEL,CMD) \ + SCSI_CHECK_LOGGING(SCSI_LOG_LLQUEUE_SHIFT, SCSI_LOG_LLQUEUE_BITS, LEVEL,CMD); +#define SCSI_LOG_LLCOMPLETE(LEVEL,CMD) \ + SCSI_CHECK_LOGGING(SCSI_LOG_LLCOMPLETE_SHIFT, SCSI_LOG_LLCOMPLETE_BITS, LEVEL,CMD); +#define SCSI_LOG_HLQUEUE(LEVEL,CMD) \ + SCSI_CHECK_LOGGING(SCSI_LOG_HLQUEUE_SHIFT, SCSI_LOG_HLQUEUE_BITS, LEVEL,CMD); +#define SCSI_LOG_HLCOMPLETE(LEVEL,CMD) \ + SCSI_CHECK_LOGGING(SCSI_LOG_HLCOMPLETE_SHIFT, SCSI_LOG_HLCOMPLETE_BITS, LEVEL,CMD); +#define SCSI_LOG_IOCTL(LEVEL,CMD) \ + SCSI_CHECK_LOGGING(SCSI_LOG_IOCTL_SHIFT, SCSI_LOG_IOCTL_BITS, LEVEL,CMD); + + +#define SCSI_SET_ERROR_RECOVERY_LOGGING(LEVEL) \ + SCSI_SET_LOGGING(SCSI_LOG_ERROR_SHIFT, SCSI_LOG_ERROR_BITS, LEVEL); +#define SCSI_SET_TIMEOUT_LOGGING(LEVEL) \ + SCSI_SET_LOGGING(SCSI_LOG_TIMEOUT_SHIFT, SCSI_LOG_TIMEOUT_BITS, LEVEL); +#define SCSI_SET_SCAN_BUS_LOGGING(LEVEL) \ + SCSI_SET_LOGGING(SCSI_LOG_SCAN_SHIFT, SCSI_LOG_SCAN_BITS, LEVEL); +#define SCSI_SET_MLQUEUE_LOGGING(LEVEL) \ + SCSI_SET_LOGGING(SCSI_LOG_MLQUEUE_SHIFT, SCSI_LOG_MLQUEUE_BITS, LEVEL); +#define SCSI_SET_MLCOMPLETE_LOGGING(LEVEL) \ + SCSI_SET_LOGGING(SCSI_LOG_MLCOMPLETE_SHIFT, SCSI_LOG_MLCOMPLETE_BITS, LEVEL); +#define SCSI_SET_LLQUEUE_LOGGING(LEVEL) \ + SCSI_SET_LOGGING(SCSI_LOG_LLQUEUE_SHIFT, SCSI_LOG_LLQUEUE_BITS, LEVEL); +#define SCSI_SET_LLCOMPLETE_LOGGING(LEVEL) \ + SCSI_SET_LOGGING(SCSI_LOG_LLCOMPLETE_SHIFT, SCSI_LOG_LLCOMPLETE_BITS, LEVEL); +#define SCSI_SET_HLQUEUE_LOGGING(LEVEL) \ + SCSI_SET_LOGGING(SCSI_LOG_HLQUEUE_SHIFT, SCSI_LOG_HLQUEUE_BITS, LEVEL); +#define SCSI_SET_HLCOMPLETE_LOGGING(LEVEL) \ + SCSI_SET_LOGGING(SCSI_LOG_HLCOMPLETE_SHIFT, SCSI_LOG_HLCOMPLETE_BITS, LEVEL); +#define SCSI_SET_IOCTL_LOGGING(LEVEL) \ + SCSI_SET_LOGGING(SCSI_LOG_IOCTL_SHIFT, SCSI_LOG_IOCTL_BITS, LEVEL); /* * the return of the status word will be in the following format : @@ -81,6 +250,7 @@ #define DID_ERROR 0x07 /* Internal error */ #define DID_RESET 0x08 /* Reset by somebody. */ #define DID_BAD_INTR 0x09 /* Got an interrupt we weren't expecting. */ +#define DID_PASSTHROUGH 0x0a /* Force command past mid-layer */ #define DRIVER_OK 0x00 /* Driver status */ /* @@ -139,14 +309,107 @@ #define IS_ABORTING 0x10 #define ASKED_FOR_SENSE 0x20 + +#define CONTIGUOUS_BUFFERS(X,Y) ((X->b_data+X->b_size) == Y->b_data) + +/* + * This is the crap from the old error handling code. We have it in a special + * place so that we can more easily delete it later on. + */ +#include "scsi_obsolete.h" + +/* + * Add some typedefs so that we can prototyope a bunch of the functions. + */ +typedef struct scsi_device Scsi_Device; +typedef struct scsi_cmnd Scsi_Cmnd; + +/* + * Here is where we prototype most of the mid-layer. + */ + +/* + * Initializes all SCSI devices. This scans all scsi busses. + */ + +extern int scsi_dev_init (void); + + + +void * scsi_malloc(unsigned int); +int scsi_free(void *, unsigned int); +extern unsigned int scsi_logging_level; /* What do we log? */ +extern unsigned int scsi_dma_free_sectors; /* How much room do we have left */ +extern unsigned int scsi_need_isa_buffer; /* True if some devices need indirection + * buffers */ +extern void scsi_make_blocked_list(void); +extern volatile int in_scan_scsis; +extern const unsigned char scsi_command_size[8]; + +/* + * These are the error handling functions defined in scsi_error.c + */ +extern void scsi_add_timer(Scsi_Cmnd * SCset, int timeout, + void (*complete)(Scsi_Cmnd *)); +extern void scsi_done (Scsi_Cmnd *SCpnt); +extern int scsi_delete_timer(Scsi_Cmnd * SCset); +extern void scsi_error_handler(void * host); +extern int scsi_retry_command(Scsi_Cmnd *); +extern void scsi_finish_command(Scsi_Cmnd *); +extern int scsi_sense_valid(Scsi_Cmnd *); +extern int scsi_decide_disposition (Scsi_Cmnd * SCpnt); +extern int scsi_block_when_processing_errors(Scsi_Device *); +extern void scsi_sleep(int); + +/* + * scsi_abort aborts the current command that is executing on host host. + * The error code, if non zero is returned in the host byte, otherwise + * DID_ABORT is returned in the hostbyte. + */ + +extern void scsi_do_cmd (Scsi_Cmnd *, const void *cmnd , + void *buffer, unsigned bufflen, + void (*done)(struct scsi_cmnd *), + int timeout, int retries); + + +extern Scsi_Cmnd * scsi_allocate_device(struct request **, Scsi_Device *, int); + +extern Scsi_Cmnd * scsi_request_queueable(struct request *, Scsi_Device *); + +extern void scsi_release_command(Scsi_Cmnd *); + +extern int max_scsi_hosts; + +extern void proc_print_scsidevice(Scsi_Device *, char *, int *, int); + +extern void print_command(unsigned char *); +extern void print_sense(const char *, Scsi_Cmnd *); +extern void print_driverbyte(int scsiresult); +extern void print_hostbyte(int scsiresult); + /* * The scsi_device struct contains what we know about each given scsi * device. */ -typedef struct scsi_device { +struct scsi_device { +/* private: */ + /* + * This information is private to the scsi mid-layer. Wrapping it in a + * struct private is a way of marking it in a sort of C++ type of way. + */ struct scsi_device * next; /* Used for linked list */ + struct scsi_device * prev; /* Used for linked list */ + struct wait_queue * device_wait;/* Used to wait if + device is busy */ + struct Scsi_Host * host; + volatile unsigned short device_busy; /* commands actually active on low-level */ + void (* scsi_request_fn)(void); /* Used to jumpstart things after an + * ioctl */ + Scsi_Cmnd * device_queue; /* queue of SCSI Command structures */ +/* public: */ unsigned char id, lun, channel; unsigned int manufacturer; /* Manufacturer of device, for using @@ -154,11 +417,7 @@ int attached; /* # of high level drivers attached to * this */ int access_count; /* Count of open channels/mounts */ - struct wait_queue * device_wait;/* Used to wait if device is busy */ - struct Scsi_Host * host; - void (*scsi_request_fn)(void); /* Used to jumpstart things after an - * ioctl */ - struct scsi_cmnd *device_queue; /* queue of SCSI Command structures */ + void *hostdata; /* available to low-level driver */ char type; char scsi_level; @@ -168,6 +427,7 @@ unsigned char sync_max_offset; /* Not greater than this offset */ unsigned char queue_depth; /* How deep a queue to use */ + unsigned online:1; unsigned writeable:1; unsigned removable:1; unsigned random:1; @@ -190,35 +450,8 @@ * this device */ unsigned expecting_cc_ua:1; /* Expecting a CHECK_CONDITION/UNIT_ATTN * because we did a bus reset. */ -} Scsi_Device; - -/* - * Use these to separate status msg and our bytes - */ - -#define status_byte(result) (((result) >> 1) & 0x1f) -#define msg_byte(result) (((result) >> 8) & 0xff) -#define host_byte(result) (((result) >> 16) & 0xff) -#define driver_byte(result) (((result) >> 24) & 0xff) -#define suggestion(result) (driver_byte(result) & SUGGEST_MASK) - -#define sense_class(sense) (((sense) >> 4) & 0x7) -#define sense_error(sense) ((sense) & 0xf) -#define sense_valid(sense) ((sense) & 0x80); - -/* - * These are the SCSI devices available on the system. - */ - -extern Scsi_Device * scsi_devices; - -/* - * Initializes all SCSI devices. This scans all scsi busses. - */ - -extern int scsi_dev_init (void); - -#include + unsigned device_blocked:1; /* Device returned QUEUE_FULL. */ +}; #ifdef __mc68000__ #include @@ -230,109 +463,6 @@ /* - * These are the return codes for the abort and reset functions. The mid-level - * code uses these to decide what to do next. Each of the low level abort - * and reset functions must correctly indicate what it has done. - * The descriptions are written from the point of view of the mid-level code, - * so that the return code is telling the mid-level drivers exactly what - * the low level driver has already done, and what remains to be done. - */ - -/* We did not do anything. - * Wait some more for this command to complete, and if this does not work, - * try something more serious. */ -#define SCSI_ABORT_SNOOZE 0 - -/* This means that we were able to abort the command. We have already - * called the mid-level done function, and do not expect an interrupt that - * will lead to another call to the mid-level done function for this command */ -#define SCSI_ABORT_SUCCESS 1 - -/* We called for an abort of this command, and we should get an interrupt - * when this succeeds. Thus we should not restore the timer for this - * command in the mid-level abort function. */ -#define SCSI_ABORT_PENDING 2 - -/* Unable to abort - command is currently on the bus. Grin and bear it. */ -#define SCSI_ABORT_BUSY 3 - -/* The command is not active in the low level code. Command probably - * finished. */ -#define SCSI_ABORT_NOT_RUNNING 4 - -/* Something went wrong. The low level driver will indicate the correct - * error condition when it calls scsi_done, so the mid-level abort function - * can simply wait until this comes through */ -#define SCSI_ABORT_ERROR 5 - -/* We do not know how to reset the bus, or we do not want to. Bummer. - * Anyway, just wait a little more for the command in question, and hope that - * it eventually finishes. If it never finishes, the SCSI device could - * hang, so use this with caution. */ -#define SCSI_RESET_SNOOZE 0 - -/* We do not know how to reset the bus, or we do not want to. Bummer. - * We have given up on this ever completing. The mid-level code will - * request sense information to decide how to proceed from here. */ -#define SCSI_RESET_PUNT 1 - -/* This means that we were able to reset the bus. We have restarted all of - * the commands that should be restarted, and we should be able to continue - * on normally from here. We do not expect any interrupts that will return - * DID_RESET to any of the other commands in the host_queue, and the mid-level - * code does not need to do anything special to keep the commands alive. - * If a hard reset was performed then all outstanding commands on the - * bus have been restarted. */ -#define SCSI_RESET_SUCCESS 2 - -/* We called for a reset of this bus, and we should get an interrupt - * when this succeeds. Each command should get its own status - * passed up to scsi_done, but this has not happened yet. - * If a hard reset was performed, then we expect an interrupt - * for *each* of the outstanding commands that will have the - * effect of restarting the commands. - */ -#define SCSI_RESET_PENDING 3 - -/* We did a reset, but do not expect an interrupt to signal DID_RESET. - * This tells the upper level code to request the sense info, and this - * should keep the command alive. */ -#define SCSI_RESET_WAKEUP 4 - -/* The command is not active in the low level code. Command probably - finished. */ -#define SCSI_RESET_NOT_RUNNING 5 - -/* Something went wrong, and we do not know how to fix it. */ -#define SCSI_RESET_ERROR 6 - -#define SCSI_RESET_SYNCHRONOUS 0x01 -#define SCSI_RESET_ASYNCHRONOUS 0x02 -#define SCSI_RESET_SUGGEST_BUS_RESET 0x04 -#define SCSI_RESET_SUGGEST_HOST_RESET 0x08 -/* - * This is a bitmask that is ored with one of the above codes. - * It tells the mid-level code that we did a hard reset. - */ -#define SCSI_RESET_BUS_RESET 0x100 -/* - * This is a bitmask that is ored with one of the above codes. - * It tells the mid-level code that we did a host adapter reset. - */ -#define SCSI_RESET_HOST_RESET 0x200 -/* - * Used to mask off bits and to obtain the basic action that was - * performed. - */ -#define SCSI_RESET_ACTION 0xff - -void * scsi_malloc(unsigned int); -int scsi_free(void *, unsigned int); -extern unsigned int dma_free_sectors; /* How much room do we have left */ -extern unsigned int need_isa_buffer; /* True if some devices need indirection - * buffers */ - -/* * The Scsi_Cmnd structure is used by scsi.c internally, and for communication * with low level drivers that support multiple outstanding commands. */ @@ -349,43 +479,22 @@ volatile int phase; } Scsi_Pointer; -typedef struct scsi_cmnd { + +struct scsi_cmnd { +/* private: */ + /* + * This information is private to the scsi mid-layer. Wrapping it in a + * struct private is a way of marking it in a sort of C++ type of way. + */ struct Scsi_Host * host; - Scsi_Device * device; - unsigned char target, lun, channel; - unsigned char cmd_len; - unsigned char old_cmd_len; - struct scsi_cmnd *next, *prev, *device_next, *reset_chain; - - /* These elements define the operation we are about to perform */ - unsigned char cmnd[12]; - unsigned request_bufflen; /* Actual request size */ - - void * request_buffer; /* Actual requested buffer */ - - /* These elements define the operation we ultimately want to perform */ - unsigned char data_cmnd[12]; - unsigned short old_use_sg; /* We save use_sg here when requesting - * sense info */ - unsigned short use_sg; /* Number of pieces of scatter-gather */ - unsigned short sglist_len; /* size of malloc'd scatter-gather list */ - unsigned short abort_reason;/* If the mid-level code requests an - * abort, this is the reason. */ - unsigned bufflen; /* Size of data buffer */ - void *buffer; /* Data buffer */ - - unsigned underflow; /* Return error if less than this amount is - * transfered */ - - unsigned transfersize; /* How much we are guaranteed to transfer with - * each SCSI transfer (ie, between disconnect / - * reconnects. Probably == sector size */ + unsigned short state; + unsigned short owner; + Scsi_Device * device; + struct scsi_cmnd * next; + struct scsi_cmnd * reset_chain; - - struct request request; /* A copy of the command we are working on */ - - unsigned char sense_buffer[16]; /* Sense for this command, if needed */ - + int eh_state; /* Used for state tracking in error handlr */ + void (*done)(struct scsi_cmnd *); /* Mid-level done function */ /* A SCSI Command is assigned a nonzero serial_number when internal_cmnd passes it to the driver's queue command function. The serial_number @@ -398,41 +507,96 @@ completed and the SCSI Command structure has already being reused for another command, so that we can avoid incorrectly aborting or resetting the new command. - */ - - unsigned long serial_number; - unsigned long serial_number_at_timeout; - - int retries; - int allowed; - int timeout_per_command, timeout_total, timeout; - + */ + + unsigned long serial_number; + unsigned long serial_number_at_timeout; + + int retries; + int allowed; + int timeout_per_command; + int timeout_total; + int timeout; + /* - * We handle the timeout differently if it happens when a reset, - * abort, etc are in process. + * We handle the timeout differently if it happens when a reset, + * abort, etc are in process. */ unsigned volatile char internal_timeout; + struct scsi_cmnd * bh_next; /* To enumerate the commands waiting + to be processed. */ + +/* public: */ + + unsigned char target; + unsigned char lun; + unsigned char channel; + unsigned char cmd_len; + unsigned char old_cmd_len; + + /* These elements define the operation we are about to perform */ + unsigned char cmnd[12]; + unsigned request_bufflen; /* Actual request size */ + + struct timer_list eh_timeout; /* Used to time out the command. */ + void * request_buffer; /* Actual requested buffer */ + + /* These elements define the operation we ultimately want to perform */ + unsigned char data_cmnd[12]; + unsigned short old_use_sg; /* We save use_sg here when requesting + * sense info */ + unsigned short use_sg; /* Number of pieces of scatter-gather */ + unsigned short sglist_len; /* size of malloc'd scatter-gather list */ + unsigned short abort_reason; /* If the mid-level code requests an + * abort, this is the reason. */ + unsigned bufflen; /* Size of data buffer */ + void * buffer; /* Data buffer */ + + unsigned underflow; /* Return error if less than + this amount is transfered */ + + unsigned transfersize; /* How much we are guaranteed to + transfer with each SCSI transfer + (ie, between disconnect / + reconnects. Probably == sector + size */ - unsigned flags; + struct request request; /* A copy of the command we are + working on */ + + unsigned char sense_buffer[16]; /* Sense for this command, + needed */ + + unsigned flags; + + /* + * These two flags are used to track commands that are in the + * mid-level queue. The idea is that a command can be there for + * one of two reasons - either the host is busy or the device is + * busy. Thus when a command on the host finishes, we only try + * and requeue commands that we might expect to be queueable. + */ + unsigned host_wait:1; + unsigned device_wait:1; + /* These variables are for the cdrom only. Once we have variable size * buffers in the buffer cache, they will go away. */ - int this_count; + int this_count; /* End of special cdrom variables */ /* Low-level done function - can be used by low-level driver to point * to completion function. Not used by mid/upper level code. */ - void (*scsi_done)(struct scsi_cmnd *); - void (*done)(struct scsi_cmnd *); /* Mid-level done function */ + void (*scsi_done)(struct scsi_cmnd *); /* * The following fields can be written to by the host specific code. * Everything else should be left alone. */ - Scsi_Pointer SCp; /* Scratchpad used by some host adapters */ + Scsi_Pointer SCp; /* Scratchpad used by some host adapters */ - unsigned char * host_scribble; /* The host adapter is allowed to + unsigned char * host_scribble; /* The host adapter is allowed to * call scsi_malloc and get some memory * and hang it here. The host adapter * is also expected to call scsi_free @@ -440,42 +604,22 @@ * obtained by scsi_malloc is guaranteed * to be at an address < 16Mb). */ - int result; /* Status code from lower level driver */ + int result; /* Status code from lower level driver */ - unsigned char tag; /* SCSI-II queued command tag */ - unsigned long pid; /* Process ID, starts at 0 */ -} Scsi_Cmnd; + unsigned char tag; /* SCSI-II queued command tag */ + unsigned long pid; /* Process ID, starts at 0 */ +}; + /* - * scsi_abort aborts the current command that is executing on host host. - * The error code, if non zero is returned in the host byte, otherwise - * DID_ABORT is returned in the hostbyte. + * Definitions and prototypes used for scsi mid-level queue. */ +#define SCSI_MLQUEUE_HOST_BUSY 0x1055 +#define SCSI_MLQUEUE_DEVICE_BUSY 0x1056 -extern int scsi_abort (Scsi_Cmnd *, int code); - -extern void scsi_do_cmd (Scsi_Cmnd *, const void *cmnd , - void *buffer, unsigned bufflen, - void (*done)(struct scsi_cmnd *), - int timeout, int retries); - - -extern Scsi_Cmnd * allocate_device(struct request **, Scsi_Device *, int); - -extern Scsi_Cmnd * request_queueable(struct request *, Scsi_Device *); -extern int scsi_reset (Scsi_Cmnd *, unsigned int); - -extern int max_scsi_hosts; - -extern void proc_print_scsidevice(Scsi_Device *, char *, int *, int); - -extern void print_command(unsigned char *); -extern void print_sense(const char *, Scsi_Cmnd *); -extern void print_driverbyte(int scsiresult); -extern void print_hostbyte(int scsiresult); +extern scsi_mlqueue_insert(Scsi_Cmnd * cmd, int reason); +extern scsi_mlqueue_finish(struct Scsi_Host * host, Scsi_Device * device); -extern void scsi_mark_host_reset(struct Scsi_Host *Host); -extern void scsi_mark_bus_reset(struct Scsi_Host *Host, int channel); #if defined(MAJOR_NR) && (MAJOR_NR != SCSI_TAPE_MAJOR) #include "hosts.h" @@ -527,9 +671,9 @@ wake_up(&next->host_wait); } - req->rq_status = RQ_INACTIVE; wake_up(&wait_for_request); wake_up(&SCpnt->device->device_wait); + scsi_release_command(SCpnt); return NULL; } diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/scsi_debug.c linux/drivers/scsi/scsi_debug.c --- v2.1.74/linux/drivers/scsi/scsi_debug.c Mon May 6 02:26:11 1996 +++ linux/drivers/scsi/scsi_debug.c Sun Dec 21 17:04:49 1997 @@ -43,9 +43,16 @@ /* A few options that we want selected */ +#define NR_HOSTS_PRESENT 1 +#define NR_FAKE_DISKS 3 +#define N_HEAD 32 +#define N_SECTOR 64 +#define DISK_READONLY(TGT) (1) +#define DISK_REMOVEABLE(TGT) (1) + /* Do not attempt to use a timer to simulate a real disk with latency */ /* Only use this in the actual kernel, not in the simulator. */ -#define IMMEDIATE +/* #define IMMEDIATE */ /* Skip some consistency checking. Good for benchmarking */ #define SPEEDY @@ -58,12 +65,12 @@ #define MAJOR_NR 8 #endif #define START_PARTITION 4 -#define SCSI_DEBUG_TIMER 20 + /* Number of jiffies to wait before completing a command */ #define DISK_SPEED 10 #define CAPACITY (0x80000) -static int starts[] = {4, 1000, 50000, CAPACITY, 0}; +static int starts[] = {N_HEAD, N_HEAD * N_SECTOR, 50000, CAPACITY, 0}; static int npart = 0; #include "scsi_debug.h" @@ -74,8 +81,8 @@ #endif #ifdef SPEEDY -#define VERIFY1_DEBUG(RW) 1 -#define VERIFY_DEBUG(RW) 1 +#define VERIFY1_DEBUG(RW) +#define VERIFY_DEBUG(RW) #else #define VERIFY1_DEBUG(RW) \ @@ -111,12 +118,16 @@ }; #endif -static volatile void (*do_done[SCSI_DEBUG_MAILBOXES])(Scsi_Cmnd *) = {NULL, }; -extern void scsi_debug_interrupt(); +typedef void (*done_fct_t)(Scsi_Cmnd *); + +static volatile done_fct_t do_done[SCSI_DEBUG_MAILBOXES] = {NULL, }; + +static void scsi_debug_intr_handle(unsigned long); -volatile Scsi_Cmnd * SCint[SCSI_DEBUG_MAILBOXES] = {NULL,}; +static struct timer_list timeout[SCSI_DEBUG_MAILBOXES]; + +Scsi_Cmnd * SCint[SCSI_DEBUG_MAILBOXES] = {NULL,}; static char SCrst[SCSI_DEBUG_MAILBOXES] = {0,}; -static volatile unsigned int timeout[8] ={0,}; /* * Semaphore used to simulate bus lockups. @@ -137,11 +148,11 @@ sgpnt = (struct scatterlist *) SCpnt->buffer; for(i=0; iuse_sg; i++) { lpnt = (int *) sgpnt[i].alt_address; - printk(":%x %x %d\n",sgpnt[i].alt_address, sgpnt[i].address, sgpnt[i].length); + printk(":%p %p %d\n",sgpnt[i].alt_address, sgpnt[i].address, sgpnt[i].length); if (lpnt) printk(" (Alt %x) ",lpnt[15]); }; } else { - printk("nosg: %x %x %d\n",SCpnt->request.buffer, SCpnt->buffer, + printk("nosg: %p %p %d\n",SCpnt->request.buffer, SCpnt->buffer, SCpnt->bufflen); lpnt = (int *) SCpnt->request.buffer; if (lpnt) printk(" (Alt %x) ",lpnt[15]); @@ -167,14 +178,14 @@ }; printk("\n"); #endif - printk("DMA free %d sectors.\n", dma_free_sectors); + printk("DMA free %d sectors.\n", scsi_dma_free_sectors); } int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *)) { unchar *cmd = (unchar *) SCpnt->cmnd; struct partition * p; - int block, start; + int block; struct buffer_head * bh = NULL; unsigned char * buff; int nbytes, sgcount; @@ -187,15 +198,28 @@ sgcount = 0; sgpnt = NULL; - DEB(if (target > 1) { SCpnt->result = DID_TIME_OUT << 16;done(SCpnt);return 0;}); + /* + * If we are being notified of the mid-level reposessing a command due to timeout, + * just return. + */ + if( done == NULL ) + { + return 0; + } + + DEB(if (target >= NR_FAKE_DISKS) + { + SCpnt->result = DID_TIME_OUT << 16;done(SCpnt);return 0; + }); buff = (unsigned char *) SCpnt->request_buffer; - if(target>=1 || SCpnt->lun != 0) { + if(target>=NR_FAKE_DISKS || SCpnt->lun != 0) + { SCpnt->result = DID_NO_CONNECT << 16; done(SCpnt); return 0; - }; + } if( SCrst[target] != 0 && !scsi_debug_lockup ) { @@ -208,11 +232,11 @@ } switch(*cmd){ case REQUEST_SENSE: - printk("Request sense...\n"); + SCSI_LOG_LLQUEUE(3,printk("Request sense...\n")); #ifndef DEBUG { int i; - printk("scsi_debug: Requesting sense buffer (%x %x %x %d):", SCpnt, buff, done, bufflen); + printk("scsi_debug: Requesting sense buffer (%p %p %p %d):", SCpnt, buff, done, bufflen); for(i=0;i<12;i++) printk("%d ",sense_buffer[i]); printk("\n"); }; @@ -224,15 +248,21 @@ done(SCpnt); return 0; case ALLOW_MEDIUM_REMOVAL: - if(cmd[4]) printk("Medium removal inhibited..."); - else printk("Medium removal enabled..."); + if(cmd[4]) + { + SCSI_LOG_LLQUEUE(2,printk("Medium removal inhibited...")); + } + else + { + SCSI_LOG_LLQUEUE(2,printk("Medium removal enabled...")); + } scsi_debug_errsts = 0; break; case INQUIRY: - printk("Inquiry...(%x %d)\n", buff, bufflen); + SCSI_LOG_LLQUEUE(3,printk("Inquiry...(%p %d)\n", buff, bufflen)); memset(buff, 0, bufflen); buff[0] = TYPE_DISK; - buff[1] = 0x80; /* Removable disk */ + buff[1] = DISK_REMOVEABLE(target) ? 0x80 : 0; /* Removable disk */ buff[2] = 1; buff[4] = 33 - 5; memcpy(&buff[8],"Foo Inc",7); @@ -241,13 +271,13 @@ scsi_debug_errsts = 0; break; case TEST_UNIT_READY: - printk("Test unit ready(%x %d)\n", buff, bufflen); + SCSI_LOG_LLQUEUE(3,printk("Test unit ready(%p %d)\n", buff, bufflen)); if (buff) memset(buff, 0, bufflen); scsi_debug_errsts = 0; break; case READ_CAPACITY: - printk("Read Capacity\n"); + SCSI_LOG_LLQUEUE(3,printk("Read Capacity\n")); if(NR_REAL < 0) NR_REAL = (MINOR(SCpnt->request.rq_dev) >> 4) & 0x0f; memset(buff, 0, bufflen); buff[0] = (CAPACITY >> 24); @@ -308,6 +338,12 @@ p->start_sect = starts[npart]; p->nr_sects = starts[npart+1] - starts [npart]; p->sys_ind = 0x81; /* Linux partition */ + p->head = (npart == 0 ? 1 : 0); + p->sector = 1; + p->cyl = starts[npart] / N_HEAD / N_SECTOR; + p->end_head = N_HEAD - 1; + p->end_sector = N_SECTOR; + p->end_cyl = starts[npart + 1] / N_HEAD / N_SECTOR; p++; npart++; }; @@ -365,7 +401,7 @@ SCpnt->result = 0; (done)(SCpnt); - return; + return 0; if (SCpnt->use_sg && !scsi_debug_errsts) if(bh) scsi_dump(SCpnt, 0); @@ -396,8 +432,15 @@ #endif scsi_debug_errsts = 0; break; + case MODE_SENSE: + /* + * Used to detect write protected status. + */ + scsi_debug_errsts = 0; + memset(buff, 0, 6); + break; default: - printk("Unknown command %d\n",*cmd); + SCSI_LOG_LLQUEUE(3,printk("Unknown command %d\n",*cmd)); SCpnt->result = DID_NO_CONNECT << 16; done(SCpnt); return 0; @@ -406,45 +449,45 @@ save_flags(flags); cli(); for(i=0;i= SCSI_DEBUG_MAILBOXES || SCint[i] != 0) - panic("Unable to find empty SCSI_DEBUG command slot.\n"); - - SCint[i] = SCpnt; - - if (done) { - DEB(printk("scsi_debug_queuecommand: now waiting for interrupt ");); - if (do_done[i]) - printk("scsi_debug_queuecommand: Two concurrent queuecommand?\n"); - else - do_done[i] = done; + /* + * If all of the slots are full, just return 1. The new error handling scheme + * allows this, and the mid-level should queue things. + */ + if (i >= SCSI_DEBUG_MAILBOXES || timeout[i].function != 0) + { + SCSI_LOG_LLQUEUE(1,printk("Command rejected - host busy\n")); + restore_flags(flags); + return 1; } - else - printk("scsi_debug_queuecommand: done can't be NULL\n"); + + SCSI_LOG_LLQUEUE(1,printk("Command accepted - slot %d\n", i)); #ifdef IMMEDIATE if( !scsi_debug_lockup ) { SCpnt->result = scsi_debug_errsts; - scsi_debug_intr_handle(); /* No timer - do this one right away */ + scsi_debug_intr_handle(i); /* No timer - do this one right away */ } restore_flags(flags); #else - timeout[i] = jiffies+DISK_SPEED; - - /* If no timers active, then set this one */ - if ((timer_active & (1 << SCSI_DEBUG_TIMER)) == 0) { - timer_table[SCSI_DEBUG_TIMER].expires = timeout[i]; - timer_active |= 1 << SCSI_DEBUG_TIMER; - }; - + SCpnt->result = scsi_debug_errsts; + timeout[i].function = scsi_debug_intr_handle; + timeout[i].data = i; + timeout[i].expires = jiffies + DISK_SPEED; + SCint[i] = SCpnt; + do_done[i] = done; + restore_flags(flags); + add_timer(&timeout[i]); + if (!done) + panic("scsi_debug_queuecommand: done can't be NULL\n"); #if 0 - printk("Sending command (%d %x %d %d)...", i, done, timeout[i],jiffies); + printk("Sending command (%d %x %d %d)...", i, done, timeout[i].expires,jiffies); #endif #endif @@ -472,92 +515,52 @@ /* A "high" level interrupt handler. This should be called once per jiffy * to simulate a regular scsi disk. We use a timer to do this. */ -static void scsi_debug_intr_handle(void) +static void scsi_debug_intr_handle(unsigned long indx) { Scsi_Cmnd * SCtmp; - int i, pending; + int pending; void (*my_done)(Scsi_Cmnd *); unsigned long flags; int to; -#ifndef IMMEDIATE - timer_table[SCSI_DEBUG_TIMER].expires = 0; - timer_active &= ~(1 << SCSI_DEBUG_TIMER); -#endif - - repeat: - save_flags(flags); - cli(); - for(i=0;i jiffies) { - if (pending > timeout[i]) pending = timeout[i]; - continue; - }; - }; - if (pending && pending != INT_MAX) { - timer_table[SCSI_DEBUG_TIMER].expires = - (pending <= jiffies ? jiffies+1 : pending); - timer_active |= 1 << SCSI_DEBUG_TIMER; - }; - restore_flags(flags); -#endif - return; - }; + SCtmp = (Scsi_Cmnd *) SCint[indx]; + my_done = do_done[indx]; + do_done[indx] = NULL; + timeout[indx].function = NULL; + SCint[indx] = NULL; + + if (!my_done) { + printk("scsi_debug_intr_handle: Unexpected interrupt\n"); + return; + } - if(i < SCSI_DEBUG_MAILBOXES){ - timeout[i] = 0; - my_done = do_done[i]; - do_done[i] = NULL; - to = timeout[i]; - timeout[i] = 0; - SCtmp = (Scsi_Cmnd *) SCint[i]; - SCint[i] = NULL; - restore_flags(flags); - - if (!my_done) { - printk("scsi_debug_intr_handle: Unexpected interrupt\n"); - return; - } - #ifdef DEBUG - printk("In intr_handle..."); - printk("...done %d %x %d %d\n",i , my_done, to, jiffies); - printk("In intr_handle: %d %x %x\n",i, SCtmp, my_done); + printk("In intr_handle..."); + printk("...done %d %x %d %d\n",i , my_done, to, jiffies); + printk("In intr_handle: %d %x %x\n",i, SCtmp, my_done); #endif - - my_done(SCtmp); + + my_done(SCtmp); #ifdef DEBUG - printk("Called done.\n"); + printk("Called done.\n"); #endif - }; - goto repeat; } int scsi_debug_detect(Scsi_Host_Template * tpnt) { - tpnt->proc_dir = &proc_scsi_scsi_debug; -#ifndef IMMEDIATE - timer_table[SCSI_DEBUG_TIMER].fn = scsi_debug_intr_handle; - timer_table[SCSI_DEBUG_TIMER].expires = 0; -#endif - return 1; + int i; + + for(i=0; i < NR_HOSTS_PRESENT; i++) + { + tpnt->proc_dir = &proc_scsi_scsi_debug; + scsi_register(tpnt,0); + } + return NR_HOSTS_PRESENT; } int scsi_debug_abort(Scsi_Cmnd * SCpnt) @@ -587,20 +590,20 @@ int scsi_debug_biosparam(Disk * disk, kdev_t dev, int* info){ int size = disk->capacity; - info[0] = 32; - info[1] = 64; + info[0] = N_HEAD; + info[1] = N_SECTOR; info[2] = (size + 2047) >> 11; if (info[2] >= 1024) info[2] = 1024; return 0; } -int scsi_debug_reset(Scsi_Cmnd * SCpnt) +int scsi_debug_reset(Scsi_Cmnd * SCpnt, unsigned int why) { int i; unsigned long flags; void (*my_done)(Scsi_Cmnd *); - printk("Bus unlocked by reset(%d)\n", SCpnt->host->suggest_bus_reset); + printk("Bus unlocked by reset - %d\n", why); scsi_debug_lockup = 0; DEB(printk("scsi_debug_reset called\n")); for(i=0;i= 10 && strncmp(buffer, "scsi_debug", 10) == 0) { buffer += 11; length -= 11; + + if( buffer[length - 1] == '\n' ) + { + buffer[length-1] = '\0'; + length--; + } + /* * OK, we are getting some kind of command. Figure out * what we are supposed to do here. Simulate bus lockups @@ -647,18 +659,18 @@ if( length == 6 && strncmp(buffer, "lockup", length) == 0 ) { scsi_debug_lockup = 1; - return length; + return orig_length; } if( length == 6 && strncmp(buffer, "unlock", length) == 0 ) { scsi_debug_lockup = 0; - return length; + return orig_length; } - printk("Unknown command:%s\n", buffer); + printk("Unknown command:%s (%d)\n", buffer, length); } else - printk("Wrong Signature:%10s\n", (char *) ((ulong)buffer-11)); + printk("Wrong Signature:%10s\n", (char *) buffer); return -EINVAL; diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/scsi_debug.h linux/drivers/scsi/scsi_debug.h --- v2.1.74/linux/drivers/scsi/scsi_debug.h Thu Jun 12 15:29:27 1997 +++ linux/drivers/scsi/scsi_debug.h Sun Dec 21 17:04:49 1997 @@ -8,7 +8,7 @@ int scsi_debug_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); int scsi_debug_abort(Scsi_Cmnd *); int scsi_debug_biosparam(Disk *, kdev_t, int[]); -int scsi_debug_reset(Scsi_Cmnd *); +int scsi_debug_reset(Scsi_Cmnd *, unsigned int); int scsi_debug_proc_info(char *, char **, off_t, int, int, int); #ifndef NULL @@ -16,15 +16,28 @@ #endif -#define SCSI_DEBUG_MAILBOXES 8 +#define SCSI_DEBUG_MAILBOXES 1 -#define SCSI_DEBUG {NULL, NULL, NULL, scsi_debug_proc_info, \ - "SCSI DEBUG", scsi_debug_detect, NULL, \ - NULL, scsi_debug_command, \ - scsi_debug_queuecommand, \ - scsi_debug_abort, \ - scsi_debug_reset, \ - NULL, \ - scsi_debug_biosparam, \ - SCSI_DEBUG_MAILBOXES, 7, SG_ALL, 1, 0, 1, ENABLE_CLUSTERING} +/* + * Allow the driver to reject commands. Thus we accept only one, but + * and the mid-level will queue the remainder. + */ +#define SCSI_DEBUG_CANQUEUE 255 + +#define SCSI_DEBUG {proc_info: scsi_debug_proc_info, \ + name: "SCSI DEBUG", \ + detect: scsi_debug_detect, \ + command: scsi_debug_command, \ + queuecommand: scsi_debug_queuecommand, \ + abort: scsi_debug_abort, \ + reset: scsi_debug_reset, \ + bios_param: scsi_debug_biosparam, \ + can_queue: SCSI_DEBUG_CANQUEUE, \ + this_id: 7, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: 3, \ + unchecked_isa_dma: 1, \ + use_clustering: ENABLE_CLUSTERING, \ + use_new_eh_code: 1, \ +} #endif diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/scsi_error.c linux/drivers/scsi/scsi_error.c --- v2.1.74/linux/drivers/scsi/scsi_error.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/scsi_error.c Sun Dec 21 17:04:49 1997 @@ -0,0 +1,1895 @@ +/* + * scsi_error.c Copyright (C) 1997 Eric Youngdale + * + * SCSI error/timeout handling + * Initial versions: Eric Youngdale. Based upon conversations with + * Leonard Zubkoff and David Miller at Linux Expo, + * ideas originating from all over the place. + * + */ + +#include +#define __NO_VERSION__ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define __KERNEL_SYSCALLS__ + +#include + +#include +#include +#include + +#include "scsi.h" +#include "hosts.h" +#include "constants.h" + +#define SHUTDOWN_SIGS (sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM)) + +#ifdef CONFIG_KERNELD +#include +#endif + +#ifdef DEBUG + #define SENSE_TIMEOUT SCSI_TIMEOUT + #define ABORT_TIMEOUT SCSI_TIMEOUT + #define RESET_TIMEOUT SCSI_TIMEOUT +#else + #define SENSE_TIMEOUT (10*HZ) + #define RESET_TIMEOUT (2*HZ) + #define ABORT_TIMEOUT (15*HZ) +#endif + +#define STATIC + +/* + * These should *probably* be handled by the host itself. + * Since it is allowed to sleep, it probably should. + */ +#define BUS_RESET_SETTLE_TIME 5*HZ +#define HOST_RESET_SETTLE_TIME 10*HZ + + +static const char RCSid[] = "$Header: /mnt/ide/home/eric/CVSROOT/linux/drivers/scsi/scsi_error.c,v 1.9 1997/12/07 23:38:23 eric Exp $"; + +STATIC int scsi_check_sense (Scsi_Cmnd * SCpnt); +STATIC int scsi_request_sense(Scsi_Cmnd *); +STATIC void scsi_send_eh_cmnd (Scsi_Cmnd * SCpnt, int timeout); +STATIC int scsi_try_to_abort_command(Scsi_Cmnd *, int); +STATIC int scsi_test_unit_ready(Scsi_Cmnd *); +STATIC int scsi_try_bus_device_reset(Scsi_Cmnd *, int timeout); +STATIC int scsi_try_bus_reset(Scsi_Cmnd *); +STATIC int scsi_try_host_reset(Scsi_Cmnd *); +STATIC int scsi_unit_is_ready(Scsi_Cmnd *); +STATIC void scsi_eh_action_done(Scsi_Cmnd *, int); +STATIC int scsi_eh_retry_command(Scsi_Cmnd *); +STATIC int scsi_eh_completed_normally(Scsi_Cmnd * SCpnt); +STATIC void scsi_restart_operations(struct Scsi_Host *); +STATIC void scsi_eh_finish_command(Scsi_Cmnd ** SClist, Scsi_Cmnd * SCpnt); + + +/* + * Function: scsi_add_timer() + * + * Purpose: Start timeout timer for a single scsi command. + * + * Arguments: SCset - command that is about to start running. + * timeout - amount of time to allow this command to run. + * complete - timeout function to call if timer isn't + * canceled. + * + * Returns: Nothing + * + * Notes: This should be turned into an inline function. + * + * More Notes: Each scsi command has it's own timer, and as it is added to + * the queue, we set up the timer. When the command completes, + * we cancel the timer. Pretty simple, really, especially + * compared to the old way of handling this crap. + */ +void +scsi_add_timer(Scsi_Cmnd * SCset, + int timeout, + void (*complete)(Scsi_Cmnd *)) +{ + + /* + * If the clock was already running for this command, then + * first delete the timer. The timer handling code gets rather + * confused if we don't do this. + */ + if( SCset->eh_timeout.function != NULL ) + { + del_timer(&SCset->eh_timeout); + } + + SCset->eh_timeout.data = (unsigned long) SCset; + SCset->eh_timeout.expires = jiffies + timeout; + SCset->eh_timeout.function = (void (*)(unsigned long))complete; + + SCSI_LOG_ERROR_RECOVERY(5,printk("Adding timer for command %p at %d (%p)\n", SCset, timeout, complete)); + + add_timer(&SCset->eh_timeout); + +} + +/* + * Function: scsi_delete_timer() + * + * Purpose: Delete/cancel timer for a given function. + * + * Arguments: SCset - command that we are canceling timer for. + * + * Returns: Amount of time remaining before command would have timed out. + * + * Notes: This should be turned into an inline function. + */ +int +scsi_delete_timer(Scsi_Cmnd * SCset) +{ + int rtn; + + rtn = jiffies - SCset->eh_timeout.expires; + del_timer(&SCset->eh_timeout); + + SCSI_LOG_ERROR_RECOVERY(5,printk("Clearing timer for command %p\n", SCset)); + + SCset->eh_timeout.data = (unsigned long) NULL; + SCset->eh_timeout.expires = 0; + SCset->eh_timeout.function = NULL; + + return rtn; +} + +/* + * Function: scsi_times_out() + * + * Purpose: Timeout function for normal scsi commands.. + * + * Arguments: SCpnt - command that is timing out. + * + * Returns: Nothing. + * + * Notes: + */ +void scsi_times_out (Scsi_Cmnd * SCpnt) +{ + + /* + * Notify the low-level code that this operation failed and we are + * reposessing the command. + */ +#ifdef ERIC_neverdef + /* + * FIXME(eric) + * Allow the host adapter to push a queue ordering tag + * out to the bus to force the command in question to complete. + * If the host wants to do this, then we just restart the timer + * for the command. Before we really do this, some real thought + * as to the optimum way to handle this should be done. We *do* + * need to force ordering every so often to ensure that all requests + * do eventually complete, but I am not sure if this is the best way + * to actually go about it. + * + * Better yet, force a sync here, but don't block since we are in an + * interrupt. + */ + if( SCpnt->host->hostt->eh_ordered_queue_tag ) + { + if( (*SCpnt->host->hostt->eh_ordered_queue_tag)(SCpnt)) + { + scsi_add_timer(SCpnt, SCpnt->internal_timeout, + scsi_times_out); + return; + } + } + /* + * FIXME(eric) - add a second special interface to handle this + * case. Ideally that interface can also be used to request + * a queu + */ + if (SCpnt->host->can_queue) + { + SCpnt->host->hostt->queuecommand (SCpnt, NULL); + } +#endif + + SCpnt->state = SCSI_STATE_TIMEOUT; + SCpnt->owner = SCSI_OWNER_ERROR_HANDLER; + + SCpnt->host->in_recovery = 1; + SCpnt->host->host_failed++; + + SCSI_LOG_TIMEOUT(3,printk("Command timed out active=%d busy=%d failed=%d\n", + atomic_read(&SCpnt->host->host_active), + SCpnt->host->host_busy, + SCpnt->host->host_failed)); + + /* + * If the host is having troubles, then look to see if this was the last + * command that might have failed. If so, wake up the error handler. + */ + if( atomic_read(&SCpnt->host->host_active) == SCpnt->host->host_failed ) + { + up(SCpnt->host->eh_wait); + } +} + +/* + * Function scsi_block_when_processing_errors + * + * Purpose: Prevent more commands from being queued while error recovery + * is taking place. + * + * Arguments: SDpnt - device on which we are performing recovery. + * + * Returns: FALSE The device was taken offline by error recovery. + * TRUE OK to proceed. + * + * Notes: We block until the host is out of error recovery, and then + * check to see whether the host or the device is offline. + */ +int +scsi_block_when_processing_errors(Scsi_Device * SDpnt) +{ + + SCSI_SLEEP( &SDpnt->host->host_wait, SDpnt->host->in_recovery); + + SCSI_LOG_ERROR_RECOVERY(5,printk("Open returning %d\n", SDpnt->online)); + + return SDpnt->online; +} + +/* + * Function: scsi_eh_times_out() + * + * Purpose: Timeout function for error handling. + * + * Arguments: SCpnt - command that is timing out. + * + * Returns: Nothing. + * + * Notes: During error handling, the kernel thread will be sleeping + * waiting for some action to complete on the device. Our only + * job is to record that it timed out, and to wake up the + * thread. + */ +STATIC +void scsi_eh_times_out (Scsi_Cmnd * SCpnt) +{ + SCpnt->request.rq_status = RQ_SCSI_DONE; + SCpnt->owner = SCSI_OWNER_ERROR_HANDLER; + SCpnt->eh_state = SCSI_STATE_TIMEOUT; + + SCSI_LOG_ERROR_RECOVERY(5,printk("In scsi_eh_times_out %p\n", SCpnt)); + + if (SCpnt->host->eh_action != NULL) + up(SCpnt->host->eh_action); + else + panic("Missing scsi error handler thread"); +} + + +/* + * Function: scsi_eh_done() + * + * Purpose: Completion function for error handling. + * + * Arguments: SCpnt - command that is timing out. + * + * Returns: Nothing. + * + * Notes: During error handling, the kernel thread will be sleeping + * waiting for some action to complete on the device. Our only + * job is to record that the action completed, and to wake up the + * thread. + */ +STATIC +void scsi_eh_done (Scsi_Cmnd * SCpnt) +{ + SCpnt->request.rq_status = RQ_SCSI_DONE; + + SCpnt->owner = SCSI_OWNER_ERROR_HANDLER; + SCpnt->eh_state = SUCCESS; + + SCSI_LOG_ERROR_RECOVERY(5,printk("In eh_done %p result:%x\n", SCpnt, + SCpnt->result)); + + if (SCpnt->host->eh_action != NULL) + up(SCpnt->host->eh_action); +} + +/* + * Function: scsi_eh_action_done() + * + * Purpose: Completion function for error handling. + * + * Arguments: SCpnt - command that is timing out. + * answer - boolean that indicates whether operation succeeded. + * + * Returns: Nothing. + * + * Notes: This callback is only used for abort and reset operations. + */ +STATIC +void scsi_eh_action_done (Scsi_Cmnd * SCpnt, int answer) +{ + SCpnt->request.rq_status = RQ_SCSI_DONE; + + SCpnt->owner = SCSI_OWNER_ERROR_HANDLER; + SCpnt->eh_state = (answer ? SUCCESS : FAILED); + + if (SCpnt->host->eh_action != NULL) + up(SCpnt->host->eh_action); +} + +/* + * Function: scsi_sense_valid() + * + * Purpose: Determine whether a host has automatically obtained sense + * information or not. If we have it, then give a recommendation + * as to what we should do next. + */ +int +scsi_sense_valid(Scsi_Cmnd * SCpnt) +{ + if (((SCpnt->sense_buffer[0] & 0x70) >> 4) != 7) + { + return FALSE; + } + return TRUE; +} + +/* + * Function: scsi_eh_retry_command() + * + * Purpose: Retry the original command + * + * Returns: SUCCESS - we were able to get the sense data. + * FAILED - we were not able to get the sense data. + * + * Notes: This function will *NOT* return until the command either + * times out, or it completes. + */ +STATIC int +scsi_eh_retry_command(Scsi_Cmnd * SCpnt) +{ + memcpy ((void *) SCpnt->cmnd, (void*) SCpnt->data_cmnd, + sizeof(SCpnt->data_cmnd)); + SCpnt->request_buffer = SCpnt->buffer; + SCpnt->request_bufflen = SCpnt->bufflen; + SCpnt->use_sg = SCpnt->old_use_sg; + SCpnt->cmd_len = SCpnt->old_cmd_len; + + SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); + + scsi_send_eh_cmnd (SCpnt, SCpnt->timeout_per_command); + + /* + * Hey, we are done. Let's look to see what happened. + */ + return SCpnt->eh_state; +} + +/* + * Function: scsi_request_sense() + * + * Purpose: Request sense data from a particular target. + * + * Returns: SUCCESS - we were able to get the sense data. + * FAILED - we were not able to get the sense data. + * + * Notes: Some hosts automatically obtain this information, others + * require that we obtain it on our own. + * + * This function will *NOT* return until the command either + * times out, or it completes. + */ +STATIC int +scsi_request_sense(Scsi_Cmnd * SCpnt) +{ + static unsigned char generic_sense[6] = {REQUEST_SENSE, 0,0,0, 255, 0}; + + memcpy ((void *) SCpnt->cmnd , (void *) generic_sense, + sizeof(generic_sense)); + + SCpnt->cmnd[1] = SCpnt->lun << 5; + SCpnt->cmnd[4] = sizeof(SCpnt->sense_buffer); + + SCpnt->request_buffer = &SCpnt->sense_buffer; + SCpnt->request_bufflen = sizeof(SCpnt->sense_buffer); + SCpnt->use_sg = 0; + SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); + + scsi_send_eh_cmnd (SCpnt, SENSE_TIMEOUT); + + /* + * Hey, we are done. Let's look to see what happened. + */ + return SCpnt->eh_state; +} + +/* + * Function: scsi_test_unit_ready() + * + * Purpose: Run test unit ready command to see if the device is talking to us or not. + * + */ +STATIC int +scsi_test_unit_ready(Scsi_Cmnd * SCpnt) +{ + static unsigned char tur_command[6] = {TEST_UNIT_READY, 0,0,0,0,0}; + + memcpy ((void *) SCpnt->cmnd , (void *) tur_command, + sizeof(tur_command)); + + SCpnt->cmnd[1] = SCpnt->lun << 5; + SCpnt->cmnd[4] = sizeof(SCpnt->sense_buffer); + + SCpnt->request_buffer = &SCpnt->sense_buffer; + SCpnt->request_bufflen = sizeof(SCpnt->sense_buffer); + SCpnt->use_sg = 0; + SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); + + scsi_send_eh_cmnd (SCpnt, SENSE_TIMEOUT); + + /* + * Hey, we are done. Let's look to see what happened. + */ + return SCpnt->eh_state; +} + +STATIC +void scsi_sleep_done (struct semaphore * sem) +{ + if( sem != NULL ) + { + up(sem); + } +} + + +void scsi_sleep (int timeout) +{ + struct semaphore sem = MUTEX_LOCKED; + struct timer_list timer; + + timer.data = (unsigned long) &sem; + timer.expires = jiffies + timeout; + timer.function = (void (*)(unsigned long))scsi_sleep_done; + + SCSI_LOG_ERROR_RECOVERY(5,printk("Sleeping for timer tics %d\n", timeout)); + + add_timer(&timer); + + down(&sem); + + del_timer(&timer); +} + +/* + * Function: scsi_send_eh_cmnd + * + * Purpose: Send a command out to a device as part of error recovery. + * + * Notes: The initialization of the structures is quite a bit different + * in this case, and furthermore, there is a different completion + * handler. + */ +STATIC void scsi_send_eh_cmnd (Scsi_Cmnd * SCpnt, int timeout) +{ + struct Scsi_Host * host; + + host = SCpnt->host; + +retry: + /* + * We will use a queued command if possible, otherwise we will emulate the + * queuing and calling of completion function ourselves. + */ + SCpnt->owner = SCSI_OWNER_LOWLEVEL; + + if (host->can_queue) + { + struct semaphore sem = MUTEX_LOCKED; + + SCpnt->eh_state = SCSI_STATE_QUEUED; + + scsi_add_timer(SCpnt, timeout, scsi_eh_times_out); + + /* + * Set up the semaphore so we wait for the command to complete. + */ + SCpnt->host->eh_action = &sem; + SCpnt->request.rq_status = RQ_SCSI_BUSY; + + host->hostt->queuecommand (SCpnt, scsi_eh_done); + down(&sem); + SCpnt->host->eh_action = NULL; + + del_timer(&SCpnt->eh_timeout); + + /* + * See if timeout. If so, tell the host to forget about it. + * In other words, we don't want a callback any more. + */ + if( SCpnt->eh_state == SCSI_STATE_TIMEOUT ) + { + SCpnt->eh_state = FAILED; + } + + SCSI_LOG_ERROR_RECOVERY(5,printk("send_eh_cmnd: %p eh_state:%x\n", + SCpnt, SCpnt->eh_state)); + } + else + { + int temp; + + /* + * We damn well had better never use this code. There is no timeout + * protection here, since we would end up waiting in the actual low + * level driver, we don't know how to wake it up. + */ + temp = host->hostt->command (SCpnt); + SCpnt->result = temp; + if( scsi_eh_completed_normally(SCpnt) ) + { + SCpnt->eh_state = SUCCESS; + } + else + { + SCpnt->eh_state = FAILED; + } + } + + /* + * Now examine the actual status codes to see whether the command actually + * did complete normally. + */ + if( SCpnt->eh_state == SUCCESS ) + { + switch( scsi_eh_completed_normally(SCpnt) ) + { + case SUCCESS: + SCpnt->eh_state = SUCCESS; + break; + case NEEDS_RETRY: + goto retry; + case FAILED: + default: + SCpnt->eh_state = FAILED; + break; + } + } + else + { + SCpnt->eh_state = FAILED; + } +} + +/* + * Function: scsi_unit_is_ready() + * + * Purpose: Called after TEST_UNIT_READY is run, to test to see if + * the unit responded in a way that indicates it is ready. + */ +STATIC int +scsi_unit_is_ready(Scsi_Cmnd * SCpnt) +{ + if (SCpnt->result) + { + if (((driver_byte (SCpnt->result) & DRIVER_SENSE) || + (status_byte (SCpnt->result) & CHECK_CONDITION)) && + ((SCpnt->sense_buffer[0] & 0x70) >> 4) == 7) + { + if (((SCpnt->sense_buffer[2] & 0xf) != NOT_READY) && + ((SCpnt->sense_buffer[2] & 0xf) != UNIT_ATTENTION) && + ((SCpnt->sense_buffer[2] & 0xf) != ILLEGAL_REQUEST)) + { + return 0; + } + } + } + + return 1; +} + +/* + * Function: scsi_eh_finish_command + * + * Purpose: Handle a command that we are finished with WRT error handling. + * + * Arguments: SClist - pointer to list into which we are putting completed commands. + * SCpnt - command that is completing + * + * Notes: We don't want to use the normal command completion while we are + * are still handling errors - it may cause other commands to be queued, + * and that would disturb what we are doing. Thus we really want to keep + * a list of pending commands for final completion, and once we + * are ready to leave error handling we handle completion for real. + */ +STATIC void +scsi_eh_finish_command(Scsi_Cmnd **SClist, Scsi_Cmnd * SCpnt) +{ + SCpnt->state = SCSI_STATE_BHQUEUE; + SCpnt->bh_next = *SClist; + /* + * Set this back so that the upper level can correctly free up + * things. + */ + SCpnt->use_sg = SCpnt->old_use_sg; + *SClist = SCpnt; +} + +/* + * Function: scsi_try_to_abort_command + * + * Purpose: Ask host adapter to abort a running command. + * + * Returns: FAILED Operation failed or not supported. + * SUCCESS Succeeded. + * + * Notes: This function will not return until the user's completion + * function has been called. There is no timeout on this + * operation. If the author of the low-level driver wishes + * this operation to be timed, they can provide this facility + * themselves. Helper functions in scsi_error.c can be supplied + * to make this easier to do. + * + * Notes: It may be possible to combine this with all of the reset + * handling to eliminate a lot of code duplication. I don't + * know what makes more sense at the moment - this is just a + * prototype. + */ +STATIC int +scsi_try_to_abort_command(Scsi_Cmnd * SCpnt, int timeout) +{ + SCpnt->eh_state = FAILED; /* Until we come up with something better */ + + if( SCpnt->host->hostt->eh_abort_handler == NULL ) + { + return FAILED; + } + + SCpnt->owner = SCSI_OWNER_LOWLEVEL; + + return SCpnt->host->hostt->eh_abort_handler(SCpnt); +} + +/* + * Function: scsi_try_bus_device_reset + * + * Purpose: Ask host adapter to perform a bus device reset for a given + * device. + * + * Returns: FAILED Operation failed or not supported. + * SUCCESS Succeeded. + * + * Notes: There is no timeout for this operation. If this operation is + * unreliable for a given host, then the host itself needs to put a + * timer on it, and set the host back to a consistent state prior + * to returning. + */ +STATIC int +scsi_try_bus_device_reset(Scsi_Cmnd * SCpnt, int timeout) +{ + SCpnt->eh_state = FAILED; /* Until we come up with something better */ + + if( SCpnt->host->hostt->eh_device_reset_handler == NULL ) + { + return FAILED; + } + + SCpnt->owner = SCSI_OWNER_LOWLEVEL; + + return SCpnt->host->hostt->eh_device_reset_handler(SCpnt); +} + +/* + * Function: scsi_try_bus_reset + * + * Purpose: Ask host adapter to perform a bus reset for a host. + * + * Returns: FAILED Operation failed or not supported. + * SUCCESS Succeeded. + * + * Notes: + */ +STATIC int +scsi_try_bus_reset(Scsi_Cmnd * SCpnt) +{ + int rtn; + + SCpnt->eh_state = FAILED; /* Until we come up with something better */ + SCpnt->owner = SCSI_OWNER_LOWLEVEL; + + if( SCpnt->host->hostt->eh_bus_reset_handler == NULL ) + { + return FAILED; + } + + rtn = SCpnt->host->hostt->eh_bus_reset_handler(SCpnt); + + /* + * If we had a successful bus reset, mark the command blocks to expect + * a condition code of unit attention. + */ + scsi_sleep(BUS_RESET_SETTLE_TIME); + if( SCpnt->eh_state == SUCCESS ) + { + Scsi_Device * SDloop; + for (SDloop = SCpnt->host->host_queue; SDloop; SDloop = SDloop->next) + { + if( SCpnt->channel == SDloop->channel ) + { + SDloop->was_reset = 1; + SDloop->expecting_cc_ua = 1; + } + } + } + + return SCpnt->eh_state; +} + +/* + * Function: scsi_try_host_reset + * + * Purpose: Ask host adapter to reset itself, and the bus. + * + * Returns: FAILED Operation failed or not supported. + * SUCCESS Succeeded. + * + * Notes: + */ +STATIC int +scsi_try_host_reset(Scsi_Cmnd * SCpnt) +{ + int rtn; + + SCpnt->eh_state = FAILED; /* Until we come up with something better */ + SCpnt->owner = SCSI_OWNER_LOWLEVEL; + + if( SCpnt->host->hostt->eh_host_reset_handler == NULL ) + { + return FAILED; + } + + rtn = SCpnt->host->hostt->eh_host_reset_handler(SCpnt); + + /* + * If we had a successful host reset, mark the command blocks to expect + * a condition code of unit attention. + */ + scsi_sleep(HOST_RESET_SETTLE_TIME); + if( SCpnt->eh_state == SUCCESS ) + { + Scsi_Device * SDloop; + for (SDloop = SCpnt->host->host_queue; SDloop; SDloop = SDloop->next) + { + SDloop->was_reset = 1; + SDloop->expecting_cc_ua = 1; + } + } + + return SCpnt->eh_state; +} + +/* + * Function: scsi_decide_disposition + * + * Purpose: Examine a command block that has come back from the low-level + * and figure out what to do next. + * + * Returns: SUCCESS - pass on to upper level. + * FAILED - pass on to error handler thread. + * RETRY - command should be retried. + * SOFTERR - command succeeded, but we need to log + * a soft error. + * + * Notes: This is *ONLY* called when we are examining the status + * after sending out the actual data command. Any commands + * that are queued for error recovery (i.e. TEST_UNIT_READY) + * do *NOT* come through here. + * + * NOTE - When this routine returns FAILED, it means the error + * handler thread is woken. In cases where the error code + * indicates an error that doesn't require the error handler + * thread (i.e. we don't need to abort/reset), then this function + * should return SUCCESS. + */ +int scsi_decide_disposition (Scsi_Cmnd * SCpnt) +{ + int rtn; + + /* + * If the device is offline, then we clearly just pass the result back + * up to the top level. + */ + if( SCpnt->device->online == FALSE ) + { + SCSI_LOG_ERROR_RECOVERY(5,printk("scsi_error.c: device offline - report as SUCCESS\n")); + return SUCCESS; + } + + /* + * First check the host byte, to see if there is anything in there + * that would indicate what we need to do. + */ + + switch(host_byte(SCpnt->result)) + { + case DID_PASSTHROUGH: + /* + * No matter what, pass this through to the upper layer. + * Nuke this special code so that it looks like we are saying + * DID_OK. + */ + SCpnt->result &= 0xff00ffff; + return SUCCESS; + case DID_OK: + /* + * Looks good. Drop through, and check the next byte. + */ + break; + case DID_NO_CONNECT: + case DID_BAD_TARGET: + case DID_ABORT: + /* + * Note - this means that we just report the status back to the + * top level driver, not that we actually think that it indicates + * sucess. + */ + return SUCCESS; + case DID_PARITY: + case DID_BUS_BUSY: + case DID_ERROR: + goto maybe_retry; + case DID_TIME_OUT: + /* + * When we scan the bus, we get timeout messages for + * these commands if there is no device available. + * Other hosts report DID_NO_CONNECT for the same thing. + */ + if( (SCpnt->cmnd[0] == TEST_UNIT_READY || + SCpnt->cmnd[0] == INQUIRY) ) + { + return SUCCESS; + } + else + { + return FAILED; + } + case DID_RESET: + /* + * In the normal case where we haven't initiated a reset, this is + * a failure. + */ + if( SCpnt->flags & IS_RESETTING ) + { + SCpnt->flags &= ~IS_RESETTING; + goto maybe_retry; + } + + /* + * Examine the sense data to figure out how to proceed from here. + * If there is no sense data, we will be forced into the error + * handler thread, where we get to examine the thing in a lot more + * detail. + */ + return scsi_check_sense (SCpnt); + default: + return FAILED; + } + + /* + * Next, check the message byte. + */ + if( msg_byte(SCpnt->result) != COMMAND_COMPLETE ) + { + return FAILED; + } + + /* + * Now, check the status byte to see if this indicates anything special. + */ + switch (status_byte(SCpnt->result)) + { + case QUEUE_FULL: + /* + * The case of trying to send too many commands to a tagged queueing + * device. + */ + return ADD_TO_MLQUEUE; + case GOOD: + case COMMAND_TERMINATED: + return SUCCESS; + case CHECK_CONDITION: + rtn = scsi_check_sense(SCpnt); + if( rtn == NEEDS_RETRY ) + { + goto maybe_retry; + } + return rtn; + case CONDITION_GOOD: + case INTERMEDIATE_GOOD: + case INTERMEDIATE_C_GOOD: + /* + * Who knows? FIXME(eric) + */ + return SUCCESS; + case BUSY: + case RESERVATION_CONFLICT: + goto maybe_retry; + default: + return FAILED; + } + return FAILED; + +maybe_retry: + + if ((++SCpnt->retries) < SCpnt->allowed) + { + return NEEDS_RETRY; + } + else + { + return FAILED; + } +} + +/* + * Function: scsi_eh_completed_normally + * + * Purpose: Examine a command block that has come back from the low-level + * and figure out what to do next. + * + * Returns: SUCCESS - pass on to upper level. + * FAILED - pass on to error handler thread. + * RETRY - command should be retried. + * SOFTERR - command succeeded, but we need to log + * a soft error. + * + * Notes: This is *ONLY* called when we are examining the status + * of commands queued during error recovery. The main + * difference here is that we don't allow for the possibility + * of retries here, and we are a lot more restrictive about what + * we consider acceptable. + */ +STATIC int scsi_eh_completed_normally (Scsi_Cmnd * SCpnt) +{ + int rtn; + /* + * First check the host byte, to see if there is anything in there + * that would indicate what we need to do. + */ + if( host_byte(SCpnt->result) == DID_RESET ) + { + if (SCpnt->flags & IS_RESETTING ) + { + /* + * OK, this is normal. We don't know whether in fact the + * command in question really needs to be rerun or not - + * if this was the original data command then the answer is yes, + * otherwise we just flag it as success. + */ + SCpnt->flags &= ~IS_RESETTING; + return NEEDS_RETRY; + } + + /* + * Rats. We are already in the error handler, so we now get to try + * and figure out what to do next. If the sense is valid, we have + * a pretty good idea of what to do. If not, we mark it as failed. + */ + return scsi_check_sense (SCpnt); + } + + if(host_byte(SCpnt->result) != DID_OK ) + { + return FAILED; + } + + /* + * Next, check the message byte. + */ + if( msg_byte(SCpnt->result) != COMMAND_COMPLETE ) + { + return FAILED; + } + + /* + * Now, check the status byte to see if this indicates anything special. + */ + switch (status_byte(SCpnt->result)) + { + case GOOD: + case COMMAND_TERMINATED: + return SUCCESS; + case CHECK_CONDITION: + rtn = scsi_check_sense(SCpnt); + if( rtn == NEEDS_RETRY ) + { + return FAILED; + } + return rtn; + case CONDITION_GOOD: + case INTERMEDIATE_GOOD: + case INTERMEDIATE_C_GOOD: + /* + * Who knows? FIXME(eric) + */ + return SUCCESS; + case BUSY: + case QUEUE_FULL: + case RESERVATION_CONFLICT: + default: + return FAILED; + } + return FAILED; +} + +/* + * Function: scsi_check_sense + * + * Purpose: Examine sense information - give suggestion as to what + * we should do with it. + */ +STATIC int scsi_check_sense (Scsi_Cmnd * SCpnt) +{ + if ( !scsi_sense_valid(SCpnt) ) + { + return FAILED; + } + + if (SCpnt->sense_buffer[2] & 0xe0) + return FAILED; + + switch (SCpnt->sense_buffer[2] & 0xf) + { + case NO_SENSE: + return SUCCESS; + case RECOVERED_ERROR: + return SOFT_ERROR; + + case ABORTED_COMMAND: + return NEEDS_RETRY; + case NOT_READY: + case UNIT_ATTENTION: + /* + * If we are expecting a CC/UA because of a bus reset that we + * performed, treat this just as a retry. Otherwise this is + * information that we should pass up to the upper-level driver + * so that we can deal with it there. + */ + if( SCpnt->device->expecting_cc_ua ) + { + SCpnt->device->expecting_cc_ua = 0; + return NEEDS_RETRY; + } + return SUCCESS; + + /* these three are not supported */ + case COPY_ABORTED: + case VOLUME_OVERFLOW: + case MISCOMPARE: + + case MEDIUM_ERROR: + return FAILED; + + case BLANK_CHECK: + case DATA_PROTECT: + case HARDWARE_ERROR: + case ILLEGAL_REQUEST: + default: + return FAILED; + } +} + + +/* + * Function: scsi_restart_operations + * + * Purpose: Restart IO operations to the specified host. + * + * Arguments: host - host that we are restarting + * + * Returns: Nothing + * + * Notes: When we entered the error handler, we blocked all further + * I/O to this device. We need to 'reverse' this process. + */ +STATIC void +scsi_restart_operations(struct Scsi_Host * host) +{ + Scsi_Device * SDpnt; + + /* + * Next free up anything directly waiting upon the host. This will be + * requests for character device operations, and also for ioctls to queued + * block devices. + */ + SCSI_LOG_ERROR_RECOVERY(5,printk("scsi_error.c: Waking up host to restart\n")); + + wake_up(&host->host_wait); + + /* + * Finally, block devices need an extra kick in the pants. This is because + * the request queueing mechanism may have queued lots of pending requests + * and there won't be a process waiting in a place where we can simply wake + * it up. Thus we simply go through and call the request function to goose + * the various top level drivers and get things moving again. + */ + for( SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next ) + { + SCSI_LOG_ERROR_RECOVERY(5,printk("Calling request function to restart things...\n")); + + if( SDpnt->scsi_request_fn != NULL ) + (*SDpnt->scsi_request_fn)(); + } +} + +/* + * Function: scsi_unjam_host + * + * Purpose: Attempt to fix a host which has a command that failed for + * some reason. + * + * Arguments: host - host that needs unjamming. + * + * Returns: Nothing + * + * Notes: When we come in here, we *know* that all commands on the + * bus have either completed, failed or timed out. We also + * know that no further commands are being sent to the host, + * so things are relatively quiet and we have freedom to + * fiddle with things as we wish. + * + * Additional note: This is only the *default* implementation. It is possible + * for individual drivers to supply their own version of this + * function, and if the maintainer wishes to do this, it is + * strongly suggested that this function be taken as a template + * and modified. This function was designed to correctly handle + * problems for about 95% of the different cases out there, and + * it should always provide at least a reasonable amount of error + * recovery. + * + * Note3: Any command marked 'FAILED' or 'TIMEOUT' must eventually + * have scsi_finish_command() called for it. We do all of + * the retry stuff here, so when we restart the host after we + * return it should have an empty queue. + */ +STATIC int +scsi_unjam_host(struct Scsi_Host * host) +{ + int devices_failed; + int numfailed; + int ourrtn; + int rtn = FALSE; + int result; + Scsi_Cmnd * SCloop; + Scsi_Cmnd * SCpnt; + Scsi_Device * SDpnt; + Scsi_Device * SDloop; + Scsi_Cmnd * SCdone; + int timed_out; + + SCdone = NULL; + + /* + * First, protect against any sort of race condition. If any of the outstanding + * commands are in states that indicate that we are not yet blocked (i.e. we are + * not in a quiet state) then we got woken up in error. If we ever end up here, + * we need to re-examine some of the assumptions. + */ + for(SDpnt=host->host_queue; SDpnt; SDpnt = SDpnt->next) + { + for(SCpnt=SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) + { + if( SCpnt->state == SCSI_STATE_FAILED + || SCpnt->state == SCSI_STATE_TIMEOUT + || SCpnt->state == SCSI_STATE_UNUSED) + { + continue; + } + + /* + * Rats. Something is still floating around out there. This could + * be the result of the fact that the upper level drivers are still frobbing + * commands that might have succeeded. There are two outcomes. One is that + * the command block will eventually be freed, and the other one is that + * the command will be queued and will be finished along the way. + */ + SCSI_LOG_ERROR_RECOVERY(1,printk("Error handler prematurely woken - commands still active (%p %x %d)\n", SCpnt, SCpnt->state, SCpnt->target)); + panic("SCSI Error handler woken too early\n"); + } + } + + /* + * Next, see if we need to request sense information. if so, + * then get it now, so we have a better idea of what to do. + * FIXME(eric) this has the unfortunate side effect that if a host + * adapter does not automatically request sense information, that we end + * up shutting it down before we request it. All hosts should be doing this + * anyways, so for now all I have to say is tough noogies if you end up in here. + * On second thought, this is probably a good idea. We *really* want to give + * authors an incentive to automatically request this. + */ + SCSI_LOG_ERROR_RECOVERY(3,printk("scsi_unjam_host: Checking to see if we need to request sense\n")); + + for(SDpnt=host->host_queue; SDpnt; SDpnt = SDpnt->next) + { + for(SCpnt=SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) + { + if( SCpnt->state != SCSI_STATE_FAILED || scsi_sense_valid(SCpnt) ) + { + continue; + } + + SCSI_LOG_ERROR_RECOVERY(2,printk("scsi_unjam_host: Requesting sense for %d\n", + SCpnt->target)); + rtn = scsi_request_sense(SCpnt); + if( rtn != SUCCESS ) + { + continue; + } + + SCSI_LOG_ERROR_RECOVERY(3,printk("Sense requested for %p - result %x\n", + SCpnt, SCpnt->result)); + SCSI_LOG_ERROR_RECOVERY(3,print_sense("bh",SCpnt)); + + result = scsi_decide_disposition(SCpnt); + + /* + * If the result was normal, then just pass it along to the + * upper level. + */ + if( result == SUCCESS ) + { + SCpnt->host->host_failed--; + scsi_eh_finish_command(&SCdone, SCpnt); + } + + if( result != NEEDS_RETRY ) + { + continue; + } + + /* + * We only come in here if we want to retry a + * command. The test to see whether the command + * should be retried should be keeping track of the + * number of tries, so we don't end up looping, of + * course. + */ + SCpnt->state = NEEDS_RETRY; + rtn = scsi_eh_retry_command(SCpnt); + if( rtn != SUCCESS ) + { + continue; + } + + /* + * We eventually hand this one back to the top level. + */ + SCpnt->host->host_failed--; + scsi_eh_finish_command(&SCdone, SCpnt); + } + } + + /* + * Go through the list of commands and figure out where we stand and how bad things + * really are. + */ + numfailed = 0; + timed_out = 0; + devices_failed = 0; + for(SDpnt=host->host_queue; SDpnt; SDpnt = SDpnt->next) + { + unsigned int device_error = 0; + + for(SCpnt=SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) + { + if( SCpnt->state == SCSI_STATE_FAILED ) + { + SCSI_LOG_ERROR_RECOVERY(5,printk("Command to ID %d failed\n", + SCpnt->target)); + numfailed++; + device_error++; + } + if( SCpnt->state == SCSI_STATE_TIMEOUT ) + { + SCSI_LOG_ERROR_RECOVERY(5,printk("Command to ID %d timedout\n", + SCpnt->target)); + timed_out++; + device_error++; + } + } + if( device_error > 0 ) + { + devices_failed++; + } + } + + SCSI_LOG_ERROR_RECOVERY(2,printk("Total of %d+%d commands on %d devices require eh work\n", + numfailed, timed_out, devices_failed)); + + if( host->host_failed == 0 ) + { + ourrtn = TRUE; + goto leave; + } + + + /* + * Next, try and see whether or not it makes sense to try and abort + * the running command. This only works out to be the case if we have + * one command that has timed out. If the command simply failed, it + * makes no sense to try and abort the command, since as far as the + * host adapter is concerned, it isn't running. + */ + + SCSI_LOG_ERROR_RECOVERY(3,printk("scsi_unjam_host: Checking to see if we want to try abort\n")); + + for(SDpnt=host->host_queue; SDpnt; SDpnt = SDpnt->next) + { + for(SCloop=SDpnt->device_queue; SCloop; SCloop = SCloop->next) + { + if( SCloop->state != SCSI_STATE_TIMEOUT ) + { + continue; + } + + rtn = scsi_try_to_abort_command(SCloop, ABORT_TIMEOUT); + + if( rtn == SUCCESS ) + { + rtn = scsi_test_unit_ready(SCloop); + + if( rtn == SUCCESS && scsi_unit_is_ready(SCloop) ) + { + rtn = scsi_eh_retry_command(SCloop); + + if( rtn == SUCCESS ) + { + SCloop->host->host_failed--; + scsi_eh_finish_command(&SCdone,SCloop); + } + } + } + } + } + + /* + * If we have corrected all of the problems, then we are done. + */ + if( host->host_failed == 0 ) + { + ourrtn = TRUE; + goto leave; + } + + /* + * Either the abort wasn't appropriate, or it didn't succeed. + * Now try a bus device reset. Still, look to see whether we have + * multiple devices that are jammed or not - if we have multiple devices, + * it makes no sense to try BUS_DEVICE_RESET - we really would need + * to try a BUS_RESET instead. + * + * Does this make sense - should we try BDR on each device individually? + * Yes, definitely. + */ + SCSI_LOG_ERROR_RECOVERY(3,printk("scsi_unjam_host: Checking to see if we want to try BDR\n")); + + for(SDpnt=host->host_queue; SDpnt; SDpnt = SDpnt->next) + { + for(SCloop=SDpnt->device_queue; SCloop; SCloop = SCloop->next) + { + if( SCloop->state == SCSI_STATE_FAILED + || SCloop->state == SCSI_STATE_TIMEOUT ) + { + break; + } + } + + if( SCloop == NULL ) + { + continue; + } + + /* + * OK, we have a device that is having problems. Try and send + * a bus device reset to it. + * + * FIXME(eric) - make sure we handle the case where multiple + * commands to the same device have failed. They all must + * get properly restarted. + */ + rtn = scsi_try_bus_device_reset(SCloop, RESET_TIMEOUT); + + if( rtn == SUCCESS ) + { + rtn = scsi_test_unit_ready(SCloop); + + if( rtn == SUCCESS && scsi_unit_is_ready(SCloop) ) + { + rtn = scsi_eh_retry_command(SCloop); + + if( rtn == SUCCESS ) + { + SCloop->host->host_failed--; + scsi_eh_finish_command(&SCdone,SCloop); + } + } + } + + } + + if( host->host_failed == 0 ) + { + ourrtn = TRUE; + goto leave; + } + + /* + * If we ended up here, we have serious problems. The only thing left + * to try is a full bus reset. If someone has grabbed the bus and isn't + * letting go, then perhaps this will help. + */ + SCSI_LOG_ERROR_RECOVERY(3,printk("scsi_unjam_host: Try hard bus reset\n")); + + /* + * We really want to loop over the various channels, and do this on + * a channel by channel basis. We should also check to see if any + * of the failed commands are on soft_reset devices, and if so, skip + * the reset. + */ + for(SDpnt=host->host_queue; SDpnt; SDpnt = SDpnt->next) + { +next_device: + for(SCpnt=SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) + { + if( SCpnt->state != SCSI_STATE_FAILED + && SCpnt->state != SCSI_STATE_TIMEOUT ) + { + continue; + } + /* + * We have a failed command. Make sure there are no other failed + * commands on the same channel that are timed out and implement a + * soft reset. + */ + for(SDloop=host->host_queue; SDloop; SDloop = SDloop->next) + { + for(SCloop=SDloop->device_queue; SCloop; SCloop = SCloop->next) + { + if( SCloop->channel != SCpnt->channel ) + { + continue; + } + + if( SCloop->state != SCSI_STATE_FAILED + && SCloop->state != SCSI_STATE_TIMEOUT ) + { + continue; + } + + if( SDloop->soft_reset && SCloop->state == SCSI_STATE_TIMEOUT ) + { + /* + * If this device uses the soft reset option, and this + * is one of the devices acting up, then our only + * option is to wait a bit, since the command is + * supposedly still running. + * + * FIXME(eric) - right now we will just end up falling + * through to the 'take device offline' case. + * + * FIXME(eric) - It is possible that the command completed + * *after* the error recovery procedure started, and if this + * is the case, we are worrying about nothing here. + */ + goto next_device; + } + } + } + + /* + * We now know that we are able to perform a reset for the + * bus that SCpnt points to. There are no soft-reset devices + * with outstanding timed out commands. + */ + rtn = scsi_try_bus_reset(SCpnt); + if( rtn == SUCCESS ) + { + for(SDloop=host->host_queue; SDloop; SDloop = SDloop->next) + { + for(SCloop=SDloop->device_queue; SCloop; SCloop = SCloop->next) + { + if( SCloop->channel != SCpnt->channel ) + { + continue; + } + + if( SCloop->state != SCSI_STATE_FAILED + && SCloop->state != SCSI_STATE_TIMEOUT ) + { + continue; + } + + rtn = scsi_test_unit_ready(SCloop); + + if( rtn == SUCCESS && scsi_unit_is_ready(SCloop) ) + { + rtn = scsi_eh_retry_command(SCloop); + + if( rtn == SUCCESS ) + { + SCpnt->host->host_failed--; + scsi_eh_finish_command(&SCdone,SCloop); + } + } + + /* + * If the bus reset worked, but we are still unable to + * talk to the device, take it offline. + * FIXME(eric) - is this really the correct thing to do? + */ + if( rtn != SUCCESS ) + { + SCloop->device->online = FALSE; + SCloop->host->host_failed--; + scsi_eh_finish_command(&SCdone,SCloop); + } + } + } + } + } + } + + if( host->host_failed == 0 ) + { + ourrtn = TRUE; + goto leave; + } + /* + * If we ended up here, we have serious problems. The only thing left + * to try is a full host reset - perhaps the firmware on the device + * crashed, or something like that. + * + * It is assumed that a succesful host reset will cause *all* information + * about the command to be flushed from both the host adapter *and* the + * device. + * + * FIXME(eric) - it isn't clear that devices that implement the soft reset + * option can ever be cleared except via cycling the power. The problem is + * that sending the host reset command will cause the host to forget + * about the pending command, but the device won't forget. For now, we + * skip the host reset option if any of the failed devices are configured + * to use the soft reset option. + */ + for(SDpnt=host->host_queue; SDpnt; SDpnt = SDpnt->next) + { +next_device2: + for(SCpnt=SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) + { + if( SCpnt->state != SCSI_STATE_FAILED + && SCpnt->state != SCSI_STATE_TIMEOUT ) + { + continue; + } + if( SDpnt->soft_reset && SCpnt->state == SCSI_STATE_TIMEOUT ) + { + /* + * If this device uses the soft reset option, and this + * is one of the devices acting up, then our only + * option is to wait a bit, since the command is + * supposedly still running. + * + * FIXME(eric) - right now we will just end up falling + * through to the 'take device offline' case. + */ + SCSI_LOG_ERROR_RECOVERY(3, + printk("scsi_unjam_host: Unable to try hard host reset\n")); + goto next_device2; + } + + SCSI_LOG_ERROR_RECOVERY(3,printk("scsi_unjam_host: Try hard host reset\n")); + + /* + * FIXME(eric) - we need to obtain a valid SCpnt to perform this call. + */ + rtn = scsi_try_host_reset(SCpnt); + if( rtn == SUCCESS ) + { + /* + * FIXME(eric) we assume that all commands are flushed from the + * controller. We should get a DID_RESET for all of the commands + * that were pending. We should ignore these so that we can + * guarantee that we are in a consistent state. + * + * I believe this to be the case right now, but this needs to be + * tested. + */ + for(SDloop=host->host_queue; SDloop; SDloop = SDloop->next) + { + for(SCloop=SDloop->device_queue; SCloop; SCloop = SCloop->next) + { + if( SCloop->state != SCSI_STATE_FAILED + && SCloop->state != SCSI_STATE_TIMEOUT ) + { + continue; + } + + rtn = scsi_test_unit_ready(SCloop); + + if( rtn == SUCCESS && scsi_unit_is_ready(SCloop) ) + { + rtn = scsi_eh_retry_command(SCloop); + + if( rtn == SUCCESS ) + { + SCpnt->host->host_failed--; + scsi_eh_finish_command(&SCdone,SCloop); + } + } + if( rtn != SUCCESS ) + { + SCloop->device->online = FALSE; + SCloop->host->host_failed--; + scsi_eh_finish_command(&SCdone,SCloop); + } + } + } + } + } + } + + + /* + * If we solved all of the problems, then let's rev up the engines again. + */ + if( host->host_failed == 0 ) + { + ourrtn = TRUE; + goto leave; + } + + /* + * If the HOST RESET failed, then for now we assume that the entire host + * adapter is too hosed to be of any use. For our purposes, however, it is + * easier to simply take the devices offline that correspond to commands + * that failed. + */ + SCSI_LOG_ERROR_RECOVERY(1,printk("scsi_unjam_host: Take device offline\n")); + + for(SDpnt=host->host_queue; SDpnt; SDpnt = SDpnt->next) + { + for(SCloop=SDpnt->device_queue; SCloop; SCloop = SCloop->next) + { + if( SCloop->state == SCSI_STATE_FAILED || SCloop->state == SCSI_STATE_TIMEOUT ) + { + SCloop->device->online = FALSE; + + /* + * This should pass the failure up to the top level driver, and + * it will have to try and do something intelligent with it. + */ + SCloop->host->host_failed--; + + if( SCloop->state == SCSI_STATE_TIMEOUT ) + { + SCloop->result |= (DRIVER_TIMEOUT << 24); + } + + SCSI_LOG_ERROR_RECOVERY(3,printk("Finishing command for device %d %x\n", + SCloop->device->id, SCloop->result)); + + scsi_eh_finish_command(&SCdone,SCloop); + } + } + } + + if( host->host_failed != 0 ) + { + panic("scsi_unjam_host: Miscount of number of failed commands.\n"); + } + + SCSI_LOG_ERROR_RECOVERY(3,printk("scsi_unjam_host: Returning\n")); + + ourrtn = FALSE; + +leave: + + /* + * We should have a list of commands that we 'finished' during the course of + * error recovery. This should be the same as the list of commands that timed out + * or failed. We are currently holding these things in a linked list - we didn't + * put them in the bottom half queue because we wanted to keep things quiet while + * we were working on recovery, and passing them up to the top level could easily + * cause the top level to try and queue something else again. + * + * Start by marking that the host is no longer in error recovery. + */ + host->in_recovery = 0; + + /* + * Take the list of commands, and stick them in the bottom half queue. + * The current implementation of scsi_done will do this for us - if need + * be we can create a special version of this function to do the + * same job for us. + */ + for(SCpnt = SCdone; SCpnt != NULL; SCpnt = SCdone) + { + SCdone = SCpnt->bh_next; + SCpnt->bh_next = NULL; + scsi_done(SCpnt); + } + + return (ourrtn); +} + + +/* + * Function: scsi_error_handler + * + * Purpose: Handle errors/timeouts of scsi commands, try and clean up + * and unjam the bus, and restart things. + * + * Arguments: host - host for which we are running. + * + * Returns: Never returns. + * + * Notes: This is always run in the context of a kernel thread. The + * idea is that we start this thing up when the kernel starts + * up (one per host that we detect), and it immediately goes to + * sleep and waits for some event (i.e. failure). When this + * takes place, we have the job of trying to unjam the bus + * and restarting things. + * + */ +void +scsi_error_handler(void * data) +{ + struct Scsi_Host * host = (struct Scsi_Host *) data; + int rtn; + struct semaphore sem = MUTEX_LOCKED; + + lock_kernel(); + + /* + * If we were started as result of loading a module, close all of the + * user space pages. We don't need them, and if we didn't close them + * they would be locked into memory. + */ + exit_mm(current); + + + current->session = 1; + current->pgrp = 1; + /* + * FIXME(eric) this is still a child process of the one that did the insmod. + * This needs to be attached to task[0] instead. + */ + + siginitsetinv(¤t->blocked, SHUTDOWN_SIGS); + current->fs->umask = 0; + + /* + * Set the name of this process. + */ + sprintf(current->comm, "scsi_eh_%d", host->host_no); + + host->eh_wait = &sem; + host->ehandler = current; + + unlock_kernel(); + + /* + * Wake up the thread that created us. + */ + SCSI_LOG_ERROR_RECOVERY(3,printk("Wake up parent %d\n", host->eh_notify->count.counter)); + + up(host->eh_notify); + + while(1) + { + /* + * If we get a signal, it means we are supposed to go + * away and die. This typically happens if the user is + * trying to unload a module. + */ + SCSI_LOG_ERROR_RECOVERY(1,printk("Error handler sleeping\n")); + down_interruptible (&sem); + + if (signal_pending(current) ) + break; + + SCSI_LOG_ERROR_RECOVERY(1,printk("Error handler waking up\n")); + + host->eh_active = 1; + + /* + * We have a host that is failing for some reason. Figure out + * what we need to do to get it up and online again (if we can). + * If we fail, we end up taking the thing offline. + */ + if( host->hostt->eh_strategy_handler != NULL ) + { + rtn = host->hostt->eh_strategy_handler(host); + } + else + { + rtn = scsi_unjam_host(host); + } + + host->eh_active = 0; + + /* + * Note - if the above fails completely, the action is to take + * individual devices offline and flush the queue of any + * outstanding requests that may have been pending. When we + * restart, we restart any I/O to any other devices on the bus + * which are still online. + */ + scsi_restart_operations(host); + } + + SCSI_LOG_ERROR_RECOVERY(1,printk("Error handler exiting\n")); + + /* + * Make sure that nobody tries to wake us up again. + */ + host->eh_wait = NULL; + + /* + * Knock this down too. From this point on, the host is flying + * without a pilot. If this is because the module is being unloaded, + * that's fine. If the user sent a signal to this thing, we are + * potentially in real danger. + */ + host->in_recovery = 0; + host->eh_active = 0; + host->ehandler = NULL; + + /* + * If anyone is waiting for us to exit (i.e. someone trying to unload + * a driver), then wake up that process to let them know we are on + * the way out the door. This may be overkill - I *think* that we + * could probably just unload the driver and send the signal, and when + * the error handling thread wakes up that it would just exit without + * needing to touch any memory associated with the driver itself. + */ + if( host->eh_notify != NULL ) + up(host->eh_notify); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-indent-level: 4 + * c-brace-imaginary-offset: 0 + * c-brace-offset: -4 + * c-argdecl-indent: 4 + * c-label-offset: -4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: 0 + * indent-tabs-mode: nil + * tab-width: 8 + * End: + */ diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/scsi_ioctl.c linux/drivers/scsi/scsi_ioctl.c --- v2.1.74/linux/drivers/scsi/scsi_ioctl.c Tue Dec 2 09:49:39 1997 +++ linux/drivers/scsi/scsi_ioctl.c Sun Dec 21 17:04:49 1997 @@ -1,8 +1,3 @@ -/* - * Don't import our own symbols, as this would severely mess up our - * symbol tables. - */ -#define _SCSI_SYMS_VER_ #define __NO_VERSION__ #include @@ -103,8 +98,10 @@ { int result; Scsi_Cmnd * SCpnt; - - SCpnt = allocate_device(NULL, dev, 1); + Scsi_Device * SDpnt; + + SCSI_LOG_IOCTL(1, printk("Trying ioctl with scsi command %d\n", cmd[0])); + SCpnt = scsi_allocate_device(NULL, dev, 1); { struct semaphore sem = MUTEX_LOCKED; SCpnt->request.sem = &sem; @@ -112,8 +109,11 @@ scsi_ioctl_done, MAX_TIMEOUT, MAX_RETRIES); down(&sem); + SCpnt->request.sem = NULL; } + SCSI_LOG_IOCTL(2, printk("Ioctl returned 0x%x\n", SCpnt->result)); + if(driver_byte(SCpnt->result) != 0) switch(SCpnt->sense_buffer[2] & 0xf) { case ILLEGAL_REQUEST: @@ -146,12 +146,16 @@ }; result = SCpnt->result; - SCpnt->request.rq_status = RQ_INACTIVE; - if (!SCpnt->device->was_reset && SCpnt->device->scsi_request_fn) - (*SCpnt->device->scsi_request_fn)(); + SCSI_LOG_IOCTL(2, printk("IOCTL Releasing command\n")); + SDpnt = SCpnt->device; + scsi_release_command(SCpnt); + SCpnt = NULL; - wake_up(&SCpnt->device->device_wait); + if (!SDpnt->was_reset && SDpnt->scsi_request_fn) + (*SDpnt->scsi_request_fn)(); + + wake_up(&SDpnt->device_wait); return result; } @@ -166,6 +170,7 @@ unsigned char cmd[12]; char * cmd_in; Scsi_Cmnd * SCpnt; + Scsi_Device * SDpnt; unsigned char opcode; int inlen, outlen, cmdlen; int needed, buf_needed; @@ -221,7 +226,7 @@ cmdlen = COMMAND_SIZE(opcode); result = verify_area(VERIFY_READ, cmd_in, - cmdlen + inlen > MAX_BUF ? MAX_BUF : inlen); + cmdlen + inlen > MAX_BUF ? MAX_BUF : cmdlen + inlen); if (result) return result; copy_from_user ((void *) cmd, cmd_in, cmdlen); @@ -261,7 +266,7 @@ #ifndef DEBUG_NO_CMD - SCpnt = allocate_device(NULL, dev, 1); + SCpnt = scsi_allocate_device(NULL, dev, 1); { struct semaphore sem = MUTEX_LOCKED; @@ -269,6 +274,7 @@ scsi_do_cmd(SCpnt, cmd, buf, needed, scsi_ioctl_done, timeout, retries); down(&sem); + SCpnt->request.sem = NULL; } /* @@ -289,14 +295,16 @@ } result = SCpnt->result; - SCpnt->request.rq_status = RQ_INACTIVE; + wake_up(&SCpnt->device->device_wait); + SDpnt = SCpnt->device; + scsi_release_command(SCpnt); + SCpnt = NULL; if (buf) scsi_free(buf, buf_needed); - if(SCpnt->device->scsi_request_fn) - (*SCpnt->device->scsi_request_fn)(); + if(SDpnt->scsi_request_fn) + (*SDpnt->scsi_request_fn)(); - wake_up(&SCpnt->device->device_wait); return result; #else { @@ -328,6 +336,17 @@ /* No idea how this happens.... */ if (!dev) return -ENXIO; + + /* + * If we are in the middle of error recovery, don't let anyone + * else try and use this device. Also, if error recovery fails, it + * may try and take the device offline, in which case all further + * access to the device is prohibited. + */ + if( !scsi_block_when_processing_errors(dev) ) + { + return -ENODEV; + } switch (cmd) { case SCSI_IOCTL_GET_IDLUN: diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/scsi_obsolete.c linux/drivers/scsi/scsi_obsolete.c --- v2.1.74/linux/drivers/scsi/scsi_obsolete.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/scsi_obsolete.c Sun Dec 21 17:53:17 1997 @@ -0,0 +1,1131 @@ +/* + * scsi.c Copyright (C) 1992 Drew Eckhardt + * Copyright (C) 1993, 1994, 1995 Eric Youngdale + * + * generic mid-level SCSI driver + * Initial versions: Drew Eckhardt + * Subsequent revisions: Eric Youngdale + * + * + * + * Bug correction thanks go to : + * Rik Faith + * Tommy Thorn + * Thomas Wuensche + * + * Modified by Eric Youngdale eric@aib.com to + * add scatter-gather, multiple outstanding request, and other + * enhancements. + * + * Native multichannel, wide scsi, /proc/scsi and hot plugging + * support added by Michael Neuffer + * + * Added request_module("scsi_hostadapter") for kerneld: + * (Put an "alias scsi_hostadapter your_hostadapter" in /etc/conf.modules) + * Bjorn Ekwall + * + * Major improvements to the timeout, abort, and reset processing, + * as well as performance modifications for large queue depths by + * Leonard N. Zubkoff + */ + +/* + *######################################################################### + *######################################################################### + *######################################################################### + *######################################################################### + * NOTE - NOTE - NOTE - NOTE - NOTE - NOTE - NOTE + * + *######################################################################### + *######################################################################### + *######################################################################### + *######################################################################### + * + * This file contains the 'old' scsi error handling. It is only present + * while the new error handling code is being debugged, and while the low + * level drivers are being converted to use the new code. Once the last + * driver uses the new code this *ENTIRE* file will be nuked. + */ + +#define __NO_VERSION__ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "scsi.h" +#include "hosts.h" +#include "constants.h" + +#ifdef CONFIG_KERNELD +#include +#endif + +#undef USE_STATIC_SCSI_MEMORY + +/* +static const char RCSid[] = "$Header: /mnt/ide/home/eric/linux/drivers/scsi/RCS/scsi_obsolete.c,v 1.3 1997/04/29 04:25:08 eric Exp $"; +*/ + + +#define INTERNAL_ERROR (panic ("Internal error in file %s, line %d.\n", __FILE__, __LINE__)) + + +static int scsi_abort (Scsi_Cmnd *, int code); +static int scsi_reset (Scsi_Cmnd *, unsigned int); + +extern void scsi_old_done (Scsi_Cmnd *SCpnt); +static int update_timeout (Scsi_Cmnd *, int); +extern void scsi_old_times_out (Scsi_Cmnd * SCpnt); +extern void internal_cmnd (Scsi_Cmnd * SCpnt); + +static volatile struct Scsi_Host * host_active = NULL; +#define SCSI_BLOCK(HOST) ((HOST->block && host_active && HOST != host_active) \ + || (HOST->can_queue && HOST->host_busy >= HOST->can_queue)) + +static unsigned char generic_sense[6] = {REQUEST_SENSE, 0,0,0, 255, 0}; + +/* + * This is the number of clock ticks we should wait before we time out + * and abort the command. This is for where the scsi.c module generates + * the command, not where it originates from a higher level, in which + * case the timeout is specified there. + * + * ABORT_TIMEOUT and RESET_TIMEOUT are the timeouts for RESET and ABORT + * respectively. + */ + +#ifdef DEBUG_TIMEOUT +static void scsi_dump_status(void); +#endif + + +#ifdef DEBUG + #define SCSI_TIMEOUT (5*HZ) +#else + #define SCSI_TIMEOUT (2*HZ) +#endif + +#ifdef DEBUG + #define SENSE_TIMEOUT SCSI_TIMEOUT + #define ABORT_TIMEOUT SCSI_TIMEOUT + #define RESET_TIMEOUT SCSI_TIMEOUT +#else + #define SENSE_TIMEOUT (5*HZ/10) + #define RESET_TIMEOUT (5*HZ/10) + #define ABORT_TIMEOUT (5*HZ/10) +#endif + + +/* Do not call reset on error if we just did a reset within 15 sec. */ +#define MIN_RESET_PERIOD (15*HZ) + + + +/* + * Flag bits for the internal_timeout array + */ +#define NORMAL_TIMEOUT 0 +#define IN_ABORT 1 +#define IN_RESET 2 +#define IN_RESET2 4 +#define IN_RESET3 8 + +/* + * This is our time out function, called when the timer expires for a + * given host adapter. It will attempt to abort the currently executing + * command, that failing perform a kernel panic. + */ + +void scsi_old_times_out (Scsi_Cmnd * SCpnt) +{ + + switch (SCpnt->internal_timeout & (IN_ABORT | IN_RESET | IN_RESET2 | IN_RESET3)) + { + case NORMAL_TIMEOUT: + { +#ifdef DEBUG_TIMEOUT + scsi_dump_status(); +#endif + } + + if (!scsi_abort (SCpnt, DID_TIME_OUT)) + return; + case IN_ABORT: + printk("SCSI host %d abort (pid %ld) timed out - resetting\n", + SCpnt->host->host_no, SCpnt->pid); + if (!scsi_reset (SCpnt, SCSI_RESET_ASYNCHRONOUS)) + return; + case IN_RESET: + case (IN_ABORT | IN_RESET): + /* This might be controversial, but if there is a bus hang, + * you might conceivably want the machine up and running + * esp if you have an ide disk. + */ + printk("SCSI host %d channel %d reset (pid %ld) timed out - " + "trying harder\n", + SCpnt->host->host_no, SCpnt->channel, SCpnt->pid); + SCpnt->internal_timeout &= ~IN_RESET; + SCpnt->internal_timeout |= IN_RESET2; + scsi_reset (SCpnt, + SCSI_RESET_ASYNCHRONOUS | SCSI_RESET_SUGGEST_BUS_RESET); + return; + case (IN_ABORT | IN_RESET | IN_RESET2): + /* Obviously the bus reset didn't work. + * Let's try even harder and call for an HBA reset. + * Maybe the HBA itself crashed and this will shake it loose. + */ + printk("SCSI host %d reset (pid %ld) timed out - trying to shake it loose\n", + SCpnt->host->host_no, SCpnt->pid); + SCpnt->internal_timeout &= ~(IN_RESET | IN_RESET2); + SCpnt->internal_timeout |= IN_RESET3; + scsi_reset (SCpnt, + SCSI_RESET_ASYNCHRONOUS | SCSI_RESET_SUGGEST_HOST_RESET); + return; + + default: + printk("SCSI host %d reset (pid %ld) timed out again -\n", + SCpnt->host->host_no, SCpnt->pid); + printk("probably an unrecoverable SCSI bus or device hang.\n"); + return; + + } + +} + + +static void scsi_request_sense (Scsi_Cmnd * SCpnt) +{ + unsigned long flags; + + save_flags(flags); + cli(); + SCpnt->flags |= WAS_SENSE | ASKED_FOR_SENSE; + update_timeout(SCpnt, SENSE_TIMEOUT); + restore_flags(flags); + + + memcpy ((void *) SCpnt->cmnd , (void *) generic_sense, + sizeof(generic_sense)); + + SCpnt->cmnd[1] = SCpnt->lun << 5; + SCpnt->cmnd[4] = sizeof(SCpnt->sense_buffer); + + SCpnt->request_buffer = &SCpnt->sense_buffer; + SCpnt->request_bufflen = sizeof(SCpnt->sense_buffer); + SCpnt->use_sg = 0; + SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); + internal_cmnd (SCpnt); +} + + + + +static int check_sense (Scsi_Cmnd * SCpnt) +{ + /* If there is no sense information, request it. If we have already + * requested it, there is no point in asking again - the firmware must + * be confused. + */ + if (((SCpnt->sense_buffer[0] & 0x70) >> 4) != 7) { + if(!(SCpnt->flags & ASKED_FOR_SENSE)) + return SUGGEST_SENSE; + else + return SUGGEST_RETRY; + } + + SCpnt->flags &= ~ASKED_FOR_SENSE; + +#ifdef DEBUG_INIT + printk("scsi%d, channel%d : ", SCpnt->host->host_no, SCpnt->channel); + print_sense("", SCpnt); + printk("\n"); +#endif + if (SCpnt->sense_buffer[2] & 0xe0) + return SUGGEST_ABORT; + + switch (SCpnt->sense_buffer[2] & 0xf) + { + case NO_SENSE: + return 0; + case RECOVERED_ERROR: + return SUGGEST_IS_OK; + + case ABORTED_COMMAND: + return SUGGEST_RETRY; + case NOT_READY: + case UNIT_ATTENTION: + /* + * If we are expecting a CC/UA because of a bus reset that we + * performed, treat this just as a retry. Otherwise this is + * information that we should pass up to the upper-level driver + * so that we can deal with it there. + */ + if( SCpnt->device->expecting_cc_ua ) + { + SCpnt->device->expecting_cc_ua = 0; + return SUGGEST_RETRY; + } + return SUGGEST_ABORT; + + /* these three are not supported */ + case COPY_ABORTED: + case VOLUME_OVERFLOW: + case MISCOMPARE: + + case MEDIUM_ERROR: + return SUGGEST_REMAP; + case BLANK_CHECK: + case DATA_PROTECT: + case HARDWARE_ERROR: + case ILLEGAL_REQUEST: + default: + return SUGGEST_ABORT; + } +} + +/* This function is the mid-level interrupt routine, which decides how + * to handle error conditions. Each invocation of this function must + * do one and *only* one of the following: + * + * (1) Call last_cmnd[host].done. This is done for fatal errors and + * normal completion, and indicates that the handling for this + * request is complete. + * (2) Call internal_cmnd to requeue the command. This will result in + * scsi_done being called again when the retry is complete. + * (3) Call scsi_request_sense. This asks the host adapter/drive for + * more information about the error condition. When the information + * is available, scsi_done will be called again. + * (4) Call reset(). This is sort of a last resort, and the idea is that + * this may kick things loose and get the drive working again. reset() + * automatically calls scsi_request_sense, and thus scsi_done will be + * called again once the reset is complete. + * + * If none of the above actions are taken, the drive in question + * will hang. If more than one of the above actions are taken by + * scsi_done, then unpredictable behavior will result. + */ +void scsi_old_done (Scsi_Cmnd * SCpnt) +{ + int status=0; + int exit=0; + int checked; + int oldto; + struct Scsi_Host * host = SCpnt->host; + int result = SCpnt->result; + SCpnt->serial_number = 0; + oldto = update_timeout(SCpnt, 0); + +#ifdef DEBUG_TIMEOUT + if(result) printk("Non-zero result in scsi_done %x %d:%d\n", + result, SCpnt->target, SCpnt->lun); +#endif + + /* If we requested an abort, (and we got it) then fix up the return + * status to say why + */ + if(host_byte(result) == DID_ABORT && SCpnt->abort_reason) + SCpnt->result = result = (result & 0xff00ffff) | + (SCpnt->abort_reason << 16); + + +#define CMD_FINISHED 0 +#define MAYREDO 1 +#define REDO 3 +#define PENDING 4 + +#ifdef DEBUG + printk("In scsi_done(host = %d, result = %06x)\n", host->host_no, result); +#endif + + if(SCpnt->flags & WAS_SENSE) + { + SCpnt->use_sg = SCpnt->old_use_sg; + SCpnt->cmd_len = SCpnt->old_cmd_len; + } + + switch (host_byte(result)) + { + case DID_OK: + if (status_byte(result) && (SCpnt->flags & WAS_SENSE)) + /* Failed to obtain sense information */ + { + SCpnt->flags &= ~WAS_SENSE; +#if 0 /* This cannot possibly be correct. */ + SCpnt->internal_timeout &= ~SENSE_TIMEOUT; +#endif + + if (!(SCpnt->flags & WAS_RESET)) + { + printk("scsi%d : channel %d target %d lun %d request sense" + " failed, performing reset.\n", + SCpnt->host->host_no, SCpnt->channel, SCpnt->target, + SCpnt->lun); + scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS); + return; + } + else + { + exit = (DRIVER_HARD | SUGGEST_ABORT); + status = CMD_FINISHED; + } + } + else switch(msg_byte(result)) + { + case COMMAND_COMPLETE: + switch (status_byte(result)) + { + case GOOD: + if (SCpnt->flags & WAS_SENSE) + { +#ifdef DEBUG + printk ("In scsi_done, GOOD status, COMMAND COMPLETE, " + "parsing sense information.\n"); +#endif + SCpnt->flags &= ~WAS_SENSE; +#if 0 /* This cannot possibly be correct. */ + SCpnt->internal_timeout &= ~SENSE_TIMEOUT; +#endif + + switch (checked = check_sense(SCpnt)) + { + case SUGGEST_SENSE: + case 0: +#ifdef DEBUG + printk("NO SENSE. status = REDO\n"); +#endif + update_timeout(SCpnt, oldto); + status = REDO; + break; + case SUGGEST_IS_OK: + break; + case SUGGEST_REMAP: +#ifdef DEBUG + printk("SENSE SUGGEST REMAP - status = CMD_FINISHED\n"); +#endif + status = CMD_FINISHED; + exit = DRIVER_SENSE | SUGGEST_ABORT; + break; + case SUGGEST_RETRY: +#ifdef DEBUG + printk("SENSE SUGGEST RETRY - status = MAYREDO\n"); +#endif + status = MAYREDO; + exit = DRIVER_SENSE | SUGGEST_RETRY; + break; + case SUGGEST_ABORT: +#ifdef DEBUG + printk("SENSE SUGGEST ABORT - status = CMD_FINISHED"); +#endif + status = CMD_FINISHED; + exit = DRIVER_SENSE | SUGGEST_ABORT; + break; + default: + printk ("Internal error %s %d \n", __FILE__, + __LINE__); + } + } /* end WAS_SENSE */ + else + { +#ifdef DEBUG + printk("COMMAND COMPLETE message returned, " + "status = CMD_FINISHED. \n"); +#endif + exit = DRIVER_OK; + status = CMD_FINISHED; + } + break; + + case CHECK_CONDITION: + case COMMAND_TERMINATED: + switch (check_sense(SCpnt)) + { + case 0: + update_timeout(SCpnt, oldto); + status = REDO; + break; + case SUGGEST_REMAP: + status = CMD_FINISHED; + exit = DRIVER_SENSE | SUGGEST_ABORT; + break; + case SUGGEST_RETRY: + status = MAYREDO; + exit = DRIVER_SENSE | SUGGEST_RETRY; + break; + case SUGGEST_ABORT: + status = CMD_FINISHED; + exit = DRIVER_SENSE | SUGGEST_ABORT; + break; + case SUGGEST_SENSE: + scsi_request_sense (SCpnt); + status = PENDING; + break; + } + break; + + case CONDITION_GOOD: + case INTERMEDIATE_GOOD: + case INTERMEDIATE_C_GOOD: + break; + + case BUSY: + case QUEUE_FULL: + update_timeout(SCpnt, oldto); + status = REDO; + break; + + case RESERVATION_CONFLICT: + printk("scsi%d, channel %d : RESERVATION CONFLICT performing" + " reset.\n", SCpnt->host->host_no, SCpnt->channel); + scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS); + return; +#if 0 + exit = DRIVER_SOFT | SUGGEST_ABORT; + status = MAYREDO; + break; +#endif + default: + printk ("Internal error %s %d \n" + "status byte = %d \n", __FILE__, + __LINE__, status_byte(result)); + + } + break; + default: + panic("scsi: unsupported message byte %d received\n", + msg_byte(result)); + } + break; + case DID_TIME_OUT: +#ifdef DEBUG + printk("Host returned DID_TIME_OUT - "); +#endif + + if (SCpnt->flags & WAS_TIMEDOUT) + { +#ifdef DEBUG + printk("Aborting\n"); +#endif + /* + Allow TEST_UNIT_READY and INQUIRY commands to timeout early + without causing resets. All other commands should be retried. + */ + if (SCpnt->cmnd[0] != TEST_UNIT_READY && + SCpnt->cmnd[0] != INQUIRY) + status = MAYREDO; + exit = (DRIVER_TIMEOUT | SUGGEST_ABORT); + } + else + { +#ifdef DEBUG + printk ("Retrying.\n"); +#endif + SCpnt->flags |= WAS_TIMEDOUT; + SCpnt->internal_timeout &= ~IN_ABORT; + status = REDO; + } + break; + case DID_BUS_BUSY: + case DID_PARITY: + status = REDO; + break; + case DID_NO_CONNECT: +#ifdef DEBUG + printk("Couldn't connect.\n"); +#endif + exit = (DRIVER_HARD | SUGGEST_ABORT); + break; + case DID_ERROR: + status = MAYREDO; + exit = (DRIVER_HARD | SUGGEST_ABORT); + break; + case DID_BAD_TARGET: + case DID_ABORT: + exit = (DRIVER_INVALID | SUGGEST_ABORT); + break; + case DID_RESET: + if (SCpnt->flags & IS_RESETTING) + { + SCpnt->flags &= ~IS_RESETTING; + status = REDO; + break; + } + + if(msg_byte(result) == GOOD && + status_byte(result) == CHECK_CONDITION) { + switch (check_sense(SCpnt)) { + case 0: + update_timeout(SCpnt, oldto); + status = REDO; + break; + case SUGGEST_REMAP: + case SUGGEST_RETRY: + status = MAYREDO; + exit = DRIVER_SENSE | SUGGEST_RETRY; + break; + case SUGGEST_ABORT: + status = CMD_FINISHED; + exit = DRIVER_SENSE | SUGGEST_ABORT; + break; + case SUGGEST_SENSE: + scsi_request_sense (SCpnt); + status = PENDING; + break; + } + } else { + status=REDO; + exit = SUGGEST_RETRY; + } + break; + default : + exit = (DRIVER_ERROR | SUGGEST_DIE); + } + + switch (status) + { + case CMD_FINISHED: + case PENDING: + break; + case MAYREDO: +#ifdef DEBUG + printk("In MAYREDO, allowing %d retries, have %d\n", + SCpnt->allowed, SCpnt->retries); +#endif + if ((++SCpnt->retries) < SCpnt->allowed) + { + if ((SCpnt->retries >= (SCpnt->allowed >> 1)) + && !(SCpnt->host->last_reset > 0 && + jiffies < SCpnt->host->last_reset + MIN_RESET_PERIOD) + && !(SCpnt->flags & WAS_RESET)) + { + printk("scsi%d channel %d : resetting for second half of retries.\n", + SCpnt->host->host_no, SCpnt->channel); + scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS); + break; + } + + } + else + { + status = CMD_FINISHED; + break; + } + /* fall through to REDO */ + + case REDO: + + if (SCpnt->flags & WAS_SENSE) + scsi_request_sense(SCpnt); + else + { + memcpy ((void *) SCpnt->cmnd, + (void*) SCpnt->data_cmnd, + sizeof(SCpnt->data_cmnd)); + SCpnt->request_buffer = SCpnt->buffer; + SCpnt->request_bufflen = SCpnt->bufflen; + SCpnt->use_sg = SCpnt->old_use_sg; + SCpnt->cmd_len = SCpnt->old_cmd_len; + internal_cmnd (SCpnt); + } + break; + default: + INTERNAL_ERROR; + } + + if (status == CMD_FINISHED) { +#ifdef DEBUG + printk("Calling done function - at address %p\n", SCpnt->done); +#endif + host->host_busy--; /* Indicate that we are free */ + + if (host->block && host->host_busy == 0) { + host_active = NULL; + + /* For block devices "wake_up" is done in end_scsi_request */ + if (MAJOR(SCpnt->request.rq_dev) != SCSI_DISK_MAJOR && + MAJOR(SCpnt->request.rq_dev) != SCSI_CDROM_MAJOR) { + struct Scsi_Host * next; + + for (next = host->block; next != host; next = next->block) + wake_up(&next->host_wait); + } + + } + + wake_up(&host->host_wait); + SCpnt->result = result | ((exit & 0xff) << 24); + SCpnt->use_sg = SCpnt->old_use_sg; + SCpnt->cmd_len = SCpnt->old_cmd_len; + SCpnt->done (SCpnt); + } + +#undef CMD_FINISHED +#undef REDO +#undef MAYREDO +#undef PENDING +} + +/* + * The scsi_abort function interfaces with the abort() function of the host + * we are aborting, and causes the current command to not complete. The + * caller should deal with any error messages or status returned on the + * next call. + * + * This will not be called reentrantly for a given host. + */ + +/* + * Since we're nice guys and specified that abort() and reset() + * can be non-reentrant. The internal_timeout flags are used for + * this. + */ + + +static int scsi_abort (Scsi_Cmnd * SCpnt, int why) +{ + int oldto; + unsigned long flags; + struct Scsi_Host * host = SCpnt->host; + + while(1) + { + save_flags(flags); + cli(); + + /* + * Protect against races here. If the command is done, or we are + * on a different command forget it. + */ + if (SCpnt->serial_number != SCpnt->serial_number_at_timeout) { + restore_flags(flags); + return 0; + } + + if (SCpnt->internal_timeout & IN_ABORT) + { + restore_flags(flags); + while (SCpnt->internal_timeout & IN_ABORT) + barrier(); + } + else + { + SCpnt->internal_timeout |= IN_ABORT; + oldto = update_timeout(SCpnt, ABORT_TIMEOUT); + + if ((SCpnt->flags & IS_RESETTING) && SCpnt->device->soft_reset) { + /* OK, this command must have died when we did the + * reset. The device itself must have lied. + */ + printk("Stale command on %d %d:%d appears to have died when" + " the bus was reset\n", + SCpnt->channel, SCpnt->target, SCpnt->lun); + } + + restore_flags(flags); + if (!host->host_busy) { + SCpnt->internal_timeout &= ~IN_ABORT; + update_timeout(SCpnt, oldto); + return 0; + } + printk("scsi : aborting command due to timeout : pid %lu, scsi%d," + " channel %d, id %d, lun %d ", + SCpnt->pid, SCpnt->host->host_no, (int) SCpnt->channel, + (int) SCpnt->target, (int) SCpnt->lun); + print_command (SCpnt->cmnd); + if (SCpnt->serial_number != SCpnt->serial_number_at_timeout) + return 0; + SCpnt->abort_reason = why; + switch(host->hostt->abort(SCpnt)) { + /* We do not know how to abort. Try waiting another + * time increment and see if this helps. Set the + * WAS_TIMEDOUT flag set so we do not try this twice + */ + case SCSI_ABORT_BUSY: /* Tough call - returning 1 from + * this is too severe + */ + case SCSI_ABORT_SNOOZE: + if(why == DID_TIME_OUT) { + save_flags(flags); + cli(); + SCpnt->internal_timeout &= ~IN_ABORT; + if(SCpnt->flags & WAS_TIMEDOUT) { + restore_flags(flags); + return 1; /* Indicate we cannot handle this. + * We drop down into the reset handler + * and try again + */ + } else { + SCpnt->flags |= WAS_TIMEDOUT; + oldto = SCpnt->timeout_per_command; + update_timeout(SCpnt, oldto); + } + restore_flags(flags); + } + return 0; + case SCSI_ABORT_PENDING: + if(why != DID_TIME_OUT) { + save_flags(flags); + cli(); + update_timeout(SCpnt, oldto); + restore_flags(flags); + } + return 0; + case SCSI_ABORT_SUCCESS: + /* We should have already aborted this one. No + * need to adjust timeout + */ + SCpnt->internal_timeout &= ~IN_ABORT; + return 0; + case SCSI_ABORT_NOT_RUNNING: + SCpnt->internal_timeout &= ~IN_ABORT; + update_timeout(SCpnt, 0); + return 0; + case SCSI_ABORT_ERROR: + default: + SCpnt->internal_timeout &= ~IN_ABORT; + return 1; + } + } + } +} + + +/* Mark a single SCSI Device as having been reset. */ + +static inline void scsi_mark_device_reset(Scsi_Device *Device) +{ + Device->was_reset = 1; + Device->expecting_cc_ua = 1; +} + + +/* Mark all SCSI Devices on a specific Host as having been reset. */ + +void scsi_mark_host_reset(struct Scsi_Host *Host) +{ + Scsi_Cmnd * SCpnt; + Scsi_Device * SDpnt; + + for (SDpnt = Host->host_queue; SDpnt; SDpnt = SDpnt->next) + { + for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) + scsi_mark_device_reset(SCpnt->device); + } +} + + +/* Mark all SCSI Devices on a specific Host Bus as having been reset. */ + +static void scsi_mark_bus_reset(struct Scsi_Host *Host, int channel) +{ + Scsi_Cmnd *SCpnt; + Scsi_Device * SDpnt; + + for (SDpnt = Host->host_queue; SDpnt; SDpnt = SDpnt->next) + { + for (SCpnt = SDpnt->device_queue; SCpnt; SCpnt = SCpnt->next) + if (SCpnt->channel == channel) + scsi_mark_device_reset(SCpnt->device); + } +} + + +static int scsi_reset (Scsi_Cmnd * SCpnt, unsigned int reset_flags) +{ + int temp; + unsigned long flags; + Scsi_Cmnd * SCpnt1; + Scsi_Device * SDpnt; + struct Scsi_Host * host = SCpnt->host; + + printk("SCSI bus is being reset for host %d channel %d.\n", + host->host_no, SCpnt->channel); + +#if 0 + /* + * First of all, we need to make a recommendation to the low-level + * driver as to whether a BUS_DEVICE_RESET should be performed, + * or whether we should do a full BUS_RESET. There is no simple + * algorithm here - we basically use a series of heuristics + * to determine what we should do. + */ + SCpnt->host->suggest_bus_reset = FALSE; + + /* + * First see if all of the active devices on the bus have + * been jammed up so that we are attempting resets. If so, + * then suggest a bus reset. Forcing a bus reset could + * result in some race conditions, but no more than + * you would usually get with timeouts. We will cross + * that bridge when we come to it. + * + * This is actually a pretty bad idea, since a sequence of + * commands will often timeout together and this will cause a + * Bus Device Reset followed immediately by a SCSI Bus Reset. + * If all of the active devices really are jammed up, the + * Bus Device Reset will quickly timeout and scsi_times_out + * will follow up with a SCSI Bus Reset anyway. + */ + SCpnt1 = host->host_queue; + while(SCpnt1) { + if( SCpnt1->request.rq_status != RQ_INACTIVE + && (SCpnt1->flags & (WAS_RESET | IS_RESETTING)) == 0 ) + break; + SCpnt1 = SCpnt1->next; + } + if( SCpnt1 == NULL ) { + reset_flags |= SCSI_RESET_SUGGEST_BUS_RESET; + } + + /* + * If the code that called us is suggesting a hard reset, then + * definitely request it. This usually occurs because a + * BUS_DEVICE_RESET times out. + * + * Passing reset_flags along takes care of this automatically. + */ + if( reset_flags & SCSI_RESET_SUGGEST_BUS_RESET ) { + SCpnt->host->suggest_bus_reset = TRUE; + } +#endif + + while (1) { + save_flags(flags); + cli(); + + /* + * Protect against races here. If the command is done, or we are + * on a different command forget it. + */ + if (reset_flags & SCSI_RESET_ASYNCHRONOUS) + if (SCpnt->serial_number != SCpnt->serial_number_at_timeout) { + restore_flags(flags); + return 0; + } + + if (SCpnt->internal_timeout & IN_RESET) + { + restore_flags(flags); + while (SCpnt->internal_timeout & IN_RESET) + barrier(); + } + else + { + SCpnt->internal_timeout |= IN_RESET; + update_timeout(SCpnt, RESET_TIMEOUT); + + if (host->host_busy) + { + restore_flags(flags); + for(SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) + { + SCpnt1 = SDpnt->device_queue; + while(SCpnt1) { + if (SCpnt1->request.rq_status != RQ_INACTIVE) { +#if 0 + if (!(SCpnt1->flags & IS_RESETTING) && + !(SCpnt1->internal_timeout & IN_ABORT)) + scsi_abort(SCpnt1, DID_RESET); +#endif + SCpnt1->flags |= (WAS_RESET | IS_RESETTING); + } + SCpnt1 = SCpnt1->next; + } + } + + host->last_reset = jiffies; + temp = host->hostt->reset(SCpnt, reset_flags); + /* + This test allows the driver to introduce an additional bus + settle time delay by setting last_reset up to 20 seconds in + the future. In the normal case where the driver does not + modify last_reset, it must be assumed that the actual bus + reset occurred immediately prior to the return to this code, + and so last_reset must be updated to the current time, so + that the delay in internal_cmnd will guarantee at least a + MIN_RESET_DELAY bus settle time. + */ + if (host->last_reset - jiffies > 20UL * HZ) + host->last_reset = jiffies; + } + else + { + if (!host->block) host->host_busy++; + restore_flags(flags); + host->last_reset = jiffies; + SCpnt->flags |= (WAS_RESET | IS_RESETTING); + temp = host->hostt->reset(SCpnt, reset_flags); + if ((host->last_reset < jiffies) || + (host->last_reset > (jiffies + 20 * HZ))) + host->last_reset = jiffies; + if (!host->block) host->host_busy--; + } + +#ifdef DEBUG + printk("scsi reset function returned %d\n", temp); +#endif + + /* + * Now figure out what we need to do, based upon + * what the low level driver said that it did. + * If the result is SCSI_RESET_SUCCESS, SCSI_RESET_PENDING, + * or SCSI_RESET_WAKEUP, then the low level driver did a + * bus device reset or bus reset, so we should go through + * and mark one or all of the devices on that bus + * as having been reset. + */ + switch(temp & SCSI_RESET_ACTION) { + case SCSI_RESET_SUCCESS: + if (temp & SCSI_RESET_HOST_RESET) + scsi_mark_host_reset(host); + else if (temp & SCSI_RESET_BUS_RESET) + scsi_mark_bus_reset(host, SCpnt->channel); + else scsi_mark_device_reset(SCpnt->device); + save_flags(flags); + cli(); + SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3); + restore_flags(flags); + return 0; + case SCSI_RESET_PENDING: + if (temp & SCSI_RESET_HOST_RESET) + scsi_mark_host_reset(host); + else if (temp & SCSI_RESET_BUS_RESET) + scsi_mark_bus_reset(host, SCpnt->channel); + else scsi_mark_device_reset(SCpnt->device); + case SCSI_RESET_NOT_RUNNING: + return 0; + case SCSI_RESET_PUNT: + SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3); + scsi_request_sense (SCpnt); + return 0; + case SCSI_RESET_WAKEUP: + if (temp & SCSI_RESET_HOST_RESET) + scsi_mark_host_reset(host); + else if (temp & SCSI_RESET_BUS_RESET) + scsi_mark_bus_reset(host, SCpnt->channel); + else scsi_mark_device_reset(SCpnt->device); + SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3); + scsi_request_sense (SCpnt); + /* + * If a bus reset was performed, we + * need to wake up each and every command + * that was active on the bus or if it was a HBA + * reset all active commands on all channels + */ + if( temp & SCSI_RESET_HOST_RESET ) + { + for(SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) + { + SCpnt1 = SDpnt->device_queue; + while(SCpnt1) { + if (SCpnt1->request.rq_status != RQ_INACTIVE + && SCpnt1 != SCpnt) + scsi_request_sense (SCpnt1); + SCpnt1 = SCpnt1->next; + } + } + } else if( temp & SCSI_RESET_BUS_RESET ) { + for(SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) + { + SCpnt1 = SDpnt->device_queue; + while(SCpnt1) { + if(SCpnt1->request.rq_status != RQ_INACTIVE + && SCpnt1 != SCpnt + && SCpnt1->channel == SCpnt->channel) + scsi_request_sense (SCpnt); + SCpnt1 = SCpnt1->next; + } + } + } + return 0; + case SCSI_RESET_SNOOZE: + /* In this case, we set the timeout field to 0 + * so that this command does not time out any more, + * and we return 1 so that we get a message on the + * screen. + */ + save_flags(flags); + cli(); + SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3); + update_timeout(SCpnt, 0); + restore_flags(flags); + /* If you snooze, you lose... */ + case SCSI_RESET_ERROR: + default: + return 1; + } + + return temp; + } + } +} + +/* + * The strategy is to cause the timer code to call scsi_times_out() + * when the soonest timeout is pending. + * The arguments are used when we are queueing a new command, because + * we do not want to subtract the time used from this time, but when we + * set the timer, we want to take this value into account. + */ + +static int update_timeout(Scsi_Cmnd * SCset, int timeout) +{ + int rtn; + + /* + * We are using the new error handling code to actually register/deregister + * timers for timeout. + */ + + if( SCset->eh_timeout.expires == 0 ) + { + rtn = 0; + } + else + { + rtn = SCset->eh_timeout.expires - jiffies; + } + + if( timeout == 0 ) + { + scsi_delete_timer(SCset); + } + else + { + scsi_add_timer(SCset, timeout, scsi_old_times_out); + } + + return rtn; +} + + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-indent-level: 4 + * c-brace-imaginary-offset: 0 + * c-brace-offset: -4 + * c-argdecl-indent: 4 + * c-label-offset: -4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: 0 + * indent-tabs-mode: nil + * tab-width: 8 + * End: + */ diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/scsi_obsolete.h linux/drivers/scsi/scsi_obsolete.h --- v2.1.74/linux/drivers/scsi/scsi_obsolete.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/scsi_obsolete.h Sun Dec 21 17:04:49 1997 @@ -0,0 +1,106 @@ +/* + * scsi_obsolete.h Copyright (C) 1997 Eric Youngdale + * + */ + +#ifndef _SCSI_OBSOLETE_H +#define _SCSI_OBSOLETE_H + +/* + * These are the return codes for the abort and reset functions. The mid-level + * code uses these to decide what to do next. Each of the low level abort + * and reset functions must correctly indicate what it has done. + * The descriptions are written from the point of view of the mid-level code, + * so that the return code is telling the mid-level drivers exactly what + * the low level driver has already done, and what remains to be done. + */ + +/* We did not do anything. + * Wait some more for this command to complete, and if this does not work, + * try something more serious. */ +#define SCSI_ABORT_SNOOZE 0 + +/* This means that we were able to abort the command. We have already + * called the mid-level done function, and do not expect an interrupt that + * will lead to another call to the mid-level done function for this command */ +#define SCSI_ABORT_SUCCESS 1 + +/* We called for an abort of this command, and we should get an interrupt + * when this succeeds. Thus we should not restore the timer for this + * command in the mid-level abort function. */ +#define SCSI_ABORT_PENDING 2 + +/* Unable to abort - command is currently on the bus. Grin and bear it. */ +#define SCSI_ABORT_BUSY 3 + +/* The command is not active in the low level code. Command probably + * finished. */ +#define SCSI_ABORT_NOT_RUNNING 4 + +/* Something went wrong. The low level driver will indicate the correct + * error condition when it calls scsi_done, so the mid-level abort function + * can simply wait until this comes through */ +#define SCSI_ABORT_ERROR 5 + +/* We do not know how to reset the bus, or we do not want to. Bummer. + * Anyway, just wait a little more for the command in question, and hope that + * it eventually finishes. If it never finishes, the SCSI device could + * hang, so use this with caution. */ +#define SCSI_RESET_SNOOZE 0 + +/* We do not know how to reset the bus, or we do not want to. Bummer. + * We have given up on this ever completing. The mid-level code will + * request sense information to decide how to proceed from here. */ +#define SCSI_RESET_PUNT 1 + +/* This means that we were able to reset the bus. We have restarted all of + * the commands that should be restarted, and we should be able to continue + * on normally from here. We do not expect any interrupts that will return + * DID_RESET to any of the other commands in the host_queue, and the mid-level + * code does not need to do anything special to keep the commands alive. + * If a hard reset was performed then all outstanding commands on the + * bus have been restarted. */ +#define SCSI_RESET_SUCCESS 2 + +/* We called for a reset of this bus, and we should get an interrupt + * when this succeeds. Each command should get its own status + * passed up to scsi_done, but this has not happened yet. + * If a hard reset was performed, then we expect an interrupt + * for *each* of the outstanding commands that will have the + * effect of restarting the commands. + */ +#define SCSI_RESET_PENDING 3 + +/* We did a reset, but do not expect an interrupt to signal DID_RESET. + * This tells the upper level code to request the sense info, and this + * should keep the command alive. */ +#define SCSI_RESET_WAKEUP 4 + +/* The command is not active in the low level code. Command probably + finished. */ +#define SCSI_RESET_NOT_RUNNING 5 + +/* Something went wrong, and we do not know how to fix it. */ +#define SCSI_RESET_ERROR 6 + +#define SCSI_RESET_SYNCHRONOUS 0x01 +#define SCSI_RESET_ASYNCHRONOUS 0x02 +#define SCSI_RESET_SUGGEST_BUS_RESET 0x04 +#define SCSI_RESET_SUGGEST_HOST_RESET 0x08 +/* + * This is a bitmask that is ored with one of the above codes. + * It tells the mid-level code that we did a hard reset. + */ +#define SCSI_RESET_BUS_RESET 0x100 +/* + * This is a bitmask that is ored with one of the above codes. + * It tells the mid-level code that we did a host adapter reset. + */ +#define SCSI_RESET_HOST_RESET 0x200 +/* + * Used to mask off bits and to obtain the basic action that was + * performed. + */ +#define SCSI_RESET_ACTION 0xff + +#endif /* SCSI_OBSOLETE_H */ diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/scsi_proc.c linux/drivers/scsi/scsi_proc.c --- v2.1.74/linux/drivers/scsi/scsi_proc.c Tue May 13 22:41:13 1997 +++ linux/drivers/scsi/scsi_proc.c Sun Dec 21 17:04:49 1997 @@ -16,11 +16,6 @@ * Michael A. Griffith */ -/* - * Don't import our own symbols, as this would severely mess up our - * symbol tables. - */ -#define _SCSI_SYMS_VER_ #define __NO_VERSION__ #include @@ -39,6 +34,7 @@ #define FALSE 0 #endif +#ifdef CONFIG_PROC_FS extern int scsi_proc_info(char *, char **, off_t, int, int, int); struct scsi_dir { @@ -295,6 +291,7 @@ *size = y; return; } +#endif /* CONFIG_SCSI_PROC */ /* * Overrides for Emacs so that we get a uniform tabbing style. diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/scsi_queue.c linux/drivers/scsi/scsi_queue.c --- v2.1.74/linux/drivers/scsi/scsi_queue.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/scsi_queue.c Sun Dec 21 17:04:49 1997 @@ -0,0 +1,321 @@ +/* + * scsi_queue.c Copyright (C) 1997 Eric Youngdale + * + * generic mid-level SCSI queueing. + * + * The point of this is that we need to track when hosts are unable to + * accept a command because they are busy. In addition, we track devices + * that cannot accept a command because of a QUEUE_FULL condition. In both + * of these cases, we enter the command in the queue. At some later point, + * we attempt to remove commands from the queue and retry them. + */ + +#include +#define __NO_VERSION__ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define __KERNEL_SYSCALLS__ + +#include + +#include +#include +#include + +#include "scsi.h" +#include "hosts.h" +#include "constants.h" + +#ifdef CONFIG_KERNELD +#include +#endif + +/* + * TODO: + * 1) Prevent multiple traversals of list to look for commands to + * queue. + * 2) Protect against multiple insertions of list at the same time. + * DONE: + * 1) Set state of scsi command to a new state value for ml queue. + * 2) Insert into queue when host rejects command. + * 3) Make sure status code is properly passed from low-level queue func + * so that internal_cmnd properly returns the right value. + * 4) Insert into queue when QUEUE_FULL. + * 5) Cull queue in bottom half handler. + * 6) Check usage count prior to queue insertion. Requeue if usage + * count is 0. + * 7) Don't send down any more commands if the host/device is busy. + */ + +static const char RCSid[] = "$Header: /mnt/ide/home/eric/CVSROOT/linux/drivers/scsi/scsi_queue.c,v 1.1 1997/10/21 11:16:38 eric Exp $"; + +/* + * Lock used to prevent more than one process from frobbing the list at the + * same time. FIXME(eric) - there should be seperate spinlocks for each host. + * This will reduce contention. + */ +spinlock_t scsi_mlqueue_lock = SPIN_LOCK_UNLOCKED; +spinlock_t scsi_mlqueue_remove_lock = SPIN_LOCK_UNLOCKED; + +/* + * Function: scsi_mlqueue_insert() + * + * Purpose: Insert a command in the midlevel queue. + * + * Arguments: cmd - command that we are adding to queue. + * reason - why we are inserting command to queue. + * + * Returns: Nothing. + * + * Notes: We do this for one of two cases. Either the host is busy + * and it cannot accept any more commands for the time being, + * or the device returned QUEUE_FULL and can accept no more + * commands. + * Notes: This could be called either from an interrupt context or a + * normal process context. + */ +int +scsi_mlqueue_insert(Scsi_Cmnd * cmd, int reason) +{ + Scsi_Cmnd * cpnt; + unsigned long flags; + struct Scsi_Host * host; + + SCSI_LOG_MLQUEUE(1,printk("Inserting command %p into mlqueue\n", cmd)); + + /* + * We are inserting the command into the ml queue. First, we + * cancel the timer, so it doesn't time out. + */ + scsi_delete_timer(cmd); + + host = cmd->host; + + /* + * Next, set the appropriate busy bit for the device/host. + */ + if( reason == SCSI_MLQUEUE_HOST_BUSY ) + { + /* + * Protect against race conditions. If the host isn't busy, + * assume that something actually completed, and that we should + * be able to queue a command now. Note that there is an implicit + * assumption that every host can always queue at least one command. + * If a host is inactive and cannot queue any commands, I don't see + * how things could possibly work anyways. + */ + if( host->host_busy == 0 ) + { + if( scsi_retry_command(cmd) == 0 ) + { + return 0; + } + } + + host->host_blocked = TRUE; + cmd->host_wait = TRUE; + } + else + { + /* + * Protect against race conditions. If the device isn't busy, + * assume that something actually completed, and that we should + * be able to queue a command now. Note that there is an implicit + * assumption that every host can always queue at least one command. + * If a host is inactive and cannot queue any commands, I don't see + * how things could possibly work anyways. + */ + if( cmd->device->device_busy == 0 ) + { + if( scsi_retry_command(cmd) == 0 ) + { + return 0; + } + } + + cmd->device->device_busy = TRUE; + cmd->device_wait = TRUE; + } + + /* + * Register the fact that we own the thing for now. + */ + cmd->state = SCSI_STATE_MLQUEUE; + cmd->owner = SCSI_OWNER_MIDLEVEL; + cmd->bh_next = NULL; + + /* + * As a performance enhancement, look to see whether the list is + * empty. If it is, then we can just atomicly insert the command + * in the list and return without locking. + */ + if( host->pending_commands == NULL ) + { + cpnt = xchg(&host->pending_commands, cmd); + if( cpnt == NULL ) + { + return 0; + } + /* + * Rats. Something slipped in while we were exchanging. + * Swap it back and fall through to do it the hard way. + */ + cmd = xchg(&host->pending_commands, cpnt); + + } + + /* + * Next append the command to the list of pending commands. + */ + spin_lock_irqsave(&scsi_mlqueue_lock, flags); + for(cpnt = host->pending_commands; cpnt && cpnt->bh_next; + cpnt = cpnt->bh_next) + { + continue; + } + if( cpnt != NULL ) + { + cpnt->bh_next = cmd; + } + else + { + host->pending_commands = cmd; + } + + spin_unlock_irqrestore(&scsi_mlqueue_lock, flags); + return 0; +} + +/* + * Function: scsi_mlqueue_finish() + * + * Purpose: Try and queue commands from the midlevel queue. + * + * Arguments: host - host that just finished a command. + * device - device that just finished a command. + * + * Returns: Nothing. + * + * Notes: This could be called either from an interrupt context or a + * normal process context. + */ +int +scsi_mlqueue_finish(struct Scsi_Host * host, Scsi_Device * device) +{ + Scsi_Cmnd * cpnt; + unsigned long flags; + Scsi_Cmnd * next; + Scsi_Cmnd * prev; + int reason = 0; + int rtn; + + SCSI_LOG_MLQUEUE(2,printk("scsi_mlqueue_finish starting\n")); + /* + * First, clear the flag for the host/device. We will then start + * pushing commands through until either something else blocks, or + * the queue is empty. + */ + if( host->host_blocked ) + { + reason = SCSI_MLQUEUE_HOST_BUSY; + host->host_blocked = FALSE; + } + + if( device->device_busy ) + { + reason = SCSI_MLQUEUE_DEVICE_BUSY; + device->device_busy = FALSE; + } + + /* + * Walk the list of commands to see if there is anything we can + * queue. This probably needs to be optimized for performance at + * some point. + */ + prev = NULL; + spin_lock_irqsave(&scsi_mlqueue_remove_lock, flags); + for(cpnt = host->pending_commands; cpnt; cpnt = next) + { + next = cpnt->bh_next; + /* + * First, see if this command is suitable for being retried now. + */ + if( reason == SCSI_MLQUEUE_HOST_BUSY ) + { + /* + * The host was busy, but isn't any more. Thus we may be + * able to queue the command now, but we were waiting for + * the device, then we should keep waiting. Similarily, if + * the device is now busy, we should also keep waiting. + */ + if( (cpnt->host_wait == FALSE) + || (device->device_busy == TRUE) ) + { + prev = cpnt; + continue; + } + } + + if( reason == SCSI_MLQUEUE_DEVICE_BUSY ) + { + /* + * The device was busy, but isn't any more. Thus we may be + * able to queue the command now, but we were waiting for + * the host, then we should keep waiting. Similarily, if + * the host is now busy, we should also keep waiting. + */ + if( (cpnt->device_wait == FALSE) + || (host->host_blocked == TRUE) ) + { + prev = cpnt; + continue; + } + } + + /* + * First, remove the command from the list. + */ + if( prev == NULL ) + { + host->pending_commands = next; + } + else + { + prev->bh_next = next; + } + cpnt->bh_next = NULL; + + rtn = scsi_retry_command(cpnt); + + /* + * If we got a non-zero return value, it means that the host rejected + * the command. The internal_cmnd function will have added the + * command back to the end of the list, so we don't have anything + * more to do here except return. + */ + if( rtn ) + { + spin_unlock_irqrestore(&scsi_mlqueue_remove_lock, flags); + SCSI_LOG_MLQUEUE(1,printk("Unable to remove command %p from mlqueue\n", cpnt)); + goto finish; + } + SCSI_LOG_MLQUEUE(1,printk("Removed command %p from mlqueue\n", cpnt)); + } + + spin_unlock_irqrestore(&scsi_mlqueue_remove_lock, flags); +finish: + SCSI_LOG_MLQUEUE(2,printk("scsi_mlqueue_finish returning\n")); + return 0; +} diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/scsi_syms.c linux/drivers/scsi/scsi_syms.c --- v2.1.74/linux/drivers/scsi/scsi_syms.c Sun Sep 7 13:10:43 1997 +++ linux/drivers/scsi/scsi_syms.c Sun Dec 21 17:04:49 1997 @@ -38,6 +38,8 @@ extern void print_command (unsigned char *command); extern void print_sense(const char * devclass, Scsi_Cmnd * SCpnt); +extern const char *const scsi_device_types[]; + EXPORT_SYMBOL(scsi_register_module); EXPORT_SYMBOL(scsi_unregister_module); EXPORT_SYMBOL(scsi_free); @@ -45,7 +47,7 @@ EXPORT_SYMBOL(scsi_register); EXPORT_SYMBOL(scsi_unregister); EXPORT_SYMBOL(scsicam_bios_param); -EXPORT_SYMBOL(allocate_device); +EXPORT_SYMBOL(scsi_allocate_device); EXPORT_SYMBOL(scsi_do_cmd); EXPORT_SYMBOL(scsi_command_size); EXPORT_SYMBOL(scsi_init_malloc); @@ -55,13 +57,17 @@ EXPORT_SYMBOL(print_sense); EXPORT_SYMBOL(print_msg); EXPORT_SYMBOL(print_status); -EXPORT_SYMBOL(dma_free_sectors); +EXPORT_SYMBOL(scsi_dma_free_sectors); EXPORT_SYMBOL(kernel_scsi_ioctl); -EXPORT_SYMBOL(need_isa_buffer); -EXPORT_SYMBOL(request_queueable); +EXPORT_SYMBOL(scsi_need_isa_buffer); +EXPORT_SYMBOL(scsi_request_queueable); +EXPORT_SYMBOL(scsi_release_command); EXPORT_SYMBOL(print_Scsi_Cmnd); -EXPORT_SYMBOL(scsi_mark_host_reset); -EXPORT_SYMBOL(scsi_mark_bus_reset); +EXPORT_SYMBOL(scsi_block_when_processing_errors); +#if defined(CONFIG_SCSI_LOGGING) /* { */ +EXPORT_SYMBOL(scsi_logging_level); +#endif + #if defined(CONFIG_PROC_FS) EXPORT_SYMBOL(proc_print_scsidevice); #endif @@ -71,7 +77,6 @@ EXPORT_SYMBOL(scsi_hostlist); EXPORT_SYMBOL(scsi_hosts); EXPORT_SYMBOL(scsi_devicelist); -EXPORT_SYMBOL(scsi_devices); EXPORT_SYMBOL(scsi_device_types); diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/scsicam.c linux/drivers/scsi/scsicam.c --- v2.1.74/linux/drivers/scsi/scsicam.c Thu Nov 7 01:25:56 1996 +++ linux/drivers/scsi/scsicam.c Sun Dec 21 17:04:49 1997 @@ -10,11 +10,6 @@ * For more information, please consult the SCSI-CAM draft. */ -/* - * Don't import our own symbols, as this would severely mess up our - * symbol tables. - */ -#define _SCSI_SYMS_VER_ #define __NO_VERSION__ #include diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c --- v2.1.74/linux/drivers/scsi/sd.c Thu Aug 14 20:49:17 1997 +++ linux/drivers/scsi/sd.c Sun Dec 21 18:02:36 1997 @@ -87,6 +87,26 @@ static int sd_detect(Scsi_Device *); static void sd_detach(Scsi_Device *); +static void sd_devname(unsigned int disknum, char * buffer) +{ + if( disknum <= 26 ) + { + sprintf(buffer, "sd%c", 'a' + (disknum >> 4)); + } + else + { + unsigned int min1; + unsigned int min2; + /* + * For larger numbers of disks, we need to go to a new + * naming scheme. + */ + min1 = (disknum >> 4) / 26; + min2 = (disknum >> 4) % 26; + sprintf(buffer, "sd%c%c", 'a' + min1, 'a' + min2); + } +} + struct Scsi_Device_Template sd_template = { NULL, "disk", "sd", NULL, TYPE_DISK, SCSI_DISK_MAJOR, 0, 0, 0, 1, @@ -99,10 +119,21 @@ int target; target = DEVICE_NR(inode->i_rdev); + SCSI_LOG_HLQUEUE(1,printk("target=%d, max=%d\n", target, sd_template.dev_max)); + if(target >= sd_template.dev_max || !rscsi_disks[target].device) return -ENXIO; /* No such device */ /* + * If the device is in error recovery, wait until it is done. + * If the device is offline, then disallow any access to it. + */ + if( !scsi_block_when_processing_errors(rscsi_disks[target].device) ) + { + return -ENXIO; + } + + /* * Make sure that only one process can do a check_change_disk at one time. * This is also used to lock out further access when the partition table * is being re-read. @@ -129,6 +160,16 @@ } /* + * It is possible that the disk changing stuff resulted in the device being taken + * offline. If this is the case, report this to the user, and don't pretend that + * the open actually succeeded. + */ + if( !rscsi_disks[target].device->online ) + { + return -ENXIO; + } + + /* * See if we are requesting a non-existent partition. Do this * after checking for disk change. */ @@ -222,14 +263,18 @@ static void rw_intr (Scsi_Cmnd *SCpnt) { int result = SCpnt->result; + char nbuff[6]; int this_count = SCpnt->bufflen >> 9; int good_sectors = (result == 0 ? this_count : 0); int block_sectors = 1; -#ifdef DEBUG - printk("sd%c : rw_intr(%d, %d)\n", 'a' + MINOR(SCpnt->request.rq_dev), - SCpnt->host->host_no, result); -#endif + sd_devname(MINOR(SCpnt->request.rq_dev) >> 4, nbuff); + + SCSI_LOG_HLCOMPLETE(1,printk("%s : rw_intr(%d, %x [%x %x])\n", nbuff, + SCpnt->host->host_no, + result, + SCpnt->sense_buffer[0], + SCpnt->sense_buffer[2])); /* Handle MEDIUM ERRORs that indicate partial success. Since this is a @@ -276,20 +321,21 @@ if (good_sectors > 0) { -#ifdef DEBUG - printk("sd%c : %d sectors remain.\n", 'a' + MINOR(SCpnt->request.rq_dev), - SCpnt->request.nr_sectors); - printk("use_sg is %d\n ",SCpnt->use_sg); -#endif + SCSI_LOG_HLCOMPLETE(1,printk("%s : %ld sectors remain.\n", nbuff, + SCpnt->request.nr_sectors)); + SCSI_LOG_HLCOMPLETE(1,printk("use_sg is %d\n ",SCpnt->use_sg)); + if (SCpnt->use_sg) { struct scatterlist * sgpnt; int i; sgpnt = (struct scatterlist *) SCpnt->buffer; for(i=0; iuse_sg; i++) { -#ifdef DEBUG - printk(":%x %x %d\n",sgpnt[i].alt_address, sgpnt[i].address, - sgpnt[i].length); + +#if 0 + SCSI_LOG_HLCOMPLETE(3,printk(":%p %p %d\n",sgpnt[i].alt_address, sgpnt[i].address, + sgpnt[i].length)); #endif + if (sgpnt[i].alt_address) { if (SCpnt->request.cmd == READ) memcpy(sgpnt[i].alt_address, sgpnt[i].address, @@ -302,10 +348,10 @@ scsi_free(SCpnt->buffer, SCpnt->sglist_len); } else { if (SCpnt->buffer != SCpnt->request.buffer) { -#ifdef DEBUG - printk("nosg: %x %x %d\n",SCpnt->request.buffer, SCpnt->buffer, - SCpnt->bufflen); -#endif + SCSI_LOG_HLCOMPLETE(3,printk("nosg: %p %p %d\n", + SCpnt->request.buffer, SCpnt->buffer, + SCpnt->bufflen)); + if (SCpnt->request.cmd == READ) memcpy(SCpnt->request.buffer, SCpnt->buffer, SCpnt->bufflen); @@ -323,10 +369,9 @@ if (!SCpnt->request.bh) { -#ifdef DEBUG - printk("sd%c : handling page request, no buffer\n", - 'a' + MINOR(SCpnt->request.rq_dev)); -#endif + SCSI_LOG_HLCOMPLETE(2,printk("%s : handling page request, no buffer\n", + nbuff)); + /* * The SCpnt->request.nr_sectors field is always done in * 512 byte sectors, even if this really isn't the case. @@ -351,20 +396,18 @@ int i; sgpnt = (struct scatterlist *) SCpnt->buffer; for(i=0; iuse_sg; i++) { -#ifdef DEBUG - printk("err: %x %x %d\n",SCpnt->request.buffer, SCpnt->buffer, - SCpnt->bufflen); -#endif + SCSI_LOG_HLCOMPLETE(3,printk("err: %p %p %d\n", + SCpnt->request.buffer, SCpnt->buffer, + SCpnt->bufflen)); if (sgpnt[i].alt_address) { scsi_free(sgpnt[i].address, sgpnt[i].length); } } scsi_free(SCpnt->buffer, SCpnt->sglist_len); /* Free list of scatter-gather pointers */ } else { -#ifdef DEBUG - printk("nosgerr: %x %x %d\n",SCpnt->request.buffer, SCpnt->buffer, - SCpnt->bufflen); -#endif + SCSI_LOG_HLCOMPLETE(2,printk("nosgerr: %p %p %d\n", + SCpnt->request.buffer, SCpnt->buffer, + SCpnt->bufflen)); if (SCpnt->buffer != SCpnt->request.buffer) scsi_free(SCpnt->buffer, SCpnt->bufflen); } @@ -485,6 +528,17 @@ SDev = rscsi_disks[DEVICE_NR(CURRENT->rq_dev)].device; /* + * If the host for this device is in error recovery mode, don't + * do anything at all here. When the host leaves error recovery + * mode, it will automatically restart things and start queueing + * commands again. + */ + if( SDev->host->in_recovery ) + { + return; + } + + /* * I am not sure where the best place to do this is. We need * to hook in a place where we are likely to come if in user * space. @@ -495,7 +549,8 @@ * We need to relock the door, but we might * be in an interrupt handler. Only do this * from user space, since we do not want to - * sleep from an interrupt. + * sleep from an interrupt. FIXME(eric) - do this + * from the kernel error handling thred. */ if( SDev->removable && !in_interrupt() ) { @@ -507,21 +562,21 @@ SDev->was_reset = 0; } - /* We have to be careful here. allocate_device will get a free pointer, + /* We have to be careful here. scsi_allocate_device will get a free pointer, * but there is no guarantee that it is queueable. In normal usage, * we want to call this, because other types of devices may have the * host all tied up, and we want to make sure that we have at least * one request pending for this type of device. We can also come * through here while servicing an interrupt, because of the need to - * start another command. If we call allocate_device more than once, + * start another command. If we call scsi_allocate_device more than once, * then the system can wedge if the command is not queueable. The - * request_queueable function is safe because it checks to make sure + * scsi_request_queueable function is safe because it checks to make sure * that the host is able to take another command before it returns * a pointer. */ if (flag++ == 0) - SCpnt = allocate_device(&CURRENT, + SCpnt = scsi_allocate_device(&CURRENT, rscsi_disks[DEVICE_NR(CURRENT->rq_dev)].device, 0); else SCpnt = NULL; @@ -539,15 +594,19 @@ * one disk drive. We want to have the interrupts off when monkeying * with the request list, because otherwise the kernel might try to * slip in a request in between somewhere. + * + * FIXME(eric) - this doesn't belong at this level. The device code in + * ll_rw_blk.c should know how to dig down into the device queue to + * figure out what it can deal with, and what it can't. Consider + * possibility of pulling entire queue down into scsi layer. */ - if (!SCpnt && sd_template.nr_dev > 1){ struct request *req1; req1 = NULL; cli(); req = CURRENT; while(req){ - SCpnt = request_queueable(req, + SCpnt = scsi_request_queueable(req, rscsi_disks[DEVICE_NR(req->rq_dev)].device); if(SCpnt) break; req1 = req; @@ -573,6 +632,7 @@ { int dev, devm, block, this_count; unsigned char cmd[10]; + char nbuff[6]; int bounce_size, contiguous; int max_sg; struct buffer_head * bh, *bhp; @@ -591,15 +651,16 @@ block = SCpnt->request.sector; this_count = 0; -#ifdef DEBUG - printk("Doing sd request, dev = %d, block = %d\n", devm, block); -#endif + SCSI_LOG_HLQUEUE(1,printk("Doing sd request, dev = %d, block = %d\n", devm, block)); if (devm >= (sd_template.dev_max << 4) || !rscsi_disks[dev].device || + !rscsi_disks[dev].device->online || block + SCpnt->request.nr_sectors > sd[devm].nr_sects) { + SCSI_LOG_HLQUEUE(2,printk("Finishing %ld sectors\n", SCpnt->request.nr_sectors)); SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors); + SCSI_LOG_HLQUEUE(2,printk("Retry with 0x%p\n", SCpnt)); goto repeat; } @@ -616,10 +677,9 @@ goto repeat; } -#ifdef DEBUG - printk("sd%c : real dev = /dev/sd%c, block = %d\n", - 'a' + devm, dev, block); -#endif + sd_devname(devm, nbuff); + SCSI_LOG_HLQUEUE(2,printk("%s : real dev = /dev/%d, block = %d\n", + nbuff, dev, block)); /* * If we have a 1K hardware sectorsize, prevent access to single @@ -702,7 +762,7 @@ SCpnt->use_sg = 0; } else if (SCpnt->host->sg_tablesize == 0 || - (need_isa_buffer && dma_free_sectors <= 10)) { + (scsi_need_isa_buffer && scsi_dma_free_sectors <= 10)) { /* Case of host adapter that cannot scatter-gather. We also * come here if we are running low on DMA buffer memory. We set @@ -712,8 +772,8 @@ * we have no choice but to panic. */ if (SCpnt->host->sg_tablesize != 0 && - need_isa_buffer && - dma_free_sectors <= 10) + scsi_need_isa_buffer && + scsi_dma_free_sectors <= 10) printk("Warning: SCSI DMA buffer space running low. Using non scatter-gather I/O.\n"); this_count = SCpnt->request.current_nr_sectors; @@ -787,7 +847,7 @@ * easier to control usage here. In other places we might * have a more pressing need, and we would be screwed if * we ran out */ - if(dma_free_sectors < (sgpnt[count].length >> 9) + 10) { + if(scsi_dma_free_sectors < (sgpnt[count].length >> 9) + 10) { sgpnt[count].address = NULL; } else { sgpnt[count].address = @@ -832,7 +892,7 @@ !sgpnt[count].alt_address) continue; if(!sgpnt[count].alt_address) {count--; continue; } - if(dma_free_sectors > 10) + if(scsi_dma_free_sectors > 10) tmp = (char *) scsi_malloc(sgpnt[count].length + bhp->b_size); else { @@ -861,7 +921,7 @@ > SCpnt->host->sg_tablesize){ bh = SCpnt->request.bh; printk("Use sg, count %d %x %d\n", - SCpnt->use_sg, count, dma_free_sectors); + SCpnt->use_sg, count, scsi_dma_free_sectors); printk("maxsg = %x, counted = %d this_count = %d\n", max_sg, counted, this_count); while(bh){ @@ -903,12 +963,10 @@ memcpy(buff, (char *)SCpnt->request.buffer, this_count << 9); } } -#ifdef DEBUG - printk("sd%c : %s %d/%d 512 byte blocks.\n", - 'a' + devm, - (SCpnt->request.cmd == WRITE) ? "writing" : "reading", - this_count, SCpnt->request.nr_sectors); -#endif + SCSI_LOG_HLQUEUE(2,printk("%s : %s %d/%ld 512 byte blocks.\n", + nbuff, + (SCpnt->request.cmd == WRITE) ? "writing" : "reading", + this_count, SCpnt->request.nr_sectors)); cmd[1] = (SCpnt->lun << 5) & 0xe0; @@ -989,6 +1047,20 @@ if(!rscsi_disks[target].device->removable) return 0; + /* + * If the device is offline, don't send any commands - just pretend as if + * the command failed. If the device ever comes back online, we can deal with + * it then. It is only because of unrecoverable errors that we would ever + * take a device offline in the first place. + */ + if( rscsi_disks[target].device->online == FALSE ) + { + rscsi_disks[target].ready = 0; + rscsi_disks[target].device->changed = 1; + return 1; /* This will force a flush, if called from + * check_disk_change */ + } + inode.i_rdev = full_dev; /* This is all we really need here */ retval = sd_ioctl(&inode, NULL, SCSI_IOCTL_TEST_UNIT_READY, 0); @@ -1031,17 +1103,32 @@ static int sd_init_onedisk(int i) { unsigned char cmd[10]; + char nbuff[6]; unsigned char *buffer; unsigned long spintime; int the_result, retries; Scsi_Cmnd * SCpnt; + /* + * Get the name of the disk, in case we need to log it somewhere. + */ + sd_devname(i, nbuff); + + /* + * If the device is offline, don't try and read capacity or any of the other + * nicities. + */ + if( rscsi_disks[i].device->online == FALSE ) + { + return i; + } + /* We need to retry the READ_CAPACITY because a UNIT_ATTENTION is * considered a fatal error, and many devices report such an error * just after a scsi bus reset. */ - SCpnt = allocate_device(NULL, rscsi_disks[i].device, 1); + SCpnt = scsi_allocate_device(NULL, rscsi_disks[i].device, 1); buffer = (unsigned char *) scsi_malloc(512); spintime = 0; @@ -1069,6 +1156,7 @@ 512, sd_init_done, SD_TIMEOUT, MAX_RETRIES); down(&sem); + SCpnt->request.sem = NULL; } the_result = SCpnt->result; @@ -1084,7 +1172,7 @@ SCpnt->sense_buffer[2] == NOT_READY) { unsigned long time1; if(!spintime){ - printk( "sd%c: Spinning up disk...", 'a' + i ); + printk( "%s: Spinning up disk...", nbuff ); cmd[0] = START_STOP; cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0; cmd[1] |= 1; /* Return immediately */ @@ -1104,6 +1192,7 @@ 512, sd_init_done, SD_TIMEOUT, MAX_RETRIES); down(&sem); + SCpnt->request.sem = NULL; } spintime = jiffies; @@ -1141,6 +1230,7 @@ 8, sd_init_done, SD_TIMEOUT, MAX_RETRIES); down(&sem); /* sleep until it is ready */ + SCpnt->request.sem = NULL; } the_result = SCpnt->result; @@ -1148,12 +1238,6 @@ } while(the_result && retries); - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ - - wake_up(&SCpnt->device->device_wait); - - /* Wake up a process waiting for device */ - /* * The SCSI standard says: * "READ CAPACITY is necessary for self configuring software" @@ -1170,22 +1254,22 @@ if (the_result) { - printk ("sd%c : READ CAPACITY failed.\n" - "sd%c : status = %x, message = %02x, host = %d, driver = %02x \n", - 'a' + i, 'a' + i, + printk ("%s : READ CAPACITY failed.\n" + "%s : status = %x, message = %02x, host = %d, driver = %02x \n", + nbuff, nbuff, status_byte(the_result), msg_byte(the_result), host_byte(the_result), driver_byte(the_result) ); if (driver_byte(the_result) & DRIVER_SENSE) - printk("sd%c : extended sense code = %1x \n", - 'a' + i, SCpnt->sense_buffer[2] & 0xf); + printk("%s : extended sense code = %1x \n", + nbuff, SCpnt->sense_buffer[2] & 0xf); else - printk("sd%c : sense not available. \n", 'a' + i); + printk("%s : sense not available. \n", nbuff); - printk("sd%c : block size assumed to be 512 bytes, disk size 1GB. \n", - 'a' + i); + printk("%s : block size assumed to be 512 bytes, disk size 1GB. \n", + nbuff); rscsi_disks[i].capacity = 0x1fffff; rscsi_disks[i].sector_size = 512; @@ -1213,7 +1297,7 @@ if (rscsi_disks[i].sector_size == 0) { rscsi_disks[i].sector_size = 512; - printk("sd%c : sector size 0 reported, assuming 512.\n", 'a' + i); + printk("%s : sector size 0 reported, assuming 512.\n", nbuff); } @@ -1222,8 +1306,8 @@ rscsi_disks[i].sector_size != 2048 && rscsi_disks[i].sector_size != 256) { - printk ("sd%c : unsupported sector size %d.\n", - 'a' + i, rscsi_disks[i].sector_size); + printk ("%s : unsupported sector size %d.\n", + nbuff, rscsi_disks[i].sector_size); if(rscsi_disks[i].device->removable){ rscsi_disks[i].capacity = 0; } else { @@ -1231,6 +1315,12 @@ rscsi_disks[i].device = NULL; sd_template.nr_dev--; sd_gendisk.nr_real--; + + /* Wake up a process waiting for device */ + wake_up(&SCpnt->device->device_wait); + scsi_release_command(SCpnt); + SCpnt = NULL; + return i; } } @@ -1268,9 +1358,9 @@ m = (mb + 50) / 100; sz_quot = m / 10; sz_rem = m - (10 * sz_quot); - printk ("SCSI device sd%c: hdwr sector= %d bytes." + printk ("SCSI device %s: hdwr sector= %d bytes." " Sectors= %d [%d MB] [%d.%1d GB]\n", - i+'a', hard_sector, rscsi_disks[i].capacity, + nbuff, hard_sector, rscsi_disks[i].capacity, mb, sz_quot, sz_rem); } if(rscsi_disks[i].sector_size == 2048) @@ -1315,23 +1405,27 @@ 512, sd_init_done, SD_TIMEOUT, MAX_RETRIES); down(&sem); + SCpnt->request.sem = NULL; } the_result = SCpnt->result; - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ - wake_up(&SCpnt->device->device_wait); if ( the_result ) { - printk ("sd%c: test WP failed, assume Write Protected\n",i+'a'); + printk ("%s: test WP failed, assume Write Protected\n",nbuff); rscsi_disks[i].write_prot = 1; } else { rscsi_disks[i].write_prot = ((buffer[2] & 0x80) != 0); - printk ("sd%c: Write Protect is %s\n",i+'a', + printk ("%s: Write Protect is %s\n",nbuff, rscsi_disks[i].write_prot ? "on" : "off"); } } /* check for write protect */ + /* Wake up a process waiting for device */ + wake_up(&SCpnt->device->device_wait); + scsi_release_command(SCpnt); + SCpnt = NULL; + rscsi_disks[i].ten = 1; rscsi_disks[i].remap = 1; scsi_free(buffer, 512); @@ -1435,11 +1529,13 @@ } static int sd_detect(Scsi_Device * SDp){ + char nbuff[6]; if(SDp->type != TYPE_DISK && SDp->type != TYPE_MOD) return 0; - printk("Detected scsi %sdisk sd%c at scsi%d, channel %d, id %d, lun %d\n", + sd_devname(sd_template.dev_noticed++, nbuff); + printk("Detected scsi %sdisk %s at scsi%d, channel %d, id %d, lun %d\n", SDp->removable ? "removable " : "", - 'a'+ (sd_template.dev_noticed++), + nbuff, SDp->host->host_no, SDp->channel, SDp->id, SDp->lun); return 1; @@ -1509,7 +1605,7 @@ for (i=max_p - 1; i >=0 ; i--) { int minor = start+i; kdev_t devi = MKDEV(MAJOR_NR, minor); - struct super_block *sb = get_super(devi); + struct super_block *sb = get_super(devi); sync_dev(devi); if (sb) invalidate_inodes(sb); invalidate_buffers(devi); @@ -1562,7 +1658,7 @@ for (i=max_p - 1; i >=0 ; i--) { int minor = start+i; kdev_t devi = MKDEV(MAJOR_NR, minor); - struct super_block *sb = get_super(devi); + struct super_block *sb = get_super(devi); sync_dev(devi); if (sb) invalidate_inodes(sb); invalidate_buffers(devi); diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/sd_ioctl.c linux/drivers/scsi/sd_ioctl.c --- v2.1.74/linux/drivers/scsi/sd_ioctl.c Fri Nov 29 01:20:08 1996 +++ linux/drivers/scsi/sd_ioctl.c Sun Dec 21 17:04:49 1997 @@ -25,9 +25,22 @@ kdev_t dev = inode->i_rdev; int error; struct Scsi_Host * host; + Scsi_Device * SDev; int diskinfo[4]; struct hd_geometry *loc = (struct hd_geometry *) arg; + SDev = rscsi_disks[MINOR(dev) >> 4].device; + /* + * If we are in the middle of error recovery, don't let anyone + * else try and use this device. Also, if error recovery fails, it + * may try and take the device offline, in which case all further + * access to the device is prohibited. + */ + if( !scsi_block_when_processing_errors(SDev) ) + { + return -ENODEV; + } + switch (cmd) { case HDIO_GETGEO: /* Return BIOS disk parameters */ if (!loc) return -EINVAL; diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/seagate.h linux/drivers/scsi/seagate.h --- v2.1.74/linux/drivers/scsi/seagate.h Tue May 13 22:41:13 1997 +++ linux/drivers/scsi/seagate.h Sun Dec 21 17:04:49 1997 @@ -27,13 +27,18 @@ #include int seagate_st0x_biosparam(Disk *, kdev_t, int*); -#define SEAGATE_ST0X { NULL, NULL, NULL, NULL, \ - NULL, seagate_st0x_detect, \ - NULL, \ - seagate_st0x_info, seagate_st0x_command, \ - seagate_st0x_queue_command, seagate_st0x_abort, \ - seagate_st0x_reset, NULL, seagate_st0x_biosparam, \ - 1, 7, SG_ALL, 1, 0, 0, DISABLE_CLUSTERING} +#define SEAGATE_ST0X { detect: seagate_st0x_detect, \ + info: seagate_st0x_info, \ + command: seagate_st0x_command, \ + queuecommand: seagate_st0x_queue_command, \ + abort: seagate_st0x_abort, \ + reset: seagate_st0x_reset, \ + bios_param: seagate_st0x_biosparam, \ + can_queue: 1, \ + this_id: 7, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: 1, \ + use_clustering: DISABLE_CLUSTERING} #endif /* ASM */ #endif /* _SEAGATE_H */ diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/sg.c linux/drivers/scsi/sg.c --- v2.1.74/linux/drivers/scsi/sg.c Sat Oct 25 02:44:17 1997 +++ linux/drivers/scsi/sg.c Sun Dec 21 17:04:49 1997 @@ -67,10 +67,22 @@ static int sg_ioctl(struct inode * inode,struct file * file, unsigned int cmd_in, unsigned long arg) { - int result; - int dev = MINOR(inode->i_rdev); + int dev = MINOR(inode->i_rdev); + int result; + if ((dev<0) || (dev>=sg_template.dev_max)) return -ENXIO; + + /* + * If we are in the middle of error recovery, then don't allow any + * access to this device. Also, error recovery *may* have taken the + * device offline, in which case all further access is prohibited. + */ + if( !scsi_block_when_processing_errors(scsi_generics[dev].device) ) + { + return -ENXIO; + } + switch(cmd_in) { case SG_SET_TIMEOUT: @@ -92,6 +104,12 @@ int flags=filp->f_flags; if (dev>=sg_template.dev_max || !scsi_generics[dev].device) return -ENXIO; + + if( !scsi_block_when_processing_errors(scsi_generics[dev].device) ) + { + return -ENXIO; + } + if (O_RDWR!=(flags & O_ACCMODE)) return -EACCES; @@ -209,6 +227,17 @@ unsigned long flags; struct scsi_generic *device=&scsi_generics[dev]; + /* + * If we are in the middle of error recovery, don't let anyone + * else try and use this device. Also, if error recovery fails, it + * may try and take the device offline, in which case all further + * access to the device is prohibited. + */ + if( !scsi_block_when_processing_errors(scsi_generics[dev].device) ) + { + return -ENXIO; + } + if (ppos != &filp->f_pos) { /* FIXME: Hmm. Seek to the right place, or fail? */ } @@ -278,7 +307,8 @@ if (!device->pending) { printk("unexpected done for sg %d\n",dev); - SCpnt->request.rq_status = RQ_INACTIVE; + scsi_release_command(SCpnt); + SCpnt = NULL; return; } @@ -325,7 +355,8 @@ * result. */ device->complete=1; - SCpnt->request.rq_status = RQ_INACTIVE; + scsi_release_command(SCpnt); + SCpnt = NULL; wake_up(&scsi_generics[dev].read_wait); } @@ -342,6 +373,17 @@ unsigned char opcode; Scsi_Cmnd * SCpnt; + /* + * If we are in the middle of error recovery, don't let anyone + * else try and use this device. Also, if error recovery fails, it + * may try and take the device offline, in which case all further + * access to the device is prohibited. + */ + if( !scsi_block_when_processing_errors(scsi_generics[dev].device) ) + { + return -ENXIO; + } + if (ppos != &filp->f_pos) { /* FIXME: Hmm. Seek to the right place, or fail? */ } @@ -444,7 +486,7 @@ * Grab a device pointer for the device we want to talk to. If we * don't want to block, just return with the appropriate message. */ - if (!(SCpnt=allocate_device(NULL,device->device, !(filp->f_flags & O_NONBLOCK)))) + if (!(SCpnt=scsi_allocate_device(NULL,device->device, !(filp->f_flags & O_NONBLOCK)))) { device->pending=0; wake_up(&device->write_wait); diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/sr.c linux/drivers/scsi/sr.c --- v2.1.74/linux/drivers/scsi/sr.c Tue Dec 2 16:45:19 1997 +++ linux/drivers/scsi/sr.c Sun Dec 21 17:04:49 1997 @@ -385,6 +385,21 @@ { check_disk_change(cdi->dev); + if( MINOR(cdi->dev) >= sr_template.dev_max + || !scsi_CDs[MINOR(cdi->dev)].device) + { + return -ENXIO; /* No such device */ + } + + /* + * If the device is in error recovery, wait until it is done. + * If the device is offline, then disallow any access to it. + */ + if( !scsi_block_when_processing_errors(scsi_CDs[MINOR(cdi->dev)].device) ) + { + return -ENXIO; + } + scsi_CDs[MINOR(cdi->dev)].device->access_count++; if (scsi_CDs[MINOR(cdi->dev)].device->host->hostt->module) __MOD_INC_USE_COUNT(scsi_CDs[MINOR(cdi->dev)].device->host->hostt->module); @@ -428,6 +443,17 @@ SDev = scsi_CDs[DEVICE_NR(CURRENT->rq_dev)].device; + /* + * If the host for this device is in error recovery mode, don't + * do anything at all here. When the host leaves error recovery + * mode, it will automatically restart things and start queueing + * commands again. + */ + if( SDev->host->in_recovery ) + { + return; + } + /* * I am not sure where the best place to do this is. We need * to hook in a place where we are likely to come if in user @@ -460,7 +486,7 @@ } if (flag++ == 0) - SCpnt = allocate_device(&CURRENT, + SCpnt = scsi_allocate_device(&CURRENT, scsi_CDs[DEVICE_NR(CURRENT->rq_dev)].device, 0); else SCpnt = NULL; restore_flags(flags); @@ -479,7 +505,7 @@ cli(); req = CURRENT; while(req){ - SCpnt = request_queueable(req, + SCpnt = scsi_request_queueable(req, scsi_CDs[DEVICE_NR(req->rq_dev)].device); if(SCpnt) break; req1 = req; @@ -537,6 +563,13 @@ goto repeat; } + if( !scsi_CDs[dev].device->online ) + { + SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors); + tries = 2; + goto repeat; + } + if (scsi_CDs[dev].device->changed) { /* * quietly refuse to do anything to a changed disc @@ -583,8 +616,8 @@ SCpnt->use_sg = 0; if (SCpnt->host->sg_tablesize > 0 && - (!need_isa_buffer || - dma_free_sectors >= 10)) { + (!scsi_need_isa_buffer || + scsi_dma_free_sectors >= 10)) { struct buffer_head * bh; struct scatterlist * sgpnt; int count, this_count_max; @@ -655,7 +688,7 @@ /* We try to avoid exhausting the DMA pool, since it is easier * to control usage here. In other places we might have a more * pressing need, and we would be screwed if we ran out */ - if(dma_free_sectors < (sgpnt[count].length >> 9) + 5) { + if(scsi_dma_free_sectors < (sgpnt[count].length >> 9) + 5) { sgpnt[count].address = NULL; } else { sgpnt[count].address = (char *) scsi_malloc(sgpnt[count].length); @@ -849,7 +882,7 @@ Scsi_Cmnd * SCpnt; buffer = (unsigned char *) scsi_malloc(512); - SCpnt = allocate_device(NULL, scsi_CDs[i].device, 1); + SCpnt = scsi_allocate_device(NULL, scsi_CDs[i].device, 1); retries = 3; do { @@ -877,9 +910,10 @@ } while(the_result && retries); - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ wake_up(&SCpnt->device->device_wait); + scsi_release_command(SCpnt); + SCpnt = NULL; if (the_result) { scsi_CDs[i].capacity = 0x1fffff; diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/sr_ioctl.c linux/drivers/scsi/sr_ioctl.c --- v2.1.74/linux/drivers/scsi/sr_ioctl.c Tue Dec 2 16:45:19 1997 +++ linux/drivers/scsi/sr_ioctl.c Sun Dec 21 17:04:49 1997 @@ -53,7 +53,7 @@ Scsi_Cmnd * SCpnt; int result, err = 0, retries = 0; - SCpnt = allocate_device(NULL, scsi_CDs[target].device, 1); + SCpnt = scsi_allocate_device(NULL, scsi_CDs[target].device, 1); retry: { @@ -63,6 +63,7 @@ (void *) sr_cmd, buffer, buflength, sr_ioctl_done, IOCTL_TIMEOUT, IOCTL_RETRIES); down(&sem); + SCpnt->request.sem = NULL; } result = SCpnt->result; @@ -125,10 +126,10 @@ }; result = SCpnt->result; - SCpnt->request.rq_status = RQ_INACTIVE; /* Deallocate */ - /* Wake up a process waiting for device */ + /* Wake up a process waiting for device*/ wake_up(&SCpnt->device->device_wait); - + scsi_release_command(SCpnt); + SCpnt = NULL; return err; } @@ -186,7 +187,7 @@ /* look for data tracks */ if (0 != (rc = sr_audio_ioctl(cdi, CDROMREADTOCHDR, &toc_h))) return (rc == -ENOMEDIUM) ? CDS_NO_DISC : CDS_NO_INFO; - + for (i = toc_h.cdth_trk0; i <= toc_h.cdth_trk1; i++) { toc_e.cdte_track = i; toc_e.cdte_format = CDROM_LBA; @@ -278,10 +279,23 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg) { u_char sr_cmd[10]; + Scsi_Device * SDev; int result, target; target = MINOR(cdi->dev); + SDev = scsi_CDs[target].device; + /* + * If we are in the middle of error recovery, don't let anyone + * else try and use this device. Also, if error recovery fails, it + * may try and take the device offline, in which case all further + * access to the device is prohibited. + */ + if( !scsi_block_when_processing_errors(SDev) ) + { + return -ENODEV; + } + switch (cmd) { /* Sun-compatible */ @@ -704,9 +718,22 @@ unsigned int cmd, unsigned long arg) { int target, err; + Scsi_Device * SDev; target = MINOR(cdi->dev); + SDev = scsi_CDs[target].device; + /* + * If we are in the middle of error recovery, don't let anyone + * else try and use this device. Also, if error recovery fails, it + * may try and take the device offline, in which case all further + * access to the device is prohibited. + */ + if( !scsi_block_when_processing_errors(SDev) ) + { + return -ENODEV; + } + switch (cmd) { case CDROMREADMODE1: case CDROMREADMODE2: diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/st.c linux/drivers/scsi/st.c --- v2.1.74/linux/drivers/scsi/st.c Fri Dec 19 15:52:59 1997 +++ linux/drivers/scsi/st.c Sun Dec 21 17:04:49 1997 @@ -230,7 +230,6 @@ } else (STp->buffer)->last_result = SCpnt->result; - SCpnt->request.rq_status = RQ_SCSI_DONE; (STp->buffer)->last_SCpnt = SCpnt; @@ -252,7 +251,7 @@ int timeout, int retries) { if (SCpnt == NULL) - if ((SCpnt = allocate_device(NULL, STp->device, 1)) == NULL) { + if ((SCpnt = scsi_allocate_device(NULL, STp->device, 1)) == NULL) { printk(KERN_ERR "st%d: Can't get SCSI request.\n", TAPE_NR(STp->devt)); return NULL; } @@ -293,7 +292,7 @@ down(&(STp->sem)); (STp->buffer)->last_result_fatal = st_chk_result((STp->buffer)->last_SCpnt); - ((STp->buffer)->last_SCpnt)->request.rq_status = RQ_INACTIVE; + scsi_release_command((STp->buffer)->last_SCpnt); if (STbuffer->writing < STbuffer->buffer_bytes) memcpy(STbuffer->b_data, @@ -340,7 +339,9 @@ if (!SCpnt) return (-EBUSY); - SCpnt->request.rq_status = RQ_INACTIVE; + scsi_release_command(SCpnt); + SCpnt = NULL; + if ((STp->buffer)->last_result != 0) printk(KERN_ERR "st%d: Stepping over filemark %s failed.\n", TAPE_NR(STp->devt), forward ? "forward" : "backward"); @@ -421,7 +422,8 @@ STp->dirty = 0; (STp->buffer)->buffer_bytes = 0; } - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ + scsi_release_command(SCpnt); + SCpnt = NULL; } return result; } @@ -543,6 +545,12 @@ if (dev >= st_template.dev_max || !scsi_tapes[dev].device) return (-ENXIO); + + if( !scsi_block_when_processing_errors(scsi_tapes[dev].device) ) + { + return -ENXIO; + } + STp = &(scsi_tapes[dev]); if (STp->in_use) { #if DEBUG @@ -644,7 +652,8 @@ STp->ready = ST_NO_TAPE; } else STp->ready = ST_NOT_READY; - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ + scsi_release_command(SCpnt); + SCpnt = NULL; STp->density = 0; /* Clear the erroneous "residue" */ STp->write_prot = 0; STp->block_size = 0; @@ -736,7 +745,8 @@ } STp->drv_write_prot = ((STp->buffer)->b_data[2] & 0x80) != 0; } - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ + scsi_release_command(SCpnt); + SCpnt = NULL; if (STp->block_size > 0) (STp->buffer)->buffer_blocks = st_buffer_size / STp->block_size; @@ -874,13 +884,15 @@ SCpnt->sense_buffer[5] | SCpnt->sense_buffer[6]) == 0))) { /* Filter out successful write at EOM */ - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ + scsi_release_command(SCpnt); + SCpnt = NULL; printk(KERN_ERR "st%d: Error on write filemark.\n", dev); if (result == 0) result = (-EIO); } else { - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ + scsi_release_command(SCpnt); + SCpnt = NULL; if (STps->drv_file >= 0) STps->drv_file++ ; STps->drv_block = 0; @@ -965,6 +977,19 @@ ST_partstat * STps; int dev = TAPE_NR(inode->i_rdev); + STp = &(scsi_tapes[dev]); + + /* + * If we are in the middle of error recovery, don't let anyone + * else try and use this device. Also, if error recovery fails, it + * may try and take the device offline, in which case all further + * access to the device is prohibited. + */ + if( !scsi_block_when_processing_errors(STp->device) ) + { + return -ENXIO; + } + if (ppos != &filp->f_pos) { /* "A request was outside the capabilities of the device." */ return -ENXIO; @@ -1098,7 +1123,10 @@ (STp->buffer)->buffer_bytes, b_point, do_count); if (i) { if (SCpnt != NULL) - SCpnt->request.rq_status = RQ_INACTIVE; + { + scsi_release_command(SCpnt); + SCpnt = NULL; + } return (-EFAULT); } @@ -1167,7 +1195,8 @@ retval = (-EIO); } - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ + scsi_release_command(SCpnt); + SCpnt = NULL; (STp->buffer)->buffer_bytes = 0; STp->dirty = 0; if (count < total) @@ -1193,7 +1222,10 @@ (STp->buffer)->buffer_bytes, b_point, count); if (i) { if (SCpnt != NULL) - SCpnt->request.rq_status = RQ_INACTIVE; + { + scsi_release_command(SCpnt); + SCpnt = NULL; + } return (-EFAULT); } filp->f_pos += count; @@ -1202,7 +1234,8 @@ } if (doing_write && (STp->buffer)->last_result_fatal != 0) { - SCpnt->request.rq_status = RQ_INACTIVE; + scsi_release_command(SCpnt); + SCpnt = NULL; return (STp->buffer)->last_result_fatal; } @@ -1212,7 +1245,7 @@ STp->block_size == 0) ) { /* Schedule an asynchronous write */ if (!SCpnt) { - SCpnt = allocate_device(NULL, STp->device, 1); + SCpnt = scsi_allocate_device(NULL, STp->device, 1); if (!SCpnt) return (-EBUSY); } @@ -1245,8 +1278,10 @@ st_sleep_done, STp->timeout, MAX_WRITE_RETRIES); } else if (SCpnt != NULL) - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ - + { + scsi_release_command(SCpnt); + SCpnt = NULL; + } STps->at_sm &= (total == 0); if (total > 0) STps->eof = ST_NOEOF; @@ -1346,7 +1381,7 @@ (STp->buffer)->buffer_bytes = bytes - transfer; } else { - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ + scsi_release_command(SCpnt); SCpnt = *aSCpnt = NULL; if (transfer == blks) { /* We did not get anything, error */ printk(KERN_NOTICE "st%d: Incorrect block size.\n", dev); @@ -1457,6 +1492,19 @@ ST_partstat * STps; int dev = TAPE_NR(inode->i_rdev); + STp = &(scsi_tapes[dev]); + + /* + * If we are in the middle of error recovery, don't let anyone + * else try and use this device. Also, if error recovery fails, it + * may try and take the device offline, in which case all further + * access to the device is prohibited. + */ + if( !scsi_block_when_processing_errors(STp->device) ) + { + return -ENXIO; + } + if (ppos != &filp->f_pos) { /* "A request was outside the capabilities of the device." */ return -ENXIO; @@ -1537,7 +1585,9 @@ special = read_tape(inode, count - total, &SCpnt); if (special < 0) { /* No need to continue read */ if (SCpnt != NULL) - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ + { + scsi_release_command(SCpnt); + } return special; } } @@ -1555,7 +1605,10 @@ (STp->buffer)->read_pointer, transfer); if (i) { if (SCpnt != NULL) - SCpnt->request.rq_status = RQ_INACTIVE; + { + scsi_release_command(SCpnt); + SCpnt = NULL; + } return (-EFAULT); } filp->f_pos += transfer; @@ -1571,7 +1624,10 @@ } /* for (total = 0, special = 0; total < count && !special; ) */ if (SCpnt != NULL) - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ + { + scsi_release_command(SCpnt); + SCpnt = NULL; + } /* Change the eof state if no data from tape or buffer */ if (total == 0) { @@ -1815,7 +1871,8 @@ if (debugging) printk(ST_DEB_MSG "st%d: Compression mode page not supported.\n", dev); #endif - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ + scsi_release_command(SCpnt); + SCpnt = NULL; return (-EIO); } #if DEBUG @@ -1830,7 +1887,8 @@ if (debugging) printk(ST_DEB_MSG "st%d: Compression not supported.\n", dev); #endif - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ + scsi_release_command(SCpnt); + SCpnt = NULL; return (-EIO); } @@ -1855,7 +1913,8 @@ if (debugging) printk(ST_DEB_MSG "st%d: Compression change failed.\n", dev); #endif - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ + scsi_release_command(SCpnt); + SCpnt = NULL; return (-EIO); } @@ -1865,7 +1924,8 @@ dev, state); #endif - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ + scsi_release_command(SCpnt); + SCpnt = NULL; STp->compression_changed = TRUE; return 0; } @@ -2238,7 +2298,8 @@ ioctl_result = (STp->buffer)->last_result_fatal; - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ + scsi_release_command(SCpnt); + SCpnt = NULL; if (!ioctl_result) { /* SCSI command successful */ STps->drv_block = blkno; @@ -2441,7 +2502,8 @@ #endif } - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ + scsi_release_command(SCpnt); + SCpnt = NULL; return result; } @@ -2556,6 +2618,9 @@ result = 0; } + scsi_release_command(SCpnt); + SCpnt = NULL; + return result; } @@ -2622,7 +2687,8 @@ SCpnt = st_do_scsi(SCpnt, STp, cmd, 200, STp->timeout, MAX_READY_RETRIES); if (SCpnt == NULL) return (-EBUSY); - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ + scsi_release_command(SCpnt); + SCpnt = NULL; if ((STp->buffer)->last_result_fatal != 0) { #if DEBUG @@ -2696,7 +2762,8 @@ SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], STp->long_timeout, MAX_READY_RETRIES); if (SCpnt == NULL) return (-EBUSY); - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ + scsi_release_command(SCpnt); + SCpnt = NULL; if ((STp->buffer)->last_result_fatal != 0) { printk(KERN_INFO "st%d: Partitioning of tape failed.\n", dev); @@ -2733,6 +2800,17 @@ #endif STm = &(STp->modes[STp->current_mode]); STps = &(STp->ps[STp->partition]); + + /* + * If we are in the middle of error recovery, don't let anyone + * else try and use this device. Also, if error recovery fails, it + * may try and take the device offline, in which case all further + * access to the device is prohibited. + */ + if( !scsi_block_when_processing_errors(STp->device) ) + { + return -ENXIO; + } cmd_type = _IOC_TYPE(cmd_in); cmd_nr = _IOC_NR(cmd_in); diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/t128.h linux/drivers/scsi/t128.h --- v2.1.74/linux/drivers/scsi/t128.h Mon Apr 29 07:14:19 1996 +++ linux/drivers/scsi/t128.h Sun Dec 21 17:04:49 1997 @@ -119,13 +119,18 @@ #if defined(HOSTS_C) || defined(MODULE) -#define TRANTOR_T128 {NULL, NULL, NULL, NULL, \ - "Trantor T128/T128F/T228", t128_detect, NULL, \ - NULL, \ - NULL, t128_queue_command, t128_abort, t128_reset, NULL, \ - t128_biosparam, \ - /* can queue */ CAN_QUEUE, /* id */ 7, SG_ALL, \ - /* cmd per lun */ CMD_PER_LUN , 0, 0, DISABLE_CLUSTERING} +#define TRANTOR_T128 { \ + name: "Trantor T128/T128F/T228", \ + detect: t128_detect, \ + queuecommand: t128_queue_command, \ + abort: t128_abort, \ + reset: t128_reset, \ + bios_param: t128_biosparam, \ + can_queue: CAN_QUEUE, \ + this_id: 7, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: CMD_PER_LUN, \ + use_clustering: DISABLE_CLUSTERING} #endif diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/u14-34f.h linux/drivers/scsi/u14-34f.h --- v2.1.74/linux/drivers/scsi/u14-34f.h Tue Sep 23 16:48:48 1997 +++ linux/drivers/scsi/u14-34f.h Sun Dec 21 17:04:49 1997 @@ -13,27 +13,16 @@ #define U14_34F_VERSION "3.11.00" -#define ULTRASTOR_14_34F { \ - NULL, /* Ptr for modules */ \ - NULL, /* usage count for modules */ \ - NULL, \ - NULL, \ - "UltraStor 14F/34F rev. " U14_34F_VERSION " ", \ - u14_34f_detect, \ - u14_34f_release, \ - NULL, \ - NULL, \ - u14_34f_queuecommand, \ - u14_34f_abort, \ - u14_34f_reset, \ - NULL, \ - u14_34f_biosparam, \ - 0, /* can_queue, reset by detect */ \ - 7, /* this_id, reset by detect */ \ - 0, /* sg_tablesize, reset by detect */ \ - 0, /* cmd_per_lun, reset by detect */ \ - 0, /* number of boards present */ \ - 1, /* unchecked isa dma, reset by detect */ \ - ENABLE_CLUSTERING \ +#define ULTRASTOR_14_34F { \ + name: "UltraStor 14F/34F rev. " U14_34F_VERSION " ", \ + detect: u14_34f_detect, \ + release: u14_34f_release, \ + queuecommand: u14_34f_queuecommand, \ + abort: u14_34f_abort, \ + reset: u14_34f_reset, \ + bios_param: u14_34f_biosparam, \ + this_id: 7, /* this_id, reset by detect */ \ + unchecked_isa_dma: 1, /* unchecked isa dma, reset by detect */ \ + use_clustering: ENABLE_CLUSTERING \ } #endif diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/ultrastor.h linux/drivers/scsi/ultrastor.h --- v2.1.74/linux/drivers/scsi/ultrastor.h Thu Jun 12 15:29:27 1997 +++ linux/drivers/scsi/ultrastor.h Sun Dec 21 17:04:49 1997 @@ -31,26 +31,19 @@ #define ULTRASTOR_24F_PORT 0xC80 -#define ULTRASTOR_14F { NULL, NULL, /* Ptr for modules*/ \ - NULL, \ - NULL, \ - "UltraStor 14F/24F/34F", \ - ultrastor_detect, \ - NULL, /* Release */ \ - ultrastor_info, \ - 0, \ - ultrastor_queuecommand, \ - ultrastor_abort, \ - ultrastor_reset, \ - 0, \ - ultrastor_biosparam, \ - ULTRASTOR_MAX_CMDS, \ - 0, \ - ULTRASTOR_14F_MAX_SG, \ - ULTRASTOR_MAX_CMDS_PER_LUN, \ - 0, \ - 1, \ - ENABLE_CLUSTERING } +#define ULTRASTOR_14F { name: "UltraStor 14F/24F/34F", \ + detect: ultrastor_detect, \ + info: ultrastor_info, \ + queuecommand: ultrastor_queuecommand, \ + abort: ultrastor_abort, \ + reset: ultrastor_reset, \ + bios_param: ultrastor_biosparam, \ + can_queue: ULTRASTOR_MAX_CMDS, \ + this_id: 0, \ + sg_tablesize: ULTRASTOR_14F_MAX_SG, \ + cmd_per_lun: ULTRASTOR_MAX_CMDS_PER_LUN,\ + unchecked_isa_dma: 1, \ + use_clustering: ENABLE_CLUSTERING } #ifdef ULTRASTOR_PRIVATE diff -u --recursive --new-file v2.1.74/linux/drivers/scsi/wd7000.h linux/drivers/scsi/wd7000.h --- v2.1.74/linux/drivers/scsi/wd7000.h Thu Jun 12 15:29:27 1997 +++ linux/drivers/scsi/wd7000.h Sun Dec 21 17:04:49 1997 @@ -38,18 +38,18 @@ #define WD7000_Q 16 #define WD7000_SG 16 -#define WD7000 { NULL, NULL, \ - NULL, \ - NULL, \ - "Western Digital WD-7000", \ - wd7000_detect, \ - NULL, \ - NULL, \ - wd7000_command, \ - wd7000_queuecommand, \ - wd7000_abort, \ - wd7000_reset, \ - NULL, \ - wd7000_biosparam, \ - WD7000_Q, 7, WD7000_SG, 1, 0, 1, ENABLE_CLUSTERING} +#define WD7000 { \ + name: "Western Digital WD-7000", \ + detect: wd7000_detect, \ + command: wd7000_command, \ + queuecommand: wd7000_queuecommand, \ + abort: wd7000_abort, \ + reset: wd7000_reset, \ + bios_param: wd7000_biosparam, \ + can_queue: WD7000_Q, \ + this_id: 7, \ + sg_tablesize: WD7000_SG, \ + cmd_per_lun: 1, \ + unchecked_isa_dma: 1, \ + use_clustering: ENABLE_CLUSTERING} #endif diff -u --recursive --new-file v2.1.74/linux/drivers/sound/.objects linux/drivers/sound/.objects --- v2.1.74/linux/drivers/sound/.objects Wed Nov 12 13:34:26 1997 +++ linux/drivers/sound/.objects Sun Dec 21 17:41:24 1997 @@ -97,3 +97,6 @@ OBJS := $(OBJS) softoss.o softoss_rs.o endif +ifdef CONFIG_VMIDI + OBJS := $(OBJS) v_midi.o +endif diff -u --recursive --new-file v2.1.74/linux/drivers/sound/.objects.orig linux/drivers/sound/.objects.orig --- v2.1.74/linux/drivers/sound/.objects.orig Wed Dec 31 16:00:00 1969 +++ linux/drivers/sound/.objects.orig Tue Sep 30 08:46:46 1997 @@ -0,0 +1,99 @@ +OBJS= + +ifdef CONFIG_AD1848 + OBJS := $(OBJS) ad1848.o +endif + +ifdef CONFIG_YM3812 + OBJS := $(OBJS) adlib_card.o +endif + +ifdef CONFIG_AUDIO + OBJS := $(OBJS) audio.o +endif + +ifdef CONFIG_CS4232 + OBJS := $(OBJS) cs4232.o +endif + +ifdef CONFIG_AUDIO + OBJS := $(OBJS) dmabuf.o +endif + +ifdef CONFIG_GUSHW + OBJS := $(OBJS) gus_card.o gus_midi.o gus_vol.o gus_wave.o ics2101.o +endif + +ifdef CONFIG_MAD16 + OBJS := $(OBJS) mad16.o +endif + +ifdef CONFIG_MAUI + OBJS := $(OBJS) maui.o +endif + +ifdef CONFIG_MIDI + OBJS := $(OBJS) midi_synth.o midibuf.o +endif + +ifdef CONFIG_MPU401 + OBJS := $(OBJS) mpu401.o +else + ifdef CONFIG_MPU_EMU + OBJS := $(OBJS) mpu401.o + endif +endif + +ifdef CONFIG_YM3812 + OBJS := $(OBJS) opl3.o +endif + +ifdef CONFIG_PAS + OBJS := $(OBJS) pas2_card.o pas2_midi.o pas2_mixer.o pas2_pcm.o +endif + +ifdef CONFIG_PSS + OBJS := $(OBJS) pss.o +endif + +ifdef CONFIG_SBDSP + OBJS := $(OBJS) sb_card.o sb_common.o sb_audio.o sb_mixer.o sb_midi.o +endif + +ifdef CONFIG_SEQUENCER + OBJS := $(OBJS) sequencer.o +endif + + +ifdef CONFIG_SEQUENCER + OBJS := $(OBJS) sound_timer.o +endif + +ifdef CONFIG_SSCAPEHW + OBJS := $(OBJS) sscape.o +endif + +ifdef CONFIG_TRIX + OBJS := $(OBJS) trix.o +endif + +ifdef CONFIG_SEQUENCER + OBJS := $(OBJS) sys_timer.o +endif + +ifdef CONFIG_UART6850 + OBJS := $(OBJS) uart6850.o +endif + +ifdef CONFIG_UART401 + OBJS := $(OBJS) uart401.o +endif + +ifdef CONFIG_OPL3SA1 + OBJS := $(OBJS) opl3sa.o +endif + +ifdef CONFIG_SOFTOSS + OBJS := $(OBJS) softoss.o softoss_rs.o +endif + diff -u --recursive --new-file v2.1.74/linux/drivers/sound/Config.in linux/drivers/sound/Config.in --- v2.1.74/linux/drivers/sound/Config.in Wed Dec 10 11:12:44 1997 +++ linux/drivers/sound/Config.in Sun Dec 21 17:41:24 1997 @@ -17,12 +17,13 @@ dep_tristate 'Yamaha OPL3-SA1 audio controller' CONFIG_OPL3SA1 $CONFIG_SOUND dep_tristate 'SoftOSS software wave table engine' CONFIG_SOFTOSS $CONFIG_SOUND dep_tristate 'FM synthesizer (YM3812/OPL-3) support' CONFIG_YM3812 $CONFIG_SOUND +dep_tristate 'Loopback MIDI device support' CONFIG_VMIDI $CONFIG_VMIDI -if [ "$CONFIG_AEDSP16" = "y" -o "$CONFIG_AEDSP16" = "m" ]; then +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" -o "$CONFIG_SB" = "m" ]; then +if [ "$CONFIG_SB" = "y" ]; then hex 'I/O base for SB Check from manual of the card' SBC_BASE 220 int 'Sound Blaster IRQ Check from manual of the card' SBC_IRQ 7 int 'Sound Blaster DMA 0, 1 or 3' SBC_DMA 1 @@ -33,12 +34,12 @@ int 'SB MPU401 IRQ (Jazz16, SM Wave and ES1688) Check from manual of the card' SB_MPU_IRQ -1 fi -if [ "$CONFIG_PAS" = "y" -o "$CONFIG_PAS" = "m" ]; then +if [ "$CONFIG_PAS" = "y" ]; then int 'PAS16 IRQ 3, 4, 5, 7, 9, 10, 11, 12, 14 or 15' PAS_IRQ 10 int 'PAS16 DMA 0, 1, 3, 5, 6 or 7' PAS_DMA 3 fi -if [ "$CONFIG_GUS" = "y" -o "$CONFIG_GUS" = "m" ]; then +if [ "$CONFIG_GUS" = "y" ]; then hex 'I/O base for GUS 210, 220, 230, 240, 250 or 260' GUS_BASE 220 int 'GUS IRQ 3, 5, 7, 9, 11, 12 or 15' GUS_IRQ 15 int 'GUS DMA 1, 3, 5, 6 or 7' GUS_DMA 6 @@ -51,23 +52,23 @@ int 'GUS DMA 0, 1 or 3' GUS16_DMA 3 fi -if [ "$CONFIG_MPU401" = "y" -o "$CONFIG_MPU401" = "m" ]; then +if [ "$CONFIG_MPU401" = "y" ]; then hex 'I/O base for MPU401 Check from manual of the card' MPU_BASE 330 int 'MPU401 IRQ Check from manual of the card' MPU_IRQ 9 fi -if [ "$CONFIG_MAUI" = "y" -o "$CONFIG_MAUI" = "M" ]; then +if [ "$CONFIG_MAUI" = "y" ]; then comment 'ERROR! You have to use old sound configuration method with Maui.' hex 'I/O base for Maui 210, 230, 260, 290, 300, 320, 338 or 330' MAUI_BASE 330 int 'Maui IRQ 5, 9, 12 or 15' MAUI_IRQ 9 fi -if [ "$CONFIG_UART6850" = "y" -o "$CONFIG_UART6850" = "m" ]; then +if [ "$CONFIG_UART6850" = "y" ]; then hex 'I/O base for UART 6850 MIDI port (Unknown)' U6850_BASE 0 int 'UART6850 IRQ (Unknown)' U6850_IRQ -1 fi -if [ "$CONFIG_PSS" = "y" -o "$CONFIG_PSS" = "y" ]; then +if [ "$CONFIG_PSS" = "y" ]; then comment 'ERROR! You have to use old sound configuration method with PSS cards.' hex 'PSS I/O base 220 or 240' PSS_BASE 220 hex 'PSS audio I/O base 530, 604, E80 or F40' PSS_MSS_BASE 530 @@ -77,14 +78,14 @@ int 'PSS MIDI IRQ 3, 4, 5, 7 or 9' PSS_MPU_IRQ 9 fi -if [ "$CONFIG_MSS" = "y" -o "$CONFIG_MSS" = "m" ]; then +if [ "$CONFIG_MSS" = "y" ]; then hex 'MSS/WSS I/O base 530, 604, E80 or F40' MSS_BASE 530 int 'MSS/WSS IRQ 7, 9, 10 or 11' MSS_IRQ 11 int 'MSS/WSS DMA 0, 1 or 3' MSS_DMA 3 int 'MSS/WSS second DMA (if possible) 0, 1 or 3' MSS_DMA2 -1 fi -if [ "$CONFIG_SSCAPE" = "y" -o "$CONFIG_SSCAPE" = "m" ]; then +if [ "$CONFIG_SSCAPE" = "y" ]; then hex 'SoundScape MIDI I/O base 320, 330, 340 or 350' SSCAPE_BASE 330 int 'SoundScape MIDI IRQ ' SSCAPE_IRQ 9 int 'SoundScape initialization DMA 0, 1 or 3' SSCAPE_DMA 3 @@ -92,7 +93,7 @@ int 'SoundScape audio IRQ 7, 9, 10 or 11' SSCAPE_MSS_IRQ 11 fi -if [ "$CONFIG_TRIX" = "y" -o "$CONFIG_TRIX" = "m" ]; then +if [ "$CONFIG_TRIX" = "y" ]; then comment 'ERROR! You have to use old sound configuration method with OPL3-SA1.' hex 'OPL3-SA1 audio I/O base 530, 604, E80 or F40' TRIX_BASE 530 int 'OPL3-SA1 audio IRQ 7, 9, 10 or 11' TRIX_IRQ 11 @@ -105,7 +106,7 @@ int 'OPL3-SA1 SB DMA 1 or 3' TRIX_SB_DMA 1 fi -if [ "$CONFIG_OPL3SA1" = "y" -o "$CONFIG_OPL3SA1" = "m" ]; then +if [ "$CONFIG_OPL3SA1" = "y" ]; then hex 'OPL3-SA1 audio I/O base 530, 604, E80 or F40' OPL3SA1_BASE 530 int 'OPL3-SA1 audio IRQ 7, 9, 10 or 11' OPL3SA1_IRQ 11 int 'OPL3-SA1 audio DMA 0, 1 or 3' OPL3SA1_DMA 0 @@ -114,7 +115,7 @@ int 'OPL3-SA1 MIDI IRQ 3, 4, 5, 7 or 9' OPL3SA1_MPU_IRQ 9 fi -if [ "$CONFIG_CS4232" = "y" -o "$CONFIG_CS4232" = "m" ]; then +if [ "$CONFIG_CS4232" = "y" ]; then hex 'CS4232 audio I/O base 530, 604, E80 or F40' CS4232_BASE 530 int 'CS4232 audio IRQ 5, 7, 9, 11, 12 or 15' CS4232_IRQ 11 int 'CS4232 audio DMA 0, 1 or 3' CS4232_DMA 0 @@ -123,7 +124,7 @@ int 'CS4232 MIDI IRQ 5, 7, 9, 11, 12 or 15' CS4232_MPU_IRQ 9 fi -if [ "$CONFIG_MAD16" = "y" -o "$CONFIG_MAD16" = "m" ]; then +if [ "$CONFIG_MAD16" = "y" ]; then hex 'MAD16 audio I/O base 530, 604, E80 or F40' MAD16_BASE 530 int 'MAD16 audio IRQ 7, 9, 10 or 11' MAD16_IRQ 11 int 'MAD16 audio DMA 0, 1 or 3' MAD16_DMA 3 @@ -132,7 +133,7 @@ int 'MAD16 MIDI IRQ 5, 7, 9 or 10' MAD16_MPU_IRQ 9 fi -if [ "$CONFIG_SOFTOSS" = "y" -o "$CONFIG_SOFTOSS" = "m" ]; then +if [ "$CONFIG_SOFTOSS" = "y" ]; then int 'Sampling rate for SoftOSS 8000 to 48000' SOFTOSS_RATE 22050 int 'Max # of concurrent voices for SoftOSS 4 to 32' SOFTOSS_VOICES 32 fi diff -u --recursive --new-file v2.1.74/linux/drivers/sound/Makefile linux/drivers/sound/Makefile --- v2.1.74/linux/drivers/sound/Makefile Wed Dec 10 11:12:44 1997 +++ linux/drivers/sound/Makefile Sun Dec 21 17:41:24 1997 @@ -90,18 +90,26 @@ L_OBJS += gus.o else ifeq ($(CONFIG_GUS),m) - M_OBJS += gus.o + M_OBJS += gus.o + endif + ifeq ($(CONFIG_GUSMAX),m) + ifneq ($(CONFIG_MSS),y) + CONFIG_MSS = m + endif endif endif ifeq ($(CONFIG_SB),y) L_OBJS += sb_audio.o sb_common.o sb_midi.o sb_mixer.o -LX_OBJS += sb_card.o uart401.o +LX_OBJS += sb_card.o +CONFIG_UART401 = y else ifeq ($(CONFIG_SB),m) M_OBJS += sb.o - MX_OBJS += uart401.o MIX_OBJS += sb_card.o + ifneq ($(CONFIG_UART401),y) + CONFIG_UART401 = m + endif endif endif @@ -181,7 +189,8 @@ L_OBJS += mad16.o else ifeq ($(CONFIG_MAD16),m) - M_OBJS += mad16.o + M_OBJS += mad16.o sb.o uart401.o + MX_OBJS += sb_card.o ad1848.o endif endif @@ -217,6 +226,14 @@ M_OBJS += opl3sa.o MX_OBJS += ad1848.o endif +endif + +ifeq ($(CONFIG_VMIDI),y) +L_OBJS += v_midi.o +else + ifeq ($(CONFIG_VMIDI),m) + M_OBJS += v_midi.o + endif endif include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.1.74/linux/drivers/sound/README.C931 linux/drivers/sound/README.C931 --- v2.1.74/linux/drivers/sound/README.C931 Wed Dec 31 16:00:00 1969 +++ linux/drivers/sound/README.C931 Sun Dec 21 17:41:24 1997 @@ -0,0 +1,62 @@ +Support for the OPTI 82C931 chip +-------------------------------- + +The opti 82C931 is supported in it's non PnP mode. +You do not need to set jumpers etc... The sound driver +will check the card status and if it is required it will +force the card into a mode that it can be programmed. + +To compile support for the OPTI 82C931 card you can use +the regular Linux config menus (ie, "make xconfig"). + +Sound card support should be enabled as a module (chose m). +Enable (m) for these items: + 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support (CONFIG_SB) + Generic OPL2/OPL3 FM synthesizer support (CONFIG_ADLIB) + Microsoft Sound System support (CONFIG_MSS) + Support for OPTi MAD16 and/or Mozart based cards (CONFIG_MAD16) + FM synthesizer (YM3812/OPL-3) support (CONFIG_YM3812) + +The configuration menu may ask for addresses, irq lines or dma +channels. If the card is used as a module the module loading +options will override these values. + +Go on and compile your kernel and modules, install the modules. + +I use this configuration as part of /etc/conf.modules: + +options sb mad16=1 +options mad16 irq=10 dma=1 io=0x530 joystick=1 cdtype=0 +options opl3 io=0x388 + +After installing everything and booting to a kernel that +matches the modules you can load the sound driver: + +modprobe mad16 +modprobe opl3 + +As a result these modules are loaded: +Module Size Used by +opl3 10416 0 (unused) +mad16 6472 0 +sb 23544 0 [mad16] +uart401 5796 0 [mad16 sb] +ad1848 16532 1 [mad16] +sound 82052 0 [opl3 mad16 sb uart401 ad1848] + +Known problems: +1. The uart401 cannot be used. This is probably a problem which + was introduced when the sound driver was modularized. + Do not try to load uart401 with options (io=xxx, irq=yyy) because + it will try to initialize itself and fail. + +2. Cannot use the sound driver in Duplex mode. Until it is fixed + use only one DMA channel (0, 1 or 3) for mad16. + +3. Configuration of the cdrom adaptor on the sound card is not + tested and probably does not work. + +4. General problem with the modularized sound driver: If you + load part of the sound driver while a sound program is + running, stopping the program may result with a situation + where the "Used by" count becomes negative. diff -u --recursive --new-file v2.1.74/linux/drivers/sound/ad1848.c linux/drivers/sound/ad1848.c --- v2.1.74/linux/drivers/sound/ad1848.c Sat Nov 29 11:25:10 1997 +++ linux/drivers/sound/ad1848.c Sun Dec 21 17:41:24 1997 @@ -2521,6 +2521,7 @@ EXPORT_SYMBOL(ad1848_detect); EXPORT_SYMBOL(ad1848_init); EXPORT_SYMBOL(ad1848_unload); +EXPORT_SYMBOL(ad1848_control); EXPORT_SYMBOL(adintr); EXPORT_SYMBOL(probe_ms_sound); EXPORT_SYMBOL(attach_ms_sound); diff -u --recursive --new-file v2.1.74/linux/drivers/sound/audio.c linux/drivers/sound/audio.c --- v2.1.74/linux/drivers/sound/audio.c Wed Dec 10 11:12:44 1997 +++ linux/drivers/sound/audio.c Sun Dec 21 17:41:24 1997 @@ -11,13 +11,12 @@ * Version 2 (June 1991). See the "COPYING" file distributed with this software * for more info. */ -#include +#include #include "sound_config.h" #if defined(CONFIG_AUDIO) || defined(MODULE) - #include "ulaw.h" #include "coproc.h" @@ -37,36 +36,38 @@ static int local_conversion[MAX_AUDIO_DEV]; #define CNV_MU_LAW 0x00000001 -static int -set_format(int dev, int fmt) + +static int set_format(int dev, int fmt) { if (fmt != AFMT_QUERY) - { - local_conversion[dev] = 0; + { + local_conversion[dev] = 0; - if (!(audio_devs[dev]->format_mask & fmt)) /* Not supported */ - if (fmt == AFMT_MU_LAW) - { - fmt = AFMT_U8; - local_conversion[dev] = CNV_MU_LAW; - } else - fmt = AFMT_U8; /* This is always supported */ - - audio_format[dev] = audio_devs[dev]->d->set_bits(dev, fmt); - local_format[dev] = fmt; - } else + if (!(audio_devs[dev]->format_mask & fmt)) /* Not supported */ + { + if (fmt == AFMT_MU_LAW) + { + fmt = AFMT_U8; + local_conversion[dev] = CNV_MU_LAW; + } + else + fmt = AFMT_U8; /* This is always supported */ + } + audio_format[dev] = audio_devs[dev]->d->set_bits(dev, fmt); + local_format[dev] = fmt; + } + else return local_format[dev]; return local_format[dev]; } -int -audio_open(int dev, struct fileinfo *file) +int audio_open(int dev, struct fileinfo *file) { - int ret; - int bits; - int dev_type = dev & 0x0f; - int mode = file->mode & O_ACCMODE; + int ret; + int bits; + int dev_type = dev & 0x0f; + int mode = file->mode & O_ACCMODE; dev = dev >> 4; @@ -82,20 +83,21 @@ return ret; if (audio_devs[dev]->coproc) + { if ((ret = audio_devs[dev]->coproc-> - open(audio_devs[dev]->coproc->devc, COPR_PCM)) < 0) - { - audio_release(dev, file); - printk("Sound: Can't access coprocessor device\n"); - - return ret; - } + open(audio_devs[dev]->coproc->devc, COPR_PCM)) < 0) + { + audio_release(dev, file); + printk(KERN_WARNING "Sound: Can't access coprocessor device\n"); + return ret; + } + } + local_conversion[dev] = 0; if (dev_type == SND_DEV_AUDIO) - { set_format(dev, AFMT_MU_LAW); - } else + else set_format(dev, bits); audio_mode[dev] = AM_NONE; @@ -105,8 +107,7 @@ return ret; } -static void -sync_output(int dev) +static void sync_output(int dev) { int p, i; int l; @@ -117,39 +118,40 @@ dmap->flags |= DMA_POST; /* Align the write pointer with fragment boundaries */ + if ((l = dmap->user_counter % dmap->fragment_size) > 0) - { - int len; - unsigned long offs = dmap->user_counter % dmap->bytes_in_use; - - len = dmap->fragment_size - l; - memset(dmap->raw_buf + offs, dmap->neutral_byte, len); - DMAbuf_move_wrpointer(dev, len); - } -/* - * Clean all unused buffer fragments. - */ + { + int len; + unsigned long offs = dmap->user_counter % dmap->bytes_in_use; + + len = dmap->fragment_size - l; + memset(dmap->raw_buf + offs, dmap->neutral_byte, len); + DMAbuf_move_wrpointer(dev, len); + } + + /* + * Clean all unused buffer fragments. + */ p = dmap->qtail; dmap->flags |= DMA_POST; for (i = dmap->qlen + 1; i < dmap->nbufs; i++) - { - p = (p + 1) % dmap->nbufs; - if (((dmap->raw_buf + p * dmap->fragment_size) + dmap->fragment_size) > - (dmap->raw_buf + dmap->buffsize)) - printk("audio: Buffer error 2\n"); - - memset(dmap->raw_buf + p * dmap->fragment_size, - dmap->neutral_byte, - dmap->fragment_size); - } + { + p = (p + 1) % dmap->nbufs; + if (((dmap->raw_buf + p * dmap->fragment_size) + dmap->fragment_size) > + (dmap->raw_buf + dmap->buffsize)) + printk(KERN_ERR "audio: Buffer error 2\n"); + + memset(dmap->raw_buf + p * dmap->fragment_size, + dmap->neutral_byte, + dmap->fragment_size); + } dmap->flags |= DMA_DIRTY; } -void -audio_release(int dev, struct fileinfo *file) +void audio_release(int dev, struct fileinfo *file) { int mode; @@ -167,8 +169,8 @@ } #if defined(NO_INLINE_ASM) || !defined(i386) -static void -translate_bytes(const unsigned char *table, unsigned char *buff, int n) + +static void translate_bytes(const unsigned char *table, unsigned char *buff, int n) { unsigned long i; @@ -184,25 +186,24 @@ translate_bytes(const void *table, void *buff, int n) { if (n > 0) - { - __asm__("cld\n" - "1:\tlodsb\n\t" - "xlatb\n\t" - "stosb\n\t" + { + __asm__("cld\n" + "1:\tlodsb\n\t" + "xlatb\n\t" + "stosb\n\t" "loop 1b\n\t": : "b"((long) table), "c"(n), "D"((long) buff), "S"((long) buff) : "bx", "cx", "di", "si", "ax"); - } + } } #endif -int -audio_write(int dev, struct fileinfo *file, const char *buf, int count) +int audio_write(int dev, struct fileinfo *file, const char *buf, int count) { - int c, p, l, buf_size; - int err; - char *dma_buf; + int c, p, l, buf_size; + int err; + char *dma_buf; dev = dev >> 4; @@ -218,61 +219,61 @@ audio_mode[dev] = AM_WRITE; if (!count) /* Flush output */ - { + { sync_output(dev); return 0; - } + } + while (c) - { - if ((err = DMAbuf_getwrbuffer(dev, &dma_buf, &buf_size, dev_nblock[dev])) < 0) - { + { + if ((err = DMAbuf_getwrbuffer(dev, &dma_buf, &buf_size, dev_nblock[dev])) < 0) + { /* Handle nonblocking mode */ - if (dev_nblock[dev] && err == -EAGAIN) - return p; /* No more space. Return # of accepted bytes */ - return err; - } - l = c; - - if (l > buf_size) - l = buf_size; - - if (!audio_devs[dev]->d->copy_user) - { - if ((dma_buf + l) > + if (dev_nblock[dev] && err == -EAGAIN) + return p; /* No more space. Return # of accepted bytes */ + return err; + } + l = c; + + if (l > buf_size) + l = buf_size; + + if (!audio_devs[dev]->d->copy_user) + { + if ((dma_buf + l) > (audio_devs[dev]->dmap_out->raw_buf + audio_devs[dev]->dmap_out->buffsize)) - { - printk("audio: Buffer error 3 (%lx,%d), (%lx, %d)\n", (long) dma_buf, l, (long) audio_devs[dev]->dmap_out->raw_buf, (int) audio_devs[dev]->dmap_out->buffsize); - return -EDOM; - } - if (dma_buf < audio_devs[dev]->dmap_out->raw_buf) - { - printk("audio: Buffer error 13 (%lx<%lx)\n", (long) dma_buf, (long) audio_devs[dev]->dmap_out->raw_buf); - return -EDOM; - } - copy_from_user(dma_buf, &(buf)[p], l); - } else - audio_devs[dev]->d->copy_user(dev, - dma_buf, 0, buf, p, l); - - if (local_conversion[dev] & CNV_MU_LAW) - { - /* - * This just allows interrupts while the conversion is running - */ - sti(); - translate_bytes(ulaw_dsp, (unsigned char *) dma_buf, l); - } - c -= l; - p += l; - DMAbuf_move_wrpointer(dev, l); + { + printk(KERN_ERR "audio: Buffer error 3 (%lx,%d), (%lx, %d)\n", (long) dma_buf, l, (long) audio_devs[dev]->dmap_out->raw_buf, (int) audio_devs[dev]->dmap_out->buffsize); + return -EDOM; + } + if (dma_buf < audio_devs[dev]->dmap_out->raw_buf) + { + printk(KERN_ERR "audio: Buffer error 13 (%lx<%lx)\n", (long) dma_buf, (long) audio_devs[dev]->dmap_out->raw_buf); + return -EDOM; + } + if(copy_from_user(dma_buf, &(buf)[p], l)) + return -EFAULT; + } + else audio_devs[dev]->d->copy_user(dev, dma_buf, 0, buf, p, l); + + if (local_conversion[dev] & CNV_MU_LAW) + { + /* + * This just allows interrupts while the conversion is running + */ + sti(); + translate_bytes(ulaw_dsp, (unsigned char *) dma_buf, l); + } + c -= l; + p += l; + DMAbuf_move_wrpointer(dev, l); - } + } return count; } -int -audio_read(int dev, struct fileinfo *file, char *buf, int count) +int audio_read(int dev, struct fileinfo *file, char *buf, int count) { int c, p, l; char *dmabuf; @@ -286,251 +287,268 @@ return -EPERM; if ((audio_mode[dev] & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX)) - { - sync_output(dev); - } + sync_output(dev); + if (audio_devs[dev]->flags & DMA_DUPLEX) audio_mode[dev] |= AM_READ; else audio_mode[dev] = AM_READ; - while (c) - { - if ((buf_no = DMAbuf_getrdbuffer(dev, &dmabuf, &l, - dev_nblock[dev])) < 0) - { - /* Nonblocking mode handling. Return current # of bytes */ + while(c) + { + if ((buf_no = DMAbuf_getrdbuffer(dev, &dmabuf, &l, + dev_nblock[dev])) < 0) + { + /* + * Nonblocking mode handling. Return current # of bytes + */ - if (dev_nblock[dev] && buf_no == -EAGAIN) - return p; + if (dev_nblock[dev] && buf_no == -EAGAIN) + return p; - if (p > 0) /* Avoid throwing away data */ + if (p > 0) /* Avoid throwing away data */ return p; /* Return it instead */ - return buf_no; - } - if (l > c) - l = c; - - /* - * Insert any local processing here. - */ - - if (local_conversion[dev] & CNV_MU_LAW) - { - /* - * This just allows interrupts while the conversion is running - */ - sti(); - - translate_bytes(dsp_ulaw, (unsigned char *) dmabuf, l); - } - { - char *fixit = dmabuf; - - copy_to_user(&(buf)[p], fixit, l); - }; - - DMAbuf_rmchars(dev, buf_no, l); - - p += l; - c -= l; - } + return buf_no; + } + if (l > c) + l = c; + + /* + * Insert any local processing here. + */ + + if (local_conversion[dev] & CNV_MU_LAW) + { + /* + * This just allows interrupts while the conversion is running + */ + sti(); + + translate_bytes(dsp_ulaw, (unsigned char *) dmabuf, l); + } + + { + char *fixit = dmabuf; + + if(copy_to_user(&(buf)[p], fixit, l)) + return -EFAULT; + }; + + DMAbuf_rmchars(dev, buf_no, l); + + p += l; + c -= l; + } return count - c; } -int -audio_ioctl(int dev, struct fileinfo *file_must_not_be_used, +int audio_ioctl(int dev, struct fileinfo *file_must_not_be_used, unsigned int cmd, caddr_t arg) { - int val; - - /* printk( "audio_ioctl(%x, %x)\n", (int)cmd, (int)arg); */ + int val; dev = dev >> 4; if (((cmd >> 8) & 0xff) == 'C') - { - if (audio_devs[dev]->coproc) /* Coprocessor ioctl */ - return audio_devs[dev]->coproc->ioctl(audio_devs[dev]->coproc->devc, cmd, arg, 0); - else - printk("/dev/dsp%d: No coprocessor for this device\n", dev); - - return -ENXIO; - } else - switch (cmd) - { - case SNDCTL_DSP_SYNC: - if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) - return 0; - - if (audio_devs[dev]->dmap_out->fragment_size == 0) - return 0; - sync_output(dev); - DMAbuf_sync(dev); - DMAbuf_reset(dev); - return 0; - break; - - case SNDCTL_DSP_POST: - if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) - return 0; - if (audio_devs[dev]->dmap_out->fragment_size == 0) - return 0; - audio_devs[dev]->dmap_out->flags |= DMA_POST | DMA_DIRTY; - sync_output(dev); - dma_ioctl(dev, SNDCTL_DSP_POST, (caddr_t) 0); - return 0; - break; - - case SNDCTL_DSP_RESET: - audio_mode[dev] = AM_NONE; - DMAbuf_reset(dev); - return 0; - break; - - case SNDCTL_DSP_GETFMTS: - return (*(int *) arg = audio_devs[dev]->format_mask); - break; - - case SNDCTL_DSP_SETFMT: - val = *(int *) arg; - return (*(int *) arg = set_format(dev, val)); - - case SNDCTL_DSP_GETISPACE: - if (!(audio_devs[dev]->open_mode & OPEN_READ)) - return 0; - if ((audio_mode[dev] & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX)) - return -EBUSY; - - { - audio_buf_info info; - - int err = dma_ioctl(dev, cmd, (caddr_t) & info); - - if (err < 0) - return err; - - memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info)); - return 0; - } + { + if (audio_devs[dev]->coproc) /* Coprocessor ioctl */ + return audio_devs[dev]->coproc->ioctl(audio_devs[dev]->coproc->devc, cmd, arg, 0); + /* else + printk(KERN_DEBUG"/dev/dsp%d: No coprocessor for this device\n", dev); */ + return -ENXIO; + } + else switch (cmd) + { + case SNDCTL_DSP_SYNC: + if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) + return 0; + + if (audio_devs[dev]->dmap_out->fragment_size == 0) + return 0; + sync_output(dev); + DMAbuf_sync(dev); + DMAbuf_reset(dev); + return 0; + + case SNDCTL_DSP_POST: + if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) + return 0; + if (audio_devs[dev]->dmap_out->fragment_size == 0) + return 0; + audio_devs[dev]->dmap_out->flags |= DMA_POST | DMA_DIRTY; + sync_output(dev); + dma_ioctl(dev, SNDCTL_DSP_POST, (caddr_t) 0); + return 0; + + case SNDCTL_DSP_RESET: + audio_mode[dev] = AM_NONE; + DMAbuf_reset(dev); + return 0; + + case SNDCTL_DSP_GETFMTS: + return (*(int *) arg = audio_devs[dev]->format_mask); + + case SNDCTL_DSP_SETFMT: + val = *(int *) arg; + return (*(int *) arg = set_format(dev, val)); + + case SNDCTL_DSP_GETISPACE: + if (!(audio_devs[dev]->open_mode & OPEN_READ)) + return 0; + if ((audio_mode[dev] & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX)) + return -EBUSY; + + { + audio_buf_info info; + + int err = dma_ioctl(dev, cmd, (caddr_t) & info); + + if (err < 0) + return err; + + memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info)); + return 0; + } - case SNDCTL_DSP_GETOSPACE: - if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) + case SNDCTL_DSP_GETOSPACE: + if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) return -EPERM; - if ((audio_mode[dev] & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX)) + if ((audio_mode[dev] & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX)) return -EBUSY; - { - audio_buf_info info; - - int err = dma_ioctl(dev, cmd, (caddr_t) & info); - - if (err < 0) - return err; - - memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info)); - return 0; - } - - case SNDCTL_DSP_NONBLOCK: - dev_nblock[dev] = 1; - return 0; - break; - - case SNDCTL_DSP_GETCAPS: - { - int info = 1; /* Revision level of this ioctl() */ - - if (audio_devs[dev]->flags & DMA_DUPLEX && - audio_devs[dev]->open_mode == OPEN_READWRITE) - info |= DSP_CAP_DUPLEX; - - if (audio_devs[dev]->coproc) - info |= DSP_CAP_COPROC; - - if (audio_devs[dev]->d->local_qlen) /* Device has hidden buffers */ - info |= DSP_CAP_BATCH; - - if (audio_devs[dev]->d->trigger) /* Supports SETTRIGGER */ - info |= DSP_CAP_TRIGGER; + { + audio_buf_info info; - info |= DSP_CAP_MMAP; + int err = dma_ioctl(dev, cmd, (caddr_t) & info); - memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info)); - return 0; - } - break; + if (err < 0) + return err; - case SOUND_PCM_WRITE_RATE: - val = *(int *) arg; - return (*(int *) arg = audio_devs[dev]->d->set_speed(dev, val)); - - case SOUND_PCM_READ_RATE: - return (*(int *) arg = audio_devs[dev]->d->set_speed(dev, 0)); - - case SNDCTL_DSP_STEREO: - { - int n; - - n = *(int *) arg; - if (n > 1) - { - printk("sound: SNDCTL_DSP_STEREO called with invalid argument %d\n", n); - return -EINVAL; - } - if (n < 0) - return -EINVAL; - - return (*(int *) arg = audio_devs[dev]->d->set_channels(dev, n + 1) - 1); - } - - case SOUND_PCM_WRITE_CHANNELS: - val = *(int *) arg; - return (*(int *) arg = audio_devs[dev]->d->set_channels(dev, val)); - - case SOUND_PCM_READ_CHANNELS: - return (*(int *) arg = audio_devs[dev]->d->set_channels(dev, 0)); - - case SOUND_PCM_READ_BITS: - return (*(int *) arg = audio_devs[dev]->d->set_bits(dev, 0)); - - case SNDCTL_DSP_SETDUPLEX: - if (audio_devs[dev]->open_mode != OPEN_READWRITE) - return -EPERM; - if (audio_devs[dev]->flags & DMA_DUPLEX) - return 0; - else - return -EIO; - break; - - case SNDCTL_DSP_PROFILE: - if (audio_devs[dev]->open_mode & OPEN_WRITE) - audio_devs[dev]->dmap_out->applic_profile = *(int *) arg; - if (audio_devs[dev]->open_mode & OPEN_READ) - audio_devs[dev]->dmap_in->applic_profile = *(int *) arg; - return 0; - break; - - default: - return dma_ioctl(dev, cmd, arg); - } + memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info)); + return 0; + } + + case SNDCTL_DSP_NONBLOCK: + dev_nblock[dev] = 1; + return 0; + + case SNDCTL_DSP_GETCAPS: + { + int info = 1; /* Revision level of this ioctl() */ + + if (audio_devs[dev]->flags & DMA_DUPLEX && + audio_devs[dev]->open_mode == OPEN_READWRITE) + info |= DSP_CAP_DUPLEX; + + if (audio_devs[dev]->coproc) + info |= DSP_CAP_COPROC; + + if (audio_devs[dev]->d->local_qlen) /* Device has hidden buffers */ + info |= DSP_CAP_BATCH; + + if (audio_devs[dev]->d->trigger) /* Supports SETTRIGGER */ + info |= DSP_CAP_TRIGGER; + + info |= DSP_CAP_MMAP; + + memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info)); + return 0; + } + + case SOUND_PCM_WRITE_RATE: + val = *(int *) arg; + return (*(int *) arg = audio_devs[dev]->d->set_speed(dev, val)); + + case SOUND_PCM_READ_RATE: + return (*(int *) arg = audio_devs[dev]->d->set_speed(dev, 0)); + + case SNDCTL_DSP_STEREO: + { + int n; + + n = *(int *) arg; + if (n > 1) + { +/* printk(KERN_DENUG "sound: SNDCTL_DSP_STEREO called with invalid argument %d\n", n);*/ + return -EINVAL; + } + if (n < 0) + return -EINVAL; + + return (*(int *) arg = audio_devs[dev]->d->set_channels(dev, n + 1) - 1); + } + + case SOUND_PCM_WRITE_CHANNELS: + val = *(int *) arg; + return (*(int *) arg = audio_devs[dev]->d->set_channels(dev, val)); + + case SOUND_PCM_READ_CHANNELS: + return (*(int *) arg = audio_devs[dev]->d->set_channels(dev, 0)); + + case SOUND_PCM_READ_BITS: + return (*(int *) arg = audio_devs[dev]->d->set_bits(dev, 0)); + + case SNDCTL_DSP_SETDUPLEX: + if (audio_devs[dev]->open_mode != OPEN_READWRITE) + return -EPERM; + if (audio_devs[dev]->flags & DMA_DUPLEX) + return 0; + else + return -EIO; + + case SNDCTL_DSP_PROFILE: + if (audio_devs[dev]->open_mode & OPEN_WRITE) + audio_devs[dev]->dmap_out->applic_profile = *(int *) arg; + if (audio_devs[dev]->open_mode & OPEN_READ) + audio_devs[dev]->dmap_in->applic_profile = *(int *) arg; + return 0; + + case SNDCTL_DSP_GETODELAY: + { + int count; + unsigned long flags; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + + if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) + return -EINVAL; + if (!(dmap->flags & DMA_ALLOC_DONE)) + return *(int *) arg = 0; + + save_flags (flags); + cli (); + /* Compute number of bytes that have been played */ + count = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT); + if (count < dmap->fragment_size && dmap->qhead != 0) + count += dmap->bytes_in_use; /* Pointer wrap not handled yet */ + count += dmap->byte_counter; + + /* Substract current count from the number of bytes written by app */ + count = dmap->user_counter - count; + if (count < 0) + count = 0; + restore_flags (flags); + + return *(int *) arg = count; + } + break; + + default: + return dma_ioctl(dev, cmd, arg); + } } -void -audio_init_devices(void) +void audio_init_devices(void) { /* * NOTE! This routine could be called several times during boot. */ } - #endif -void -reorganize_buffers(int dev, struct dma_buffparms *dmap, int recording) +void reorganize_buffers(int dev, struct dma_buffparms *dmap, int recording) { /* * This routine breaks the physical device buffers to logical ones. @@ -538,8 +556,8 @@ struct audio_operations *dsp_dev = audio_devs[dev]; - unsigned i, n; - unsigned sr, nc, sz, bsz; + unsigned i, n; + unsigned sr, nc, sz, bsz; sr = dsp_dev->d->set_speed(dev, 0); nc = dsp_dev->d->set_channels(dev, 0); @@ -551,12 +569,13 @@ dmap->neutral_byte = NEUTRAL16; if (sr < 1 || nc < 1 || sz < 1) - { - printk("Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz); - sr = DSP_DEFAULT_SPEED; - nc = 1; - sz = 8; - } + { +/* printk(KERN_DEBUG "Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz);*/ + sr = DSP_DEFAULT_SPEED; + nc = 1; + sz = 8; + } + sz = sr * nc * sz; sz /= 8; /* #bits -> #bytes */ @@ -567,52 +586,54 @@ dmap->needs_reorg = 0; if (dmap->fragment_size == 0) - { /* Compute the fragment size using the default algorithm */ + { + /* Compute the fragment size using the default algorithm */ + + /* + * Compute a buffer size for time not exceeding 1 second. + * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds + * of sound (using the current speed, sample size and #channels). + */ + + bsz = dmap->buffsize; + while (bsz > sz) + bsz /= 2; + + if (bsz == dmap->buffsize) + bsz /= 2; /* Needs at least 2 buffers */ + + /* + * Split the computed fragment to smaller parts. After 3.5a9 + * the default subdivision is 4 which should give better + * results when recording. + */ + + if (dmap->subdivision == 0) /* Not already set */ + { + dmap->subdivision = 4; /* Init to the default value */ + + if ((bsz / dmap->subdivision) > 4096) + dmap->subdivision *= 2; + if ((bsz / dmap->subdivision) < 4096) + dmap->subdivision = 1; + } + bsz /= dmap->subdivision; - /* - * Compute a buffer size for time not exceeding 1 second. - * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds - * of sound (using the current speed, sample size and #channels). - */ - - bsz = dmap->buffsize; - while (bsz > sz) - bsz /= 2; - - if (bsz == dmap->buffsize) - bsz /= 2; /* Needs at least 2 buffers */ - -/* - * Split the computed fragment to smaller parts. After 3.5a9 - * the default subdivision is 4 which should give better - * results when recording. - */ - - if (dmap->subdivision == 0) /* Not already set */ - { - dmap->subdivision = 4; /* Init to the default value */ - - if ((bsz / dmap->subdivision) > 4096) - dmap->subdivision *= 2; - if ((bsz / dmap->subdivision) < 4096) - dmap->subdivision = 1; - } - bsz /= dmap->subdivision; - - if (bsz < 16) - bsz = 16; /* Just a sanity check */ - - dmap->fragment_size = bsz; - } else - { - /* - * The process has specified the buffer size with SNDCTL_DSP_SETFRAGMENT or - * the buffer size computation has already been done. - */ - if (dmap->fragment_size > (dmap->buffsize / 2)) - dmap->fragment_size = (dmap->buffsize / 2); - bsz = dmap->fragment_size; - } + if (bsz < 16) + bsz = 16; /* Just a sanity check */ + + dmap->fragment_size = bsz; + } + else + { + /* + * The process has specified the buffer size with SNDCTL_DSP_SETFRAGMENT or + * the buffer size computation has already been done. + */ + if (dmap->fragment_size > (dmap->buffsize / 2)) + dmap->fragment_size = (dmap->buffsize / 2); + bsz = dmap->fragment_size; + } if (audio_devs[dev]->min_fragment) if (bsz < (1 << audio_devs[dev]->min_fragment)) @@ -632,42 +653,39 @@ n = dmap->max_fragments; if (n < 2) - { - n = 2; - bsz /= 2; - } + { + n = 2; + bsz /= 2; + } dmap->nbufs = n; dmap->bytes_in_use = n * bsz; dmap->fragment_size = bsz; dmap->max_byte_counter = (dmap->data_rate * 60 * 60) + - dmap->bytes_in_use; /* Approximately one hour */ + dmap->bytes_in_use; /* Approximately one hour */ if (dmap->raw_buf) - { - memset(dmap->raw_buf, - dmap->neutral_byte, - dmap->bytes_in_use); - } + { + memset(dmap->raw_buf, dmap->neutral_byte, dmap->bytes_in_use); + } + for (i = 0; i < dmap->nbufs; i++) - { - dmap->counts[i] = 0; - } + { + dmap->counts[i] = 0; + } dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY; } -static int -dma_subdivide(int dev, struct dma_buffparms *dmap, caddr_t arg, int fact) +static int dma_subdivide(int dev, struct dma_buffparms *dmap, caddr_t arg, int fact) { if (fact == 0) - { - fact = dmap->subdivision; - if (fact == 0) - fact = 1; - return (*(int *) arg = fact); - } - if (dmap->subdivision != 0 || - dmap->fragment_size) /* Too late to change */ + { + fact = dmap->subdivision; + if (fact == 0) + fact = 1; + return (*(int *) arg = fact); + } + if (dmap->subdivision != 0 || dmap->fragment_size) /* Too late to change */ return -EINVAL; if (fact > MAX_REALTIME_FACTOR) @@ -680,10 +698,9 @@ return (*(int *) arg = fact); } -static int -dma_set_fragment(int dev, struct dma_buffparms *dmap, caddr_t arg, int fact) +static int dma_set_fragment(int dev, struct dma_buffparms *dmap, caddr_t arg, int fact) { - int bytes, count; + int bytes, count; if (fact == 0) return -EIO; @@ -736,309 +753,305 @@ return 0; } -int -dma_ioctl(int dev, unsigned int cmd, caddr_t arg) +int dma_ioctl(int dev, unsigned int cmd, caddr_t arg) { struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out; struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in; switch (cmd) - { - - case SNDCTL_DSP_SUBDIVIDE: - { - int fact; - int ret = 0; - - fact = *(int *) arg; - - if (audio_devs[dev]->open_mode & OPEN_WRITE) - ret = dma_subdivide(dev, dmap_out, arg, fact); - if (ret < 0) - return ret; - - if (audio_devs[dev]->open_mode != OPEN_WRITE || - (audio_devs[dev]->flags & DMA_DUPLEX && - audio_devs[dev]->open_mode & OPEN_READ)) - ret = dma_subdivide(dev, dmap_in, arg, fact); - - return ret; - } - break; - - case SNDCTL_DSP_GETISPACE: - case SNDCTL_DSP_GETOSPACE: - { - struct dma_buffparms *dmap = dmap_out; - - audio_buf_info *info = (audio_buf_info *) arg; - - if (cmd == SNDCTL_DSP_GETISPACE && - !(audio_devs[dev]->open_mode & OPEN_READ)) - return -EINVAL; - - if (cmd == SNDCTL_DSP_GETOSPACE && - !(audio_devs[dev]->open_mode & OPEN_WRITE)) - return -EINVAL; - - if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX) - dmap = dmap_in; - - if (dmap->mapping_flags & DMA_MAP_MAPPED) - return -EINVAL; - - if (!(dmap->flags & DMA_ALLOC_DONE)) - reorganize_buffers(dev, dmap, (cmd == SNDCTL_DSP_GETISPACE)); - - info->fragstotal = dmap->nbufs; - - if (cmd == SNDCTL_DSP_GETISPACE) - info->fragments = dmap->qlen; - else - { - if (!DMAbuf_space_in_queue(dev)) - info->fragments = 0; - else - { - info->fragments = DMAbuf_space_in_queue(dev); - if (audio_devs[dev]->d->local_qlen) - { - int tmp = audio_devs[dev]->d->local_qlen(dev); - - if (tmp && info->fragments) - tmp--; /* - * This buffer has been counted twice - */ - info->fragments -= tmp; - } - } - } - - if (info->fragments < 0) - info->fragments = 0; - else if (info->fragments > dmap->nbufs) - info->fragments = dmap->nbufs; - - info->fragsize = dmap->fragment_size; - info->bytes = info->fragments * dmap->fragment_size; - - if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen) - info->bytes -= dmap->counts[dmap->qhead]; - else - { - info->fragments = info->bytes / dmap->fragment_size; - info->bytes -= dmap->user_counter % dmap->fragment_size; - } - } - return 0; - - case SNDCTL_DSP_SETTRIGGER: - { - unsigned long flags; - - int bits; - int changed; - - bits = *(int *) arg; - bits &= audio_devs[dev]->open_mode; - - if (audio_devs[dev]->d->trigger == NULL) - return -EINVAL; - - if (!(audio_devs[dev]->flags & DMA_DUPLEX)) - if ((bits & PCM_ENABLE_INPUT) && (bits & PCM_ENABLE_OUTPUT)) - { - printk("Sound: Device doesn't have full duplex capability\n"); - return -EINVAL; - } - save_flags(flags); - cli(); - changed = audio_devs[dev]->enable_bits ^ bits; - - if ((changed & bits) & PCM_ENABLE_INPUT && audio_devs[dev]->go) - { - int err; - - reorganize_buffers(dev, dmap_in, 1); - - if ((err = audio_devs[dev]->d->prepare_for_input(dev, - dmap_in->fragment_size, dmap_in->nbufs)) < 0) - return -err; - - dmap_in->dma_mode = DMODE_INPUT; - audio_devs[dev]->enable_bits = bits; - DMAbuf_activate_recording(dev, dmap_in); - } - if ((changed & bits) & PCM_ENABLE_OUTPUT && - (dmap_out->mapping_flags & DMA_MAP_MAPPED || dmap_out->qlen > 0) && - audio_devs[dev]->go) - { - - if (!(dmap_out->flags & DMA_ALLOC_DONE)) - { - reorganize_buffers(dev, dmap_out, 0); - } - dmap_out->dma_mode = DMODE_OUTPUT; - ; - audio_devs[dev]->enable_bits = bits; - dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size; - DMAbuf_launch_output(dev, dmap_out); - ; - } - audio_devs[dev]->enable_bits = bits; - if (changed && audio_devs[dev]->d->trigger) - { - audio_devs[dev]->d->trigger(dev, bits * audio_devs[dev]->go); - } - restore_flags(flags); - } - case SNDCTL_DSP_GETTRIGGER: - return (*(int *) arg = audio_devs[dev]->enable_bits); - break; + { - case SNDCTL_DSP_SETSYNCRO: - - if (!audio_devs[dev]->d->trigger) - return -EINVAL; - - audio_devs[dev]->d->trigger(dev, 0); - audio_devs[dev]->go = 0; - return 0; - break; - - case SNDCTL_DSP_GETIPTR: - { - count_info info; - unsigned long flags; - struct dma_buffparms *dmap = dmap_in; - - if (!(audio_devs[dev]->open_mode & OPEN_READ)) - return -EINVAL; - - save_flags(flags); - cli(); - info.bytes = dmap->byte_counter; - info.ptr = DMAbuf_get_buffer_pointer(dev, dmap, DMODE_INPUT) & ~3; - if (info.ptr < dmap->fragment_size && dmap->qtail != 0) - info.bytes += dmap->bytes_in_use; /* Pointer wrap not handled yet */ - - info.blocks = dmap->qlen; - info.bytes += info.ptr; - memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info)); - - if (dmap->mapping_flags & DMA_MAP_MAPPED) - dmap->qlen = 0; /* Reset interrupt counter */ - restore_flags(flags); - return 0; - } - break; - - case SNDCTL_DSP_GETOPTR: - { - count_info info; - unsigned long flags; - struct dma_buffparms *dmap = dmap_out; - - if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) - return -EINVAL; - - save_flags(flags); - cli(); - info.bytes = dmap->byte_counter; - info.ptr = DMAbuf_get_buffer_pointer(dev, dmap, DMODE_OUTPUT) & ~3; - if (info.ptr < dmap->fragment_size && dmap->qhead != 0) - info.bytes += dmap->bytes_in_use; /* Pointer wrap not handled yet */ - info.blocks = dmap->qlen; - info.bytes += info.ptr; - memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info)); - - if (dmap->mapping_flags & DMA_MAP_MAPPED) - dmap->qlen = 0; /* Reset interrupt counter */ - - restore_flags(flags); - return 0; - } - break; - - case SNDCTL_DSP_GETODELAY: - { - int count; - unsigned long flags; - struct dma_buffparms *dmap = dmap_out; - - if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) - return -EINVAL; - if (!(dmap->flags & DMA_ALLOC_DONE)) - return (*(int *) arg = 0); - - save_flags (flags); - cli (); - /* Compute number of bytes that have been played */ - count = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT); - if (count < dmap->fragment_size && dmap->qhead != 0) - count += dmap->bytes_in_use; /* Pointer wrap not handled yet */ - count += dmap->byte_counter; - - /* Substract current count from the number of bytes written by app */ - count = dmap->user_counter - count; - if (count < 0) - count = 0; - restore_flags (flags); - - return (*(int *) arg = count); - } - break; - - case SNDCTL_DSP_POST: - ; - if (audio_devs[dev]->dmap_out->qlen > 0) - if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE)) - DMAbuf_launch_output(dev, audio_devs[dev]->dmap_out); - ; - return 0; - break; - - case SNDCTL_DSP_GETBLKSIZE: - { - int fragment_size; - struct dma_buffparms *dmap = dmap_out; - - if (audio_devs[dev]->open_mode & OPEN_WRITE) - reorganize_buffers(dev, dmap_out, - (audio_devs[dev]->open_mode == OPEN_READ)); - if (audio_devs[dev]->open_mode != OPEN_WRITE || - (audio_devs[dev]->flags & DMA_DUPLEX && - audio_devs[dev]->open_mode & OPEN_READ)) - reorganize_buffers(dev, dmap_in, - (audio_devs[dev]->open_mode == OPEN_READ)); - if (audio_devs[dev]->open_mode == OPEN_READ) - dmap = dmap_in; - fragment_size = dmap->fragment_size; - return (*(int *) arg = fragment_size); - } - break; - - case SNDCTL_DSP_SETFRAGMENT: - { - int fact; - int ret; - - fact = *(int *) arg; - ret = dma_set_fragment(dev, dmap_out, arg, fact); - if (ret < 0) - return ret; - - if (audio_devs[dev]->flags & DMA_DUPLEX && - audio_devs[dev]->open_mode & OPEN_READ) - ret = dma_set_fragment(dev, dmap_in, arg, fact); - - return ret; - } - break; - - default: - return audio_devs[dev]->d->ioctl(dev, cmd, arg); - } + case SNDCTL_DSP_SUBDIVIDE: + { + int fact; + int ret = 0; + + fact = *(int *) arg; + + if (audio_devs[dev]->open_mode & OPEN_WRITE) + ret = dma_subdivide(dev, dmap_out, arg, fact); + if (ret < 0) + return ret; + + if (audio_devs[dev]->open_mode != OPEN_WRITE || + (audio_devs[dev]->flags & DMA_DUPLEX && + audio_devs[dev]->open_mode & OPEN_READ)) + ret = dma_subdivide(dev, dmap_in, arg, fact); + + return ret; + } + break; + + case SNDCTL_DSP_GETISPACE: + case SNDCTL_DSP_GETOSPACE: + { + struct dma_buffparms *dmap = dmap_out; + + audio_buf_info *info = (audio_buf_info *) arg; + + if (cmd == SNDCTL_DSP_GETISPACE && + !(audio_devs[dev]->open_mode & OPEN_READ)) + return -EINVAL; + + if (cmd == SNDCTL_DSP_GETOSPACE && + !(audio_devs[dev]->open_mode & OPEN_WRITE)) + return -EINVAL; + + if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX) + dmap = dmap_in; + + if (dmap->mapping_flags & DMA_MAP_MAPPED) + return -EINVAL; + + if (!(dmap->flags & DMA_ALLOC_DONE)) + reorganize_buffers(dev, dmap, (cmd == SNDCTL_DSP_GETISPACE)); + + info->fragstotal = dmap->nbufs; + + if (cmd == SNDCTL_DSP_GETISPACE) + info->fragments = dmap->qlen; + else + { + if (!DMAbuf_space_in_queue(dev)) + info->fragments = 0; + else + { + info->fragments = DMAbuf_space_in_queue(dev); + if (audio_devs[dev]->d->local_qlen) + { + int tmp = audio_devs[dev]->d->local_qlen(dev); + + if (tmp && info->fragments) + tmp--; /* + * This buffer has been counted twice + */ + info->fragments -= tmp; + } + } + } + + if (info->fragments < 0) + info->fragments = 0; + else if (info->fragments > dmap->nbufs) + info->fragments = dmap->nbufs; + + info->fragsize = dmap->fragment_size; + info->bytes = info->fragments * dmap->fragment_size; + + if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen) + info->bytes -= dmap->counts[dmap->qhead]; + else + { + info->fragments = info->bytes / dmap->fragment_size; + info->bytes -= dmap->user_counter % dmap->fragment_size; + } + } + return 0; + case SNDCTL_DSP_SETTRIGGER: + { + unsigned long flags; + + int bits; + int changed; + + bits = *(int *) arg; + bits &= audio_devs[dev]->open_mode; + + if (audio_devs[dev]->d->trigger == NULL) + return -EINVAL; + + if (!(audio_devs[dev]->flags & DMA_DUPLEX)) + if ((bits & PCM_ENABLE_INPUT) && (bits & PCM_ENABLE_OUTPUT)) + { + /* printk(KERN_WARNING "Sound: Device doesn't have full duplex capability\n");*/ + return -EINVAL; + } + save_flags(flags); + cli(); + changed = audio_devs[dev]->enable_bits ^ bits; + + if ((changed & bits) & PCM_ENABLE_INPUT && audio_devs[dev]->go) + { + int err; + + reorganize_buffers(dev, dmap_in, 1); + + if ((err = audio_devs[dev]->d->prepare_for_input(dev, + dmap_in->fragment_size, dmap_in->nbufs)) < 0) + return -err; + + dmap_in->dma_mode = DMODE_INPUT; + audio_devs[dev]->enable_bits = bits; + DMAbuf_activate_recording(dev, dmap_in); + } + + if ((changed & bits) & PCM_ENABLE_OUTPUT && + (dmap_out->mapping_flags & DMA_MAP_MAPPED || dmap_out->qlen > 0) && + audio_devs[dev]->go) + { + + if (!(dmap_out->flags & DMA_ALLOC_DONE)) + { + reorganize_buffers(dev, dmap_out, 0); + } + dmap_out->dma_mode = DMODE_OUTPUT; + audio_devs[dev]->enable_bits = bits; + dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size; + DMAbuf_launch_output(dev, dmap_out); + } + audio_devs[dev]->enable_bits = bits; + + if (changed && audio_devs[dev]->d->trigger) + { + audio_devs[dev]->d->trigger(dev, bits * audio_devs[dev]->go); + } + restore_flags(flags); + } + /* Falls through... */ + + case SNDCTL_DSP_GETTRIGGER: + return (*(int *) arg = audio_devs[dev]->enable_bits); + + case SNDCTL_DSP_SETSYNCRO: + + if (!audio_devs[dev]->d->trigger) + return -EINVAL; + + audio_devs[dev]->d->trigger(dev, 0); + audio_devs[dev]->go = 0; + return 0; + break; + + case SNDCTL_DSP_GETIPTR: + { + count_info info; + unsigned long flags; + struct dma_buffparms *dmap = dmap_in; + + if (!(audio_devs[dev]->open_mode & OPEN_READ)) + return -EINVAL; + + save_flags(flags); + cli(); + info.bytes = dmap->byte_counter; + info.ptr = DMAbuf_get_buffer_pointer(dev, dmap, DMODE_INPUT) & ~3; + if (info.ptr < dmap->fragment_size && dmap->qtail != 0) + info.bytes += dmap->bytes_in_use; /* Pointer wrap not handled yet */ + + info.blocks = dmap->qlen; + info.bytes += info.ptr; + memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info)); + + if (dmap->mapping_flags & DMA_MAP_MAPPED) + dmap->qlen = 0; /* Reset interrupt counter */ + restore_flags(flags); + return 0; + } + break; + + case SNDCTL_DSP_GETOPTR: + { + count_info info; + unsigned long flags; + struct dma_buffparms *dmap = dmap_out; + + if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) + return -EINVAL; + + save_flags(flags); + cli(); + info.bytes = dmap->byte_counter; + info.ptr = DMAbuf_get_buffer_pointer(dev, dmap, DMODE_OUTPUT) & ~3; + if (info.ptr < dmap->fragment_size && dmap->qhead != 0) + info.bytes += dmap->bytes_in_use; /* Pointer wrap not handled yet */ + info.blocks = dmap->qlen; + info.bytes += info.ptr; + memcpy((&((char *) arg)[0]), (char *) &info, sizeof(info)); + + if (dmap->mapping_flags & DMA_MAP_MAPPED) + dmap->qlen = 0; /* Reset interrupt counter */ + + restore_flags(flags); + return 0; + } + break; + + case SNDCTL_DSP_GETODELAY: + { + int count; + unsigned long flags; + struct dma_buffparms *dmap = dmap_out; + + if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) + return -EINVAL; + if (!(dmap->flags & DMA_ALLOC_DONE)) + return (*(int *) arg = 0); + + save_flags(flags); + cli(); + /* Compute number of bytes that have been played */ + count = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT); + if (count < dmap->fragment_size && dmap->qhead != 0) + count += dmap->bytes_in_use; /* Pointer wrap not handled yet */ + count += dmap->byte_counter; + + /* Substract current count from the number of bytes written by app */ + count = dmap->user_counter - count; + if (count < 0) + count = 0; + restore_flags (flags); + + return (*(int *) arg = count); + } + + case SNDCTL_DSP_POST: + if (audio_devs[dev]->dmap_out->qlen > 0) + if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE)) + DMAbuf_launch_output(dev, audio_devs[dev]->dmap_out); + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + { + int fragment_size; + struct dma_buffparms *dmap = dmap_out; + + if (audio_devs[dev]->open_mode & OPEN_WRITE) + reorganize_buffers(dev, dmap_out, + (audio_devs[dev]->open_mode == OPEN_READ)); + if (audio_devs[dev]->open_mode != OPEN_WRITE || + (audio_devs[dev]->flags & DMA_DUPLEX && + audio_devs[dev]->open_mode & OPEN_READ)) + reorganize_buffers(dev, dmap_in, + (audio_devs[dev]->open_mode == OPEN_READ)); + if (audio_devs[dev]->open_mode == OPEN_READ) + dmap = dmap_in; + fragment_size = dmap->fragment_size; + return (*(int *) arg = fragment_size); + } + + case SNDCTL_DSP_SETFRAGMENT: + { + int fact; + int ret = 0; + + fact = *(int *) arg; + if (audio_devs[dev]->open_mode & OPEN_WRITE) + ret = dma_set_fragment(dev, dmap_out, arg, fact); + if (ret < 0) + return ret; + + if (audio_devs[dev]->open_mode != OPEN_WRITE || + (audio_devs[dev]->flags & DMA_DUPLEX && + audio_devs[dev]->open_mode & OPEN_READ)) + ret = dma_set_fragment(dev, dmap_in, arg, fact); + + return ret; + } + break; + + default: + return audio_devs[dev]->d->ioctl(dev, cmd, arg); + } } diff -u --recursive --new-file v2.1.74/linux/drivers/sound/configure.c linux/drivers/sound/configure.c --- v2.1.74/linux/drivers/sound/configure.c Wed Dec 10 11:12:44 1997 +++ linux/drivers/sound/configure.c Sun Dec 21 17:41:24 1997 @@ -58,7 +58,8 @@ #define OPT_UNUSED5 23 #define OPT_YM3812_AUTO 24 #define OPT_YM3812 25 -#define OPT_LAST 25 /* Last defined OPT number */ +#define OPT_VMIDI 26 +#define OPT_LAST 27 /* Last defined OPT number */ #define DUMMY_OPTS (B(OPT_YM3812_AUTO)) @@ -66,12 +67,12 @@ B(OPT_MPU401)|B(OPT_PSS)|B(OPT_GUS16)|B(OPT_GUSMAX)| \ B(OPT_MSS)|B(OPT_SSCAPE)|B(OPT_UART6850)|B(OPT_TRIX)| \ B(OPT_MAD16)|B(OPT_CS4232)|B(OPT_MAUI)|B(OPT_ADLIB)| \ - B(OPT_SPNP)|B(OPT_OPL3SA1)|B(OPT_SOFTOSS)) + B(OPT_SPNP)|B(OPT_OPL3SA1)|B(OPT_SOFTOSS)|B(OPT_VMIDI)) #define MPU_DEVS (B(OPT_PSS)|\ B(OPT_CS4232)|B(OPT_SPNP)|B(OPT_MAUI)|B(OPT_SSCAPE)) #define UART401_DEVS (SBDSP_DEVS|B(OPT_TRIX)|B(OPT_MAD16)|B(OPT_SPNP)|\ B(OPT_OPL3SA1)) -#define NON_AUDIO_CARDS (B(OPT_ADLIB)|B(OPT_MPU401)|B(OPT_UART6850)|B(OPT_MAUI)) +#define NON_AUDIO_CARDS (B(OPT_ADLIB)|B(OPT_MPU401)|B(OPT_UART6850)|B(OPT_MAUI)|B(OPT_VMIDI)) #define AUDIO_CARDS (ANY_DEVS & ~NON_AUDIO_CARDS) #define MIDI_CARDS (ANY_DEVS & ~(B(OPT_ADLIB)|B(OPT_MSS))) #define AD1848_DEVS (B(OPT_GUS16)|B(OPT_MSS)|B(OPT_PSS)|B(OPT_GUSMAX)|\ @@ -144,7 +145,8 @@ {B(OPT_MPU401) | B(OPT_MAUI), 0, "UNUSED4", 0, 0, 0}, {MIDI_CARDS, 0, "UNUSED5", 1, 0, 1}, {B(OPT_ADLIB), 0, "YM3812_AUTO", 0, OPT_YM3812, 0}, - {B(OPT_PSS) | B(OPT_SB) | B(OPT_PAS) | B(OPT_ADLIB) | B(OPT_MSS) | B(OPT_PSS), B(OPT_YM3812_AUTO), "YM3812", 1, 0, 1} + {B(OPT_PSS) | B(OPT_SB) | B(OPT_PAS) | B(OPT_ADLIB) | B(OPT_MSS) | B(OPT_PSS), B(OPT_YM3812_AUTO), "YM3812", 1, 0, 1}, + {0, 0, "VMIDI", 1, 0, 0} }; char *questions[] = @@ -176,6 +178,7 @@ "*** Unused option 5 ***", "This should not be asked", "FM synthesizer (YM3812/OPL-3) support", + "Loopback MIDI device support", "Is the sky really falling" }; @@ -270,6 +273,8 @@ "This enables the Yamaha FM synthesizer chip used on many sound\n" "cards.\n", + + "This enable Loopback virtual MIDI device\n", "Is the sky really falling" }; diff -u --recursive --new-file v2.1.74/linux/drivers/sound/dev_table.h linux/drivers/sound/dev_table.h --- v2.1.74/linux/drivers/sound/dev_table.h Sat Nov 29 11:25:11 1997 +++ linux/drivers/sound/dev_table.h Sun Dec 21 17:41:24 1997 @@ -26,6 +26,7 @@ #define SNDCARD_OPL3SA1_SB 39 #define SNDCARD_OPL3SA1_MPU 40 #define SNDCARD_SOFTOSS 36 +#define SNDCARD_VMIDI 37 void attach_opl3sa_wss (struct address_info *hw_config); int probe_opl3sa_wss (struct address_info *hw_config); @@ -421,6 +422,10 @@ attach_softsyn_card, probe_softsyn, unload_softsyn}, #endif +#if defined(CONFIG_VMIDI) && defined(CONFIG_MIDI) && !defined(CONFIG_VMIDI_MODULE) + {"VMIDI", 0, SNDCARD_VMIDI,"Loopback MIDI Device", attach_v_midi, probe_v_midi, unload_v_midi}, +#endif + @@ -566,6 +571,11 @@ #if defined(CONFIG_YM3812) {SNDCARD_ADLIB, {FM_MONO, 0, 0, -1}, SND_DEFAULT_ENABLE}, #endif + +#if defined(CONFIG_VMIDI) && defined(CONFIG_MIDI) + {SNDCARD_VMIDI, {0, 0, 0, -1}, SND_DEFAULT_ENABLE}, +#endif + {0, {0}, 0} }; diff -u --recursive --new-file v2.1.74/linux/drivers/sound/dmabuf.c linux/drivers/sound/dmabuf.c --- v2.1.74/linux/drivers/sound/dmabuf.c Wed Dec 10 11:12:44 1997 +++ linux/drivers/sound/dmabuf.c Sun Dec 21 17:41:24 1997 @@ -1174,7 +1174,7 @@ if (!(audio_devs[dev]->flags & DMA_AUTOMODE)) dmap->flags &= ~DMA_ACTIVE; - while (dmap->qlen < 0) + while (dmap->qlen <= 0) { dmap->underrun_count++; diff -u --recursive --new-file v2.1.74/linux/drivers/sound/gus_card.c linux/drivers/sound/gus_card.c --- v2.1.74/linux/drivers/sound/gus_card.c Sat Nov 29 11:25:11 1997 +++ linux/drivers/sound/gus_card.c Sun Dec 21 17:41:24 1997 @@ -259,8 +259,10 @@ void cleanup_module(void) { +#if defined(CONFIG_GUS16) if (db16) unload_gus_db16(&config); +#endif unload_gus(&config); SOUND_LOCK_END; } diff -u --recursive --new-file v2.1.74/linux/drivers/sound/gus_wave.c linux/drivers/sound/gus_wave.c --- v2.1.74/linux/drivers/sound/gus_wave.c Sat Nov 29 11:25:11 1997 +++ linux/drivers/sound/gus_wave.c Sun Dec 21 17:41:24 1997 @@ -159,7 +159,7 @@ 19293 /* 32 */ }; -static struct patch_info *samples; +static struct patch_info *samples = NULL; static long sample_ptrs[MAX_SAMPLE + 1]; static int sample_map[32]; static int free_sample; @@ -3005,8 +3005,7 @@ return n; } -void -gus_wave_init(struct address_info *hw_config) +void gus_wave_init(struct address_info *hw_config) { unsigned long flags; unsigned char val; @@ -3025,16 +3024,19 @@ hw_config->slots[5] = -1; /* No mixer */ if (!gus_pnp_flag) + { if (irq < 0 || irq > 15) - { - printk("ERROR! Invalid IRQ#%d. GUS Disabled", irq); - return; - } + { + printk("ERROR! Invalid IRQ#%d. GUS Disabled", irq); + return; + } + } + if (dma < 0 || dma > 7 || dma == 4) - { - printk("ERROR! Invalid DMA#%d. GUS Disabled", dma); - return; - } + { + printk("ERROR! Invalid DMA#%d. GUS Disabled", dma); + return; + } gus_irq = irq; gus_dma = dma; gus_dma2 = dma2; @@ -3043,9 +3045,9 @@ gus_dma2 = dma; /* - * Try to identify the GUS model. - * - * Versions < 3.6 don't have the digital ASIC. Try to probe it first. + * Try to identify the GUS model. + * + * Versions < 3.6 don't have the digital ASIC. Try to probe it first. */ save_flags(flags); @@ -3055,126 +3057,128 @@ restore_flags(flags); if (gus_pnp_flag || (val != 0xff && (val & 0x06))) /* Should be 0x02?? */ - { - int ad_flags = 0; - - if (gus_pnp_flag) - ad_flags = 0x12345678; /* Interwave "magic" */ - /* - * It has the digital ASIC so the card is at least v3.4. - * Next try to detect the true model. - */ + { + int ad_flags = 0; - if (gus_pnp_flag) /* Hack hack hack */ - val = 10; - else - val = inb(u_MixSelect); - - /* - * Value 255 means pre-3.7 which don't have mixer. - * Values 5 thru 9 mean v3.7 which has a ICS2101 mixer. - * 10 and above is GUS MAX which has the CS4231 codec/mixer. - * + if (gus_pnp_flag) + ad_flags = 0x12345678; /* Interwave "magic" */ + /* + * It has the digital ASIC so the card is at least v3.4. + * Next try to detect the true model. */ - if (val == 255 || val < 5) - { - model_num = "3.4"; - gus_type = 0x34; - } else if (val < 10) - { - model_num = "3.7"; - gus_type = 0x37; - mixer_type = ICS2101; - request_region(u_MixSelect, 1, "GUS mixer"); - } else - { - model_num = "MAX"; - gus_type = 0x40; - mixer_type = CS4231; + if (gus_pnp_flag) /* Hack hack hack */ + val = 10; + else + val = inb(u_MixSelect); + + /* + * Value 255 means pre-3.7 which don't have mixer. + * Values 5 thru 9 mean v3.7 which has a ICS2101 mixer. + * 10 and above is GUS MAX which has the CS4231 codec/mixer. + * + */ + + if (val == 255 || val < 5) + { + model_num = "3.4"; + gus_type = 0x34; + } + else if (val < 10) + { + model_num = "3.7"; + gus_type = 0x37; + mixer_type = ICS2101; + request_region(u_MixSelect, 1, "GUS mixer"); + } + else + { + model_num = "MAX"; + gus_type = 0x40; + mixer_type = CS4231; #ifdef CONFIG_GUSMAX - { - unsigned char max_config = 0x40; /* Codec enable */ - - if (gus_dma2 == -1) - gus_dma2 = gus_dma; - - if (gus_dma > 3) - max_config |= 0x10; /* 16 bit capture DMA */ - - if (gus_dma2 > 3) - max_config |= 0x20; /* 16 bit playback DMA */ - - max_config |= (gus_base >> 4) & 0x0f; /* Extract the X from 2X0 */ - - outb((max_config), gus_base + 0x106); /* UltraMax control */ - } + { + unsigned char max_config = 0x40; /* Codec enable */ - if (ad1848_detect(gus_base + 0x10c, &ad_flags, hw_config->osp)) - { - char *name = "GUS MAX"; - int old_num_mixers = num_mixers; + if (gus_dma2 == -1) + gus_dma2 = gus_dma; - if (gus_pnp_flag) - name = "GUS PnP"; + if (gus_dma > 3) + max_config |= 0x10; /* 16 bit capture DMA */ - gus_mic_vol = gus_line_vol = gus_pcm_volume = 100; - gus_wave_volume = 90; - have_gus_max = 1; - if (hw_config->name) - name = hw_config->name; - - hw_config->slots[1] = ad1848_init(name, gus_base + 0x10c, - -irq, - gus_dma2, /* Playback DMA */ - gus_dma, /* Capture DMA */ - 1, /* Share DMA channels with GF1 */ - hw_config->osp); - - if (num_mixers > old_num_mixers) - { /* GUS has it's own mixer map */ - AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_SYNTH); - AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD); - AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE); - } - } else - printk("[Where's the CS4231?]"); + if (gus_dma2 > 3) + max_config |= 0x20; /* 16 bit playback DMA */ + + max_config |= (gus_base >> 4) & 0x0f; /* Extract the X from 2X0 */ + + outb((max_config), gus_base + 0x106); /* UltraMax control */ + } + + if (ad1848_detect(gus_base + 0x10c, &ad_flags, hw_config->osp)) + { + char *name = "GUS MAX"; + int old_num_mixers = num_mixers; + + if (gus_pnp_flag) + name = "GUS PnP"; + + gus_mic_vol = gus_line_vol = gus_pcm_volume = 100; + gus_wave_volume = 90; + have_gus_max = 1; + if (hw_config->name) + name = hw_config->name; + + hw_config->slots[1] = ad1848_init(name, gus_base + 0x10c, + -irq, gus_dma2, /* Playback DMA */ + gus_dma, /* Capture DMA */ + 1, /* Share DMA channels with GF1 */ + hw_config->osp); + + if (num_mixers > old_num_mixers) + { + /* GUS has it's own mixer map */ + AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_SYNTH); + AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD); + AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE); + } + } + else + printk(KERN_WARNING "[Where's the CS4231?]"); #else - printk("\n\n\nGUS MAX support was not compiled in!!!\n\n\n\n"); + printk("\n\n\nGUS MAX support was not compiled in!!!\n\n\n\n"); #endif - } - } else - { - /* - * ASIC not detected so the card must be 2.2 or 2.4. - * There could still be the 16-bit/mixer daughter card. - */ - } + } + } + else + { + /* + * ASIC not detected so the card must be 2.2 or 2.4. + * There could still be the 16-bit/mixer daughter card. + */ + } if (hw_config->name) - { - - strncpy(tmp, hw_config->name, 45); - tmp[45] = 0; - sprintf(tmp2, "%s (%dk)", tmp, (int) gus_mem_size / 1024); - tmp2[sizeof(tmp2) - 1] = 0; - } else if (gus_pnp_flag) - { - sprintf(tmp2, "Gravis UltraSound PnP (%dk)", - (int) gus_mem_size / 1024); - } else + { + strncpy(tmp, hw_config->name, 45); + tmp[45] = 0; + sprintf(tmp2, "%s (%dk)", tmp, (int) gus_mem_size / 1024); + tmp2[sizeof(tmp2) - 1] = 0; + } + else if (gus_pnp_flag) + { + sprintf(tmp2, "Gravis UltraSound PnP (%dk)", + (int) gus_mem_size / 1024); + } + else sprintf(tmp2, "Gravis UltraSound %s (%dk)", model_num, (int) gus_mem_size / 1024); - samples = (struct patch_info *) (sound_mem_blocks[sound_nblocks] = vmalloc((MAX_SAMPLE + 1) * sizeof(*samples))); - sound_mem_sizes[sound_nblocks] = (MAX_SAMPLE + 1) * sizeof(*samples); - if (sound_nblocks < 1024) - sound_nblocks++;; + samples = (struct patch_info *)vmalloc((MAX_SAMPLE + 1) * sizeof(*samples)); if (samples == NULL) - { - printk(KERN_WARNING "gus_init: Cant allocate memory for instrument tables\n"); - return; - } + { + printk(KERN_WARNING "gus_init: Cant allocate memory for instrument tables\n"); + return; + } conf_printf(tmp2, hw_config); tmp2[sizeof(gus_info.name) - 1] = 0; strcpy(gus_info.name, tmp2); @@ -3182,27 +3186,28 @@ if ((sdev = sound_alloc_synthdev()) == -1) printk(KERN_WARNING "gus_init: Too many synthesizers\n"); else - { - voice_alloc = &guswave_operations.alloc; - if (iw_mode) - guswave_operations.id = "IWAVE"; - hw_config->slots[0] = sdev; - synth_devs[sdev] = &guswave_operations; - sequencer_init(); + { + voice_alloc = &guswave_operations.alloc; + if (iw_mode) + guswave_operations.id = "IWAVE"; + hw_config->slots[0] = sdev; + synth_devs[sdev] = &guswave_operations; + sequencer_init(); #if defined(CONFIG_SEQUENCER) || defined(MODULE) - gus_tmr_install(gus_base + 8); + gus_tmr_install(gus_base + 8); #endif - } + } reset_sample_memory(); gus_initialize(); if (gus_mem_size > 0) + { if ((dev = sound_alloc_audiodev()) != -1) - { - hw_config->slots[4] = dev; - if ((gus_devnum = sound_install_audiodrv(AUDIO_DRIVER_VERSION, + { + hw_config->slots[4] = dev; + if ((gus_devnum = sound_install_audiodrv(AUDIO_DRIVER_VERSION, "Ultrasound", &gus_audio_driver, sizeof(struct audio_driver), @@ -3213,56 +3218,58 @@ NULL, dma, dma2)) < 0) - return; - - audio_devs[gus_devnum]->min_fragment = 9; /* 512k */ - audio_devs[gus_devnum]->max_fragment = 11; /* 8k (must match size of bounce_buf */ - audio_devs[gus_devnum]->mixer_dev = -1; /* Next mixer# */ - audio_devs[gus_devnum]->flags |= DMA_HARDSTOP; + { + return; + } + + audio_devs[gus_devnum]->min_fragment = 9; /* 512k */ + audio_devs[gus_devnum]->max_fragment = 11; /* 8k (must match size of bounce_buf */ + audio_devs[gus_devnum]->mixer_dev = -1; /* Next mixer# */ + audio_devs[gus_devnum]->flags |= DMA_HARDSTOP; } else - printk("GUS: Too many audio devices available\n"); - + printk(KERN_WARNING "GUS: Too many audio devices available\n"); + } + /* - * Mixer dependent initialization. + * Mixer dependent initialization. */ switch (mixer_type) - { - case ICS2101: - gus_mic_vol = gus_line_vol = gus_pcm_volume = 100; - gus_wave_volume = 90; - request_region(u_MixSelect, 1, "GUS mixer"); - hw_config->slots[5] = ics2101_mixer_init(); - audio_devs[gus_devnum]->mixer_dev = hw_config->slots[5]; /* Next mixer# */ - return; - - case CS4231: - /* Initialized elsewhere (ad1848.c) */ - default: - hw_config->slots[5] = gus_default_mixer_init(); - audio_devs[gus_devnum]->mixer_dev = hw_config->slots[5]; /* Next mixer# */ - return; - } + { + case ICS2101: + gus_mic_vol = gus_line_vol = gus_pcm_volume = 100; + gus_wave_volume = 90; + request_region(u_MixSelect, 1, "GUS mixer"); + hw_config->slots[5] = ics2101_mixer_init(); + audio_devs[gus_devnum]->mixer_dev = hw_config->slots[5]; /* Next mixer# */ + return; + + case CS4231: + /* Initialized elsewhere (ad1848.c) */ + default: + hw_config->slots[5] = gus_default_mixer_init(); + audio_devs[gus_devnum]->mixer_dev = hw_config->slots[5]; /* Next mixer# */ + return; + } } -void -gus_wave_unload(struct address_info *hw_config) +void gus_wave_unload(struct address_info *hw_config) { #ifdef CONFIG_GUSMAX if (have_gus_max) - { - ad1848_unload(gus_base + 0x10c, + { + ad1848_unload(gus_base + 0x10c, -gus_irq, gus_dma2, /* Playback DMA */ gus_dma, /* Capture DMA */ 1); /* Share DMA channels with GF1 */ - } + } #endif if (mixer_type == ICS2101) - { - release_region(u_MixSelect, 1); - } + { + release_region(u_MixSelect, 1); + } if (hw_config->slots[0] != -1) sound_unload_synthdev(hw_config->slots[0]); if (hw_config->slots[1] != -1) @@ -3273,10 +3280,13 @@ sound_unload_audiodev(hw_config->slots[4]); if (hw_config->slots[5] != -1) sound_unload_mixerdev(hw_config->slots[4]); + + if(samples) + vfree(samples); + samples=NULL; } -static void -do_loop_irq(int voice) +static void do_loop_irq(int voice) { unsigned char tmp; int mode, parm; diff -u --recursive --new-file v2.1.74/linux/drivers/sound/mad16.c linux/drivers/sound/mad16.c --- v2.1.74/linux/drivers/sound/mad16.c Wed Dec 10 11:12:44 1997 +++ linux/drivers/sound/mad16.c Sun Dec 21 17:41:24 1997 @@ -112,6 +112,7 @@ static int board_type = C928; static int *mad16_osp; +static int c931_detected; /* minor diferences from C930 */ #ifndef DDB #define DDB(x) @@ -225,6 +226,44 @@ DDB(printk("MC7 not writable2 (%x)\n", tmp)); return 0; } + + tmp = mad_read(MC0_PORT+18); + if (tmp == 0xff) + return 1; + /* We probably have a C931 */ + DDB(printk("Detected C931 config=0x%02x\n", tmp)); + c931_detected = 1; + + /* + * We cannot configure the chip if it is in PnP mode. + * If we have a CSN assigned (bit 8 in MC13) we first try + * a software reset, then a software power off, finally + * Clearing PnP mode. The last option is not + * Bit 8 in MC13 + */ + if ((mad_read(MC0_PORT+13) & 0x80) == 0) + return 1; + + /* Software reset */ + mad_write(MC9_PORT, 0x02); + mad_write(MC9_PORT, 0x00); + + if ((mad_read(MC0_PORT+13) & 0x80) == 0) + return 1; + + /* Power off, and on again */ + mad_write(MC9_PORT, 0xc2); + mad_write(MC9_PORT, 0xc0); + + if ((mad_read(MC0_PORT+13) & 0x80) == 0) + return 1; + + /* Force off PnP mode, This is not recommended because + * the PnP bios will not recognize the chip on the next + * warm boot and may assignd different resources to other + * PnP/PCI cards. + */ + mad_write(MC0_PORT+17, 0x04); return 1; } @@ -329,10 +368,20 @@ static int init_c930(struct address_info *hw_config) { - unsigned char cfg; + unsigned char cfg = 0; - cfg = (mad_read(MC1_PORT) & ~0x30); - /* mad_write(MC1_PORT, 0); */ +#ifdef MAD16_CONF + cfg |= (0x0f & MAD16_CONF); +#endif + + if(c931_detected) + { + /* Bit 0 has reversd meaning. Bits 1 and 2 sese + reversed on write. + Support only IDE cdrom. IDE port programmed + somewhere else. */ + cfg = (cfg & 0x09) ^ 0x07; + } switch (hw_config->io_base) { @@ -358,7 +407,14 @@ /* MC2 is CD configuration. Don't touch it. */ mad_write(MC3_PORT, 0); /* Disable SB mode IRQ and DMA */ - mad_write(MC4_PORT, 0x52); /* ??? */ +#ifdef MAD16_CDSEL + if(MAD16_CDSEL & 0x20) + mad_write(MC4_PORT, 0x66); /* opl4 */ + else + mad_write(MC4_PORT, 0x56); /* opl3 */ +#else + mad_write(MC4_PORT, 0x56); +#endif mad_write(MC5_PORT, 0x3C); /* Init it into mode2 */ mad_write(MC6_PORT, 0x02); /* Enable WSS, Disable MPU and SB */ mad_write(MC7_PORT, 0xCB); diff -u --recursive --new-file v2.1.74/linux/drivers/sound/midibuf.c linux/drivers/sound/midibuf.c --- v2.1.74/linux/drivers/sound/midibuf.c Tue Dec 2 09:49:39 1997 +++ linux/drivers/sound/midibuf.c Sun Dec 21 17:41:24 1997 @@ -528,4 +528,11 @@ { } +int +MIDIbuf_avail(int dev) +{ + return DATA_AVAIL (midi_in_buf[dev]); +} + + #endif diff -u --recursive --new-file v2.1.74/linux/drivers/sound/sb_audio.c linux/drivers/sound/sb_audio.c --- v2.1.74/linux/drivers/sound/sb_audio.c Sat Nov 29 11:25:11 1997 +++ linux/drivers/sound/sb_audio.c Sun Dec 21 17:41:24 1997 @@ -833,7 +833,7 @@ if (bits != 0) { - if (devc->bits == AFMT_U8 || bits == AFMT_S16_LE) + if (bits == AFMT_U8 || bits == AFMT_S16_LE) devc->bits = bits; else devc->bits = AFMT_U8; diff -u --recursive --new-file v2.1.74/linux/drivers/sound/softoss.h linux/drivers/sound/softoss.h --- v2.1.74/linux/drivers/sound/softoss.h Sat Nov 29 11:25:11 1997 +++ linux/drivers/sound/softoss.h Sun Dec 21 17:41:24 1997 @@ -119,7 +119,7 @@ /* * Programs */ - int programs[MAX_SAMPLE]; + int programs[MAX_PATCH]; /* * Timer parameters diff -u --recursive --new-file v2.1.74/linux/drivers/sound/sound_calls.h linux/drivers/sound/sound_calls.h --- v2.1.74/linux/drivers/sound/sound_calls.h Sat Nov 29 11:25:11 1997 +++ linux/drivers/sound/sound_calls.h Sun Dec 21 17:41:24 1997 @@ -74,6 +74,7 @@ int MIDIbuf_ioctl (int dev, struct fileinfo *file, unsigned int cmd, caddr_t arg); int MIDIbuf_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait); +int MIDIbuf_avail(int dev); void MIDIbuf_bytes_received(int dev, unsigned char *buf, int count); void MIDIbuf_init(void); @@ -285,4 +286,9 @@ /* From maui.c */ void attach_maui(struct address_info * hw_config); int probe_maui(struct address_info *hw_config); + +/* From v_midi.c */ +void attach_v_midi (struct address_info *hw_config); +int probe_v_midi (struct address_info *hw_config); +void unload_v_midi (struct address_info *hw_config); diff -u --recursive --new-file v2.1.74/linux/drivers/sound/sound_syms.c linux/drivers/sound/sound_syms.c --- v2.1.74/linux/drivers/sound/sound_syms.c Sat Nov 29 11:25:11 1997 +++ linux/drivers/sound/sound_syms.c Sun Dec 21 17:41:24 1997 @@ -17,11 +17,13 @@ EXPORT_SYMBOL(mixer_devs); EXPORT_SYMBOL(audio_devs); +EXPORT_SYMBOL(num_mixers); EXPORT_SYMBOL(num_audiodevs); EXPORT_SYMBOL(note_to_freq); EXPORT_SYMBOL(compute_finetune); EXPORT_SYMBOL(seq_copy_to_input); +EXPORT_SYMBOL(sequencer_init); EXPORT_SYMBOL(sequencer_timer); EXPORT_SYMBOL(sound_install_audiodrv); @@ -35,17 +37,25 @@ EXPORT_SYMBOL(sound_alloc_mixerdev); EXPORT_SYMBOL(sound_alloc_timerdev); EXPORT_SYMBOL(sound_alloc_synthdev); +EXPORT_SYMBOL(sound_mem_blocks); +EXPORT_SYMBOL(sound_mem_sizes); +EXPORT_SYMBOL(sound_nblocks); EXPORT_SYMBOL(sound_unload_audiodev); EXPORT_SYMBOL(sound_unload_mididev); EXPORT_SYMBOL(sound_unload_mixerdev); EXPORT_SYMBOL(sound_unload_timerdev); EXPORT_SYMBOL(sound_unload_synthdev); +EXPORT_SYMBOL(load_mixer_volumes); + EXPORT_SYMBOL(DMAbuf_start_dma); +EXPORT_SYMBOL(DMAbuf_open_dma); +EXPORT_SYMBOL(DMAbuf_close_dma); EXPORT_SYMBOL(DMAbuf_inputintr); EXPORT_SYMBOL(DMAbuf_outputintr); EXPORT_SYMBOL(dma_ioctl); +EXPORT_SYMBOL(conf_printf); EXPORT_SYMBOL(conf_printf2); EXPORT_SYMBOL(sound_timer_init); @@ -76,3 +86,4 @@ EXPORT_SYMBOL(midi_synth_setup_voice); EXPORT_SYMBOL(midi_synth_send_sysex); EXPORT_SYMBOL(midi_synth_bender); +EXPORT_SYMBOL(midi_synth_load_patch); diff -u --recursive --new-file v2.1.74/linux/drivers/sound/v_midi.c linux/drivers/sound/v_midi.c --- v2.1.74/linux/drivers/sound/v_midi.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/sound/v_midi.c Sun Dec 21 17:41:24 1997 @@ -0,0 +1,358 @@ +/* + * sound/sb_dsp.c + * + * The low level driver for the Sound Blaster DS chips. + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1996 + * + * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ +#include +#include + + + +#include "sound_config.h" +#include "soundmodule.h" + +#if defined(CONFIG_VMIDI) || defined(MODULE) + +#include "v_midi.h" + +static vmidi_devc *v_devc[2] = { NULL, NULL}; +static int midi1,midi2; + +#ifdef MODULE + +static struct address_info config; /* dummy */ + +int +init_module(void) +{ + printk("MIDI Loopback device driver\n"); + if (!probe_v_midi(&config)) + return -ENODEV; + attach_v_midi(&config); + SOUND_LOCK; + return 0; +} + +void +cleanup_module(void) +{ + unload_v_midi(&config); + SOUND_LOCK_END; +} + +#endif + +/* + * The DSP channel can be used either for input or output. Variable + * 'sb_irq_mode' will be set when the program calls read or write first time + * after open. Current version doesn't support mode changes without closing + * and reopening the device. Support for this feature may be implemented in a + * future version of this driver. + */ + + +void (*midi_input_intr) (int dev, unsigned char data); + +static int +v_midi_open (int dev, int mode, + void (*input) (int dev, unsigned char data), + void (*output) (int dev) +) +{ + vmidi_devc *devc = midi_devs[dev]->devc; + unsigned long flags; + + if (devc == NULL) + return -(ENXIO); + + save_flags (flags); + cli (); + if (devc->opened) + { + restore_flags (flags); + return -(EBUSY); + } + devc->opened = 1; + restore_flags (flags); + + devc->intr_active = 1; + + if (mode & OPEN_READ) + { + devc->input_opened = 1; + devc->midi_input_intr = input; + } + + return 0; +} + +static void +v_midi_close (int dev) +{ + vmidi_devc *devc = midi_devs[dev]->devc; + unsigned long flags; + + if (devc == NULL) + return; + + save_flags (flags); + cli (); + devc->intr_active = 0; + devc->input_opened = 0; + devc->opened = 0; + restore_flags (flags); +} + +static int +v_midi_out (int dev, unsigned char midi_byte) +{ + vmidi_devc *devc = midi_devs[dev]->devc; + vmidi_devc *pdevc = midi_devs[devc->pair_mididev]->devc; + + if (devc == NULL) + return -(ENXIO); + + if (pdevc->input_opened > 0){ + if (MIDIbuf_avail(pdevc->my_mididev) > 500) + return 0; + pdevc->midi_input_intr (pdevc->my_mididev, midi_byte); + } + + return 1; +} + +static int +v_midi_start_read (int dev) +{ + return 0; +} + +static int +v_midi_end_read (int dev) +{ + vmidi_devc *devc = midi_devs[dev]->devc; + + if (devc == NULL) + return -(ENXIO); + + devc->intr_active = 0; + return 0; +} + +static int +v_midi_ioctl (int dev, unsigned cmd, caddr_t arg) +{ + return -(EPERM); +} + + +#define MIDI_SYNTH_NAME "Loopback MIDI" +#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT + +#include "midi_synth.h" + +static struct midi_operations v_midi_operations = +{ + {"Loopback MIDI Port 1", 0, 0, SNDCARD_VMIDI}, + &std_midi_synth, + {0}, + v_midi_open, + v_midi_close, + v_midi_ioctl, + v_midi_out, + v_midi_start_read, + v_midi_end_read, + NULL, + NULL, + NULL, + NULL +}; + +static struct midi_operations v_midi_operations2 = +{ + {"Loopback MIDI Port 2", 0, 0, SNDCARD_VMIDI}, + &std_midi_synth, + {0}, + v_midi_open, + v_midi_close, + v_midi_ioctl, + v_midi_out, + v_midi_start_read, + v_midi_end_read, + NULL, + NULL, + NULL, + NULL +}; + +void +attach_v_midi (struct address_info *hw_config) +{ + + /* printk("Attaching v_midi device.....\n"); */ + if (sound_nblocks >= (1024 - 6)){ + printk("Sound: Loop Back MIDI: not enough sound driver memory block table\n"); + return; + } + + midi1 = sound_alloc_mididev(); + if (midi1 == -1) + { + printk ("Sound: Too many midi devices detected\n"); + return; + } + midi_devs[midi1] = (struct midi_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct midi_operations))); + sound_mem_sizes[sound_nblocks] = sizeof (struct midi_operations); + + if (sound_nblocks < 1024) + sound_nblocks++;; + if (midi_devs[midi1] == NULL) + { + printk (KERN_WARNING "Loop Back MIDI: Failed to allocate memory\n"); + sound_unload_mididev(midi1); + return; + } + + midi2 = sound_alloc_mididev(); + if (midi2 == -1) + { + printk ("Sound: Too many midi devices detected\n"); + sound_unload_mididev(midi1); + return; + } + + midi_devs[midi2] = (struct midi_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct midi_operations))); + sound_mem_sizes[sound_nblocks] = sizeof (struct midi_operations); + + if (sound_nblocks < 1024) + sound_nblocks++;; + if (midi_devs[midi2] == NULL) + { + printk (KERN_WARNING "Loop Back MIDI: Failed to allocate memory\n"); + sound_unload_mididev(midi1); + sound_unload_mididev(midi2); + return; + } + + /* printk("VMIDI1: %d VMIDI2: %d\n",midi1,midi2); */ + + /* for MIDI-1 */ + v_devc[0] = (struct vmidi_devc *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct vmidi_devc))); + sound_mem_sizes[sound_nblocks] = sizeof (struct vmidi_devc); + + if (sound_nblocks < 1024) + sound_nblocks++;; + if (v_devc[0] == NULL) + { + printk (KERN_WARNING "Loop Back MIDI: Failed to allocate memory\n"); + sound_unload_mididev(midi1); + sound_unload_mididev(midi2); + return; + } + + memcpy ((char *) midi_devs[midi1], (char *) &v_midi_operations, + sizeof (struct midi_operations)); + + v_devc[0]->my_mididev = midi1; + v_devc[0]->pair_mididev = midi2; + v_devc[0]->opened = v_devc[0]->input_opened = 0; + v_devc[0]->intr_active = 0; + v_devc[0]->midi_input_intr = NULL; + + midi_devs[midi1]->devc = v_devc[0]; + + midi_devs[midi1]->converter = (struct synth_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct synth_operations))); + sound_mem_sizes[sound_nblocks] = sizeof (struct synth_operations); + + if (sound_nblocks < 1024) + sound_nblocks++;; + + if (midi_devs[midi1]->converter == NULL) + { + printk (KERN_WARNING "Loop Back MIDI: Failed to allocate memory\n"); + sound_unload_mididev(midi1); + sound_unload_mididev(midi2); + return; + } + + std_midi_synth.midi_dev = midi1; + memcpy ((char *) midi_devs[midi1]->converter, (char *) &std_midi_synth, + sizeof (struct synth_operations)); + + midi_devs[midi1]->converter->id = "V_MIDI 1"; + + /* for MIDI-2 */ + v_devc[1] = (struct vmidi_devc *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct vmidi_devc))); + sound_mem_sizes[sound_nblocks] = sizeof (struct vmidi_devc); + + if (sound_nblocks < 1024) + sound_nblocks++;; + if (v_devc[1] == NULL) + { + printk (KERN_WARNING "Loop Back MIDI: Failed to allocate memory\n"); + sound_unload_mididev(midi1); + sound_unload_mididev(midi2); + return; + } + + + memcpy ((char *) midi_devs[midi2], (char *) &v_midi_operations2, + sizeof (struct midi_operations)); + + v_devc[1]->my_mididev = midi2; + v_devc[1]->pair_mididev = midi1; + v_devc[1]->opened = v_devc[1]->input_opened = 0; + v_devc[1]->intr_active = 0; + v_devc[1]->midi_input_intr = NULL; + + midi_devs[midi2]->devc = v_devc[1]; + + + midi_devs[midi2]->converter = (struct synth_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct synth_operations))); + sound_mem_sizes[sound_nblocks] = sizeof (struct synth_operations); + + if (sound_nblocks < 1024) + sound_nblocks++;; + + if (midi_devs[midi2]->converter == NULL) + { + printk (KERN_WARNING "Loop Back MIDI: Failed to allocate memory\n"); + sound_unload_mididev(midi1); + sound_unload_mididev(midi2); + return; + } + + std_midi_synth.midi_dev = midi2; + memcpy ((char *) midi_devs[midi2]->converter, (char *) &std_midi_synth, + sizeof (struct synth_operations)); + + midi_devs[midi2]->converter->id = "V_MIDI 2"; + + sequencer_init(); + /* printk("Attached v_midi device\n"); */ + +} + +int +probe_v_midi(struct address_info *hw_config) +{ + return(1); /* always OK */ +} + + +void +unload_v_midi(struct address_info *hw_config) +{ + sound_unload_mididev(midi1); + sound_unload_mididev(midi2); +; +} + +#endif diff -u --recursive --new-file v2.1.74/linux/drivers/sound/v_midi.h linux/drivers/sound/v_midi.h --- v2.1.74/linux/drivers/sound/v_midi.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/sound/v_midi.h Sun Dec 21 17:41:24 1997 @@ -0,0 +1,15 @@ +typedef struct vmidi_devc { + int dev; + + /* State variables */ + int opened; + + + /* MIDI fields */ + int my_mididev; + int pair_mididev; + int input_opened; + int intr_active; + void (*midi_input_intr) (int dev, unsigned char data); + } vmidi_devc; + diff -u --recursive --new-file v2.1.74/linux/fs/Config.in linux/fs/Config.in --- v2.1.74/linux/fs/Config.in Sun Dec 21 16:17:44 1997 +++ linux/fs/Config.in Sun Dec 21 17:27:18 1997 @@ -25,10 +25,6 @@ tristate 'NFS filesystem support' CONFIG_NFS_FS if [ "$CONFIG_NFS_FS" = "y" -a "$CONFIG_IP_PNP" = "y" ]; then bool ' Root file system on NFS' CONFIG_ROOT_NFS - if [ "$CONFIG_ROOT_NFS" = "y" ]; then - bool ' BOOTP support' CONFIG_RNFS_BOOTP - bool ' RARP support' CONFIG_RNFS_RARP - fi fi tristate 'NFS server support' CONFIG_NFSD if [ "$CONFIG_NFS_FS" = "y" -o "$CONFIG_NFSD" = "y" ]; then diff -u --recursive --new-file v2.1.74/linux/fs/attr.c linux/fs/attr.c --- v2.1.74/linux/fs/attr.c Wed Sep 3 20:52:43 1997 +++ linux/fs/attr.c Sun Dec 21 17:41:24 1997 @@ -41,8 +41,8 @@ if ((current->fsuid != inode->i_uid) && !fsuser()) goto error; /* Also check the setgid bit! */ - if (!fsuser() && !in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid : - inode->i_gid)) + if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid : + inode->i_gid) && !fsuser()) attr->ia_mode &= ~S_ISGID; } @@ -75,7 +75,7 @@ inode->i_ctime = attr->ia_ctime; if (ia_valid & ATTR_MODE) { inode->i_mode = attr->ia_mode; - if (!fsuser() && !in_group_p(inode->i_gid)) + if (!in_group_p(inode->i_gid) && !fsuser()) inode->i_mode &= ~S_ISGID; } mark_inode_dirty(inode); diff -u --recursive --new-file v2.1.74/linux/fs/binfmt_elf.c linux/fs/binfmt_elf.c --- v2.1.74/linux/fs/binfmt_elf.c Tue Dec 2 09:49:39 1997 +++ linux/fs/binfmt_elf.c Sat Dec 20 21:52:17 1997 @@ -1150,8 +1150,8 @@ notes[0].datasz = sizeof(prstatus); notes[0].data = &prstatus; prstatus.pr_info.si_signo = prstatus.pr_cursig = signr; - prstatus.pr_sigpend = current->signal; - prstatus.pr_sighold = current->blocked; + prstatus.pr_sigpend = current->signal.sig[0]; + prstatus.pr_sighold = current->blocked.sig[0]; psinfo.pr_pid = prstatus.pr_pid = current->pid; psinfo.pr_ppid = prstatus.pr_ppid = current->p_pptr->pid; psinfo.pr_pgrp = prstatus.pr_pgrp = current->pgrp; diff -u --recursive --new-file v2.1.74/linux/fs/buffer.c linux/fs/buffer.c --- v2.1.74/linux/fs/buffer.c Wed Dec 10 11:12:44 1997 +++ linux/fs/buffer.c Sun Dec 21 17:04:49 1997 @@ -263,6 +263,17 @@ sync_inodes(dev); sync_buffers(dev, 0); sync_dquots(dev, -1); + /* + * FIXME(eric) we need to sync the physical devices here. + * This is because some (scsi) controllers have huge amounts of + * cache onboard (hundreds of Mb), and we need to instruct + * them to commit all of the dirty memory to disk, and we should + * not return until this has happened. + * + * This would need to get implemented by going through the assorted + * layers so that each block major number can be synced, and this + * would call down into the upper and mid-layer scsi. + */ } int fsync_dev(kdev_t dev) diff -u --recursive --new-file v2.1.74/linux/fs/coda/Makefile linux/fs/coda/Makefile --- v2.1.74/linux/fs/coda/Makefile Tue Dec 2 16:45:19 1997 +++ linux/fs/coda/Makefile Sun Dec 21 14:45:13 1997 @@ -3,8 +3,8 @@ # O_TARGET := coda.o -O_OBJS := psdev.o upcall.o cnode.o super.o dir.o coda_linux.o symlink.o pioctl.o file.o namecache.o\ - sysctl.o +O_OBJS := psdev.o cache.o cnode.o super.o dir.o file.o upcall.o coda_linux.o\ + symlink.o pioctl.o sysctl.o M_OBJS := $(O_TARGET) # If you want debugging output, please uncomment the following line diff -u --recursive --new-file v2.1.74/linux/fs/coda/cnode.c linux/fs/coda/cnode.c --- v2.1.74/linux/fs/coda/cnode.c Tue Dec 2 16:45:19 1997 +++ linux/fs/coda/cnode.c Sun Dec 21 14:45:14 1997 @@ -12,15 +12,12 @@ extern int coda_debug; extern int coda_print_entry; -extern int coda_fetch_inode(struct inode *inode, struct coda_vattr *attr); /* cnode.c */ -struct cnode *coda_cnode_alloc(void); -void coda_cnode_free(struct cnode *); -int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb); +static struct cnode *coda_cnode_alloc(void); /* return pointer to new empty cnode */ -struct cnode *coda_cnode_alloc(void) +static struct cnode *coda_cnode_alloc(void) { struct cnode *result = NULL; @@ -31,7 +28,8 @@ } memset(result, 0, (int) sizeof(struct cnode)); - return result; + INIT_LIST_HEAD(&(result->c_cnhead)); + return result; } /* release cnode memory */ @@ -39,7 +37,30 @@ { CODA_FREE(cinode, sizeof(struct cnode)); } + +static void coda_fill_inode (struct inode *inode, struct coda_vattr *attr) +{ + CDEBUG(D_SUPER, "ino: %ld\n", inode->i_ino); + + if (coda_debug & D_SUPER ) + print_vattr(attr); + + coda_vattr_to_iattr(inode, attr); + + if (S_ISREG(inode->i_mode)) + inode->i_op = &coda_file_inode_operations; + else if (S_ISDIR(inode->i_mode)) + inode->i_op = &coda_dir_inode_operations; + else if (S_ISLNK(inode->i_mode)) + inode->i_op = &coda_symlink_inode_operations; + else { + printk ("coda_read_inode: what's this? i_mode = %o\n", + inode->i_mode); + inode->i_op = NULL; + } +} + /* this is effectively coda_iget: - get attributes (might be cached) - get the inode for the fid using vfs iget @@ -97,15 +118,9 @@ } CHECK_CNODE(cnp); - /* refresh the attributes */ - error = coda_fetch_inode(*inode, &attr); - if ( error ) { - printk("coda_cnode_make: fetch_inode returned %d\n", error); - clear_inode(*inode); - coda_cnode_free(cnp); - return -error; - } - CDEBUG(D_CNODE, "Done linking: ino %ld, at 0x%x with cnp 0x%x, cnp->c_vnode 0x%x\n", (*inode)->i_ino, (int) (*inode), (int) cnp, (int)cnp->c_vnode); + /* fill in the inode attributes */ + coda_fill_inode(*inode, &attr); + CDEBUG(D_CNODE, "Done linking: ino %ld, at 0x%x with cnp 0x%x, cnp->c_vnode 0x%x\n", (*inode)->i_ino, (int) (*inode), (int) cnp, (int)cnp->c_vnode); EXIT; return 0; @@ -122,49 +137,31 @@ } -/* compute the inode number from the FID - same routine as in vproc.cc (venus) - XXX look at the exceptional case of root fids etc -*/ -static ino_t -coda_fid2ino(ViceFid *fid) -{ - u_long ROOT_VNODE = 1; - u_long ROOT_UNIQUE = 1; - ViceFid nullfid = { 0, 0, 0}; - - if ( coda_fideq(fid, &nullfid) ) { - printk("coda_fid2ino: called with NULL Fid!\n"); - return 0; - } - - /* what do we return for the root fid */ - - /* Other volume root. We need the relevant mount point's - fid, but we don't know what that is! */ - if (fid->Vnode == ROOT_VNODE && fid->Unique == ROOT_UNIQUE) { - return(0); - } - - /* Non volume root. */ - return(fid->Unique + (fid->Vnode << 10) + (fid->Volume << 20)); -} /* convert a fid to an inode. Avoids having a hash table such as present in the Mach minicache */ -struct inode * -coda_fid2inode(ViceFid *fid, struct super_block *sb) { +struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb) { ino_t nr; struct inode *inode; struct cnode *cnp; - - nr = coda_fid2ino(fid); + char str[50]; +ENTRY; + + CDEBUG(D_INODE, "%s\n", coda_f2s(fid, str)); + nr = coda_f2i(fid); inode = iget(sb, nr); + if ( !inode ) { + printk("coda_fid_to_inode: null from iget, sb %p, nr %ld.\n", + sb, nr); + return NULL; + } + /* check if this inode is linked to a cnode */ cnp = (struct cnode *) inode->u.generic_ip; if ( cnp == NULL ) { iput(inode); + EXIT; return NULL; } /* make sure fid is the one we want */ diff -u --recursive --new-file v2.1.74/linux/fs/coda/coda_linux.c linux/fs/coda/coda_linux.c --- v2.1.74/linux/fs/coda/coda_linux.c Tue Dec 2 16:45:19 1997 +++ linux/fs/coda/coda_linux.c Sun Dec 21 14:45:14 1997 @@ -22,11 +22,11 @@ #include #include #include -#include +#include /* initialize the debugging variables */ -int coda_debug =815; -int coda_print_entry = 1; +int coda_debug = 0; +int coda_print_entry = 0; int coda_access_cache = 1; /* caller must allocate 36 byte string ! */ @@ -39,6 +39,14 @@ return s; } +int coda_iscontrol(const char *name, size_t length) +{ + if ((CFS_CONTROLLEN == length) && + (strncmp(name, CFS_CONTROL, CFS_CONTROLLEN) == 0)) + return 1; + return 0; +} + int coda_isroot(struct inode *i) { if ( i->i_sb->s_root->d_inode == i ) { @@ -47,11 +55,10 @@ return 0; } } + -void coda_load_creds(struct CodaCred *cred) +void coda_load_creds(struct coda_cred *cred) { - int i; - cred->cr_uid = (vuid_t) current->uid; cred->cr_euid = (vuid_t) current->euid; cred->cr_suid = (vuid_t) current->suid; @@ -61,10 +68,211 @@ cred->cr_egid = (vgid_t) current->egid; cred->cr_sgid = (vgid_t) current->sgid; cred->cr_fsgid = (vgid_t) current->fsgid; +} + +int coda_cred_ok(struct coda_cred *cred) +{ + return(current->fsuid == cred->cr_fsuid); +} + +int coda_cred_eq(struct coda_cred *cred1, struct coda_cred *cred2) +{ + return (cred1->cr_fsuid == cred2->cr_fsuid); +} + +unsigned short coda_flags_to_cflags(unsigned short flags) +{ + unsigned short coda_flags = 0; + + if ( flags & (O_RDONLY | O_RDWR) ) + coda_flags |= C_O_READ; + + if ( flags & (O_WRONLY | O_RDWR) ) + coda_flags |= C_O_WRITE; + + if ( flags & O_TRUNC ) + coda_flags |= C_O_TRUNC; + + return coda_flags; +} + - for ( i = 0 ; i < NGROUPS ; ++i ) { - cred->cr_groups[i] = (vgid_t) current->groups[i]; +/* utility functions below */ +void coda_vattr_to_iattr(struct inode *inode, struct coda_vattr *attr) +{ + int inode_type; + /* inode's i_dev, i_flags, i_ino are set by iget + XXX: is this all we need ?? + */ + switch (attr->va_type) { + case C_VNON: + inode_type = 0; + break; + case C_VREG: + inode_type = S_IFREG; + break; + case C_VDIR: + inode_type = S_IFDIR; + break; + case C_VLNK: + inode_type = S_IFLNK; + break; + default: + inode_type = 0; + } + inode->i_mode |= inode_type; + + if (attr->va_mode != (u_short) -1) + inode->i_mode = attr->va_mode | inode_type; + if (attr->va_uid != -1) + inode->i_uid = (uid_t) attr->va_uid; + if (attr->va_gid != -1) + inode->i_gid = (gid_t) attr->va_gid; + if (attr->va_nlink != -1) + inode->i_nlink = attr->va_nlink; + if (attr->va_size != -1) + inode->i_size = attr->va_size; + /* XXX This needs further study */ + /* + inode->i_blksize = attr->va_blocksize; + inode->i_blocks = attr->va_size/attr->va_blocksize + + (attr->va_size % attr->va_blocksize ? 1 : 0); + */ + if (attr->va_atime.tv_sec != -1) + inode->i_atime = attr->va_atime.tv_sec; + if (attr->va_mtime.tv_sec != -1) + inode->i_mtime = attr->va_mtime.tv_sec; + if (attr->va_ctime.tv_sec != -1) + inode->i_ctime = attr->va_ctime.tv_sec; +} +/* + * BSD sets attributes that need not be modified to -1. + * Linux uses the valid field to indicate what should be + * looked at. The BSD type field needs to be deduced from linux + * mode. + * So we have to do some translations here. + */ + +void coda_iattr_to_vattr(struct iattr *iattr, struct coda_vattr *vattr) +{ + umode_t mode; + unsigned int valid; + + /* clean out */ + vattr->va_mode = (umode_t) -1; + vattr->va_uid = (vuid_t) -1; + vattr->va_gid = (vgid_t) -1; + vattr->va_size = (off_t) -1; + vattr->va_atime.tv_sec = (time_t) -1; + vattr->va_mtime.tv_sec = (time_t) -1; + vattr->va_ctime.tv_sec = (time_t) -1; + vattr->va_atime.tv_nsec = (time_t) -1; + vattr->va_mtime.tv_nsec = (time_t) -1; + vattr->va_ctime.tv_nsec = (time_t) -1; + vattr->va_type = C_VNON; + vattr->va_fileid = (long)-1; + vattr->va_gen = (long)-1; + vattr->va_bytes = (long)-1; + vattr->va_nlink = (short)-1; + vattr->va_blocksize = (long)-1; + vattr->va_rdev = (dev_t)-1; + vattr->va_flags = 0; + + /* determine the type */ + mode = iattr->ia_mode; + if ( S_ISDIR(mode) ) { + vattr->va_type = C_VDIR; + } else if ( S_ISREG(mode) ) { + vattr->va_type = C_VREG; + } else if ( S_ISLNK(mode) ) { + vattr->va_type = C_VLNK; + } else { + /* don't do others */ + vattr->va_type = C_VNON; } + /* set those vattrs that need change */ + valid = iattr->ia_valid; + if ( valid & ATTR_MODE ) { + vattr->va_mode = iattr->ia_mode; + } + if ( valid & ATTR_UID ) { + vattr->va_uid = (vuid_t) iattr->ia_uid; + } + if ( valid & ATTR_GID ) { + vattr->va_gid = (vgid_t) iattr->ia_gid; + } + if ( valid & ATTR_SIZE ) { + vattr->va_size = iattr->ia_size; + } + if ( valid & ATTR_ATIME ) { + vattr->va_atime.tv_sec = iattr->ia_atime; + vattr->va_atime.tv_nsec = 0; + } + if ( valid & ATTR_MTIME ) { + vattr->va_mtime.tv_sec = iattr->ia_mtime; + vattr->va_mtime.tv_nsec = 0; + } + if ( valid & ATTR_CTIME ) { + vattr->va_ctime.tv_sec = iattr->ia_ctime; + vattr->va_ctime.tv_nsec = 0; + } + } + + +void print_vattr(struct coda_vattr *attr) +{ + char *typestr; + + switch (attr->va_type) { + case C_VNON: + typestr = "C_VNON"; + break; + case C_VREG: + typestr = "C_VREG"; + break; + case C_VDIR: + typestr = "C_VDIR"; + break; + case C_VBLK: + typestr = "C_VBLK"; + break; + case C_VCHR: + typestr = "C_VCHR"; + break; + case C_VLNK: + typestr = "C_VLNK"; + break; + case C_VSOCK: + typestr = "C_VSCK"; + break; + case C_VFIFO: + typestr = "C_VFFO"; + break; + case C_VBAD: + typestr = "C_VBAD"; + break; + default: + typestr = "????"; + break; + } + + printk("attr: type %s (%o) mode %o uid %d gid %d rdev %d\n", + typestr, (int)attr->va_type, (int)attr->va_mode, + (int)attr->va_uid, (int)attr->va_gid, (int)attr->va_rdev); + + printk(" fileid %d nlink %d size %d blocksize %d bytes %d\n", + (int)attr->va_fileid, (int)attr->va_nlink, + (int)attr->va_size, + (int)attr->va_blocksize,(int)attr->va_bytes); + printk(" gen %ld flags %ld\n", + attr->va_gen, attr->va_flags); + printk(" atime sec %d nsec %d\n", + (int)attr->va_atime.tv_sec, (int)attr->va_atime.tv_nsec); + printk(" mtime sec %d nsec %d\n", + (int)attr->va_mtime.tv_sec, (int)attr->va_mtime.tv_nsec); + printk(" ctime sec %d nsec %d\n", + (int)attr->va_ctime.tv_sec, (int)attr->va_ctime.tv_nsec); +} diff -u --recursive --new-file v2.1.74/linux/fs/coda/dir.c linux/fs/coda/dir.c --- v2.1.74/linux/fs/coda/dir.c Tue Dec 2 16:45:19 1997 +++ linux/fs/coda/dir.c Sun Dec 21 14:45:14 1997 @@ -1,5 +1,5 @@ /* - * Direcotry operations for Coda filesystem + * Directory operations for Coda filesystem * Original version: (C) 1996 P. Braam and M. Callahan * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University * @@ -22,7 +22,7 @@ #include #include #include -#include +#include /* dir inode-ops */ static int coda_create(struct inode *dir, struct dentry *new, int mode); @@ -43,8 +43,15 @@ /* support routines */ static int coda_venus_readdir(struct file *filp, void *dirent, filldir_t filldir); +int coda_fsync(struct file *, struct dentry *dentry); - +struct dentry_operations coda_dentry_operations = +{ + NULL, /* revalidate */ + NULL, /* hash */ + NULL, + coda_dentry_delete +}; struct inode_operations coda_dir_inode_operations = { @@ -80,7 +87,10 @@ NULL, /* mmap */ coda_open, /* open */ coda_release, /* release */ - NULL, /* fsync */ + coda_fsync, /* fsync */ + NULL, + NULL, + NULL }; @@ -88,9 +98,10 @@ /* acces routines: lookup, readlink, permission */ static int coda_lookup(struct inode *dir, struct dentry *entry) { - struct cnode *dircnp, *savedcnp; + struct cnode *dircnp; struct inode *res_inode = NULL; struct ViceFid resfid; + int dropme = 0; /* to indicate entry should not be cached */ int type; int error = 0; const char *name = entry->d_name.name; @@ -110,78 +121,56 @@ CHECK_CNODE(dircnp); if ( length > CFS_MAXNAMLEN ) { - printk("name too long: lookup, %s (%s)\n", - coda_f2s(&dircnp->c_fid, str), name); + printk("name too long: lookup, %s (%*s)\n", + coda_f2s(&dircnp->c_fid, str), length, name); return -ENAMETOOLONG; } - CDEBUG(D_INODE, "lookup: %s in %s\n", name, + CDEBUG(D_INODE, "lookup: %*s in %s\n", length, name, coda_f2s(&dircnp->c_fid, str)); /* control object, create inode on the fly */ - if ( coda_isroot(dir) && (CFS_CONTROLLEN == length) && - (strncmp(name, CFS_CONTROL, CFS_CONTROLLEN) == 0) ) { + if (coda_isroot(dir) && coda_iscontrol(name, length)) { error = coda_cnode_makectl(&res_inode, dir->i_sb); CDEBUG(D_SPECIAL, - "Lookup on CTL object; iput of ino %ld, count %d\n", + "Lookup on CTL object; dir ino %ld, count %d\n", dir->i_ino, dir->i_count); goto exit; } - /* do we have it already in name cache */ - if ( (savedcnp = cfsnc_lookup(dircnp, name, length)) != NULL ) { - CHECK_CNODE(savedcnp); - res_inode = CTOI(savedcnp); - iget(res_inode->i_sb, res_inode->i_ino); - CDEBUG(D_INODE, "cache hit for ino: %ld, count: %d!\n", - res_inode->i_ino, res_inode->i_count); - goto exit; - } - CDEBUG(D_INODE, "name not found in cache!\n"); - - /* name not cached */ error = venus_lookup(dir->i_sb, &(dircnp->c_fid), - (const char *)name, length, &type, &resfid); + (const char *)name, length, &type, &resfid); res_inode = NULL; - if (!error) { + if (!error || (error == -CFS_NOCACHE) ) { + if (error == -CFS_NOCACHE) + dropme = 1; error = coda_cnode_make(&res_inode, &resfid, dir->i_sb); if (error) - return -EACCES; - /* put the thing in the name cache */ - savedcnp = ITOC(res_inode); - CHECK_CNODE(savedcnp); - CDEBUG(D_INODE, "ABOUT to enter into cache.\n"); - cfsnc_enter(dircnp, name, length, savedcnp); - CDEBUG(D_INODE, "entered in cache\n"); + return -error; } else if (error != -ENOENT) { - CDEBUG(D_INODE, "error for %s(%s)%d\n", - coda_f2s(&dircnp->c_fid, str), name, error); + CDEBUG(D_INODE, "error for %s(%*s)%d\n", + coda_f2s(&dircnp->c_fid, str), length, name, error); return error; } - CDEBUG(D_INODE, "lookup: %s is (%s) type %d result %d\n", - name, coda_f2s(&resfid, str), type, error); - - /* at last we have our inode number from Venus, - now allocate storage for - the cnode and do iget, and fill in the attributes */ - + CDEBUG(D_INODE, "lookup: %s is (%s) type %d result %d, dropme %d\n", + name, coda_f2s(&resfid, str), type, error, dropme); exit: entry->d_time = 0; - entry->d_op = NULL; + entry->d_op = &coda_dentry_operations; d_add(entry, res_inode); + if ( dropme ) + d_drop(entry); EXIT; return 0; } - int coda_permission(struct inode *inode, int mask) { struct cnode *cp; int error; - int mode = inode->i_mode; char str[50]; ENTRY; @@ -191,16 +180,10 @@ return 0; } - /* we should be able to trust what is in the mode - although Venus should be told to return the - correct modes to the kernel */ - if ( coda_access_cache == 1 ) { - if (current->fsuid == inode->i_uid) - mode >>= 6; - else if (in_group_p(inode->i_gid)) - mode >>= 3; - if (((mode & mask & 0007) == mask) ) - return 0; + if ( coda_access_cache == 1 ) { + if ( coda_cache_check(inode, mask) ) { + return 0; + } } cp = ITOC(inode); @@ -212,6 +195,10 @@ CDEBUG(D_INODE, "fid: %s, ino: %ld (mask: %o) error: %d\n", coda_f2s(&(cp->c_fid), str), inode->i_ino, mask, error); + if ( error == 0 ) { + coda_cache_enter(inode, mask); + } + return error; } @@ -235,6 +222,10 @@ printk("coda_create: inode is null or not a directory\n"); return -ENOENT; } + + if (coda_isroot(dir) && coda_iscontrol(name, length)) + return -EPERM; + dircnp = ITOC(dir); CHECK_CNODE(dircnp); @@ -263,7 +254,7 @@ /* invalidate the directory cnode's attributes */ dircnp->c_flags &= ~C_VATTR; - cfsnc_zapfid(&(dircnp->c_fid)); +/* cfsnc_zapfid(&(dircnp->c_fid)); */ d_instantiate(de, result); return 0; @@ -280,15 +271,18 @@ struct ViceFid newfid; char fidstr[50]; + if (!dir || !S_ISDIR(dir->i_mode)) { printk("coda_mkdir: inode is NULL or not a directory\n"); return -ENOENT; } - dircnp = ITOC(dir); - CHECK_CNODE(dircnp); - if ( len > CFS_MAXNAMLEN ) return -ENAMETOOLONG; + if (coda_isroot(dir) && coda_iscontrol(name, len)) + return -EPERM; + + dircnp = ITOC(dir); + CHECK_CNODE(dircnp); CDEBUG(D_INODE, "mkdir %s (len %d) in %s, mode %o.\n", name, len, coda_f2s(&(dircnp->c_fid), fidstr), mode); @@ -312,7 +306,7 @@ /* invalidate the directory cnode's attributes */ dircnp->c_flags &= ~C_VATTR; - cfsnc_zapfid(&(dircnp->c_fid)); +/* cfsnc_zapfid(&(dircnp->c_fid)); */ dir->i_nlink++; d_instantiate(de, inode); @@ -330,6 +324,9 @@ ENTRY; + if (coda_isroot(dir_inode) && coda_iscontrol(name, len)) + return -EPERM; + dir_cnp = ITOC(dir_inode); CHECK_CNODE(dir_cnp); cnp = ITOC(inode); @@ -350,8 +347,8 @@ if ( ! error ) { dir_cnp->c_flags &= ~C_VATTR; - cfsnc_zapfid(&(dir_cnp->c_fid)); - cfsnc_zapfid(&(cnp->c_fid)); +/* cfsnc_zapfid(&(dir_cnp->c_fid)); */ +/* cfsnc_zapfid(&(cnp->c_fid)); */ inode->i_nlink++; d_instantiate(de, inode); @@ -362,9 +359,8 @@ } -static int -coda_symlink(struct inode *dir_inode, struct dentry *de, - const char *symname) +static int coda_symlink(struct inode *dir_inode, struct dentry *de, + const char *symname) { const char *name = de->d_name.name; int len = de->d_name.len; @@ -374,23 +370,23 @@ ENTRY; - error = -ENAMETOOLONG; - if ( len > CFS_MAXNAMLEN ) { - return error; - } + if (coda_isroot(dir_inode) && coda_iscontrol(name, len)) + return -EPERM; + + if ( len > CFS_MAXNAMLEN ) + return -ENAMETOOLONG; symlen = strlen(symname); - if ( symlen > CFS_MAXNAMLEN ) { - return error; - } + if ( symlen > CFS_MAXPATHLEN ) + return -ENAMETOOLONG; + CDEBUG(D_INODE, "symname: %s, length: %d\n", symname, symlen); error = venus_symlink(dir_inode->i_sb, &(dir_cnp->c_fid), name, len, symname, symlen); - if ( !error ) { - d_drop(de); - } + if ( !error ) + d_drop(de); CDEBUG(D_INODE, "in symlink result %d\n",error); EXIT; @@ -416,7 +412,7 @@ coda_f2s(&(dircnp->c_fid), fidstr), dir->i_ino); /* this file should no longer be in the namecache! */ - cfsnc_zapfile(dircnp, (const char *)name, len); +/* cfsnc_zapfile(dircnp, (const char *)name, len); */ error = venus_remove(dir->i_sb, &(dircnp->c_fid), name, len); @@ -427,7 +423,7 @@ /* cache management */ dircnp->c_flags &= ~C_VATTR; - cfsnc_zapfid(&(dircnp->c_fid)); +/* cfsnc_zapfid(&(dircnp->c_fid)); */ de->d_inode->i_nlink--; d_delete(de); @@ -452,8 +448,15 @@ if (len > CFS_MAXNAMLEN) return -ENAMETOOLONG; - /* this directory name should no longer be in the namecache */ - cfsnc_zapfile(dircnp, (const char *)name, len); + error = -EBUSY; + if (de->d_count > 1) { + /* Attempt to shrink child dentries ... */ + shrink_dcache_parent(de); + if (de->d_count > 1) + return error; + } + /* Drop the dentry to force a new lookup */ + d_drop(de); error = venus_rmdir(dir->i_sb, &(dircnp->c_fid), name, len); @@ -462,9 +465,6 @@ return error; } - dircnp->c_flags &= ~C_VATTR; - cfsnc_zapfid(&(dircnp->c_fid)); - dir->i_nlink--; d_delete(de); @@ -496,8 +496,8 @@ } /* the old file should go from the namecache */ - cfsnc_zapfile(old_cnp, (const char *)old_name, old_length); - cfsnc_zapfile(new_cnp, (const char *)new_name, new_length); +/* cfsnc_zapfile(old_cnp, (const char *)old_name, old_length); */ +/* cfsnc_zapfile(new_cnp, (const char *)new_name, new_length); */ /* cross directory moves */ if (new_dir != old_dir && @@ -585,19 +585,17 @@ int error = 0; struct inode *cont_inode = NULL; unsigned short flags = f->f_flags; + unsigned short coda_flags = coda_flags_to_cflags(flags); ENTRY; CDEBUG(D_SPECIAL, "OPEN inode number: %ld, flags %o.\n", f->f_dentry->d_inode->i_ino, flags); - if ( flags & O_CREAT ) { - flags &= ~O_EXCL; /* taken care of by coda_create ?? */ - } cnp = ITOC(i); CHECK_CNODE(cnp); - error = venus_open(i->i_sb, &(cnp->c_fid), flags, &ino, &dev); + error = venus_open(i->i_sb, &(cnp->c_fid), coda_flags, &ino, &dev); if (error) { CDEBUG(D_FILE, "venus: dev %d, inode %ld, out->result %d\n", dev, ino, error); @@ -618,14 +616,13 @@ CDEBUG(D_FILE, "GRAB: coda_inode_grab: ino %ld, ops at %x\n", cont_inode->i_ino, (int)cont_inode->i_op); cnp->c_ovp = cont_inode; - cnp->c_odentry.d_inode = cont_inode; } cnp->c_ocount++; /* if opened for writing flush cache entry. */ - if ( flags & (O_WRONLY | O_RDWR) ) { - cfsnc_zapfid(&(cnp->c_fid)); - } +/* if ( flags & (O_WRONLY | O_RDWR) ) { */ +/* cfsnc_zapfid(&(cnp->c_fid)); */ +/* } */ CDEBUG(D_FILE, "result %d, coda i->i_count is %d for ino %ld\n", error, i->i_count, i->i_ino); @@ -641,6 +638,7 @@ struct cnode *cnp; int error; unsigned short flags = f->f_flags; + unsigned short cflags = coda_flags_to_cflags(flags); ENTRY; @@ -664,7 +662,7 @@ --cnp->c_owrite; } - error = venus_release(i->i_sb, &(cnp->c_fid), flags); + error = venus_release(i->i_sb, &(cnp->c_fid), cflags); CDEBUG(D_FILE, "coda_release: result: %d\n", error); return error; @@ -693,6 +691,7 @@ struct venus_dirent *vdirent; struct getdents_callback *dents_callback; int string_offset; + int size; char debug[255]; ENTRY; @@ -703,8 +702,8 @@ dents_callback = (struct getdents_callback *) getdent; - count = dents_callback->count; - CODA_ALLOC(buff, void *, count); + size = count = dents_callback->count; + CODA_ALLOC(buff, void *, size); if ( ! buff ) { printk("coda_venus_readdir: out of memory.\n"); return -ENOMEM; @@ -764,6 +763,6 @@ } exit: - CODA_FREE(buff, count); + CODA_FREE(buff, size); return error; } diff -u --recursive --new-file v2.1.74/linux/fs/coda/file.c linux/fs/coda/file.c --- v2.1.74/linux/fs/coda/file.c Wed Dec 10 11:12:44 1997 +++ linux/fs/coda/file.c Sun Dec 21 14:45:14 1997 @@ -18,11 +18,11 @@ #include #include -#include #include #include #include #include +#include /* file operations */ static int coda_readpage(struct inode * inode, struct page * page); @@ -31,6 +31,8 @@ static int coda_file_mmap(struct file * file, struct vm_area_struct * vma); /* exported from this file */ +int coda_fsync(struct file *, struct dentry *dentry); + struct inode_operations coda_file_inode_operations = { &coda_file_operations, /* default file operations */ NULL, /* create */ @@ -64,7 +66,11 @@ coda_file_mmap, /* mmap */ coda_open, /* open */ coda_release, /* release */ - NULL, /* fsync */ + coda_fsync, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL /* lock */ }; /* File file operations */ @@ -181,8 +187,44 @@ return result; } +int coda_fsync(struct file *coda_file, struct dentry *coda_dentry) +{ + struct cnode *cnp; + struct inode *coda_inode = coda_dentry->d_inode; + struct inode *cont_inode = NULL; + struct file cont_file; + struct dentry cont_dentry; + int result = 0; + ENTRY; + + if (!(S_ISREG(coda_inode->i_mode) || S_ISDIR(coda_inode->i_mode) || + S_ISLNK(coda_inode->i_mode))) + return -EINVAL; + + cnp = ITOC(coda_inode); + CHECK_CNODE(cnp); + + cont_inode = cnp->c_ovp; + if ( cont_inode == NULL ) { + printk("coda_file_write: cached inode is 0!\n"); + return -1; + } + + coda_prepare_openfile(coda_inode, coda_file, cont_inode, + &cont_file, &cont_dentry); + + down(&cont_inode->i_sem); + + result = file_fsync(&cont_file ,&cont_dentry); + if ( result == 0 ) { + result = venus_fsync(coda_inode->i_sb, &(cnp->c_fid)); + } + up(&cont_inode->i_sem); + coda_restore_codafile(coda_inode, coda_file, cont_inode, &cont_file); + return result; +} /* * support routines */ diff -u --recursive --new-file v2.1.74/linux/fs/coda/namecache.c linux/fs/coda/namecache.c --- v2.1.74/linux/fs/coda/namecache.c Wed Dec 10 11:12:44 1997 +++ linux/fs/coda/namecache.c Wed Dec 31 16:00:00 1969 @@ -1,832 +0,0 @@ -/* - * Cache operations for Coda. - * Original version: (C) 1996 Peter Braam - * Rewritten for Linux 2.1: (C) 1997 Carnegie Mellon University - * - * Carnegie Mellon encourages users of this code to contribute improvements - * to the Coda project. Contact Peter Braam . - */ - -/* - * This module contains the routines to implement the CFS name cache. The - * purpose of this cache is to reduce the cost of translating pathnames - * into Vice FIDs. Each entry in the cache contains the name of the file, - * the vnode (FID) of the parent directory, and the cred structure of the - * user accessing the file. - * - * The first time a file is accessed, it is looked up by the local Venus - * which first insures that the user has access to the file. In addition - * we are guaranteed that Venus will invalidate any name cache entries in - * case the user no longer should be able to access the file. For these - * reasons we do not need to keep access list information as well as a - * cred structure for each entry. - * - * The table can be accessed through the routines cnc_init(), cnc_enter(), - * cnc_lookup(), cnc_rmfidcred(), cnc_rmfid(), cnc_rmcred(), and cnc_purge(). - * There are several other routines which aid in the implementation of the - * hash table. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -int cfsnc_use; - -static struct cfscache * cfsnc_find(struct cnode *dcp, const char * name, int namelen, int hash); -static void cfsnc_remove(struct cfscache *cncp); -static inline int nchash(const char *, int, struct cnode *); -static inline int ncmatch(struct cfscache *, const char *, int, - struct cnode *); -static inline void hashins(struct cfscache *a, struct cfscache *pred); -static inline void hashrem(struct cfscache *a); -static inline void hashnull(struct cfscache *); -static inline void lrurem(struct cfscache *a); -static inline void lruins(struct cfscache *a, struct cfscache *pred); -static void cfsnc_gather_stats(void); - - -/* externals */ -extern int coda_fideq(ViceFid *fid1, ViceFid *fid2); -extern int coda_debug; -extern int coda_print_entry; -extern struct super_block *coda_super_block; - - - -/* - * Declaration of the name cache data structure. - */ - -int cfsnc_use = 0; /* Indicate use of CFS Name Cache */ -int cfsnc_size = CFSNC_CACHESIZE; /* size of the cache */ -int cfsnc_hashsize = CFSNC_HASHSIZE; /* size of the primary hash */ -int cfsnc_flushme = 0; -int cfsnc_procsize = 0; -static int cfsnc_force = 0; - -struct cfshash { - struct cfscache *hash_next, *hash_prev; - int length; -}; - -struct cfslruhead { - struct cfscache *dummy1, *dummy2; - struct cfscache *lru_next, *lru_prev; -}; - -struct cfscache *cfsncheap; /* pointer to the cache entries */ -struct cfshash *cfsnchash; /* hash table of cfscache pointers */ -struct cfslruhead cfsnc_lru; /* head of lru chain; prev = lru */ - -struct cfsnc_statistics cfsnc_stat; /* Keep various stats */ - -#define TOTAL_CACHE_SIZE (sizeof(struct cfscache) * cfsnc_size) -#define TOTAL_HASH_SIZE (sizeof(struct cfshash) * cfsnc_hashsize) -int cfsnc_initialized = 0; /* Initially the cache has not been initialized */ - -/* - * for testing purposes - */ -int cfsnc_debug = 1; - - -/* - * Auxillary routines -- shouldn't be entry points - */ - - -/* - * Hash function for the primary hash. - * First try -- (first + last letters + length + (int)cp) mod size - * 2nd try -- same, except dir fid.vnode instead of cp - */ -static inline int -nchash(const char *name, int namelen, struct cnode *cp) -{ - return ((name[0] + name[namelen-1] + - namelen + (int)(cp)) & (cfsnc_hashsize-1)); -} - -/* matching function */ -static inline int ncmatch(struct cfscache *cp, const char *name, int namelen, - struct cnode *dcp) -{ - return ((namelen == cp->namelen) && (dcp == cp->dcp) && - (memcmp(cp->name,name,namelen) == 0)); -} - -/* insert a behind pred */ -static inline void hashins(struct cfscache *a, struct cfscache *pred) -{ - a->hash_next = pred->hash_next; - pred->hash_next->hash_prev= a; - pred->hash_next = a; - a->hash_prev = pred; -} - -static inline void hashrem(struct cfscache *a) -{ - a->hash_prev->hash_next = a->hash_next; - a->hash_next->hash_prev = a->hash_prev; -} - -static inline void hashnull(struct cfscache *elem) { - elem->hash_next = elem; - elem->hash_prev = elem; -} - -static inline void lrurem(struct cfscache *a) -{ - a->lru_prev->lru_next = a->lru_next; - a->lru_next->lru_prev = a->lru_prev; -} - -static inline void lruins(struct cfscache *a, struct cfscache *pred) -{ - pred->lru_next->lru_prev= a; - a->lru_next = pred->lru_next; - - a->lru_prev = pred; - pred->lru_next = a; -} - -static struct cfscache * -cfsnc_find(struct cnode *dcp, const char * name, int namelen, int hash) -{ - /* - * hash to find the appropriate bucket, look through the chain - * for the right entry - */ - register struct cfscache *cncp; - int count = 1; - - CDEBUG(D_CACHE, "dcp 0x%x, name %s, len %d, hash %d\n", - (int)dcp, name, namelen, hash); - - for (cncp = cfsnchash[hash].hash_next; - cncp != (struct cfscache *)&cfsnchash[hash]; - cncp = cncp->hash_next, count++) - { - - if (ncmatch(cncp, name, namelen, dcp)) - { - cfsnc_stat.Search_len += count; - CDEBUG(D_CACHE, "dcp 0x%x,found.\n", (int) dcp); - return(cncp); - - } - } - CDEBUG(D_CACHE, "dcp 0x%x,not found.\n", (int) dcp); - return((struct cfscache *)0); -} - -static void -cfsnc_remove(struct cfscache *cncp) -{ - /* - * remove an entry -- VN_RELE(cncp->dcp, cp), crfree(cred), - * remove it from it's hash chain, and - * place it at the head of the lru list. - */ - CDEBUG(D_CACHE, "remove %s from parent %lx.%lx.%lx\n", - cncp->name, (cncp->dcp)->c_fid.Volume, - (cncp->dcp)->c_fid.Vnode, (cncp->dcp)->c_fid.Unique); - - hashrem(cncp); - hashnull(cncp); /* have it be a null chain */ - - /* VN_RELE(CTOV(cncp->dcp)); */ - iput(CTOI(cncp->cp)); - /* crfree(cncp->cred); */ - - memset(DATA_PART(cncp), 0 ,DATA_SIZE); - cncp->cp = NULL; - cncp->dcp = (struct cnode *) 0; - - /* Put the null entry just after the least-recently-used entry */ - lrurem(cncp); - lruins(cncp, cfsnc_lru.lru_prev); -} - - -/* - * Entry points for the CFS Name Cache - */ - -/* - * Initialize the cache, the LRU structure and the Hash structure(s) - */ -void -cfsnc_init(void) -{ - register int i; - - /* zero the statistics structure */ - cfsnc_procsize = 10000 * cfsnc_hashsize + cfsnc_size; - memset(&cfsnc_stat, 0, (sizeof(struct cfsnc_statistics))); - - CODA_ALLOC(cfsncheap, struct cfscache *, TOTAL_CACHE_SIZE); - CODA_ALLOC(cfsnchash, struct cfshash *, TOTAL_HASH_SIZE); - - cfsnc_lru.lru_next = cfsnc_lru.lru_prev = (struct cfscache *)&cfsnc_lru; - - /* initialize the heap */ - for (i=0; i < cfsnc_size; i++) { - lruins(&cfsncheap[i], (struct cfscache *) &cfsnc_lru); - hashnull(&cfsncheap[i]); - cfsncheap[i].cp = cfsncheap[i].dcp = (struct cnode *)0; - } - - for (i=0; i < cfsnc_hashsize; i++) { /* initialize the hashtable */ - hashnull((struct cfscache *)&cfsnchash[i]); - cfsnchash[i].length=0; /* bucket length */ - } - - cfsnc_initialized = 1; - CDEBUG(D_CACHE, "cfsnc_initialized is now 1.\n"); -} - -/* - * Enter a new (dir cnode, name) pair into the cache, updating the - * LRU and Hash as needed. - */ - -void -cfsnc_enter(struct cnode *dcp, register const char *name, int namelen, struct cnode *cp) -{ - register struct cfscache *cncp; - register int hash; - - if (cfsnc_use == 0) /* Cache is off */ - return; - - CDEBUG(D_CACHE, "dcp 0x%x cp 0x%x name %s, ind 0x%x \n", - (int)dcp, (int)cp, name, (int)cp->c_vnode); - - if (namelen > CFSNC_NAMELEN) { - CDEBUG(D_CACHE, "long name enter %s\n",name); - cfsnc_stat.long_name_enters++; /* record stats */ - return; - } - - hash = nchash(name, namelen, dcp); - CDEBUG(D_CACHE, "Calling find with name %s, dcp %d, hash %d\n", - name, (int) dcp, (int) hash); - - cncp = cfsnc_find(dcp, name, namelen, hash); - if (cncp != (struct cfscache *) 0) { - printk("cfsnc_enter: Duplicate cache entry; tell Peter.\n"); - cfsnc_stat.dbl_enters++; /* duplicate entry */ - return; - } - - cfsnc_stat.enters++; /* record the enters statistic */ - - /* Grab the lru element in the lru chain */ - cncp = cfsnc_lru.lru_prev; - - lrurem(cncp); /* remove it from the lists */ - - /* if cncp is on hash list remove it */ - if ( cncp->dcp != (struct cnode *) 0 ) { - /* We have to decrement the appropriate hash bucket length - here, so we have to find the hash bucket */ - cfsnchash[nchash(cncp->name, cncp->namelen, cncp->dcp)].length--; - cfsnc_stat.lru_rm++; /* zapped a valid entry */ - hashrem(cncp); - iput(CTOI(cncp->cp)); - /* VN_RELE(CTOV(cncp->dcp)); */ - /* crfree(cncp->cred); */ - } - /* - * Put a hold on the current vnodes and fill in the cache entry. - */ - iget((CTOI(cp))->i_sb, CTOI(cp)->i_ino); - /* VN_HOLD(CTOV(dcp)); */ - /* XXXX crhold(cred); */ - cncp->dcp = dcp; - cncp->cp = cp; - cncp->namelen = namelen; - /* cncp->cred = cred; */ - - memcpy(cncp->name, name, (unsigned)namelen); - - /* Insert into the lru and hash chains. */ - - lruins(cncp, (struct cfscache *) &cfsnc_lru); - hashins(cncp, (struct cfscache *)&cfsnchash[hash]); - cfsnchash[hash].length++; /* Used for tuning */ - CDEBUG(D_CACHE, "Entering:\n"); - coda_print_ce(cncp); -} - -/* - * Find the (dir cnode, name) pair in the cache, if it's cred - * matches the input, return it, otherwise return 0 - */ - -struct cnode * -cfsnc_lookup(struct cnode *dcp, register const char *name, int namelen) -{ - register int hash; - register struct cfscache *cncp; - /* this should go into a callback funcntion for /proc/sys - don't know how at the moment? */ - if (cfsnc_flushme == 1) { - cfsnc_flush(); - cfsnc_flushme = 0; - } - - if (cfsnc_procsize != 10000*cfsnc_hashsize + cfsnc_size ) { - int hsh = cfsnc_procsize/10000; - int siz = cfsnc_procsize%10000; - int rc; - - if ( (hsh > 1) && (siz > 2) ) { - rc = cfsnc_resize(hsh, siz); - if ( !rc ) { - printk("Coda:cache size (hash,size) (%d,%d)\n", - hsh, siz); - } else { - printk("Coda: cache resize failed\n"); - } - } - } - - if (cfsnc_use == 0) /* Cache is off */ - return((struct cnode *) 0); - - if (namelen > CFSNC_NAMELEN) { - CDEBUG(D_CACHE,"long name lookup %s\n",name); - cfsnc_stat.long_name_lookups++; /* record stats */ - return((struct cnode *) 0); - } - - /* Use the hash function to locate the starting point, - then the search routine to go down the list looking for - the correct cred. - */ - - hash = nchash(name, namelen, dcp); - CDEBUG(D_CACHE, "Calling find with name %s, dcp %d, hash %d\n", - name, (int) dcp, (int) hash); - cncp = cfsnc_find(dcp, name, namelen, hash); - if (cncp == (struct cfscache *) 0) { - cfsnc_stat.misses++; /* record miss */ - return((struct cnode *) 0); - } - - cfsnc_stat.hits++; - - /* put this entry at the mru end of the LRU */ - lrurem(cncp); - lruins(cncp, (struct cfscache *) &cfsnc_lru); - - /* move it to the front of the hash chain */ - /* don't need to change the hash bucket length */ - hashrem(cncp); - hashins(cncp, (struct cfscache *) &cfsnchash[hash]); - - CDEBUG(D_CACHE, "lookup: dcp 0x%x, name %s, cp 0x%x\n", - (int) dcp, name, (int) cncp->cp); - - return(cncp->cp); -} - -/* - * Remove all entries with a parent which has the input fid. - */ - -void -cfsnc_zapParentfid(ViceFid *fid) -{ - /* To get to a specific fid, we might either have another hashing - function or do a sequential search through the cache for the - appropriate entries. The later may be acceptable since I don't - think callbacks or whatever Case 1 covers are frequent occurences. - */ - register struct cfscache *cncp, *ncncp; - register int i; - - if (cfsnc_use == 0) /* Cache is off */ - return; - - CDEBUG(D_CACHE, " fid 0x%lx, 0x%lx, 0x%lx \n", - fid->Volume, fid->Vnode, fid->Unique); - - cfsnc_stat.zapPfids++; - - for (i = 0; i < cfsnc_hashsize; i++) { - - /* - * Need to save the hash_next pointer in case we remove the - * entry. remove causes hash_next to point to itself. - */ - - for (cncp = cfsnchash[i].hash_next; - cncp != (struct cfscache *) &cfsnchash[i]; - cncp = ncncp) { - ncncp = cncp->hash_next; - if ( coda_fideq(&cncp->dcp->c_fid, fid) ) { - cfsnchash[i].length--; /* Used for tuning */ - cfsnc_remove(cncp); - } - } - } -} - -/* - * Remove all entries which have the same fid as the input - */ -void -cfsnc_zapfid(ViceFid *fid) -{ - /* See comment for zapParentfid. This routine will be used - if attributes are being cached. - */ - register struct cfscache *cncp, *ncncp; - register int i; - - if (cfsnc_use == 0) /* Cache is off */ - return; - - CDEBUG(D_CACHE, "Zapfid: fid 0x%lx, 0x%lx, 0x%lx \n", - fid->Volume, fid->Vnode, fid->Unique); - - cfsnc_stat.zapFids++; - - for (i = 0; i < cfsnc_hashsize; i++) { - for (cncp = cfsnchash[i].hash_next; - cncp != (struct cfscache *) &cfsnchash[i]; - cncp = ncncp) { - ncncp = cncp->hash_next; - if (coda_fideq(&(cncp->cp->c_fid), fid)) { - CDEBUG(D_CACHE, "Found cncp: name %s\n", cncp->name); - cfsnchash[i].length--; /* Used for tuning */ - cfsnc_remove(cncp); - } - } - } -} - - -/* - * Remove all entries which have the (dir vnode, name) pair - */ -void -cfsnc_zapfile(struct cnode *dcp, register const char *name, int length) -{ - /* use the hash function to locate the file, then zap all - entries of it regardless of the cred. - */ - register struct cfscache *cncp; - int hash; - - if (cfsnc_use == 0) /* Cache is off */ - return; - - CDEBUG(D_CACHE,"Zapfile: dcp 0x%x name %s \n", - (int) dcp, name); - - if (length > CFSNC_NAMELEN) { - cfsnc_stat.long_remove++; /* record stats */ - return; - } - - cfsnc_stat.zapFile++; - - hash = nchash(name, length, dcp); - /* remove entries: remember they might exist for more than a - single cred */ - while ( (cncp = cfsnc_find(dcp, name, length, hash)) != NULL ) { - cfsnchash[hash].length--; - cfsnc_remove(cncp); - } -} - -/* - * Remove all the entries for a particular user. Used when tokens expire. - * A user is determined by his/her effective user id (id_uid). - */ - -void -cfsnc_purge_user(struct CodaCred *cred) -{ - /* I think the best approach is to go through the entire cache - via HASH or whatever and zap all entries which match the - input cred. Or just flush the whole cache. - It might be best to go through on basis of LRU since cache - will almost always be full and LRU is more straightforward. - */ - - register struct cfscache *cncp; - int hash; - - if (cfsnc_use == 0) /* Cache is off */ - return; - - CDEBUG(D_CACHE,"ZapDude: uid %ld\n",cred->cr_uid); - cfsnc_stat.zapUsers++; - - for (cncp = cfsnc_lru.lru_next; - cncp != (struct cfscache *) &cfsnc_lru; - cncp = cncp->lru_next) { - - if ((CFSNC_VALID(cncp)) && - ((cncp->cred)->cr_uid == cred->cr_uid)) { - /* Seems really ugly, but we have to decrement the appropriate - hash bucket length here, so we have to find the hash bucket - */ - hash = nchash(cncp->name, cncp->namelen, cncp->dcp); - cfsnchash[hash].length--; /* For performance tuning */ - - cfsnc_remove(cncp); - } - } -} - -/* - * Flush the entire name cache. In response to a flush of the Venus cache. - */ - -void -cfsnc_flush(void) -{ - /* One option is to deallocate the current name cache and - call init to start again. Or just deallocate, then rebuild. - Or again, we could just go through the array and zero the - appropriate fields. - */ - - /* - * Go through the whole lru chain and kill everything as we go. - * I don't use remove since that would rebuild the lru chain - * as it went and that seemed unneccesary. - */ - register struct cfscache *cncp; - int i; - - if ((cfsnc_use == 0 || cfsnc_initialized == 0) && (cfsnc_force == 0) ) - return; - - cfsnc_stat.Flushes++; - - for (cncp = cfsnc_lru.lru_next; - cncp != (struct cfscache *) &cfsnc_lru; - cncp = cncp->lru_next) { - if ( cncp->cp ) { - hashrem(cncp); /* only zero valid nodes */ - hashnull(cncp); - iput(CTOI(cncp->cp)); - /* crfree(cncp->cred); */ - memset(DATA_PART(cncp), 0, DATA_SIZE); - } - } - - for (i = 0; i < cfsnc_hashsize; i++) - cfsnchash[i].length = 0; -} - -/* - * This routine replaces a ViceFid in the name cache with another. - * It is added to allow Venus during reintegration to replace - * locally allocated temp fids while disconnected with global fids - * even when the reference count on those fids are not zero. - */ -void -cfsnc_replace(ViceFid *f1, ViceFid *f2) -{ - /* - * Replace f1 with f2 throughout the name cache - */ - int hash; - register struct cfscache *cncp; - - CDEBUG(D_CACHE, - "cfsnc_replace fid_1 = (%lx.%lx.%lx) and fid_2 = (%lx.%lx.%lx)\n", - f1->Volume, f1->Vnode, f1->Unique, - f2->Volume, f2->Vnode, f2->Unique); - - for (hash = 0; hash < cfsnc_hashsize; hash++) { - for (cncp = cfsnchash[hash].hash_next; - cncp != (struct cfscache *) &cfsnchash[hash]; - cncp = cncp->hash_next) { - if (!memcmp(&cncp->cp->c_fid, f1, sizeof(ViceFid))) { - memcpy(&cncp->cp->c_fid, f2, sizeof(ViceFid)); - continue; /* no need to check cncp->dcp now */ - } - if (!memcmp(&cncp->dcp->c_fid, f1, sizeof(ViceFid))) - memcpy(&cncp->dcp->c_fid, f2, sizeof(ViceFid)); - } - } -} - -/* - * Debugging routines - */ - -/* - * This routine should print out all the hash chains to the console. - */ - -void -print_cfsnc(void) -{ - int hash; - register struct cfscache *cncp; - - for (hash = 0; hash < cfsnc_hashsize; hash++) { - printk("\nhash %d\n",hash); - - for (cncp = cfsnchash[hash].hash_next; - cncp != (struct cfscache *)&cfsnchash[hash]; - cncp = cncp->hash_next) { - printk("cp 0x%x dcp 0x%x cred 0x%x name %s ino %d count %d dev %d\n", - (int)cncp->cp, (int)cncp->dcp, - (int)cncp->cred, cncp->name, CTOI(cncp->cp)->i_count, CTOI(cncp->cp)->i_count, CTOI(cncp->cp)->i_dev); - } - } -} - -int -cfsnc_get_info(char *buffer, char **start, off_t offset, int length, int dummy) -{ - int hash; - int len=0; - off_t pos=0; - off_t begin; - struct cfscache *cncp; - char tmpbuf[80]; - - if (offset < 80) - len += sprintf(buffer, "%-79s\n", - "hash len volume vnode unique name ino pino ct"); - if ( !cfsnc_initialized ) { - *start = buffer; - return len; - } - pos = 80; - for (hash = 0; hash < cfsnc_hashsize; hash++) { - for (cncp = cfsnchash[hash].hash_next; - cncp != (struct cfscache *)&cfsnchash[hash]; - cncp = cncp->hash_next) { - pos += 80; - if (pos < offset) - continue; - sprintf(tmpbuf, "%4d %3d %8x %8x %8x %16s %10ld %10ld %2d", - hash, cfsnchash[hash].length, (int) cncp->cp->c_fid.Volume, - (int) cncp->cp->c_fid.Vnode, (int) cncp->cp->c_fid.Unique , cncp->name, - CTOI(cncp->cp)->i_ino, - CTOI(cncp->dcp)->i_ino, - CTOI(cncp->cp)->i_count); - len += sprintf(buffer+len, "%-79s\n", tmpbuf); - if(len >= length) - break; - } - if(len>= length) - break; - } - begin = len - (pos - offset); - *start = buffer + begin; - len -= begin; - if(len>length) - len = length; - return len; -} - -int -cfsnc_nc_info(char *buffer, char **start, off_t offset, int length, int dummy) -{ - int len=0; - off_t begin; - - cfsnc_gather_stats(); - - /* this works as long as we are below 1024 characters! */ - len += sprintf(buffer,"Coda minicache statistics\n\n"); - len += sprintf(buffer+len, "cfsnc_hits : %d\n", cfsnc_stat.hits); - len += sprintf(buffer+len, "cfsnc_misses : %d\n", cfsnc_stat.misses); - len += sprintf(buffer+len, "cfsnc_enters : %d\n", cfsnc_stat.enters); - len += sprintf(buffer+len, "cfsnc_dbl_enters : %d\n", cfsnc_stat.dbl_enters); - len += sprintf(buffer+len, "cfsnc_long_name_enters : %d\n", cfsnc_stat.long_name_enters); - len += sprintf(buffer+len, "cfsnc_long_name_lookups : %d\n", cfsnc_stat.long_name_lookups); - len += sprintf(buffer+len, "cfsnc_long_remove : %d\n", cfsnc_stat.long_remove); - len += sprintf(buffer+len, "cfsnc_lru_rm : %d\n", cfsnc_stat.lru_rm); - len += sprintf(buffer+len, "cfsnc_zapPfids : %d\n", cfsnc_stat.zapPfids); - len += sprintf(buffer+len, "cfsnc_zapFids : %d\n", cfsnc_stat.zapFids); - len += sprintf(buffer+len, "cfsnc_zapFile : %d\n", cfsnc_stat.zapFile); - len += sprintf(buffer+len, "cfsnc_zapUsers : %d\n", cfsnc_stat.zapUsers); - len += sprintf(buffer+len, "cfsnc_Flushes : %d\n", cfsnc_stat.Flushes); - len += sprintf(buffer+len, "cfsnc_SumLen : %d\n", cfsnc_stat.Sum_bucket_len); - len += sprintf(buffer+len, "cfsnc_Sum2Len : %d\n", cfsnc_stat.Sum2_bucket_len); - len += sprintf(buffer+len, "cfsnc_# 0 len : %d\n", cfsnc_stat.Num_zero_len); - len += sprintf(buffer+len, "cfsnc_MaxLen : %d\n", cfsnc_stat.Max_bucket_len); - len += sprintf(buffer+len, "cfsnc_SearchLen : %d\n", cfsnc_stat.Search_len); - begin = offset; - *start = buffer + begin; - len -= begin; - - if(len>length) - len = length; - if (len< 0) - len = 0; - return len; -} - - - -void -coda_print_ce(struct cfscache *ce) -{ -CDEBUG(D_CACHE, "cp 0x%x, dcp 0x%x, name %s, inod 0x%x, ino %d, count %d, dev %d\n", - (int)ce->cp, (int)ce->dcp, ce->name, (int)CTOI(ce->cp),(int)CTOI(ce->cp)->i_ino, CTOI(ce->cp)->i_count, CTOI(ce->cp)->i_dev); -} - -static void -cfsnc_gather_stats(void) -{ - int i, max = 0, sum = 0, temp, zeros = 0, ave, n; - - for (i = 0; i < cfsnc_hashsize; i++) { - if (cfsnchash[i].length) { - sum += cfsnchash[i].length; - } else { - zeros++; - } - - if (cfsnchash[i].length > max) - max = cfsnchash[i].length; - } - -/* - * When computing the Arithmetic mean, only count slots which - * are not empty in the distribution. - */ - cfsnc_stat.Sum_bucket_len = sum; - cfsnc_stat.Num_zero_len = zeros; - cfsnc_stat.Max_bucket_len = max; - - if ((n = cfsnc_hashsize - zeros) > 0) - ave = sum / n; - else - ave = 0; - - sum = 0; - for (i = 0; i < cfsnc_hashsize; i++) { - if (cfsnchash[i].length) { - temp = cfsnchash[i].length - ave; - sum += temp * temp; - } - } - cfsnc_stat.Sum2_bucket_len = sum; -} - -/* - * The purpose of this routine is to allow the hash and cache sizes to be - * changed dynamically. This should only be used in controlled environments, - * it makes no effort to lock other users from accessing the cache while it - * is in an improper state (except by turning the cache off). - */ -int -cfsnc_resize(int hashsize, int heapsize) -{ - if ( !cfsnc_use ) - return 0; - - if ((hashsize % 2) || (heapsize % 2)) { /* Illegal hash or cache sizes */ - return(EINVAL); - } - - cfsnc_use = 0; /* Turn the cache off */ - cfsnc_force = 1; /* otherwise we can't flush */ - - cfsnc_flush(); /* free any cnodes in the cache */ - cfsnc_force = 0; - - /* WARNING: free must happen *before* size is reset */ - CODA_FREE(cfsncheap,TOTAL_CACHE_SIZE); - CODA_FREE(cfsnchash,TOTAL_HASH_SIZE); - - cfsnc_hashsize = hashsize; - cfsnc_size = heapsize; - - cfsnc_init(); /* Set up a cache with the new size */ - - cfsnc_use = 1; /* Turn the cache back on */ - return(0); -} - - - diff -u --recursive --new-file v2.1.74/linux/fs/coda/pioctl.c linux/fs/coda/pioctl.c --- v2.1.74/linux/fs/coda/pioctl.c Tue Dec 2 16:45:19 1997 +++ linux/fs/coda/pioctl.c Sun Dec 21 14:45:14 1997 @@ -18,10 +18,10 @@ #include #include -#include #include #include #include +#include #include /* pioctl ops */ diff -u --recursive --new-file v2.1.74/linux/fs/coda/psdev.c linux/fs/coda/psdev.c --- v2.1.74/linux/fs/coda/psdev.c Wed Dec 10 11:12:44 1997 +++ linux/fs/coda/psdev.c Sun Dec 21 14:45:14 1997 @@ -35,35 +35,75 @@ #include #include #include +#include #include #include #include #include #include -#include +#include #include +/* + * Where is the prototype? + */ + +int proc_register_dynamic(struct proc_dir_entry * dir, + struct proc_dir_entry * dp); + /* * Coda stuff */ extern struct file_system_type coda_fs_type; -extern int coda_downcall(int opcode, struct outputArgs *out); extern int init_coda_fs(void); extern int cfsnc_get_info(char *buffer, char **start, off_t offset, int length, int dummy); extern int cfsnc_nc_info(char *buffer, char **start, off_t offset, int length, int dummy); /* statistics */ struct coda_upcallstats coda_callstats; - +int coda_hard = 0; /* introduces a timeout on upcalls */ +unsigned long coda_timeout = 10; /* .. secs, then signals will dequeue */ extern struct coda_sb_info coda_super_info[MAX_CODADEVS]; struct vcomm psdev_vcomm[MAX_CODADEVS]; -/* - * Device operations - */ +/* queue stuff for the messages */ +static inline void init_queue(struct queue *head) +{ + head->forw = head; + head->back = head; +} + +static inline struct vmsg *q_getnext(struct queue *elt) +{ + return (struct vmsg *)(elt->forw); +} +static inline int q_end(struct vmsg *msg, struct queue *queue) +{ + return (struct queue *)msg == queue; +} + +static inline int q_empty(struct queue *queue) +{ + return queue->forw == queue; +} + +/* insert before head, ie. at the tail */ +void coda_q_insert(struct queue *el, struct queue *head) +{ + el->forw = head->back->forw; + el->back = head->back; + head->back->forw = el; + head->back = el; +} + +void coda_q_remove(struct queue *el) +{ + el->forw->back = el->back; + el->back->forw = el->forw; +} static struct vcomm *coda_psdev2vcomm(struct file *file) { @@ -75,6 +115,10 @@ return vcp; } +/* + * Device operations + */ + static unsigned int coda_psdev_poll(struct file *file, poll_table * wait) { @@ -85,7 +129,7 @@ return -ENXIO; poll_wait(&(vcp->vc_waitq), wait); - if (!EMPTY(vcp->vc_pending)) + if (!q_empty(&(vcp->vc_pending))) mask |= POLLIN | POLLRDNORM; return mask; @@ -101,7 +145,6 @@ { struct vcomm *vcp = coda_psdev2vcomm(file); struct vmsg *vmp; - struct outputArgs *out; int error = 0; int size; u_long uniq; @@ -111,7 +154,7 @@ if (!vcp) return -ENXIO; - /* Peek at the opcode, unique id */ + /* Peek at the opcode, uniquefier */ if (copy_from_user(opcodebuf, buf, 2 * sizeof(u_long))) return -EFAULT; opcode = opcodebuf[0]; @@ -121,67 +164,69 @@ current->pid, opcode, uniq); if (DOWNCALL(opcode)) { - struct outputArgs pbuf; - + struct super_block *sb = NULL; + union outputArgs *dcbuf; + size = sizeof(*dcbuf); + + sb = vcp->vc_sb; + if ( !sb ) { + printk("coda_psdev_write: downcall, no SB!\n"); + return count; + } CDEBUG(D_PSDEV, "handling downcall\n"); - /* get the rest of the data. */ - size = sizeof(pbuf); - if ( count < sizeof(pbuf) ) { - printk("Coda: downcall opc %ld, uniq %ld, not enough!\n", + if ( count < sizeof(struct cfs_out_hdr) ) { + printk("coda_downcall opc %ld uniq %ld, not enough!\n", opcode, uniq); - size =count; - } else if ( count > sizeof(pbuf) ) { + return count; + } + CODA_ALLOC(dcbuf, union outputArgs *, size); + if ( count > size ) { printk("Coda: downcall opc %ld, uniq %ld, too much!", opcode, uniq); - size = sizeof(pbuf); + count = size; } - if (copy_from_user(&pbuf, buf, size)) + if (copy_from_user(dcbuf, buf, count)) return -EFAULT; - /* what errors for coda_downcall should be - * sent to Venus ? - */ - error = coda_downcall(opcode, &pbuf); + /* what downcall errors does Venus handle ? */ + error = coda_downcall(opcode, dcbuf, sb); + if ( error) { printk("psdev_write: coda_downcall error: %d\n", error); return 0; } + CODA_FREE(dcbuf, size); return count; } /* Look for the message on the processing queue. */ - for (vmp = (struct vmsg *)GETNEXT(vcp->vc_processing); - !EOQ(vmp, vcp->vc_processing); - vmp = (struct vmsg *)GETNEXT(vmp->vm_chain)) { - if (vmp->vm_unique == uniq) break; - CDEBUG(D_PSDEV,"Eureka: uniq %ld on queue!\n", uniq); + for (vmp = q_getnext(&(vcp->vc_processing)); + !q_end(vmp, &(vcp->vc_processing)); + vmp = q_getnext(&(vmp->vm_chain))) { + if (vmp->vm_unique == uniq) { + break; + CDEBUG(D_PSDEV,"Eureka: uniq %ld on queue!\n", uniq); + } } - if (EOQ(vmp, vcp->vc_processing)) { + if (q_end(vmp, &(vcp->vc_processing))) { printk("psdev_write: msg (%ld, %ld) not found\n", opcode, uniq); return(-ESRCH); } /* Remove the message from the processing queue */ - REMQUE(vmp->vm_chain); + coda_q_remove(&(vmp->vm_chain)); /* move data into response buffer. */ - /* Don't need to copy opcode and uniquifier. */ - out = (struct outputArgs *)vmp->vm_data; - /* get the rest of the data. */ if (vmp->vm_outSize < count) { - printk("Coda write: too much outs: %d, cnt: %d, opc: %ld, uniq: %ld.\n", - vmp->vm_outSize, count, opcode, uniq); - wake_up_interruptible(&vmp->vm_sleep); - return -EINVAL; - } else if (vmp->vm_outSize > count) { - printk("Coda write: too much outs: %d, cnt: %d, opc: %ld, uniq: %ld.\n", + printk("psdev_write: too much cnt: %d, cnt: %d, opc: %ld, uniq: %ld.\n", vmp->vm_outSize, count, opcode, uniq); + count = vmp->vm_outSize; /* don't have more space! */ } - if (copy_from_user(out, buf, count)) + if (copy_from_user(vmp->vm_data, buf, count)) return -EFAULT; /* adjust outsize. is this usefull ?? */ @@ -192,7 +237,7 @@ "Found! Count %d for (opc,uniq)=(%ld,%ld), vmsg at %x\n", count, opcode, uniq, (int)&vmp); - wake_up_interruptible(&vmp->vm_sleep); + wake_up(&vmp->vm_sleep); return(count); } @@ -201,7 +246,7 @@ */ static ssize_t coda_psdev_read(struct file * file, char * buf, - size_t count, loff_t *off) + size_t count, loff_t *off) { struct vcomm *vcp = coda_psdev2vcomm(file); struct vmsg *vmp; @@ -211,41 +256,39 @@ return -ENXIO; /* Get message at head of request queue. */ - if (EMPTY(vcp->vc_pending)) { - return 0; /* Nothing to read */ + if (q_empty(&(vcp->vc_pending))) { + return 0; } - vmp = (struct vmsg *)GETNEXT(vcp->vc_pending); - REMQUE(vmp->vm_chain); + vmp = q_getnext(&(vcp->vc_pending)); + coda_q_remove(&(vmp->vm_chain)); /* Move the input args into userspace */ - if (vmp->vm_inSize <= count) result = vmp->vm_inSize; if (count < vmp->vm_inSize) { - printk ("psdev_read: warning: venus read %d bytes of %d long - message\n",count, vmp->vm_inSize); + printk ("psdev_read: Venus read %d bytes of %d in message\n", + count, vmp->vm_inSize); } if ( copy_to_user(buf, vmp->vm_data, result)) return -EFAULT; if (vmp->vm_chain.forw == 0 || vmp->vm_chain.back == 0) - coda_panic("coda_psdev_read: bad chain"); + printk("coda_psdev_read: bad chain"); - /* If request was a signal, free up the message and don't - enqueue it in the reply queue. */ + /* If request was a signal, don't enqueue */ if (vmp->vm_opcode == CFS_SIGNAL) { CDEBUG(D_PSDEV, "vcread: signal msg (%d, %d)\n", vmp->vm_opcode, vmp->vm_unique); - CODA_FREE((caddr_t)vmp->vm_data, (u_int)VC_IN_NO_DATA); - CODA_FREE((caddr_t)vmp, (u_int)sizeof(struct vmsg)); + CODA_FREE(vmp->vm_data, sizeof(struct cfs_in_hdr)); + CODA_FREE(vmp, sizeof(struct vmsg)); return count; } vmp->vm_flags |= VM_READ; - INSQUE(vmp->vm_chain, vcp->vc_processing); + coda_q_insert(&(vmp->vm_chain), &(vcp->vc_processing)); return result; } @@ -254,22 +297,22 @@ static int coda_psdev_open(struct inode * inode, struct file * file) { register struct vcomm *vcp = NULL; - ENTRY; - vcp = coda_psdev2vcomm(file); + vcp =coda_psdev2vcomm(file); if (!vcp) - return -ENODEV; - memset(vcp, 0, sizeof(struct vcomm)); + return -ENODEV; - MOD_INC_USE_COUNT; + if (vcp->vc_inuse) + return -EBUSY; - INIT_QUEUE(vcp->vc_pending); - INIT_QUEUE(vcp->vc_processing); + memset(vcp, 0, sizeof(struct vcomm)); + vcp->vc_inuse = 1; + MOD_INC_USE_COUNT; - cfsnc_init(); - CDEBUG(D_PSDEV, "Name cache initialized.\n"); + init_queue(&(vcp->vc_pending)); + init_queue(&(vcp->vc_processing)); memset(&coda_callstats, 0, sizeof(struct coda_upcallstats)); EXIT; @@ -283,6 +326,7 @@ struct vcomm *vcp; struct vmsg *vmp; unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); + ENTRY; vcp = coda_psdev2vcomm(file); @@ -294,49 +338,43 @@ /* flush the name cache so that we can unmount */ CDEBUG(D_PSDEV, "Flushing the cache.\n"); - cfsnc_flush(); - cfsnc_use = 0; + /* cfsnc_flush(); */ + /* cfsnc_use = 0; */ CDEBUG(D_PSDEV, "Done.\n"); - /* prevent future operations on this vfs from succeeding by - * auto- unmounting any vfs mounted via this device. This - * frees user or sysadm from having to remember where all - * mount points are located. Put this before WAKEUPs to avoid - * queuing new messages between the WAKEUP and the unmount - * (which can happen if we're unlucky) */ - + /* if operations are in progress perhaps the kernel + can profit from setting the C_DYING flag on the root + cnode of Coda filesystems */ if (coda_super_info[minor].sbi_root) { struct cnode *cnp = ITOC(coda_super_info[minor].sbi_root); - /* Let unmount know this is for real */ cnp->c_flags |= C_DYING; - /* XXX Could we force an unmount here? */ - } + } else + vcp->vc_inuse = 0; /* Wakeup clients so they can return. */ - for (vmp = (struct vmsg *)GETNEXT(vcp->vc_pending); - !EOQ(vmp, vcp->vc_pending); - vmp = (struct vmsg *)GETNEXT(vmp->vm_chain)) { + for (vmp = q_getnext(&(vcp->vc_pending)); + !q_end(vmp, &(vcp->vc_pending)); + vmp = q_getnext(&(vmp->vm_chain))) { /* Free signal request messages and don't wakeup cause no one is waiting. */ if (vmp->vm_opcode == CFS_SIGNAL) { - CODA_FREE((caddr_t)vmp->vm_data, (u_int)VC_IN_NO_DATA); - CODA_FREE((caddr_t)vmp, (u_int)sizeof(struct vmsg)); + CODA_FREE(vmp->vm_data, sizeof(struct cfs_in_hdr)); + CODA_FREE(vmp, (u_int)sizeof(struct vmsg)); continue; } - - wake_up_interruptible(&vmp->vm_sleep); + wake_up(&vmp->vm_sleep); } - for (vmp = (struct vmsg *)GETNEXT(vcp->vc_processing); - !EOQ(vmp, vcp->vc_processing); - vmp = (struct vmsg *)GETNEXT(vmp->vm_chain)) { - wake_up_interruptible(&vmp->vm_sleep); + for (vmp = q_getnext(&(vcp->vc_processing)); + !q_end(vmp, &(vcp->vc_processing)); + vmp = q_getnext(&(vmp->vm_chain))) { + wake_up(&vmp->vm_sleep); } mark_vcomm_closed(vcp); - cfsnc_use = 0; - MOD_DEC_USE_COUNT; + MOD_DEC_USE_COUNT; + EXIT; return 0; } @@ -358,35 +396,16 @@ NULL /* lock */ }; -int init_coda_psdev(void) -{ - - if(register_chrdev(CODA_PSDEV_MAJOR,"coda_psdev", &coda_psdev_fops)) { - printk(KERN_ERR "coda_psdev: unable to get major %d\n", - CODA_PSDEV_MAJOR); - return -EIO; - } - - return 0; -} - #ifdef CONFIG_PROC_FS struct proc_dir_entry proc_coda = { 0, 4, "coda", - S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0, + S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR, 2, 0, 0, 0, &proc_net_inode_operations, }; -struct proc_dir_entry proc_coda_cache = { - 0 , 10, "coda-cache", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_net_inode_operations, - cfsnc_get_info - }; - struct proc_dir_entry proc_coda_ncstats = { 0 , 12, "coda-ncstats", S_IFREG | S_IRUGO, 1, 0, 0, @@ -396,25 +415,48 @@ #endif -#ifdef MODULE -int init_module(void) + +int init_coda_psdev(void) { - int status; - printk(KERN_INFO "Coda Kernel/User communications module 0.04\n"); + if(register_chrdev(CODA_PSDEV_MAJOR,"coda_psdev", &coda_psdev_fops)) { + printk(KERN_ERR "coda_psdev: unable to get major %d\n", + CODA_PSDEV_MAJOR); + return -EIO; + } + memset(psdev_vcomm, 0, sizeof(psdev_vcomm)); + memset(coda_super_info, 0, sizeof(coda_super_info)); + memset(&coda_callstats, 0, sizeof(coda_callstats)); #ifdef CONFIG_PROC_FS - proc_register(&proc_root,&proc_coda); - proc_register(&proc_coda, &proc_coda_cache); - proc_register(&proc_coda, &proc_coda_ncstats); - coda_sysctl_init(); + proc_register(&proc_root,&proc_coda); + proc_register(&proc_coda, &proc_coda_ncstats); + coda_sysctl_init(); #endif + return 0; +} + + +#ifdef MODULE + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Peter J. Braam "); - init_coda_psdev(); - - if ((status = init_coda_fs()) != 0) - { - printk("coda: failed in init_coda_fs!\n"); - } +int init_module(void) +{ + int status; + printk(KERN_INFO "Coda Kernel/User communications module 1.0\n"); + + status = init_coda_psdev(); + if ( status ) { + printk("Problem (%d) in init_coda_psdev\n", status); + return status; + } + + status = init_coda_fs(); + if (status) { + printk("coda: failed in init_coda_fs!\n"); + } return status; } @@ -425,20 +467,17 @@ ENTRY; - unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev"); - if ( (err = unregister_filesystem(&coda_fs_type)) != 0 ) { printk("coda: failed to unregister filesystem\n"); } + unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev"); + #if CONFIG_PROC_FS coda_sysctl_clean(); - proc_unregister(&proc_coda, proc_coda_cache.low_ino); proc_unregister(&proc_coda, proc_coda_ncstats.low_ino); proc_unregister(&proc_root, proc_coda.low_ino); #endif } #endif - - diff -u --recursive --new-file v2.1.74/linux/fs/coda/super.c linux/fs/coda/super.c --- v2.1.74/linux/fs/coda/super.c Tue Dec 2 16:45:20 1997 +++ linux/fs/coda/super.c Sun Dec 21 14:45:14 1997 @@ -1,9 +1,11 @@ /* * Super block/filesystem wide operations * - * Peter J. Braam , - * Michael Callahan Aug 1996 - * Rewritten for Linux 2.1.57 Peter Braam + * Copryright (C) 1996 Peter J. Braam and + * Michael Callahan + * + * Rewritten for Linux 2.1.?? Peter Braam + * Copyright (C) Carnegie Mellon University */ #define __NO_VERSION__ @@ -33,7 +35,7 @@ #include #include #include -#include +#include /* VFS super_block ops */ @@ -46,31 +48,11 @@ static int coda_statfs(struct super_block *sb, struct statfs *buf, int bufsiz); - /* helper functions */ -void print_vattr( struct coda_vattr *attr ); -static inline struct coda_sb_info *coda_psinode2sb(struct inode *inode); static inline struct vcomm *coda_psinode2vcomm(struct inode *inode); static int coda_get_psdev(void *, struct inode **); -static void coda_vattr_to_iattr(struct inode *, struct coda_vattr *); -static void coda_iattr_to_vattr(struct iattr *, struct coda_vattr *); -int coda_fetch_inode(struct inode *, struct coda_vattr *); - -extern inline struct vcomm *coda_psdev_vcomm(struct inode *inode); -extern int coda_cnode_make(struct inode **inode, ViceFid *fid, - struct super_block *sb); -extern struct cnode *coda_cnode_alloc(void); -extern void coda_cnode_free(struct cnode *); -char *coda_f2s(struct ViceFid *, char *); - -extern int cfsnc_initialized; -extern int coda_debug; -extern int coda_print_entry; - -extern struct inode_operations coda_file_inode_operations; -extern struct inode_operations coda_dir_inode_operations; -extern struct inode_operations coda_ioctl_inode_operations; -extern struct inode_operations coda_symlink_inode_operations; +static struct coda_sb_info *coda_psinode2sbi(struct inode *inode); + /* exported operations */ struct super_operations coda_super_operations = { @@ -91,37 +73,38 @@ struct coda_sb_info coda_super_info[MAX_CODADEVS]; - static struct super_block * coda_read_super(struct super_block *sb, void *data, int silent) { struct inode *psdev = 0, *root = 0; struct coda_sb_info *sbi = NULL; - struct vcomm *vc; + struct vcomm *vc = NULL; ViceFid fid; kdev_t dev = sb->s_dev; int error; char str[50]; -ENTRY; + ENTRY; MOD_INC_USE_COUNT; if (coda_get_psdev(data, &psdev)) - goto exit; + goto error; vc = coda_psinode2vcomm(psdev); if ( !vc ) - goto exit; + goto error; + vc->vc_sb = sb; + vc->vc_inuse = 1; - sbi = coda_psinode2sb(psdev); + sbi = coda_psinode2sbi(psdev); if ( !sbi ) - goto exit; - + goto error; sbi->sbi_psdev = psdev; sbi->sbi_vcomm = vc; + INIT_LIST_HEAD(&(sbi->sbi_cchead)); lock_super(sb); sb->u.generic_sbp = sbi; - sb->s_blocksize = 1024; /* XXXXX */ + sb->s_blocksize = 1024; /* XXXXX what do we put here?? */ sb->s_blocksize_bits = 10; sb->s_magic = CODA_SUPER_MAGIC; sb->s_dev = dev; @@ -129,22 +112,22 @@ /* get root fid from Venus: this needs the root inode */ error = venus_rootfid(sb, &fid); - if ( error ) { - unlock_super(sb); printk("coda_read_super: coda_get_rootfid failed with %d\n", - error); - goto exit; + error); + sb->s_dev = 0; + unlock_super(sb); + goto error; } printk("coda_read_super: rootfid is %s\n", coda_f2s(&fid, str)); + /* make root inode */ error = coda_cnode_make(&root, &fid, sb); if ( error || !root ) { printk("Failure of coda_cnode_make for root: error %d\n", error); - unlock_super(sb); sb->s_dev = 0; - root = NULL; - goto exit; + unlock_super(sb); + goto error; } printk("coda_read_super: rootinode is %ld dev %d\n", @@ -152,13 +135,20 @@ sbi->sbi_root = root; sb->s_root = d_alloc_root(root, NULL); unlock_super(sb); + EXIT; return sb; -EXIT; -exit: +error: +EXIT; MOD_DEC_USE_COUNT; - sbi->sbi_vcomm = NULL; - sbi->sbi_root = NULL; + if (sbi) { + sbi->sbi_vcomm = NULL; + sbi->sbi_root = NULL; + } + if ( vc ) { + vc->vc_sb = NULL; + vc->vc_inuse = 0; + } if (root) { iput(root); coda_cnode_free(ITOC(root)); @@ -167,7 +157,6 @@ return NULL; } - static void coda_put_super(struct super_block *sb) { struct coda_sb_info *sb_info; @@ -177,27 +166,31 @@ lock_super(sb); sb->s_dev = 0; + coda_cache_clear_all(sb); sb_info = coda_sbp(sb); + sb_info->sbi_vcomm->vc_inuse = 0; + sb_info->sbi_vcomm->vc_sb = NULL; + printk("Coda: Bye bye.\n"); memset(sb_info, 0, sizeof(* sb_info)); unlock_super(sb); MOD_DEC_USE_COUNT; -EXIT; + EXIT; } + /* all filling in of inodes postponed until lookup */ static void coda_read_inode(struct inode *inode) { - inode->u.generic_ip = NULL; - /* inode->i_blksize = inode->i_sb->s_blocksize; - inode->i_mode = 0; - inode->i_op = NULL; - NFS_CACHEINV(inode); */ + ENTRY; + inode->u.generic_ip = NULL; + return; } -static void coda_put_inode(struct inode *inode) +static void coda_put_inode(struct inode *in) { - CDEBUG(D_INODE,"ino: %ld, cnp: %x\n", inode->i_ino, - (int) inode->u.generic_ip); + ENTRY; + + CDEBUG(D_INODE,"ino: %ld, cnp: %p\n", in->i_ino, in->u.generic_ip); } static void coda_delete_inode(struct inode *inode) @@ -215,19 +208,20 @@ } cnp = ITOC(inode); - open_inode = cnp->c_ovp; if ( open_inode ) { CDEBUG(D_SUPER, "DELINO cached file: ino %ld count %d.\n", open_inode->i_ino, open_inode->i_count); cnp->c_ovp = NULL; - cnp->c_odentry.d_inode = NULL; - iput( open_inode ); + iput(open_inode); } + + coda_cache_clear_cnp(cnp); + inode->u.generic_ip = NULL; coda_cnode_free(cnp); clear_inode(inode); -EXIT; + EXIT; } static int coda_notify_change(struct inode *inode, struct iattr *iattr) @@ -235,20 +229,21 @@ struct cnode *cnp; struct coda_vattr vattr; int error; -ENTRY; + + ENTRY; memset(&vattr, 0, sizeof(vattr)); cnp = ITOC(inode); CHECK_CNODE(cnp); coda_iattr_to_vattr(iattr, &vattr); - vattr.va_type = VNON; /* cannot set type */ + vattr.va_type = C_VNON; /* cannot set type */ CDEBUG(D_SUPER, "vattr.va_mode %o\n", vattr.va_mode); error = venus_setattr(inode->i_sb, &cnp->c_fid, &vattr); if ( !error ) { coda_vattr_to_iattr(inode, &vattr); - cfsnc_zapfid(&(cnp->c_fid)); + coda_cache_clear_cnp(cnp); } CDEBUG(D_SUPER, "inode.i_mode %o, error %d\n", inode->i_mode, error); @@ -257,14 +252,12 @@ return error; } -/* we need _something_ */ +/* we need _something_ for this routine. Let's mimic AFS */ static int coda_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) { struct statfs tmp; -#define NB_SFS_SIZ 0x895440 - tmp.f_type = CODA_SUPER_MAGIC; tmp.f_bsize = 1024; tmp.f_blocks = 9000000; @@ -284,278 +277,38 @@ "coda", 0, coda_read_super, NULL }; - - - int init_coda_fs(void) { return register_filesystem(&coda_fs_type); } +/* MODULE stuff is in psdev.c */ -/* utility functions below */ -static void coda_vattr_to_iattr(struct inode *inode, struct coda_vattr *attr) -{ - int inode_type; - /* inode's i_dev, i_flags, i_ino are set by iget - XXX: is this all we need ?? - */ - switch (attr->va_type) { - case VNON: - inode_type = 0; - break; - case VREG: - inode_type = S_IFREG; - break; - case VDIR: - inode_type = S_IFDIR; - break; - case VLNK: - inode_type = S_IFLNK; - break; - default: - inode_type = 0; - } - inode->i_mode |= inode_type; - - if (attr->va_mode != (u_short) -1) - inode->i_mode = attr->va_mode | inode_type; - if (attr->va_uid != -1) - inode->i_uid = (uid_t) attr->va_uid; - if (attr->va_gid != -1) - inode->i_gid = (gid_t) attr->va_gid; - if (attr->va_nlink != -1) - inode->i_nlink = attr->va_nlink; - if (attr->va_size != -1) - inode->i_size = attr->va_size; - /* XXX This needs further study */ - /* - inode->i_blksize = attr->va_blocksize; - inode->i_blocks = attr->va_size/attr->va_blocksize - + (attr->va_size % attr->va_blocksize ? 1 : 0); - */ - if (attr->va_atime.tv_sec != -1) - inode->i_atime = attr->va_atime.tv_sec; - if (attr->va_mtime.tv_sec != -1) - inode->i_mtime = attr->va_mtime.tv_sec; - if (attr->va_ctime.tv_sec != -1) - inode->i_ctime = attr->va_ctime.tv_sec; -} -/* - * BSD sets attributes that need not be modified to -1. - * Linux uses the valid field to indicate what should be - * looked at. The BSD type field needs to be deduced from linux - * mode. - * So we have to do some translations here. - */ - -void coda_iattr_to_vattr(struct iattr *iattr, struct coda_vattr *vattr) -{ - umode_t mode; - unsigned int valid; - - /* clean out */ - vattr->va_mode = (umode_t) -1; - vattr->va_uid = (vuid_t) -1; - vattr->va_gid = (vgid_t) -1; - vattr->va_size = (off_t) -1; - vattr->va_atime.tv_sec = (time_t) -1; - vattr->va_mtime.tv_sec = (time_t) -1; - vattr->va_ctime.tv_sec = (time_t) -1; - vattr->va_atime.tv_nsec = (time_t) -1; - vattr->va_mtime.tv_nsec = (time_t) -1; - vattr->va_ctime.tv_nsec = (time_t) -1; - vattr->va_type = VNON; - vattr->va_fileid = (long)-1; - vattr->va_gen = (long)-1; - vattr->va_bytes = (long)-1; - vattr->va_fsid = (long)-1; - vattr->va_nlink = (short)-1; - vattr->va_blocksize = (long)-1; - vattr->va_rdev = (dev_t)-1; - vattr->va_flags = 0; - - /* determine the type */ - mode = iattr->ia_mode; - if ( S_ISDIR(mode) ) { - vattr->va_type = VDIR; - } else if ( S_ISREG(mode) ) { - vattr->va_type = VREG; - } else if ( S_ISLNK(mode) ) { - vattr->va_type = VLNK; - } else { - /* don't do others */ - vattr->va_type = VNON; - } - - /* set those vattrs that need change */ - valid = iattr->ia_valid; - if ( valid & ATTR_MODE ) { - vattr->va_mode = iattr->ia_mode; - } - if ( valid & ATTR_UID ) { - vattr->va_uid = (vuid_t) iattr->ia_uid; - } - if ( valid & ATTR_GID ) { - vattr->va_gid = (vgid_t) iattr->ia_gid; - } - if ( valid & ATTR_SIZE ) { - vattr->va_size = iattr->ia_size; - } - if ( valid & ATTR_ATIME ) { - vattr->va_atime.tv_sec = iattr->ia_atime; - vattr->va_atime.tv_nsec = 0; - } - if ( valid & ATTR_MTIME ) { - vattr->va_mtime.tv_sec = iattr->ia_mtime; - vattr->va_mtime.tv_nsec = 0; - } - if ( valid & ATTR_CTIME ) { - vattr->va_ctime.tv_sec = iattr->ia_ctime; - vattr->va_ctime.tv_nsec = 0; - } - -} - - -void print_vattr(struct coda_vattr *attr) -{ - char *typestr; - - switch (attr->va_type) { - case VNON: - typestr = "VNON"; - break; - case VREG: - typestr = "VREG"; - break; - case VDIR: - typestr = "VDIR"; - break; - case VBLK: - typestr = "VBLK"; - break; - case VCHR: - typestr = "VCHR"; - break; - case VLNK: - typestr = "VLNK"; - break; - case VSOCK: - typestr = "VSCK"; - break; - case VFIFO: - typestr = "VFFO"; - break; - case VBAD: - typestr = "VBAD"; - break; - default: - typestr = "????"; - break; - } - - - printk("attr: type %s (%o) mode %o uid %d gid %d fsid %d rdev %d\n", - typestr, (int)attr->va_type, (int)attr->va_mode, (int)attr->va_uid, - (int)attr->va_gid, (int)attr->va_fsid, (int)attr->va_rdev); - - printk(" fileid %d nlink %d size %d blocksize %d bytes %d\n", - (int)attr->va_fileid, (int)attr->va_nlink, - (int)attr->va_size, - (int)attr->va_blocksize,(int)attr->va_bytes); - printk(" gen %ld flags %ld vaflags %d\n", - attr->va_gen, attr->va_flags, attr->va_vaflags); - printk(" atime sec %d nsec %d\n", - (int)attr->va_atime.tv_sec, (int)attr->va_atime.tv_nsec); - printk(" mtime sec %d nsec %d\n", - (int)attr->va_mtime.tv_sec, (int)attr->va_mtime.tv_nsec); - printk(" ctime sec %d nsec %d\n", - (int)attr->va_ctime.tv_sec, (int)attr->va_ctime.tv_nsec); -} - -/* */ -int coda_fetch_inode (struct inode *inode, struct coda_vattr *attr) -{ - struct cnode *cp; - int ino, error=0; - CDEBUG(D_SUPER, "fetch for ino: %ld\n", inode->i_ino); - - ino = inode->i_ino; - if (!ino) - printk("coda_fetch_inode: inode called with i_ino = 0 (don't worry)\n"); - - inode->i_op = NULL; - inode->i_mode = 0; - - cp = ITOC(inode); - CHECK_CNODE(cp); - - /* root inode */ - if (cp->c_fid.Volume == 0 && - cp->c_fid.Vnode == 0 && - cp->c_fid.Unique == 0) { - inode->i_ino = 1; - inode->i_op = NULL; - return 0; - } - - if (IS_CTL_FID( &(cp->c_fid) )) { - /* This is the special magic control file. - Venus doesn't want - to hear a GETATTR about this! */ - inode->i_op = &coda_ioctl_inode_operations; - return 0; - } - - if ( ! attr ) { - printk("coda_fetch_inode: called with NULL vattr, ino %ld\n", - inode->i_ino); - return -1; /* XXX */ - } - - if (coda_debug & D_SUPER ) print_vattr(attr); - coda_vattr_to_iattr(inode, attr); - - if (S_ISREG(inode->i_mode)) - inode->i_op = &coda_file_inode_operations; - else if (S_ISDIR(inode->i_mode)) - inode->i_op = &coda_dir_inode_operations; - else if (S_ISLNK(inode->i_mode)) - inode->i_op = &coda_symlink_inode_operations; - else { - printk ("coda_read_inode: what kind of inode is this? i_mode = %o\n", inode->i_mode); - inode->i_op = NULL; - } - return error; -} - -static inline struct vcomm * -coda_psinode2vcomm(struct inode *inode) +/* helpers */ +static inline struct vcomm *coda_psinode2vcomm(struct inode *inode) { unsigned int minor = MINOR(inode->i_rdev); -CDEBUG(D_PSDEV,"minor %d\n", minor); + CDEBUG(D_PSDEV,"minor %d\n", minor); if ( minor < MAX_CODADEVS ) return &(psdev_vcomm[minor]); else return NULL; } -static inline struct coda_sb_info * -coda_psinode2sb(struct inode *inode) +static struct coda_sb_info *coda_psinode2sbi(struct inode *inode) { unsigned int minor = MINOR(inode->i_rdev); -CDEBUG(D_PSDEV,"minor %d\n", minor); - if ( minor < MAX_CODADEVS ) + CDEBUG(D_PSDEV,"minor %d\n", minor); + if ( (minor >= 0) && (minor < MAX_CODADEVS)) return &(coda_super_info[minor]); else return NULL; } -static int -coda_get_psdev(void *data, struct inode **res_dev) +/* name lookup for psdev passed in by mount */ +static int coda_get_psdev(void *data, struct inode **res_dev) { char **psdev_path; struct inode *psdev = 0; @@ -563,50 +316,46 @@ if ( ! data ) { - printk("coda_read_super: no data!\n"); - goto error; - } else { - psdev_path = data; - } + printk("coda_get_psdev: no data!\n"); + return 1; + } + + psdev_path = data; ent = namei((char *) *psdev_path); if (IS_ERR(ent)) { printk("namei error %ld for %d\n", PTR_ERR(ent), (int) psdev_path); - goto error; + return 1; } psdev = ent->d_inode; - if (!S_ISCHR(psdev->i_mode)) { printk("not a character device\n"); - goto error; + return 1; } -CDEBUG(D_PSDEV,"major %d, minor %d, count %d\n", MAJOR(psdev->i_rdev), - MINOR(psdev->i_rdev), psdev->i_count); + CDEBUG(D_PSDEV,"major %d, minor %d, count %d\n", + MAJOR(psdev->i_rdev), + MINOR(psdev->i_rdev), psdev->i_count); if (MAJOR(psdev->i_rdev) != CODA_PSDEV_MAJOR) { printk("device %d not a Coda PSDEV device\n", MAJOR(psdev->i_rdev)); - goto error; + return 1; } if (MINOR(psdev->i_rdev) >= MAX_CODADEVS) { printk("minor %d not an allocated Coda PSDEV\n", psdev->i_rdev); - goto error; + return 1; } if (psdev->i_count < 1) { printk("coda device minor %d not open (i_count = %d)\n", MINOR(psdev->i_rdev), psdev->i_count); - goto error; + return 1; } *res_dev = psdev; - + EXIT; return 0; - -EXIT; -error: - return 1; } diff -u --recursive --new-file v2.1.74/linux/fs/coda/symlink.c linux/fs/coda/symlink.c --- v2.1.74/linux/fs/coda/symlink.c Tue Dec 2 16:45:20 1997 +++ linux/fs/coda/symlink.c Sun Dec 21 14:45:14 1997 @@ -22,9 +22,10 @@ #include #include #include -#include +#include static int coda_readlink(struct inode *inode, char *buffer, int length); +static struct dentry *coda_follow_link(struct inode *, struct dentry *); struct inode_operations coda_symlink_inode_operations = { NULL, /* no file-operations */ @@ -38,7 +39,7 @@ NULL, /* mknod */ NULL, /* rename */ coda_readlink, /* readlink */ - NULL, /* follow_link */ + coda_follow_link, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ @@ -76,7 +77,43 @@ copy_to_user(buffer, buf, len); put_user('\0', buffer + len); error = len; - CODA_FREE(buf, len); } + if ( buf ) + CODA_FREE(buf, len); return error; +} + +static struct dentry *coda_follow_link(struct inode *inode, + struct dentry *base) +{ + int error; + struct cnode *cnp; + unsigned int len; + char mem[CFS_MAXPATHLEN]; + char *path; +ENTRY; + CDEBUG(D_INODE, "(%x/%ld)\n", inode->i_dev, inode->i_ino); + + cnp = ITOC(inode); + CHECK_CNODE(cnp); + + len = CFS_MAXPATHLEN; + error = venus_readlink(inode->i_sb, &(cnp->c_fid), mem, &len); + + if (error) { + dput(base); + return ERR_PTR(error); + } + len = strlen(mem); + path = kmalloc(len + 1, GFP_KERNEL); + if (!path) { + dput(base); + return ERR_PTR(-ENOMEM); + } + memcpy(path, mem, len); + path[len] = 0; + + base = lookup_dentry(path, base, 1); + kfree(path); + return base; } diff -u --recursive --new-file v2.1.74/linux/fs/coda/sysctl.c linux/fs/coda/sysctl.c --- v2.1.74/linux/fs/coda/sysctl.c Wed Dec 10 11:12:44 1997 +++ linux/fs/coda/sysctl.c Sun Dec 21 14:45:14 1997 @@ -8,6 +8,7 @@ */ /* sysctl entries for Coda! */ +#include #include #include #include @@ -21,14 +22,18 @@ #include #include -#include +#include +#include +#include +#include +#include #include extern int coda_debug; -extern int cfsnc_use; +/* extern int cfsnc_use; */ extern int coda_print_entry; -extern int cfsnc_flushme; +/* extern int cfsnc_flushme; */ extern int cfsnc_procsize; -extern void cfsnc_flush(void); +/* extern void cfsnc_flush(void); */ void coda_sysctl_init(void); void coda_sysctl_clean(void); @@ -38,20 +43,20 @@ struct ctl_table_header *fs_table_header, *coda_table_header; #define FS_CODA 1 /* Coda file system */ -#define CODA_DEBUG 1 /* control debugging */ -#define CODA_ENTRY 2 /* control enter/leave pattern */ -#define CODA_FLUSH 3 /* flush the cache on next lookup */ -#define CODA_MC 4 /* use/do not use the minicache */ -#define CODA_PROCSIZE 5 /* resize the cache on next lookup */ +#define CODA_DEBUG 1 /* control debugging */ +#define CODA_ENTRY 2 /* control enter/leave pattern */ +#define CODA_TIMEOUT 3 /* timeout on upcalls to become intrble */ +#define CODA_MC 4 /* use/do not use the access cache */ +#define CODA_HARD 5 /* mount type "hard" or "soft" */ static ctl_table coda_table[] = { {CODA_DEBUG, "debug", &coda_debug, sizeof(int), 0644, NULL, &coda_dointvec}, {CODA_ENTRY, "printentry", &coda_print_entry, sizeof(int), 0644, NULL, &coda_dointvec}, - {CODA_MC, "minicache", &cfsnc_use, sizeof(int), 0644, NULL, &coda_dointvec}, - {CODA_FLUSH, "flushme", &cfsnc_flushme, sizeof(int), 0644, NULL, &coda_dointvec}, - {CODA_PROCSIZE, "resize", &cfsnc_procsize, sizeof(int), 0644, NULL, &coda_dointvec}, + {CODA_MC, "accesscache", &coda_access_cache, sizeof(int), 0644, NULL, &coda_dointvec}, + {CODA_TIMEOUT, "timeout", &coda_timeout, sizeof(int), 0644, NULL, &coda_dointvec}, + {CODA_HARD, "hard", &coda_hard, sizeof(int), 0644, NULL, &coda_dointvec}, { 0 } }; diff -u --recursive --new-file v2.1.74/linux/fs/coda/upcall.c linux/fs/coda/upcall.c --- v2.1.74/linux/fs/coda/upcall.c Tue Dec 2 16:45:20 1997 +++ linux/fs/coda/upcall.c Sun Dec 21 14:45:14 1997 @@ -35,33 +35,53 @@ #include #include #include -#include +#include +#define UPARG(op)\ +do {\ + CODA_ALLOC(inp, union inputArgs *, insize);\ + outp = (union outputArgs *) (inp);\ + inp->ih.opcode = (op);\ + inp->ih.pid = current->pid;\ + inp->ih.pgid = current->pgrp;\ + coda_load_creds(&(inp->ih.cred));\ + outsize = insize;\ +} while (0) + +static inline int max(int a, int b) +{ + if ( a > b ) + return a; + else + return b; +} + +#define INSIZE(tag) sizeof(struct cfs_ ## tag ## _in) +#define OUTSIZE(tag) sizeof(struct cfs_ ## tag ## _out) +#define SIZE(tag) max(INSIZE(tag), OUTSIZE(tag)) -static vcsize = (sizeof(struct inputArgs) > sizeof(struct outputArgs)) ? - sizeof(struct inputArgs): sizeof(struct outputArgs); /* the upcalls */ int venus_rootfid(struct super_block *sb, ViceFid *fidp) { - struct inputArgs *inp; - struct outputArgs *outp; - int error=0; - int size; + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; ENTRY; - UPARG(vcsize, CFS_ROOT); - error = coda_upcall(coda_sbp(sb), VC_IN_NO_DATA, &size, inp); + insize = SIZE(root); + UPARG(CFS_ROOT); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); if (error) { printk("coda_get_rootfid: error %d\n", error); } else { - *fidp = (ViceFid) outp->d.cfs_root.VFid; + *fidp = (ViceFid) outp->cfs_root.VFid; CDEBUG(D_SUPER, "VolumeId: %lx, VnodeId: %lx.\n", fidp->Volume, fidp->Vnode); } - if (inp) CODA_FREE(inp, VC_IN_NO_DATA); + if (inp) CODA_FREE(inp, insize); EXIT; return -error; } @@ -69,19 +89,19 @@ int venus_getattr(struct super_block *sb, struct ViceFid *fid, struct coda_vattr *attr) { - struct inputArgs *inp; - struct outputArgs *outp; - int size, error; + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; ENTRY; - - UPARG(vcsize, CFS_GETATTR); - inp->d.cfs_getattr.VFid = *fid; - error = coda_upcall(coda_sbp(sb), vcsize, &size, inp); + insize = SIZE(getattr); + UPARG(CFS_GETATTR); + inp->cfs_getattr.VFid = *fid; + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); if ( !error ) - *attr = (struct coda_vattr) outp->d.cfs_getattr.attr; + *attr = outp->cfs_getattr.attr; - if (inp) CODA_FREE(inp, sizeof(struct inputArgs)); + if (inp) CODA_FREE(inp, insize); EXIT; return -error; } @@ -89,19 +109,20 @@ int venus_setattr(struct super_block *sb, struct ViceFid *fid, struct coda_vattr *vattr) { - struct inputArgs *inp; - struct outputArgs *outp; - int error, size; + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; - UPARG(vcsize, CFS_SETATTR); + insize= SIZE(setattr); + UPARG(CFS_SETATTR); - inp->d.cfs_setattr.VFid = *fid; - inp->d.cfs_setattr.attr = *vattr; + inp->cfs_setattr.VFid = *fid; + inp->cfs_setattr.attr = *vattr; - error = coda_upcall(coda_sbp(sb), vcsize, &size, inp); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - CDEBUG(D_SUPER, " result %ld\n", outp->result); - if ( inp ) CODA_FREE(inp, vcsize); + CDEBUG(D_SUPER, " result %d\n", error); + if ( inp ) CODA_FREE(inp, insize); return -error; } @@ -109,26 +130,26 @@ const char *name, int length, int * type, struct ViceFid *resfid) { - struct inputArgs *inp; - struct outputArgs *outp; - int insize, size, error=0, payload_offset; - - insize = VC_INSIZE(cfs_lookup_in) + CFS_MAXNAMLEN +1; - UPARG(insize, CFS_LOOKUP); + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int offset; + + offset = INSIZE(lookup); + insize = max(offset + length +1, OUTSIZE(lookup)); + UPARG(CFS_LOOKUP); - inp->d.cfs_lookup.VFid = *fid; + inp->cfs_lookup.VFid = *fid; + inp->cfs_lookup.name = offset; /* send Venus a null terminated string */ - payload_offset = VC_INSIZE(cfs_lookup_in); - inp->d.cfs_lookup.name = (char *) payload_offset; - memcpy((char *)inp + payload_offset, name, length); - *((char *)inp + payload_offset + length) = '\0'; + memcpy((char *)(inp) + offset, name, length); + *((char *)inp + offset + length) = '\0'; - size = payload_offset + length + 1; - error = coda_upcall(coda_sbp(sb), size, &size, inp); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); if ( !error ) { - *resfid = outp->d.cfs_lookup.VFid; - *type = outp->d.cfs_lookup.vtype; + *resfid = outp->cfs_lookup.VFid; + *type = outp->cfs_lookup.vtype; } if (inp) CODA_FREE(inp, insize); @@ -138,53 +159,48 @@ int venus_release(struct super_block *sb, struct ViceFid *fid, int flags) { - struct inputArgs *inp; - struct outputArgs *outp; - int size = sizeof(struct outputArgs); - int error = 0; - - CODA_ALLOC(inp, struct inputArgs *, sizeof(struct inputArgs)); - outp = (struct outputArgs *)inp; - INIT_IN(inp, CFS_CLOSE); - coda_load_creds(&(inp->cred)); + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + + insize = SIZE(close); + UPARG(CFS_CLOSE); - inp->d.cfs_close.VFid = *fid; - inp->d.cfs_close.flags = flags; + inp->cfs_close.VFid = *fid; + inp->cfs_close.flags = flags; - error = coda_upcall(coda_sbp(sb), size, &size, inp); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - if (inp) CODA_FREE(inp, sizeof(struct inputArgs)); + if (inp) + CODA_FREE(inp, insize); return -error; } int venus_open(struct super_block *sb, struct ViceFid *fid, int flags, ino_t *ino, dev_t *dev) { - struct inputArgs *inp = NULL; - struct outputArgs *outp = NULL; - int size = sizeof(struct inputArgs); - int error = 0; - - CODA_ALLOC(inp, struct inputArgs *, sizeof(struct inputArgs)); - outp = (struct outputArgs *)inp; - INIT_IN(inp, CFS_OPEN); - coda_load_creds(&(inp->cred)); + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + + insize = SIZE(open); + UPARG(CFS_OPEN); - inp->d.cfs_open.VFid = *fid; - inp->d.cfs_open.flags = flags; + inp->cfs_open.VFid = *fid; + inp->cfs_open.flags = flags; - error = coda_upcall(coda_sbp(sb), size, &size, inp); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); if ( !error ) { - *ino = outp->d.cfs_open.inode; - *dev = outp->d.cfs_open.dev; + *ino = outp->cfs_open.inode; + *dev = outp->cfs_open.dev; } else { *ino = 0; *dev = 0; } if (inp) - CODA_FREE(inp, sizeof(struct inputArgs)); + CODA_FREE(inp, insize); return -error; } @@ -193,69 +209,69 @@ const char *name, int length, struct ViceFid *newfid, struct coda_vattr *attrs) { - struct inputArgs *inp; - struct outputArgs *outp; - int error=0, size, payload_offset; - - payload_offset = VC_INSIZE(cfs_mkdir_in); - size = CFS_MAXNAMLEN + payload_offset; - UPARG(size, CFS_MKDIR); - - inp->d.cfs_mkdir.VFid = *dirfid; - inp->d.cfs_mkdir.attr = *attrs; - inp->d.cfs_mkdir.name = (char *) payload_offset; - + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int offset; + + offset = INSIZE(mkdir); + insize = max(offset + length + 1, OUTSIZE(mkdir)); + UPARG(CFS_MKDIR); + + inp->cfs_mkdir.VFid = *dirfid; + inp->cfs_mkdir.attr = *attrs; + inp->cfs_mkdir.name = offset; /* Venus must get null terminated string */ - memcpy((char *)inp + payload_offset, name, length); - *((char *)inp + payload_offset + length) = '\0'; - size = payload_offset + length + 1; + memcpy((char *)(inp) + offset, name, length); + *((char *)inp + offset + length) = '\0'; - error = coda_upcall(coda_sbp(sb), size, &size, inp); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - *attrs = outp->d.cfs_mkdir.attr; - *newfid = outp->d.cfs_mkdir.VFid; + *attrs = outp->cfs_mkdir.attr; + *newfid = outp->cfs_mkdir.VFid; if (inp) - CODA_FREE(inp, size); + CODA_FREE(inp, insize); return -error; } int venus_rename(struct super_block *sb, struct ViceFid *old_fid, struct ViceFid *new_fid, size_t old_length, - size_t new_length, const char *old_name, const char *new_name) + size_t new_length, const char *old_name, + const char *new_name) { - struct inputArgs *inp; - struct outputArgs *outp; - int error, offset, size, s; + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int offset, s; - size = 2*CFS_MAXNAMLEN + VC_INSIZE(cfs_rename_in) +8; - UPARG(size, CFS_RENAME); - - inp->d.cfs_rename.sourceFid = *old_fid; - inp->d.cfs_rename.destFid = *new_fid; - - offset = VC_INSIZE(cfs_rename_in); + offset = INSIZE(rename); + insize = max(offset + new_length + old_length + 8, + OUTSIZE(rename)); + UPARG(CFS_RENAME); + + inp->cfs_rename.sourceFid = *old_fid; + inp->cfs_rename.destFid = *new_fid; + inp->cfs_rename.srcname = offset; /* Venus must receive an null terminated string */ - inp->d.cfs_rename.srcname = (char *)offset; s = ( old_length & ~0x3) +4; /* round up to word boundary */ - memcpy((char *)inp + offset, old_name, old_length); + memcpy((char *)(inp) + offset, old_name, old_length); *((char *)inp + offset + old_length) = '\0'; /* another null terminated string for Venus */ offset += s; - inp->d.cfs_rename.destname = (char *)offset; + inp->cfs_rename.destname = offset; s = ( new_length & ~0x3) +4; /* round up to word boundary */ - memcpy((char *)inp + offset, new_name, new_length); + memcpy((char *)(inp) + offset, new_name, new_length); *((char *)inp + offset + new_length) = '\0'; - size += s; CDEBUG(D_INODE, "destname in packet: %s\n", - (char *)inp + (int) inp->d.cfs_rename.destname); - error = coda_upcall(coda_sbp(sb), size, &size, inp); + (char *)inp + (int) inp->cfs_rename.destname); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - if (inp) CODA_FREE(inp, size); + if (inp) CODA_FREE(inp, insize); return -error; } @@ -263,131 +279,138 @@ const char *name, int length, int excl, int mode, struct ViceFid *newfid, struct coda_vattr *attrs) { - struct inputArgs *inp; - struct outputArgs *outp; - int error=0, size, payload_offset; - - payload_offset = VC_INSIZE(cfs_create_in); - size = CFS_MAXNAMLEN + payload_offset; - UPARG(size, CFS_CREATE); - - inp->d.cfs_create.VFid = *dirfid; - inp->d.cfs_create.attr.va_mode = mode; - inp->d.cfs_create.excl = excl; - inp->d.cfs_create.mode = mode; - inp->d.cfs_create.name = (char *) payload_offset; + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int offset; + + offset = INSIZE(create); + insize = max(offset + length + 1, OUTSIZE(create)); + UPARG(CFS_CREATE); + + inp->cfs_create.VFid = *dirfid; + inp->cfs_create.attr.va_mode = mode; + inp->cfs_create.excl = excl; + inp->cfs_create.mode = mode; + inp->cfs_create.name = offset; /* Venus must get null terminated string */ - memcpy((char *)inp + payload_offset, name, length); - *((char *)inp + payload_offset + length) = '\0'; - size = payload_offset + length + 1; - - error = coda_upcall(coda_sbp(sb), size, &size, inp); + memcpy((char *)(inp) + offset, name, length); + *((char *)inp + offset + length) = '\0'; + + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - *attrs = outp->d.cfs_create.attr; - *newfid = outp->d.cfs_create.VFid; + *attrs = outp->cfs_create.attr; + *newfid = outp->cfs_create.VFid; if (inp) - CODA_FREE(inp, size); + CODA_FREE(inp, insize); return -error; } int venus_rmdir(struct super_block *sb, struct ViceFid *dirfid, const char *name, int length) { - struct inputArgs *inp; - struct outputArgs *outp; - int error=0, size, payload_offset; - - payload_offset = VC_INSIZE(cfs_rmdir_in); - size = CFS_MAXNAMLEN + payload_offset; - UPARG(size, CFS_RMDIR); - - inp->d.cfs_rmdir.VFid = *dirfid; - inp->d.cfs_rmdir.name = (char *) payload_offset; - memcpy((char *)inp + payload_offset, name, size); + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int offset; + + offset = INSIZE(rmdir); + insize = max(offset + length + 1, OUTSIZE(rmdir)); + UPARG(CFS_RMDIR); + + inp->cfs_rmdir.VFid = *dirfid; + inp->cfs_rmdir.name = offset; + memcpy((char *)(inp) + offset, name, length); + *((char *)inp + offset + length) = '\0'; - error = coda_upcall(coda_sbp(sb), size, &size, inp); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); if ( inp ) - CODA_FREE(inp, size); + CODA_FREE(inp, insize); return -error; } int venus_remove(struct super_block *sb, struct ViceFid *dirfid, const char *name, int length) { - struct inputArgs *inp; - struct outputArgs *outp; - int error=0, size, payload_offset; - - payload_offset = VC_INSIZE(cfs_remove_in); - size = CFS_MAXNAMLEN + payload_offset; - UPARG(size, CFS_REMOVE); - - inp->d.cfs_remove.VFid = *dirfid; - inp->d.cfs_remove.name = (char *)payload_offset; - memcpy((char *)inp + payload_offset, name, size); + union inputArgs *inp; + union outputArgs *outp; + int error=0, insize, outsize, offset; + + offset = INSIZE(remove); + insize = max(offset + length + 1, OUTSIZE(remove)); + UPARG(CFS_REMOVE); + + inp->cfs_remove.VFid = *dirfid; + inp->cfs_remove.name = offset; + memcpy((char *)(inp) + offset, name, length); + *((char *)inp + offset + length) = '\0'; - error = coda_upcall(coda_sbp(sb), size, &size, inp); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); if ( inp ) - CODA_FREE(inp, size); + CODA_FREE(inp, insize); return -error; } int venus_readlink(struct super_block *sb, struct ViceFid *fid, char *buffer, int *length) { - int error, size, retlen; + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int retlen; char *result; - struct inputArgs *inp; - struct outputArgs *outp; - char *buf=NULL; /*[CFS_MAXNAMLEN + VC_INSIZE(cfs_readlink_in)];*/ - size = CFS_MAXPATHLEN + VC_INSIZE(cfs_readlink_in); - UPARG(size, CFS_READLINK); - inp->d.cfs_readlink.VFid = *fid; + insize = max(INSIZE(readlink), OUTSIZE(readlink)+ *length + 1); + UPARG(CFS_READLINK); + + inp->cfs_readlink.VFid = *fid; - error = coda_upcall(coda_sbp(sb), size, &size, inp); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); if (! error) { - retlen = outp->d.cfs_readlink.count; + retlen = outp->cfs_readlink.count; if ( retlen > *length ) retlen = *length; *length = retlen; - result = (char *)outp + (int)outp->d.cfs_readlink.data; + result = (char *)outp + (int)outp->cfs_readlink.data; memcpy(buffer, result, retlen); + *(buffer + retlen) = '\0'; } - if (inp) CODA_FREE(buf, size); + if (inp) CODA_FREE(inp, insize); CDEBUG(D_INODE, " result %d\n",error); EXIT; return -error; } + + int venus_link(struct super_block *sb, struct ViceFid *fid, struct ViceFid *dirfid, const char *name, int len ) { - int error, payload_offset, size; - struct inputArgs *inp; - struct outputArgs *outp; - - size = CFS_MAXNAMLEN + sizeof(struct inputArgs); - UPARG(size, CFS_LINK); - - payload_offset = (VC_INSIZE(cfs_link_in)); - inp->d.cfs_link.sourceFid = *fid; - inp->d.cfs_link.destFid = *dirfid; - inp->d.cfs_link.tname = (char *)payload_offset; + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int offset; + + offset = INSIZE(link); + insize = max(offset + len + 1, OUTSIZE(link)); + UPARG(CFS_LINK); + + inp->cfs_link.sourceFid = *fid; + inp->cfs_link.destFid = *dirfid; + inp->cfs_link.tname = offset; /* make sure strings are null terminated */ - memcpy((char *)inp + payload_offset, name, len); - *((char *)inp + payload_offset + len) = '\0'; - size = payload_offset + len + 1; + memcpy((char *)(inp) + offset, name, len); + *((char *)inp + offset + len) = '\0'; - error = coda_upcall(coda_sbp(sb), size, &size, inp); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); if (inp) - CODA_FREE(inp, size); + CODA_FREE(inp, insize); CDEBUG(D_INODE, " result %d\n",error); EXIT; return -error; @@ -397,62 +420,73 @@ const char *name, int len, const char *symname, int symlen) { - int error, payload_offset, size, s; - struct inputArgs *inp; - struct outputArgs *outp; - - - /* - * allocate space for regular input, - * plus 1 path and 1 name, plus padding - */ - size = sizeof(struct inputArgs) + CFS_MAXNAMLEN + CFS_MAXNAMLEN + 8; - UPARG(size, CFS_SYMLINK); - - /* inp->d.cfs_symlink.attr = *tva; XXXXXX */ - inp->d.cfs_symlink.VFid = *fid; - - payload_offset = VC_INSIZE(cfs_symlink_in); - inp->d.cfs_symlink.srcname =(char*) payload_offset; - - s = ( symlen & ~0x3 ) + 4; /* Round up to word boundary. */ - - /* don't forget to copy out the null termination */ - memcpy((char *)inp + payload_offset, symname, symlen); - *((char *)inp + payload_offset + symlen) = '\0'; + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + int offset, s; + + offset = INSIZE(symlink); + insize = max(offset + len + symlen + 8, OUTSIZE(symlink)); + UPARG(CFS_SYMLINK); + + /* inp->cfs_symlink.attr = *tva; XXXXXX */ + inp->cfs_symlink.VFid = *fid; + + /* Round up to word boundary and null terminate */ + inp->cfs_symlink.srcname = offset; + s = ( symlen & ~0x3 ) + 4; + memcpy((char *)(inp) + offset, symname, symlen); + *((char *)inp + offset + symlen) = '\0'; - payload_offset += s; - inp->d.cfs_symlink.tname = (char *) payload_offset; - s = (len & ~0x3) + 4; /* Round up to word boundary. */ - memcpy((char *)inp + payload_offset, name, len); - *((char *)inp + payload_offset + len) = '\0'; + /* Round up to word boundary and null terminate */ + offset += s; + inp->cfs_symlink.tname = offset; + s = (len & ~0x3) + 4; + memcpy((char *)(inp) + offset, name, len); + *((char *)inp + offset + len) = '\0'; - size = payload_offset + s; - error = coda_upcall(coda_sbp(sb), size, &size, inp); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); if (inp) - CODA_FREE(inp, size); + CODA_FREE(inp, insize); CDEBUG(D_INODE, " result %d\n",error); EXIT; return -error; } +int venus_fsync(struct super_block *sb, struct ViceFid *fid) +{ + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; + + insize=SIZE(fsync); + UPARG(CFS_FSYNC); + + inp->cfs_fsync.VFid = *fid; + error = coda_upcall(coda_sbp(sb), sizeof(union inputArgs), + &outsize, inp); + + if ( inp ) + CODA_FREE(inp, insize); + return -error; +} + int venus_access(struct super_block *sb, struct ViceFid *fid, int mask) { - struct inputArgs *inp; - struct outputArgs *outp; - int size; - int error; + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; - size = sizeof(struct inputArgs); - UPARG(size, CFS_ACCESS); + insize = SIZE(access); + UPARG(CFS_ACCESS); - inp->d.cfs_access.VFid = *fid; - inp->d.cfs_access.flags = mask << 6; + inp->cfs_access.VFid = *fid; + inp->cfs_access.flags = mask; - error = coda_upcall(coda_sbp(sb), size, &size, inp); + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); - if (inp) CODA_FREE(inp, sizeof(struct inputArgs)); + if (inp) CODA_FREE(inp, insize); EXIT; return -error; } @@ -461,41 +495,46 @@ int venus_pioctl(struct super_block *sb, struct ViceFid *fid, unsigned int cmd, struct PioctlData *data) { - struct inputArgs *inp; - struct outputArgs *outp; - int size, error = 0; + union inputArgs *inp; + union outputArgs *outp; + int insize, outsize, error; int iocsize; char str[50]; - size = VC_MAXMSGSIZE; - UPARG(size, CFS_IOCTL); + insize = VC_MAXMSGSIZE; + UPARG(CFS_IOCTL); /* build packet for Venus */ - if (data->vi.in_size > VC_DATASIZE) { + if (data->vi.in_size > VC_MAXDATASIZE) { error = EINVAL; goto exit; } - inp->d.cfs_ioctl.VFid = *fid; + inp->cfs_ioctl.VFid = *fid; /* the cmd field was mutated by increasing its size field to * reflect the path and follow args. We need to subtract that * out before sending the command to Venus. */ - inp->d.cfs_ioctl.cmd = (cmd & ~(IOCPARM_MASK << 16)); - iocsize = ((cmd >> 16) & IOCPARM_MASK) - sizeof(char *) - sizeof(int); - inp->d.cfs_ioctl.cmd |= (iocsize & IOCPARM_MASK) << 16; + inp->cfs_ioctl.cmd = (cmd & ~(PIOCPARM_MASK << 16)); + iocsize = ((cmd >> 16) & PIOCPARM_MASK) - sizeof(char *) - sizeof(int); + inp->cfs_ioctl.cmd |= (iocsize & PIOCPARM_MASK) << 16; - /* in->d.cfs_ioctl.rwflag = flag; */ - inp->d.cfs_ioctl.len = data->vi.in_size; - inp->d.cfs_ioctl.data = (char *)(VC_INSIZE(cfs_ioctl_in)); + /* in->cfs_ioctl.rwflag = flag; */ + inp->cfs_ioctl.len = data->vi.in_size; + inp->cfs_ioctl.data = (char *)(INSIZE(ioctl)); /* get the data out of user space */ - if ( copy_from_user((char*)inp + (int)inp->d.cfs_ioctl.data, +#ifdef L20 + memcpy_fromfs((char*)inp + (int)inp->cfs_ioctl.data, + data->vi.in, data->vi.in_size); +#else + if ( copy_from_user((char*)inp + (int)inp->cfs_ioctl.data, data->vi.in, data->vi.in_size) ) { error = EINVAL; goto exit; } - error = coda_upcall(coda_sbp(sb), size, &size, inp); +#endif + error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); if (error) { printk("coda_pioctl: Venus returns: %d for %s\n", @@ -504,23 +543,32 @@ } /* Copy out the OUT buffer. */ - if (outp->d.cfs_ioctl.len > data->vi.out_size) { + if (outp->cfs_ioctl.len > data->vi.out_size) { CDEBUG(D_FILE, "return len %d <= request len %d\n", - outp->d.cfs_ioctl.len, + outp->cfs_ioctl.len, data->vi.out_size); error = EINVAL; } else { - if (copy_to_user(data->vi.out, - (char *)outp + (int)outp->d.cfs_ioctl.data, + error = verify_area(VERIFY_WRITE, data->vi.out, + data->vi.out_size); + if ( error ) goto exit; +#ifdef L20 + memcpy_tofs(data->vi.out, + (char *)outp + (int)outp->cfs_ioctl.data, + data->vi.out_size); +#else + if (copy_to_user(data->vi.out, + (char *)outp + (int)outp->cfs_ioctl.data, data->vi.out_size)) { error = EINVAL; goto exit; } +#endif } exit: if (inp) - CODA_FREE(inp, VC_MAXMSGSIZE); + CODA_FREE(inp, insize); return -error; } @@ -535,11 +583,38 @@ * reply and return Venus' error, also POSITIVE. * */ +static inline void coda_waitfor_upcall(struct vmsg *vmp) +{ + struct wait_queue wait = { current, NULL }; + + vmp->vm_posttime = jiffies; + + add_wait_queue(&vmp->vm_sleep, &wait); + for (;;) { + if ( coda_hard == 0 ) + current->state = TASK_INTERRUPTIBLE; + else + current->state = TASK_UNINTERRUPTIBLE; + + if ( vmp->vm_flags & VM_WRITE ) + break; + if (signal_pending(current) && + (jiffies > vmp->vm_posttime + coda_timeout * HZ) ) + break; + schedule(); + } + remove_wait_queue(&vmp->vm_sleep, &wait); + current->state = TASK_RUNNING; + + return; +} + + int coda_upcall(struct coda_sb_info *sbi, int inSize, int *outSize, - struct inputArgs *buffer) + union inputArgs *buffer) { struct vcomm *vcommp; - struct outputArgs *out; + union outputArgs *out; struct vmsg *vmp; int error = 0; @@ -550,7 +625,7 @@ } vcommp = sbi->sbi_vcomm; - clstats(((struct inputArgs *)buffer)->opcode); + clstats(((union inputArgs *)buffer)->ih.opcode); if (!vcomm_open(vcommp)) return(ENODEV); @@ -561,16 +636,15 @@ vmp->vm_flags = 0; vmp->vm_inSize = inSize; vmp->vm_outSize = *outSize ? *outSize : inSize; - vmp->vm_opcode = ((struct inputArgs *)buffer)->opcode; + vmp->vm_opcode = ((union inputArgs *)buffer)->ih.opcode; vmp->vm_unique = ++vcommp->vc_seq; vmp->vm_sleep = NULL; /* Fill in the common input args. */ - ((struct inputArgs *)buffer)->unique = vmp->vm_unique; + ((union inputArgs *)buffer)->ih.unique = vmp->vm_unique; /* Append msg to pending queue and poke Venus. */ - - INSQUE(vmp->vm_chain, vcommp->vc_pending); + coda_q_insert(&(vmp->vm_chain), &(vcommp->vc_pending)); CDEBUG(D_UPCALL, "Proc %d wake Venus for(opc,uniq) =(%d,%d) msg at %x.zzz.\n", current->pid, vmp->vm_opcode, vmp->vm_unique, (int)vmp); @@ -582,13 +656,15 @@ * read but before the reply, we dequeue, send a signal * message, and return. If it occurs after the reply we ignore * it. In no case do we want to restart the syscall. If it - * was interrupted by a venus shutdown (vcclose), return + * was interrupted by a venus shutdown (psdev_close), return * ENODEV. */ - /* Ignore return, We have to check anyway */ - + /* Go to sleep. Wake up on signals only after the timeout. */ + coda_waitfor_upcall(vmp); - interruptible_sleep_on(&vmp->vm_sleep); + CDEBUG(D_TIMING, "opc: %d time: %ld uniq: %d size: %d\n", + vmp->vm_opcode, jiffies - vmp->vm_posttime, + vmp->vm_unique, vmp->vm_outSize); CDEBUG(D_UPCALL, "..process %d woken up by Venus for vmp at 0x%x, data at %x\n", current->pid, (int)vmp, (int)vmp->vm_data); @@ -596,60 +672,61 @@ /* Op went through, interrupt or not... */ if (vmp->vm_flags & VM_WRITE) { error = 0; - out = (struct outputArgs *)vmp->vm_data; - error = out->result; + out = (union outputArgs *)vmp->vm_data; + error = out->oh.result; CDEBUG(D_UPCALL, - "upcall: (u,o,r) (%ld, %ld, %ld) out at %x\n", - out->unique, out->opcode, out->result, (int)out); + "upcall: (u,o,r) (%ld, %ld, %ld) out at %p\n", + out->oh.unique, out->oh.opcode, out->oh.result, out); *outSize = vmp->vm_outSize; goto exit; - } - if (!(vmp->vm_flags & VM_READ)) { + } + if ( !(vmp->vm_flags & VM_READ) && signal_pending(current)) { /* Interrupted before venus read it. */ CDEBUG(D_UPCALL, "Interrupted before read:(op,un) (%d.%d), flags = %x\n", vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags); - REMQUE(vmp->vm_chain); - error = ERESTARTSYS; + coda_q_remove(&(vmp->vm_chain)); + error = ERESTARTNOHAND; goto exit; } - if ( vmp->vm_flags & VM_READ) { + if ( (vmp->vm_flags & VM_READ) && signal_pending(current) ) { /* interrupted after Venus did its read, send signal */ - struct inputArgs *dog; + union inputArgs *dog; struct vmsg *svmp; CDEBUG(D_UPCALL, "Sending Venus a signal: op = %d.%d, flags = %x\n", vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags); - REMQUE(vmp->vm_chain); - error = ERESTARTSYS; + coda_q_remove(&(vmp->vm_chain)); + error = ERESTARTNOHAND; CODA_ALLOC(svmp, struct vmsg *, sizeof (struct vmsg)); - CODA_ALLOC((svmp->vm_data), char *, VC_IN_NO_DATA); + CODA_ALLOC((svmp->vm_data), char *, sizeof(struct cfs_in_hdr)); + dog = (union inputArgs *)svmp->vm_data; + dog->ih.opcode = CFS_SIGNAL; + dog->ih.unique = vmp->vm_unique; + + svmp->vm_flags = 0; + svmp->vm_opcode = dog->ih.opcode; + svmp->vm_unique = dog->ih.unique; + svmp->vm_inSize = sizeof(struct cfs_in_hdr); + svmp->vm_outSize = sizeof(struct cfs_in_hdr); CDEBUG(D_UPCALL, "coda_upcall: enqueing signal msg (%d, %d)\n", svmp->vm_opcode, svmp->vm_unique); - dog = (struct inputArgs *)svmp->vm_data; - dog->opcode = CFS_SIGNAL; - dog->unique = vmp->vm_unique; - - svmp->vm_flags = 0; - svmp->vm_opcode = dog->opcode; - svmp->vm_unique = dog->unique; - svmp->vm_inSize = VC_IN_NO_DATA; - svmp->vm_outSize = VC_IN_NO_DATA; /* insert at head of queue! */ - INSQUE(svmp->vm_chain, vcommp->vc_pending); + coda_q_insert(&(svmp->vm_chain), vcommp->vc_pending.forw); wake_up_interruptible(&vcommp->vc_waitq); + } else { + printk("Coda: Strange interruption..\n"); + error = EINTR; } } else { /* If venus died i.e. !VC_OPEN(vcommp) */ - printk("coda_upcall: Venus dead upon (op,un) (%d.%d) flags %d\n", + printk("coda_upcall: Venus dead on (op,un) (%d.%d) flags %d\n", vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags); - - /* if (! (vmp->vm_flags & VM_WRITE) ) */ error = ENODEV; } @@ -670,78 +747,106 @@ * CFS_FLUSH -- flush all entries from the name cache and the cnode cache. * CFS_PURGEUSER -- flush all entries from the name cache for a specific user * This call is a result of token expiration. - * Linux does a cfsnc_flush since cred's are not maintained. * * The next arise as the result of callbacks on a file or directory. * CFS_ZAPDIR -- flush the attributes for the dir from its cnode. * Zap all children of this directory from the namecache. * CFS_ZAPFILE -- flush the cached attributes for a file. - * CFS_ZAPVNODE -- in linux the same as zap file (no creds). + * CFS_ZAPVNODE -- intended to be a zapfile for just one cred. Not used? * * The next is a result of Venus detecting an inconsistent file. * CFS_PURGEFID -- flush the attribute for the file - * If it is a dir (odd vnode), purge its - * children from the namecache - * remove the file from the namecache. + * purge it and its children from the dcache * * The last allows Venus to replace local fids with global ones * during reintegration. * * CFS_REPLACE -- replace one ViceFid with another throughout the name cache */ -int coda_downcall(int opcode, struct outputArgs * out) +int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) { /* Handle invalidate requests. */ switch (opcode) { - case CFS_FLUSH : { - clstats(CFS_FLUSH); - cfsnc_flush(); - return(0); - } - case CFS_PURGEUSER : { - clstats(CFS_PURGEUSER); - cfsnc_flush(); - return(0); - } - case CFS_ZAPDIR : { - ViceFid *fid = &out->d.cfs_zapdir.CodaFid; - clstats(CFS_ZAPDIR); - cfsnc_zapfid(fid); - cfsnc_zapParentfid(fid); - CDEBUG(D_UPCALL, "zapdir: fid = (%lx.%lx.%lx), \n",fid->Volume, - fid->Vnode, - fid->Unique); - return(0); - } - case CFS_ZAPVNODE : { - clstats(CFS_ZAPVNODE); - cfsnc_zapfid(&out->d.cfs_zapvnode.VFid); - return(0); - } - case CFS_ZAPFILE : { - clstats(CFS_ZAPFILE); - cfsnc_zapfid(&out->d.cfs_zapfile.CodaFid); - return 0; - } - case CFS_PURGEFID : { - ViceFid *fid = &out->d.cfs_purgefid.CodaFid; - clstats(CFS_PURGEFID); - cfsnc_zapfid(fid); - cfsnc_zapParentfid(fid); - CDEBUG(D_UPCALL, "purgefid: fid = (%lx.%lx.%lx)\n", - fid->Volume, fid->Vnode, - fid->Unique); - return 0; - } - case CFS_REPLACE : { - clstats(CFS_REPLACE); - cfsnc_replace(&out->d.cfs_replace.OldFid, - &out->d.cfs_replace.NewFid); - return (0); - } + case CFS_FLUSH : { + clstats(CFS_FLUSH); + CDEBUG(D_DOWNCALL, "CFS_FLUSH\n"); + coda_cache_clear_all(sb); + shrink_dcache_sb(sb); + return(0); + } + case CFS_PURGEUSER : { + struct coda_cred *cred = &out->cfs_purgeuser.cred; + CDEBUG(D_DOWNCALL, "CFS_PURGEUSER\n"); + if ( !cred ) { + printk("PURGEUSER: null cred!\n"); + return 0; + } + clstats(CFS_PURGEUSER); + coda_cache_clear_cred(sb, cred); + return(0); + } + case CFS_ZAPDIR : { + ViceFid *fid = &out->cfs_zapdir.CodaFid; + char str[50]; + if ( !fid ) { + printk("ZAPDIR: Null fid\n"); + return 0; + } + CDEBUG(D_DOWNCALL, "zapdir: fid = %s\n", coda_f2s(fid, str)); + clstats(CFS_ZAPDIR); + coda_zapfid(fid, sb, C_ZAPDIR); + return(0); + } + case CFS_ZAPVNODE : { + ViceFid *fid = &out->cfs_zapvnode.VFid; + char str[50]; + struct coda_cred *cred = &out->cfs_zapvnode.cred; + if ( !fid || !cred ) { + printk("ZAPVNODE: Null fid or cred\n"); + return 0; + } + CDEBUG(D_DOWNCALL, "zapvnode: fid = %s\n", coda_f2s(fid, str)); + coda_zapfid(fid, sb, C_ZAPFID); + coda_cache_clear_cred(sb, cred); + clstats(CFS_ZAPVNODE); + return(0); + } + case CFS_ZAPFILE : { + struct ViceFid *fid = &out->cfs_zapfile.CodaFid; + char str[50]; + clstats(CFS_ZAPFILE); + if ( !fid ) { + printk("ZAPFILE: Null fid\n"); + return 0; + } + CDEBUG(D_DOWNCALL, "zapfile: fid = %s\n", coda_f2s(fid, str)); + coda_zapfid(fid, sb, C_ZAPFID); + return 0; + } + case CFS_PURGEFID : { + ViceFid *fid = &out->cfs_purgefid.CodaFid; + char str[50]; + if ( !fid ) { + printk("PURGEFID: Null fid\n"); + return 0; + } + CDEBUG(D_DOWNCALL, "purgefid: fid = %s\n", coda_f2s(fid, str)); + clstats(CFS_PURGEFID); + coda_zapfid(fid, sb, C_ZAPDIR); + return 0; + } + case CFS_REPLACE : { + printk("CFS_REPLACCE\n"); + clstats(CFS_REPLACE); + CDEBUG(D_DOWNCALL, "CFS_REPLACE\n"); + coda_cache_clear_all(sb); + shrink_dcache_sb(sb); + return (0); + } } return 0; } + diff -u --recursive --new-file v2.1.74/linux/fs/ext2/acl.c linux/fs/ext2/acl.c --- v2.1.74/linux/fs/ext2/acl.c Tue Jul 2 09:08:42 1996 +++ linux/fs/ext2/acl.c Sun Dec 21 17:41:24 1997 @@ -39,11 +39,7 @@ */ if ((mask & S_IWOTH) && IS_IMMUTABLE(inode)) return -EACCES; - /* - * Special case, access is always granted for root - */ - if (fsuser()) - return 0; + /* * If no ACL, checks using the file mode */ @@ -51,7 +47,11 @@ mode >>= 6; else if (in_group_p (inode->i_gid)) mode >>= 3; - if (((mode & mask & S_IRWXO) == mask)) + /* + * Access is always granted for root. We now check last, + * though, for BSD process accounting correctness + */ + if (((mode & mask & S_IRWXO) == mask) || fsuser()) return 0; else return -EACCES; diff -u --recursive --new-file v2.1.74/linux/fs/ext2/balloc.c linux/fs/ext2/balloc.c --- v2.1.74/linux/fs/ext2/balloc.c Sun Jul 6 20:13:54 1997 +++ linux/fs/ext2/balloc.c Sun Dec 21 17:41:24 1997 @@ -295,9 +295,9 @@ 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) && - (!fsuser() && (sb->u.ext2_sb.s_resuid != current->fsuid) && + ((sb->u.ext2_sb.s_resuid != current->fsuid) && (sb->u.ext2_sb.s_resgid == 0 || - !in_group_p (sb->u.ext2_sb.s_resgid)))) { + !in_group_p (sb->u.ext2_sb.s_resgid)) && !fsuser())) { unlock_super (sb); return 0; } diff -u --recursive --new-file v2.1.74/linux/fs/ext2/inode.c linux/fs/ext2/inode.c --- v2.1.74/linux/fs/ext2/inode.c Thu Jul 17 10:06:06 1997 +++ linux/fs/ext2/inode.c Sun Dec 21 17:41:24 1997 @@ -640,7 +640,7 @@ (ATTR_FLAG_APPEND | ATTR_FLAG_IMMUTABLE)) ^ (inode->u.ext2_i.i_flags & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL))) { - if (!fsuser() || securelevel > 0) + if (securelevel > 0 || !fsuser()) return -EPERM; } else if ((current->fsuid != inode->i_uid) && !fsuser()) diff -u --recursive --new-file v2.1.74/linux/fs/fat/inode.c linux/fs/fat/inode.c --- v2.1.74/linux/fs/fat/inode.c Fri Dec 19 15:53:00 1997 +++ linux/fs/fat/inode.c Sat Dec 20 21:33:20 1997 @@ -566,7 +566,7 @@ tmp.f_bavail = free; tmp.f_files = 0; tmp.f_ffree = 0; - tmp.f_namelen = 12; + tmp.f_namelen = MSDOS_SB(sb)->options.isvfat ? 260 : 12; return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } diff -u --recursive --new-file v2.1.74/linux/fs/filesystems.c linux/fs/filesystems.c --- v2.1.74/linux/fs/filesystems.c Sun Dec 21 16:17:44 1997 +++ linux/fs/filesystems.c Sun Dec 21 14:45:14 1997 @@ -86,6 +86,10 @@ init_nfs_fs(); #endif +#ifdef CONFIG_CODA_FS + init_coda_fs(); +#endif + #ifdef CONFIG_SMB_FS init_smb_fs(); #endif diff -u --recursive --new-file v2.1.74/linux/fs/minix/namei.c linux/fs/minix/namei.c --- v2.1.74/linux/fs/minix/namei.c Mon Aug 11 14:47:05 1997 +++ linux/fs/minix/namei.c Sun Dec 21 17:41:24 1997 @@ -414,9 +414,9 @@ retval = -EPERM; inode = dentry->d_inode; - if ((dir->i_mode & S_ISVTX) && !fsuser() && + if ((dir->i_mode & S_ISVTX) && current->fsuid != inode->i_uid && - current->fsuid != dir->i_uid) + current->fsuid != dir->i_uid && !fsuser()) goto end_rmdir; if (inode->i_dev != dir->i_dev) goto end_rmdir; @@ -480,9 +480,9 @@ schedule(); goto repeat; } - if ((dir->i_mode & S_ISVTX) && !fsuser() && + if ((dir->i_mode & S_ISVTX) && current->fsuid != inode->i_uid && - current->fsuid != dir->i_uid) + current->fsuid != dir->i_uid && !fsuser()) goto end_unlink; if (de->inode != inode->i_ino) { retval = -ENOENT; diff -u --recursive --new-file v2.1.74/linux/fs/namei.c linux/fs/namei.c --- v2.1.74/linux/fs/namei.c Fri Dec 19 15:53:00 1997 +++ linux/fs/namei.c Sun Dec 21 17:31:49 1997 @@ -843,6 +843,9 @@ if (dir->d_inode->i_sb && dir->d_inode->i_sb->dq_op) dir->d_inode->i_sb->dq_op->initialize(dir->d_inode, -1); + if (dentry->d_count > 1) + shrink_dcache_parent(dentry); + error = dir->d_inode->i_op->rmdir(dir->d_inode, dentry); exit_lock: diff -u --recursive --new-file v2.1.74/linux/fs/nfs/dir.c linux/fs/nfs/dir.c --- v2.1.74/linux/fs/nfs/dir.c Fri Dec 19 15:53:00 1997 +++ linux/fs/nfs/dir.c Sun Dec 21 17:33:27 1997 @@ -439,7 +439,8 @@ */ if (list_empty(&dentry->d_hash) && dentry->d_inode) { struct inode *inode = dentry->d_inode; - if (inode->i_count > 1) { + int max_count = (S_ISDIR(inode->i_mode) ? 1 : inode->i_nlink); + if (inode->i_count > max_count) { printk("nfs_dentry_delete: %s/%s: ino=%ld, count=%d, nlink=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino, inode->i_count, inode->i_nlink); @@ -499,7 +500,7 @@ inode = nfs_fhget(dir->i_sb, &fhandle, &fattr); if (inode) { #ifdef NFS_PARANOIA -if (inode->i_count > 1) +if (inode->i_count > (S_ISDIR(inode->i_mode) ? 1 : inode->i_nlink)) printk("nfs_lookup: %s/%s ino=%ld in use, count=%d, nlink=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino, inode->i_count, inode->i_nlink); @@ -511,6 +512,11 @@ error = 0; } } +#ifdef NFS_PARANOIA +if (error) +printk("nfs_lookup: %s/%s failed, error=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, error); +#endif out: return error; } @@ -555,7 +561,7 @@ inode = nfs_fhget(dentry->d_sb, fhandle, fattr); if (inode) { #ifdef NFS_PARANOIA -if (inode->i_count > 1) +if (inode->i_count > (S_ISDIR(inode->i_mode) ? 1 : inode->i_nlink)) printk("nfs_instantiate: %s/%s ino=%ld in use, count=%d, nlink=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino, inode->i_count, inode->i_nlink); @@ -669,8 +675,9 @@ return -ENOENT; } + error = -ENAMETOOLONG; if (dentry->d_name.len > NFS_MAXNAMLEN) - return -ENAMETOOLONG; + goto out; /* For some reason mode doesn't have the S_IFDIR flag ... */ mode |= S_IFDIR; @@ -702,6 +709,7 @@ drop: d_drop(dentry); } +out: return error; } @@ -876,11 +884,11 @@ goto out; } while(sdentry->d_inode != NULL); /* need negative lookup */ + nfs_invalidate_dircache(dir); error = nfs_proc_rename(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name, NFS_FH(dir), silly); if (!error) { - nfs_invalidate_dircache(dir); nfs_renew_times(dentry); d_move(dentry, sdentry); dentry->d_flags |= DCACHE_NFSFS_RENAMED; @@ -1036,10 +1044,10 @@ * can't instantiate the new inode. */ d_drop(dentry); + nfs_invalidate_dircache(dir); error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name, symname, &sattr); if (!error) { - nfs_invalidate_dircache(dir); nfs_renew_times(dentry->d_parent); } else if (error == -EEXIST) { printk("nfs_proc_symlink: %s/%s already exists??\n", @@ -1068,10 +1076,10 @@ if (dentry->d_name.len > NFS_MAXNAMLEN) goto out; + nfs_invalidate_dircache(dir); error = nfs_proc_link(NFS_SERVER(inode), NFS_FH(inode), NFS_FH(dir), dentry->d_name.name); if (!error) { - nfs_invalidate_dircache(dir); inode->i_count ++; inode->i_nlink ++; /* no need to wait for nfs_refresh_inode() */ d_instantiate(dentry, inode); @@ -1209,6 +1217,8 @@ d_drop(new_dentry); rehash = update; } + nfs_invalidate_dircache(new_dir); + nfs_invalidate_dircache(old_dir); error = nfs_proc_rename(NFS_SERVER(old_dir), NFS_FH(old_dir), old_dentry->d_name.name, NFS_FH(new_dir), new_dentry->d_name.name); @@ -1221,8 +1231,6 @@ new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count); #endif if (!error) { - nfs_invalidate_dircache(new_dir); - nfs_invalidate_dircache(old_dir); /* Update the dcache if needed */ if (update) d_move(old_dentry, new_dentry); diff -u --recursive --new-file v2.1.74/linux/fs/nfs/inode.c linux/fs/nfs/inode.c --- v2.1.74/linux/fs/nfs/inode.c Fri Dec 19 15:53:00 1997 +++ linux/fs/nfs/inode.c Sun Dec 21 17:33:27 1997 @@ -260,12 +260,8 @@ server->client = clnt; /* Fire up rpciod if not yet running */ -#ifdef RPCIOD_RESULT - if (rpciod_up()) + if (rpciod_up() != 0) goto out_no_iod; -#else - rpciod_up(); -#endif /* * Keep the super block locked while we try to get @@ -290,13 +286,11 @@ printk("nfs_read_super: get root inode failed\n"); iput(root_inode); rpciod_down(); -#ifdef RPCIOD_RESULT goto out_shutdown; out_no_iod: - printk("nfs_read_super: couldn't start rpciod!\n"); + printk("NFS: couldn't start rpciod!\n"); out_shutdown: -#endif rpc_shutdown_client(server->client); goto out_unlock; diff -u --recursive --new-file v2.1.74/linux/fs/nfs/nfs2xdr.c linux/fs/nfs/nfs2xdr.c --- v2.1.74/linux/fs/nfs/nfs2xdr.c Wed Oct 15 16:04:23 1997 +++ linux/fs/nfs/nfs2xdr.c Sat Dec 20 21:06:01 1997 @@ -564,6 +564,7 @@ { NFSERR_EAGAIN, EAGAIN }, { NFSERR_ACCES, EACCES }, { NFSERR_EXIST, EEXIST }, + { NFSERR_XDEV, EXDEV }, { NFSERR_NODEV, ENODEV }, { NFSERR_NOTDIR, ENOTDIR }, { NFSERR_ISDIR, EISDIR }, diff -u --recursive --new-file v2.1.74/linux/fs/nfs/nfs3xdr.c linux/fs/nfs/nfs3xdr.c --- v2.1.74/linux/fs/nfs/nfs3xdr.c Wed Oct 15 16:04:23 1997 +++ linux/fs/nfs/nfs3xdr.c Sat Dec 20 21:06:01 1997 @@ -584,6 +584,7 @@ { NFSERR_EAGAIN, EAGAIN }, { NFSERR_ACCES, EACCES }, { NFSERR_EXIST, EEXIST }, + { NFSERR_XDEV, EXDEV }, { NFSERR_NODEV, ENODEV }, { NFSERR_NOTDIR, ENOTDIR }, { NFSERR_ISDIR, EISDIR }, diff -u --recursive --new-file v2.1.74/linux/fs/nfs/nfsroot.c linux/fs/nfs/nfsroot.c --- v2.1.74/linux/fs/nfs/nfsroot.c Tue Dec 2 09:49:40 1997 +++ linux/fs/nfs/nfsroot.c Sun Dec 21 17:27:18 1997 @@ -1,14 +1,10 @@ /* - * $Id: nfsroot.c,v 1.38 1997/07/17 03:21:06 davem Exp $ + * $Id: nfsroot.c,v 1.43 1997/10/16 19:55:27 mj Exp $ * * Copyright (C) 1995, 1996 Gero Kuhlmann * - * For parts of this file: - * Copyright (C) 1996, 1997 Martin Mares - * * Allow an NFS filesystem to be mounted as root. The way this works is: - * (1) Determine the local IP address via RARP or BOOTP or from the - * kernel command line. + * (1) Use the IP autoconfig mechanism to set local IP addresses and routes. * (2) Handle RPC negotiation with the system which replied to RARP or * was reported as a boot server by BOOTP or manually. * (3) The actual mounting is done later, when init() is running. @@ -47,7 +43,7 @@ * from being used (thanks to Leo Spiekman) * Andy Walker : Allow to specify the NFS server in nfs_root * without giving a path name - * Swen Th=FCmmler : Allow to specify the NFS options in nfs_root + * Swen Thümmler : Allow to specify the NFS options in nfs_root * without giving a path name. Fix BOOTP request * for domainname (domainname is NIS domain, not * DNS domain!). Skip dummy devices for BOOTP. @@ -57,1004 +53,58 @@ * Jakub Jelinek : Free used code segment. * Marko Kohtala : Fixed some bugs. * Martin Mares : Debug message cleanup - * + * Martin Mares : Changed to use the new generic IP layer autoconfig + * code. BOOTP and RARP moved there. + * Martin Mares : Default path now contains host name instead of + * host IP address (but host name defaults to IP + * address anyway). */ - -/* Define this to allow debugging output */ -#undef NFSROOT_DEBUG -#undef NFSROOT_BOOTP_DEBUG - - -#include #include #include #include #include #include -#include -#include #include - -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_AX25 -#include /* For AX25_P_IP */ -#endif -#include -#include -#include -#include #include #include #include #include #include -#include -#include - -#include -#include - -#define NFSDBG_FACILITY NFSDBG_ROOT -/* Range of privileged ports */ -#define STARTPORT 600 -#define ENDPORT 1023 -#define NPORTS (ENDPORT - STARTPORT + 1) - - -/* Define the timeout for waiting for a RARP/BOOTP reply */ -#define CONF_BASE_TIMEOUT (HZ*5) /* Initial timeout: 5 seconds */ -#define CONF_RETRIES 10 /* 10 retries */ -#define CONF_TIMEOUT_RANDOM (HZ) /* Maximum amount of randomization */ -#define CONF_TIMEOUT_MULT *5/4 /* Speed of timeout growth */ -#define CONF_TIMEOUT_MAX (HZ*30) /* Maximum allowed timeout */ - - -/* List of open devices */ -struct open_dev { - struct device *dev; - unsigned short old_flags; - struct open_dev *next; -}; +#include +#include +#include +#include -static struct open_dev *open_base __initdata = NULL; +/* Define this to allow debugging output */ +#undef NFSROOT_DEBUG +#define NFSDBG_FACILITY NFSDBG_ROOT +/* Default path we try to mount. "%s" gets replaced by our IP address */ +#define NFS_ROOT "/tftpboot/%s" +#define NFS_ROOT_NAME_LEN 256 -/* IP configuration */ -static struct device *root_dev __initdata = NULL; /* Device selected for booting */ -static char user_dev_name[IFNAMSIZ] __initdata = { 0, };/* Name of user-selected boot device */ -static __u32 myaddr __initdata = 0; /* My IP address */ -static __u32 servaddr __initdata = 0; /* Server IP address */ -static __u32 gateway __initdata = 0; /* Gateway IP address */ -static __u32 netmask __initdata = 0; /* Netmask for local subnet */ - - -/* BOOTP/RARP variables */ -static int bootp_flag __initdata = 0; /* User said: Use BOOTP! */ -static int rarp_flag __initdata = 0; /* User said: Use RARP! */ -static int bootp_dev_count __initdata = 0; /* Number of devices allowing BOOTP */ -static int rarp_dev_count __initdata = 0; /* Number of devices allowing RARP */ -static __u32 rarp_serv __initdata = 0; /* IP address of RARP server */ - -#if defined(CONFIG_RNFS_BOOTP) || defined(CONFIG_RNFS_RARP) -#define CONFIG_RNFS_DYNAMIC /* Enable dynamic IP config */ -static volatile int pkt_arrived __initdata = 0; /* BOOTP/RARP packet detected */ +/* Parameters passed from the kernel command line */ +static char nfs_root_name[NFS_ROOT_NAME_LEN] __initdata = "default"; -#define ARRIVED_BOOTP 1 -#define ARRIVED_RARP 2 -#endif +/* Address of NFS server */ +static __u32 servaddr __initdata = 0; +/* Name of directory to mount */ +static char nfs_path[NFS_MAXPATHLEN] __initdata = { 0, }; /* NFS-related data */ static struct nfs_mount_data nfs_data __initdata = { 0, };/* NFS mount info */ -static char nfs_path[NFS_MAXPATHLEN] __initdata = { 0, };/* Name of directory to mount */ -static int nfs_port __initdata = 0; /* Port to connect to for NFS */ -static int mount_port __initdata = 0; /* Mount daemon port number */ - - -/* Yes, we use sys_socket, but there's no include file for it */ -extern asmlinkage int sys_socket(int family, int type, int protocol); - +static int nfs_port __initdata = 0; /* Port to connect to for NFS */ +static int mount_port __initdata = 0; /* Mount daemon port number */ /*************************************************************************** - Device Handling Subroutines - - ***************************************************************************/ - -/* - * Setup and initialize all network devices. If there is a user-preferred - * interface, ignore all other interfaces. - */ -__initfunc(static int root_dev_open(void)) -{ - struct open_dev *openp, **last; - struct device *dev; - unsigned short old_flags; - - last = &open_base; - for (dev = dev_base; dev != NULL; dev = dev->next) { - if (dev->type < ARPHRD_SLIP && - dev->family == AF_INET && - !(dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) && - (0 != strncmp(dev->name, "dummy", 5)) && - (!user_dev_name[0] || !strcmp(dev->name, user_dev_name))) { - /* First up the interface */ - old_flags = dev->flags; - dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING; - if (!(old_flags & IFF_UP) && dev_open(dev)) { - dev->flags = old_flags; - continue; - } - openp = (struct open_dev *) kmalloc(sizeof(struct open_dev), - GFP_ATOMIC); - if (openp == NULL) - continue; - openp->dev = dev; - openp->old_flags = old_flags; - *last = openp; - last = &openp->next; - bootp_dev_count++; - if (!(dev->flags & IFF_NOARP)) - rarp_dev_count++; - dprintk("Root-NFS: Opened %s\n", dev->name); - } - } - *last = NULL; - - if (!bootp_dev_count && !rarp_dev_count) { - printk(KERN_ERR "Root-NFS: Unable to open at least one network device\n"); - return -1; - } - return 0; -} - -static inline void -set_sockaddr(struct sockaddr_in *sin, __u32 addr, __u16 port) -{ - sin->sin_family = AF_INET; - sin->sin_addr.s_addr = addr; - sin->sin_port = port; -} - -__initfunc(static int -root_dev_chg_route(int op, struct device *dev, __u32 dest, __u32 mask, __u32 gw)) -{ - struct rtentry route; - mm_segment_t oldfs; - int err; - - memset(&route, 0, sizeof(struct rtentry)); /* or else! */ - - route.rt_dev = dev->name; - route.rt_mtu = dev->mtu; - route.rt_flags = RTF_UP; - set_sockaddr((struct sockaddr_in *) &route.rt_dst, dest & mask, 0); - set_sockaddr((struct sockaddr_in *) &route.rt_genmask, mask, 0); - - if (gw != 0) { - set_sockaddr((struct sockaddr_in *) &route.rt_gateway, gw, 0); - route.rt_flags |= RTF_GATEWAY; - if ((gw ^ myaddr) & netmask) { - printk(KERN_ERR "Root-NFS: Gateway not on local network!\n"); - return -ENETUNREACH; - } - } - - oldfs = get_fs(); - set_fs(KERNEL_DS); - err = ip_rt_ioctl(op, &route); - set_fs(oldfs); - -#ifdef NFSROOT_DEBUG - /* in_ntoa in ipv4/utils.c uses a single static buffer, so - * must make multiple printk calls, one for each in_ntoa - * invocation... - */ - printk(KERN_NOTICE "%s route ", (op == SIOCADDRT ? "add" : "del")); - printk("%s ", in_ntoa(dest)); - printk("%s ", in_ntoa(mask)); - printk("%s: res %d\n", in_ntoa(gw), err); -#endif - - return err; -} - -__initfunc(static int -root_dev_add_route(struct device *dev, __u32 dest, __u32 mask, __u32 gateway)) -{ - return root_dev_chg_route(SIOCADDRT, dev, dest, mask, gateway); -} - -__initfunc(static int -root_dev_del_route(struct device *dev, __u32 dest, __u32 mask, __u32 gateway)) -{ - return root_dev_chg_route(SIOCDELRT, dev, dest, mask, gateway); -} - -/* - * Restore the state of all devices. However, keep the root device open - * for the upcoming mount. - */ -__initfunc(static void root_dev_close(void)) -{ - struct open_dev *openp; - struct open_dev *nextp; - - openp = open_base; - while (openp != NULL) { - nextp = openp->next; - openp->next = NULL; - if (openp->dev != root_dev) { - if (!(openp->old_flags & IFF_UP)) { - dev_close(openp->dev); - } - - openp->dev->flags = openp->old_flags; - } - kfree_s(openp, sizeof(struct open_dev)); - openp = nextp; - } -} - - - -/*************************************************************************** - - RARP Subroutines - - ***************************************************************************/ - -#ifdef CONFIG_RNFS_RARP - -extern void arp_send(int type, int ptype, unsigned long target_ip, - struct device *dev, unsigned long src_ip, - unsigned char *dest_hw, unsigned char *src_hw, - unsigned char *target_hw); - -static int root_rarp_recv(struct sk_buff *skb, struct device *dev, - struct packet_type *pt); - - -static struct packet_type rarp_packet_type __initdata = { - 0, /* Should be: __constant_htons(ETH_P_RARP) - * - but this _doesn't_ come out constant! */ - NULL, /* Listen to all devices */ - root_rarp_recv, - NULL, - NULL -}; - - -/* - * Register the packet type for RARP - */ -__initfunc(static void root_rarp_open(void)) -{ - rarp_packet_type.type = htons(ETH_P_RARP); - dev_add_pack(&rarp_packet_type); -} - - -/* - * Deregister the RARP packet type - */ -__initfunc(static void root_rarp_close(void)) -{ - rarp_packet_type.type = htons(ETH_P_RARP); - dev_remove_pack(&rarp_packet_type); -} - - -/* - * Receive RARP packets. - */ -__initfunc(static int -root_rarp_recv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)) -{ - struct arphdr *rarp = (struct arphdr *)skb->h.raw; - unsigned char *rarp_ptr = (unsigned char *) (rarp + 1); - unsigned long sip, tip; - unsigned char *sha, *tha; /* s for "source", t for "target" */ - - /* If this test doesn't pass, it's not IP, or we should ignore it anyway */ - if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd)) { - kfree_skb(skb, FREE_READ); - return 0; - } - - /* If it's not a RARP reply, delete it. */ - if (rarp->ar_op != htons(ARPOP_RREPLY)) { - kfree_skb(skb, FREE_READ); - return 0; - } - - /* If it's not ethernet or AX25, delete it. */ - if ((rarp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25) || -#ifdef CONFIG_AX25 - (rarp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) || -#endif - rarp->ar_pln != 4) { - kfree_skb(skb, FREE_READ); - return 0; - } - - /* Extract variable width fields */ - sha = rarp_ptr; - rarp_ptr += dev->addr_len; - memcpy(&sip, rarp_ptr, 4); - rarp_ptr += 4; - tha = rarp_ptr; - rarp_ptr += dev->addr_len; - memcpy(&tip, rarp_ptr, 4); - - /* Discard packets which are not meant for us. */ - if (memcmp(tha, dev->dev_addr, dev->addr_len)) { - kfree_skb(skb, FREE_READ); - return 0; - } - /* Discard packets which are not from specified server. */ - if (rarp_flag && !bootp_flag && - rarp_serv != INADDR_NONE && - rarp_serv != sip) { - kfree_skb(skb, FREE_READ); - return 0; - } - - /* - * The packet is what we were looking for. Setup the global - * variables. - */ - cli(); - if (pkt_arrived) { - sti(); - kfree_skb(skb, FREE_READ); - return 0; - } - pkt_arrived = ARRIVED_RARP; - sti(); - root_dev = dev; - - if (myaddr == INADDR_NONE) - myaddr = tip; - if (servaddr == INADDR_NONE) - servaddr = sip; - kfree_skb(skb, FREE_READ); - return 0; -} - - -/* - * Send RARP request packet over all devices which allow RARP. - */ -__initfunc(static void root_rarp_send(void)) -{ - struct open_dev *openp; - struct device *dev; - int num = 0; - - for (openp = open_base; openp != NULL; openp = openp->next) { - dev = openp->dev; - if (!(dev->flags & IFF_NOARP)) { - arp_send(ARPOP_RREQUEST, ETH_P_RARP, 0, dev, 0, NULL, - dev->dev_addr, dev->dev_addr); - num++; - } - } -} -#endif - - - -/*************************************************************************** - - BOOTP Subroutines - - ***************************************************************************/ - -#ifdef CONFIG_RNFS_BOOTP - -static struct device *bootp_dev __initdata = NULL; /* Device selected as best BOOTP target */ - -static struct socket *bootp_xmit_sock __initdata = NULL;/* BOOTP send socket */ -static struct socket *bootp_recv_sock __initdata = NULL;/* BOOTP receive socket */ - -struct bootp_pkt { /* BOOTP packet format */ - u8 op; /* 1=request, 2=reply */ - u8 htype; /* HW address type */ - u8 hlen; /* HW address length */ - u8 hops; /* Used only by gateways */ - u32 xid; /* Transaction ID */ - u16 secs; /* Seconds since we started */ - u16 flags; /* Just what is says */ - u32 client_ip; /* Client's IP address if known */ - u32 your_ip; /* Assigned IP address */ - u32 server_ip; /* Server's IP address */ - u32 relay_ip; /* IP address of BOOTP relay */ - u8 hw_addr[16]; /* Client's HW address */ - u8 serv_name[64]; /* Server host name */ - u8 boot_file[128]; /* Name of boot file */ - u8 vendor_area[128]; /* Area for extensions */ -}; - -#define BOOTP_REQUEST 1 -#define BOOTP_REPLY 2 - -static struct bootp_pkt *xmit_bootp __initdata = NULL; /* Packet being transmitted */ -static struct bootp_pkt *recv_bootp __initdata = NULL; /* Packet being received */ - -static int bootp_have_route __initdata = 0; /* BOOTP route installed */ - - -/* - * Free BOOTP packet buffers - */ -__initfunc(static void root_free_bootp(void)) -{ - if (xmit_bootp) { - kfree_s(xmit_bootp, sizeof(struct bootp_pkt)); - xmit_bootp = NULL; - } - if (recv_bootp) { - kfree_s(recv_bootp, sizeof(struct bootp_pkt)); - recv_bootp = NULL; - } -} - - -/* - * Allocate memory for BOOTP packet buffers - */ -static inline int root_alloc_bootp(void) -{ - if (!(xmit_bootp = kmalloc(sizeof(struct bootp_pkt), GFP_KERNEL)) || - !(recv_bootp = kmalloc(sizeof(struct bootp_pkt), GFP_KERNEL))) { - printk(KERN_ERR "BOOTP: Out of memory!\n"); - return -1; - } - return 0; -} - - -/* - * Create default route for BOOTP sending - */ -__initfunc(static int root_add_bootp_route(void)) -{ - if (root_dev_add_route(bootp_dev, 0, 0, 0) < 0) { - printk(KERN_ERR "BOOTP: Failed to add route\n"); - return -1; - } - bootp_have_route = 1; - return 0; -} - - -/* - * Delete default route for BOOTP sending - */ -__initfunc(static int root_del_bootp_route(void)) -{ - if (bootp_have_route && root_dev_del_route(bootp_dev, 0, 0, 0) < 0) { - printk(KERN_ERR "BOOTP: Deleting of route failed!\n"); - return -1; - } - bootp_have_route = 0; - return 0; -} - - -/* - * Open UDP socket. - */ -__initfunc(static int root_open_udp_sock(struct socket **sock)) -{ - int err; - - if ((err = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_UDP, sock)) < 0) - printk(KERN_ERR "BOOTP: Cannot open UDP socket!\n"); - return err; -} - - -/* - * Connect UDP socket. - */ -__initfunc(static int -root_connect_udp_sock(struct socket *sock, u32 addr, u16 port)) -{ - struct sockaddr_in sa; - int result; - - set_sockaddr(&sa, htonl(addr), htons(port)); - result = sock->ops->connect(sock, (struct sockaddr *) &sa, sizeof(sa), 0); - if (result < 0) { - printk(KERN_ERR "BOOTP: connect() failed\n"); - return -1; - } - return 0; -} - - -/* - * Bind UDP socket. - */ -__initfunc(static int -root_bind_udp_sock(struct socket *sock, u32 addr, u16 port)) -{ - struct sockaddr_in sa; - int result; - - set_sockaddr(&sa, htonl(addr), htons(port)); - result = sock->ops->bind(sock, (struct sockaddr *) &sa, sizeof(sa)); - if (result < 0) { - printk(KERN_ERR "BOOTP: bind() failed\n"); - return -1; - } - return 0; -} - - -/* - * Send UDP packet. - */ -static inline int root_send_udp(struct socket *sock, void *buf, int size) -{ - mm_segment_t oldfs; - int result; - struct msghdr msg; - struct iovec iov; - - oldfs = get_fs(); - set_fs(get_ds()); - iov.iov_base = buf; - iov.iov_len = size; - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - result = sock_sendmsg(sock, &msg, size); - set_fs(oldfs); - - return (result != size); -} - - -/* - * Try to receive UDP packet. - */ -static inline int root_recv_udp(struct socket *sock, void *buf, int size) -{ - mm_segment_t oldfs; - int result; - struct msghdr msg; - struct iovec iov; - - oldfs = get_fs(); - set_fs(get_ds()); - iov.iov_base = buf; - iov.iov_len = size; - memset(&msg, 0, sizeof(msg)); - msg.msg_flags = MSG_DONTWAIT; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - result = sock_recvmsg(sock, &msg, size, MSG_DONTWAIT); - set_fs(oldfs); - return result; -} - - -/* - * Initialize BOOTP extension fields in the request. - */ -__initfunc(static void root_bootp_init_ext(u8 *e)) -{ - *e++ = 99; /* RFC1048 Magic Cookie */ - *e++ = 130; - *e++ = 83; - *e++ = 99; - *e++ = 1; /* Subnet mask request */ - *e++ = 4; - e += 4; - *e++ = 3; /* Default gateway request */ - *e++ = 4; - e += 4; - *e++ = 12; /* Host name request */ - *e++ = 32; - e += 32; - *e++ = 40; /* NIS Domain name request */ - *e++ = 32; - e += 32; - *e++ = 17; /* Boot path */ - *e++ = 32; - e += 32; - *e = 255; /* End of the list */ -} - - -/* - * Deinitialize the BOOTP mechanism. - */ -__initfunc(static void root_bootp_close(void)) -{ - if (bootp_xmit_sock) - sock_release(bootp_xmit_sock); - if (bootp_recv_sock) - sock_release(bootp_recv_sock); - root_del_bootp_route(); - root_free_bootp(); -} - - -/* - * Initialize the BOOTP mechanism. - */ -__initfunc(static int root_bootp_open(void)) -{ - struct open_dev *openp; - struct device *dev, *best_dev; - - /* - * Select the best interface for BOOTP. We try to select a first - * Ethernet-like interface. It's shame I know no simple way how to send - * BOOTP's to all interfaces, but it doesn't apply to usual diskless - * stations as they don't have multiple interfaces. - */ - - best_dev = NULL; - for (openp = open_base; openp != NULL; openp = openp->next) { - dev = openp->dev; - if (dev->flags & IFF_BROADCAST) { - if (!best_dev || - ((best_dev->flags & IFF_NOARP) && !(dev->flags & IFF_NOARP))) - best_dev = dev; - } - } - - if (!best_dev) { - printk(KERN_ERR "BOOTP: This cannot happen!\n"); - return -1; - } - bootp_dev = best_dev; - - /* Allocate memory for BOOTP packets */ - if (root_alloc_bootp()) - return -1; - - /* Construct BOOTP request */ - memset(xmit_bootp, 0, sizeof(struct bootp_pkt)); - xmit_bootp->op = BOOTP_REQUEST; - get_random_bytes(&xmit_bootp->xid, sizeof(xmit_bootp->xid)); - xmit_bootp->htype = best_dev->type; - xmit_bootp->hlen = best_dev->addr_len; - memcpy(xmit_bootp->hw_addr, best_dev->dev_addr, best_dev->addr_len); - root_bootp_init_ext(xmit_bootp->vendor_area); - -#ifdef NFSROOT_BOOTP_DEBUG - { - int x; - printk(KERN_NOTICE "BOOTP: XID=%08x, DE=%s, HT=%02x, HL=%02x, HA=", - xmit_bootp->xid, - best_dev->name, - xmit_bootp->htype, - xmit_bootp->hlen); - for(x=0; xhlen; x++) - printk("%02x", xmit_bootp->hw_addr[x]); - printk("\n"); - } -#endif - - /* Create default route to that interface */ - if (root_add_bootp_route()) - return -1; - - /* Open the sockets */ - if (root_open_udp_sock(&bootp_xmit_sock) || - root_open_udp_sock(&bootp_recv_sock)) - return -1; - - /* Bind/connect the sockets */ - bootp_xmit_sock->sk->broadcast = 1; - bootp_xmit_sock->sk->reuse = 1; - bootp_recv_sock->sk->reuse = 1; - if (root_bind_udp_sock(bootp_recv_sock, INADDR_ANY, 68) || - root_bind_udp_sock(bootp_xmit_sock, INADDR_ANY, 68) || - root_connect_udp_sock(bootp_xmit_sock, INADDR_BROADCAST, 67)) - return -1; - - return 0; -} - - -/* - * Send BOOTP request. - */ -__initfunc(static int root_bootp_send(u32 jiffies)) -{ - xmit_bootp->secs = htons(jiffies / HZ); - return root_send_udp(bootp_xmit_sock, xmit_bootp, sizeof(struct bootp_pkt)); -} - - -/* - * Copy BOOTP-supplied string if not already set. - */ -__initfunc(static int -root_bootp_string(char *dest, char *src, int len, int max)) -{ - if (*dest || !len) - return 0; - if (len > max-1) - len = max-1; - strncpy(dest, src, len); - dest[len] = '\0'; - return 1; -} - - -/* - * Process BOOTP extension. - */ -__initfunc(static void root_do_bootp_ext(u8 *ext)) -{ -#ifdef NFSROOT_BOOTP_DEBUG - u8 *c; - - printk(KERN_DEBUG "BOOTP: Got extension %02x",*ext); - for(c=ext+2; cop != BOOTP_REPLY || - recv_bootp->htype != xmit_bootp->htype || - recv_bootp->hlen != xmit_bootp->hlen || - recv_bootp->xid != xmit_bootp->xid) { - dprintk("?"); - return; - } - - /* Record BOOTP packet arrival in the global variables */ - cli(); - if (pkt_arrived) { - sti(); - return; - } - pkt_arrived = ARRIVED_BOOTP; - sti(); - root_dev = bootp_dev; - - /* Extract basic fields */ - myaddr = recv_bootp->your_ip; - if (servaddr==INADDR_NONE) - servaddr = recv_bootp->server_ip; - - /* Parse extensions */ - if (recv_bootp->vendor_area[0] == 99 && /* Check magic cookie */ - recv_bootp->vendor_area[1] == 130 && - recv_bootp->vendor_area[2] == 83 && - recv_bootp->vendor_area[3] == 99) { - ext = &recv_bootp->vendor_area[4]; - end = (u8 *) recv_bootp + len; - while (ext < end && *ext != 255) { - if (*ext == 0) /* Padding */ - ext++; - else { - opt = ext; - ext += ext[1] + 2; - if (ext <= end) - root_do_bootp_ext(opt); - } - } - } -} -#endif - - - -/*************************************************************************** - - Dynamic configuration of IP. - - ***************************************************************************/ - -#ifdef CONFIG_RNFS_DYNAMIC - -/* - * Determine client and server IP numbers and appropriate device by using - * the RARP and BOOTP protocols. - */ -__initfunc(static int root_auto_config(void)) -{ - int retries; - unsigned long timeout, jiff; - unsigned long start_jiffies; - - /* - * If neither BOOTP nor RARP was selected, return with an error. This - * routine gets only called when some pieces of information are mis- - * sing, and without BOOTP and RARP we are not able to get that in- - * formation. - */ - if (!bootp_flag && !rarp_flag) { - printk(KERN_ERR "Root-NFS: Neither RARP nor BOOTP selected.\n"); - return -1; - } - -#ifdef CONFIG_RNFS_BOOTP - if (bootp_flag && !bootp_dev_count) { - printk(KERN_ERR "Root-NFS: No suitable device for BOOTP found.\n"); - bootp_flag = 0; - } -#else - bootp_flag = 0; -#endif - -#ifdef CONFIG_RNFS_RARP - if (rarp_flag && !rarp_dev_count) { - printk(KERN_ERR "Root-NFS: No suitable device for RARP found.\n"); - rarp_flag = 0; - } -#else - rarp_flag = 0; -#endif - - if (!bootp_flag && !rarp_flag) - /* Error message already printed */ - return -1; - - /* - * Setup RARP and BOOTP protocols - */ -#ifdef CONFIG_RNFS_RARP - if (rarp_flag) - root_rarp_open(); -#endif -#ifdef CONFIG_RNFS_BOOTP - if (bootp_flag && root_bootp_open() < 0) { - root_bootp_close(); - return -1; - } -#endif - - /* - * Send requests and wait, until we get an answer. This loop - * seems to be a terrible waste of CPU time, but actually there is - * only one process running at all, so we don't need to use any - * scheduler functions. - * [Actually we could now, but the nothing else running note still - * applies.. - AC] - */ - printk(KERN_NOTICE "Sending %s%s%s requests...", - bootp_flag ? "BOOTP" : "", - bootp_flag && rarp_flag ? " and " : "", - rarp_flag ? "RARP" : ""); - start_jiffies = jiffies; - retries = CONF_RETRIES; - get_random_bytes(&timeout, sizeof(timeout)); - timeout = CONF_BASE_TIMEOUT + (timeout % (unsigned) CONF_TIMEOUT_RANDOM); - for(;;) { -#ifdef CONFIG_RNFS_BOOTP - if (bootp_flag && root_bootp_send(jiffies - start_jiffies) < 0) { - printk(" BOOTP failed!\n"); - root_bootp_close(); - bootp_flag = 0; - if (!rarp_flag) - break; - } -#endif -#ifdef CONFIG_RNFS_RARP - if (rarp_flag) - root_rarp_send(); -#endif - printk("."); - jiff = jiffies + timeout; - while (jiffies < jiff && !pkt_arrived) -#ifdef CONFIG_RNFS_BOOTP - root_bootp_recv(); -#else - ; -#endif - if (pkt_arrived) { - printk(" OK\n"); - break; - } - if (! --retries) { - printk(" timed out!\n"); - break; - } - timeout = timeout CONF_TIMEOUT_MULT; - if (timeout > CONF_TIMEOUT_MAX) - timeout = CONF_TIMEOUT_MAX; - } - -#ifdef CONFIG_RNFS_RARP - if (rarp_flag) - root_rarp_close(); -#endif -#ifdef CONFIG_RNFS_BOOTP - if (bootp_flag) - root_bootp_close(); -#endif - - if (!pkt_arrived) - return -1; - - printk(KERN_NOTICE "Root-NFS: Got %s answer from %s, ", - (pkt_arrived == ARRIVED_BOOTP) ? "BOOTP" : "RARP", - in_ntoa(servaddr)); - printk("my address is %s\n", in_ntoa(myaddr)); - - return 0; -} -#endif - -/* Get default netmask - used to be exported from net/ipv4 */ -static inline unsigned long -ip_get_mask(unsigned long addr) -{ - if (!addr) - return 0; - addr = ntohl(addr); - if (IN_CLASSA(addr)) - return htonl(IN_CLASSA_NET); - if (IN_CLASSB(addr)) - return htonl(IN_CLASSB_NET); - if (IN_CLASSC(addr)) - return htonl(IN_CLASSC_NET); - return 0; -} - -/*************************************************************************** - Parsing of options ***************************************************************************/ - /* * The following integer options are recognized */ @@ -1125,31 +175,32 @@ *cp++ = '\0'; servaddr = in_aton(name); name = cp; + } else if ((servaddr = root_server_addr) == INADDR_NONE) { + printk(KERN_ERR "Root-NFS: No NFS server available, giving up.\n"); + return -1; } /* Clear the nfs_data structure and setup the server hostname */ memset(&nfs_data, 0, sizeof(nfs_data)); - strncpy(nfs_data.hostname, in_ntoa(servaddr), - sizeof(nfs_data.hostname)-1); + strncpy(nfs_data.hostname, in_ntoa(servaddr), sizeof(nfs_data.hostname)-1); nfs_data.namlen = strlen(nfs_data.hostname); /* Set the name of the directory to mount */ - if (nfs_path[0] == '\0' || strncmp(name, "default", 7)) - strncpy(buf, name, NFS_MAXPATHLEN); + if (root_server_path[0] && !strcmp(name, "default")) + strncpy(buf, root_server_path, NFS_MAXPATHLEN-1); else - strncpy(buf, nfs_path, NFS_MAXPATHLEN); + strncpy(buf, name, NFS_MAXPATHLEN-1); + buf[NFS_MAXPATHLEN-1] = '\0'; if ((options = strchr(buf, ','))) *options++ = '\0'; if (!strcmp(buf, "default")) strcpy(buf, NFS_ROOT); - cp = in_ntoa(myaddr); + cp = system_utsname.nodename; if (strlen(buf) + strlen(cp) > NFS_MAXPATHLEN) { printk(KERN_ERR "Root-NFS: Pathname for remote directory too long.\n"); return -1; } - /* update nfs_path with path from nfsroot=... command line parameter */ - if (*buf) - sprintf(nfs_path, buf, cp); + sprintf(nfs_path, buf, cp); /* Set some default values */ nfs_port = -1; @@ -1198,17 +249,6 @@ #ifdef NFSROOT_DEBUG __initfunc(static void root_nfs_print(void)) { -#define IN_NTOA(x) (((x) == INADDR_NONE) ? "none" : in_ntoa(x)) - - printk(KERN_NOTICE "Root-NFS: IP config: dev=%s, ", - root_dev ? root_dev->name : "none"); - printk("local=%s, ", IN_NTOA(myaddr)); - printk("server=%s, ", IN_NTOA(servaddr)); - printk("gw=%s, ", IN_NTOA(gateway)); - printk("mask=%s, ", IN_NTOA(netmask)); - printk("host=%s, domain=%s\n", - system_utsname.nodename[0] ? system_utsname.nodename : "none", - system_utsname.domainname[0] ? system_utsname.domainname : "none"); printk(KERN_NOTICE "Root-NFS: Mounting %s on server %s as root\n", nfs_path, nfs_data.hostname); printk(KERN_NOTICE "Root-NFS: rsize = %d, wsize = %d, timeo = %d, retrans = %d\n", @@ -1216,245 +256,25 @@ printk(KERN_NOTICE "Root-NFS: acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n", nfs_data.acregmin, nfs_data.acregmax, nfs_data.acdirmin, nfs_data.acdirmax); - printk(KERN_NOTICE "Root-NFS: port = %d, flags = %08x\n", - nfs_port, nfs_data.flags); - -#undef IN_NTOA + printk(KERN_NOTICE "Root-NFS: nfsd port = %d, mountd port = %d, flags = %08x\n", + nfs_port, mount_port, nfs_data.flags); } #endif -/* - * Decode any IP configuration options in the "nfsaddrs" kernel command - * line parameter. It consists of option fields separated by colons in - * the following order: - * - * :::::: - * - * Any of the fields can be empty which means to use a default value: - * - address given by BOOTP or RARP - * - address of host returning BOOTP or RARP packet - * - none, or the address returned by BOOTP - * - automatically determined from , or the - * one returned by BOOTP - * - in ASCII notation, or the name returned - * by BOOTP - * - use all available devices for RARP and the first - * one for BOOTP - * - use both protocols to determine my own address - */ -__initfunc(static void root_nfs_addrs(char *addrs)) -{ - char *cp, *ip, *dp; - int num = 0; - - /* Clear all addresses and strings */ - myaddr = servaddr = rarp_serv = gateway = netmask = INADDR_NONE; - system_utsname.nodename[0] = '\0'; - system_utsname.domainname[0] = '\0'; - user_dev_name[0] = '\0'; - bootp_flag = rarp_flag = 1; - - /* The following is just a shortcut for automatic IP configuration */ - if (!strcmp(addrs, "bootp")) { - rarp_flag = 0; - return; - } else if (!strcmp(addrs, "rarp")) { - bootp_flag = 0; - return; - } else if (!strcmp(addrs, "both")) { - return; - } - - /* Parse the whole string */ - ip = addrs; - while (ip && *ip) { - if ((cp = strchr(ip, ':'))) - *cp++ = '\0'; - if (strlen(ip) > 0) { - dprintk("Root-NFS: Config string num %d is \"%s\"\n", - num, ip); - switch (num) { - case 0: - if ((myaddr = in_aton(ip)) == INADDR_ANY) - myaddr = INADDR_NONE; - break; - case 1: - if ((servaddr = in_aton(ip)) == INADDR_ANY) - servaddr = INADDR_NONE; - break; - case 2: - if ((gateway = in_aton(ip)) == INADDR_ANY) - gateway = INADDR_NONE; - break; - case 3: - if ((netmask = in_aton(ip)) == INADDR_ANY) - netmask = INADDR_NONE; - break; - case 4: - if ((dp = strchr(ip, '.'))) { - *dp++ = '\0'; - strncpy(system_utsname.domainname, dp, __NEW_UTS_LEN); - system_utsname.domainname[__NEW_UTS_LEN] = '\0'; - } - strncpy(system_utsname.nodename, ip, __NEW_UTS_LEN); - system_utsname.nodename[__NEW_UTS_LEN] = '\0'; - break; - case 5: - strncpy(user_dev_name, ip, IFNAMSIZ); - user_dev_name[IFNAMSIZ-1] = '\0'; - break; - case 6: - if (!strcmp(ip, "rarp")) - bootp_flag = 0; - else if (!strcmp(ip, "bootp")) - rarp_flag = 0; - else if (strcmp(ip, "both")) - bootp_flag = rarp_flag = 0; - break; - default: - break; - } - } - ip = cp; - num++; - } - rarp_serv = servaddr; -} - - -/* - * Set the interface address and configure a route to the server. - */ -__initfunc(static int root_nfs_setup(void)) -{ - /* Set the default system name in case none was previously found */ - if (!system_utsname.nodename[0]) { - strncpy(system_utsname.nodename, in_ntoa(myaddr), __NEW_UTS_LEN); - system_utsname.nodename[__NEW_UTS_LEN] = '\0'; - } - - /* Set the correct netmask */ - if (netmask == INADDR_NONE) - netmask = ip_get_mask(myaddr); - - /* Setup the device correctly */ - root_dev->family = AF_INET; - root_dev->pa_addr = myaddr; - root_dev->pa_mask = netmask; - root_dev->pa_brdaddr = root_dev->pa_addr | ~root_dev->pa_mask; - root_dev->pa_dstaddr = 0; - - /* Sticky situation, but it has a solution. We opened it earlier, - * but before we knew what pa_addr etc. to give to it, thus the - * routing code did not add a RTF_LOCAL route for it (how could - * it?) so we send the pseudo device state change event now. -DaveM - */ - ip_rt_event(NETDEV_CHANGE, root_dev); - - /* - * Now add a route to the server. If there is no gateway given, - * the server is on the same subnet, so we establish only a route to - * the local network. Otherwise we create a route to the gateway (the - * same local network router as in the former case) and then setup a - * gatewayed default route. Note that this gives sufficient network - * setup even for full system operation in all common cases. - */ - if (root_dev_add_route(root_dev, myaddr, netmask, 0)) - { - printk(KERN_ERR "Root-NFS: Adding of local route failed!\n"); - return -1; - } - - if (gateway != INADDR_NONE) { /* Default route */ - if (root_dev_add_route(root_dev, INADDR_ANY, INADDR_ANY, gateway)) { - printk(KERN_ERR "Root-NFS: Adding of default route failed!\n"); - return -1; - } - } else if ((servaddr ^ myaddr) & netmask) { - printk(KERN_ERR "Root-NFS: Boot server not on local network and no default gateway configured!\n"); - return -1; - } - - return 0; -} - - -/* - * Get the necessary IP addresses and prepare for mounting the required - * NFS filesystem. - */ -__initfunc(int nfs_root_init(char *nfsname, char *nfsaddrs)) +__initfunc(int root_nfs_init(void)) { #ifdef NFSROOT_DEBUG nfs_debug |= NFSDBG_ROOT; #endif /* - * Decode IP addresses and other configuration info contained - * in the nfsaddrs string (which came from the kernel command - * line). - */ - root_nfs_addrs(nfsaddrs); - - /* - * Setup all network devices - */ - if (root_dev_open() < 0) - return -1; - - /* - * If the config information is insufficient (e.g., our IP address or - * IP address of the boot server is missing or we have multiple network - * interfaces and no default was set), use BOOTP or RARP to get the - * missing values. - * - * Note that we don't try to set up correct routes for multiple - * interfaces (could be solved by trying icmp echo requests), because - * it's only necessary in the rare case of multiple ethernet devices - * in the (diskless) system and if the server is on another subnet. - * If only one interface is installed, the routing is obvious. - */ - if ((myaddr == INADDR_NONE || - servaddr == INADDR_NONE || - (open_base != NULL && open_base->next != NULL)) -#ifdef CONFIG_RNFS_DYNAMIC - && root_auto_config() < 0 -#endif - ) { - root_dev_close(); - return -1; - } - if (root_dev == NULL) { - if (open_base != NULL && open_base->next == NULL) { - root_dev = open_base->dev; - } else { - printk(KERN_ERR "Root-NFS: Multiple devices and no server\n"); - root_dev_close(); - return -1; - } - } - - /* - * Close all network devices except the device which connects to - * server - */ - root_dev_close(); - - /* * Decode the root directory path name and NFS options from * the kernel command line. This has to go here in order to * be able to use the client IP address for the remote root * directory (necessary for pure RARP booting). */ - if (root_nfs_name(nfsname) < 0) - return -1; - - /* - * Setup devices and routes. The server directory is actually - * mounted after init() has been started. - */ - if (root_nfs_setup() < 0) + if (root_nfs_name(nfs_root_name) < 0) return -1; #ifdef NFSROOT_DEBUG @@ -1465,34 +285,64 @@ } +/* + * Parse NFS server and directory information passed on the kernel + * command line. + */ +__initfunc(void nfs_root_setup(char *line, int *ints)) +{ + ROOT_DEV = MKDEV(UNNAMED_MAJOR, 255); + if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) { + strncpy(nfs_root_name, line, sizeof(nfs_root_name)); + nfs_root_name[sizeof(nfs_root_name)-1] = '\0'; + } else { + int n = strlen(line) + strlen(NFS_ROOT); + if (n >= sizeof(nfs_root_name)) + line[sizeof(nfs_root_name) - strlen(NFS_ROOT) - 1] = '\0'; + sprintf(nfs_root_name, NFS_ROOT, line); + } +} + + /*************************************************************************** Routines to actually mount the root directory ***************************************************************************/ + /* - * Query server portmapper for the port of a daemon program + * Construct sockaddr_in from address and port number. + */ +static inline void +set_sockaddr(struct sockaddr_in *sin, __u32 addr, __u16 port) +{ + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = addr; + sin->sin_port = port; +} + +/* + * Query server portmapper for the port of a daemon program. */ __initfunc(static int root_nfs_getport(int program, int version)) { - struct sockaddr_in sin; + struct sockaddr_in sin; printk(KERN_NOTICE "Looking up port of RPC %d/%d on %s\n", program, version, in_ntoa(servaddr)); - set_sockaddr(&sin, servaddr, 0); - return rpc_getport_external(&sin, program, version, IPPROTO_UDP); + set_sockaddr(&sin, servaddr, 0); + return rpc_getport_external(&sin, program, version, IPPROTO_UDP); } /* - * Get portnumbers for mountd and nfsd from server - * The RPC layer does support portmapper queries; the only reason to - * keep this code is that we may want to use fallback ports. But is there - * actually someone who does not run portmap? + * Use portmapper to find mountd and nfsd port numbers if not overriden + * by the user. Use defaults if portmapper is not available. + * XXX: Is there any nfs server with no portmapper? */ __initfunc(static int root_nfs_ports(void)) { - int port; + int port; if (nfs_port < 0) { if ((port = root_nfs_getport(NFS_PROGRAM, NFS_VERSION)) < 0) { @@ -1510,10 +360,8 @@ "number from server, using default\n"); port = NFS_MNT_PORT; } - mount_port = htons(port); - dprintk("Root-NFS: Portmapper on server returned %d " - "as mountd port\n", port); + dprintk("Root-NFS: mountd port is %d\n", port); return 0; } @@ -1521,12 +369,12 @@ /* * Get a file handle from the server for the directory which is to be - * mounted + * mounted. */ __initfunc(static int root_nfs_get_handle(void)) { struct sockaddr_in sin; - int status; + int status; set_sockaddr(&sin, servaddr, mount_port); status = nfs_mount(&sin, nfs_path, &nfs_data.root); @@ -1539,7 +387,7 @@ /* - * Now actually mount the given directory + * Now actually mount the given directory. */ __initfunc(static int root_nfs_do_mount(struct super_block *sb)) { @@ -1559,11 +407,10 @@ */ __initfunc(int nfs_root_mount(struct super_block *sb)) { - if (root_nfs_ports() < 0) - return -1; - if (root_nfs_get_handle() < 0) - return -1; - if (root_nfs_do_mount(sb) < 0) + if (root_nfs_init() < 0 + || root_nfs_ports() < 0 + || root_nfs_get_handle() < 0 + || root_nfs_do_mount(sb) < 0) return -1; return 0; } diff -u --recursive --new-file v2.1.74/linux/fs/nfs/write.c linux/fs/nfs/write.c --- v2.1.74/linux/fs/nfs/write.c Fri Dec 19 15:53:00 1997 +++ linux/fs/nfs/write.c Sun Dec 21 17:33:27 1997 @@ -832,7 +832,7 @@ req->wb_flags |= NFS_WRITE_WANTLOCK; if (WB_WANTLOCK(req) && test_and_set_bit(PG_locked, &page->flags)) { - dprintk("NFS: page already locked in writeback_lock!\n"); + printk("NFS: page already locked in writeback_lock!\n"); task->tk_timeout = 2 * HZ; rpc_sleep_on(&write_queue, task, NULL, NULL); return; diff -u --recursive --new-file v2.1.74/linux/fs/nls/nls_base.c linux/fs/nls/nls_base.c --- v2.1.74/linux/fs/nls/nls_base.c Sat Nov 1 11:04:27 1997 +++ linux/fs/nls/nls_base.c Sun Dec 21 17:41:24 1997 @@ -205,8 +205,10 @@ struct nls_table *load_nls(char *charset) { struct nls_table *nls; +#ifdef CONFIG_KERNELD char buf[40]; int ret; +#endif nls = find_nls(charset); if (nls) { diff -u --recursive --new-file v2.1.74/linux/fs/super.c linux/fs/super.c --- v2.1.74/linux/fs/super.c Tue Dec 2 09:49:40 1997 +++ linux/fs/super.c Sun Dec 21 17:31:17 1997 @@ -419,6 +419,11 @@ current->state = TASK_RUNNING; } +/* + * Note: check the dirty flag before waiting, so we don't + * hold up the sync while mounting a device. (The newly + * mounted device won't need syncing.) + */ void sync_supers(kdev_t dev) { struct super_block * sb; @@ -428,6 +433,9 @@ continue; if (dev && sb->s_dev != dev) continue; + if (!sb->s_dirt) + continue; + /* N.B. Should lock the superblock while writing */ wait_on_super(sb); if (!sb->s_dev || !sb->s_dirt) continue; @@ -444,13 +452,14 @@ if (!dev) return NULL; +restart: s = 0+super_blocks; while (s < NR_SUPER+super_blocks) if (s->s_dev == dev) { wait_on_super(s); if (s->s_dev == dev) return s; - s = 0+super_blocks; + goto restart; } else s++; return NULL; @@ -494,33 +503,44 @@ struct file_system_type *type; if (!dev) - return NULL; + goto out_fail; check_disk_change(dev); s = get_super(dev); if (s) return s; - if (!(type = get_fs_type(name))) { + type = get_fs_type(name); + if (!type) { printk("VFS: on device %s: get_fs_type(%s) failed\n", kdevname(dev), name); - return NULL; + goto out_fail; } for (s = 0+super_blocks ;; s++) { if (s >= NR_SUPER+super_blocks) - return NULL; - if (!(s->s_dev)) - break; + goto out_fail; + if (s->s_dev) + continue; + if (s->s_lock) { + printk("VFS: empty superblock %p locked!\n", s); + continue; + } + break; } s->s_dev = dev; s->s_flags = flags; - if (!type->read_super(s,data, silent)) { - s->s_dev = 0; - return NULL; - } - s->s_dev = dev; - s->s_rd_only = 0; s->s_dirt = 0; + /* N.B. Should lock superblock now ... */ + if (!type->read_super(s,data, silent)) + goto fail; + s->s_dev = dev; /* N.B. why do this again?? */ + s->s_rd_only = 0; s->s_type = type; return s; + + /* N.B. s_dev should be cleared in type->read_super */ +fail: + s->s_dev = 0; +out_fail: + return NULL; } /* @@ -1040,35 +1060,26 @@ int retval; #ifdef CONFIG_ROOT_NFS - if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) - if (nfs_root_init(nfs_root_name, nfs_root_addrs) < 0) { - printk(KERN_ERR "Root-NFS: Unable to contact NFS " - "server for root fs, using /dev/fd0 instead\n"); - ROOT_DEV = MKDEV(FLOPPY_MAJOR, 0); - } if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) { ROOT_DEV = 0; if ((fs_type = get_fs_type("nfs"))) { - sb = &super_blocks[0]; - while (sb->s_dev) sb++; - sb->s_dev = get_unnamed_dev(); - sb->s_flags = root_mountflags & ~MS_RDONLY; - if (nfs_root_mount(sb) >= 0) { - sb->s_rd_only = 0; - sb->s_dirt = 0; - sb->s_type = fs_type; - 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", "/"); - if (!vfsmnt) - panic("VFS: add_vfsmnt failed for NFS root.\n"); - vfsmnt->mnt_sb = sb; - vfsmnt->mnt_flags = sb->s_flags; - return; + if ((vfsmnt = add_vfsmnt(ROOT_DEV, "/dev/root", "/"))) { + sb = vfsmnt->mnt_sb; + sb->s_dev = get_unnamed_dev(); + sb->s_flags = root_mountflags & ~MS_RDONLY; + if (nfs_root_mount(sb) >= 0) { + sb->s_rd_only = 0; + sb->s_dirt = 0; + sb->s_type = fs_type; + 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"); + return; + } + sb->s_dev = 0; + put_unnamed_dev(sb->s_dev); } - sb->s_dev = 0; } if (!ROOT_DEV) { printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n"); diff -u --recursive --new-file v2.1.74/linux/fs/sysv/namei.c linux/fs/sysv/namei.c --- v2.1.74/linux/fs/sysv/namei.c Fri Dec 19 15:53:02 1997 +++ linux/fs/sysv/namei.c Sun Dec 21 17:41:24 1997 @@ -420,9 +420,9 @@ retval = -EPERM; inode = dentry->d_inode; - if ((dir->i_mode & S_ISVTX) && !fsuser() && + if ((dir->i_mode & S_ISVTX) && current->fsuid != inode->i_uid && - current->fsuid != dir->i_uid) + current->fsuid != dir->i_uid && !fsuser()) goto end_rmdir; if (inode->i_dev != dir->i_dev) goto end_rmdir; @@ -485,9 +485,9 @@ schedule(); goto repeat; } - if ((dir->i_mode & S_ISVTX) && !fsuser() && + if ((dir->i_mode & S_ISVTX) && current->fsuid != inode->i_uid && - current->fsuid != dir->i_uid) + current->fsuid != dir->i_uid && !fsuser()) goto end_unlink; if (de->inode != inode->i_ino) { retval = -ENOENT; diff -u --recursive --new-file v2.1.74/linux/fs/umsdos/namei.c linux/fs/umsdos/namei.c --- v2.1.74/linux/fs/umsdos/namei.c Mon Jun 16 16:35:59 1997 +++ linux/fs/umsdos/namei.c Sun Dec 21 17:41:24 1997 @@ -335,16 +335,18 @@ PRINTK (("ret %d ",ret)); if (ret == 0){ /* check sticky bit on old_dir */ - if ( !(old_dir->i_mode & S_ISVTX) || fsuser() || + if ( !(old_dir->i_mode & S_ISVTX) || current->fsuid == old_info.entry.uid || - current->fsuid == old_dir->i_uid ) { + current->fsuid == old_dir->i_uid || + fsuser()) { /* Does new_name already exist? */ PRINTK(("new findentry ")); ret = umsdos_findentry(new_dir,&new_info,0); if (ret != 0 || /* if destination file exists, are we allowed to replace it ? */ - !(new_dir->i_mode & S_ISVTX) || fsuser() || + !(new_dir->i_mode & S_ISVTX) || current->fsuid == new_info.entry.uid || - current->fsuid == new_dir->i_uid ) { + current->fsuid == new_dir->i_uid || + fsuser()) { PRINTK (("new newentry ")); umsdos_ren_init(&new_info,&old_info,flags); ret = umsdos_newentry (new_dir,&new_info); @@ -885,9 +887,10 @@ PRINTK (("isempty %d i_count %d ",empty, atomic_read(&sdir->i_count))); /* check sticky bit */ - if ( !(dir->i_mode & S_ISVTX) || fsuser() || + if ( !(dir->i_mode & S_ISVTX) || current->fsuid == sdir->i_uid || - current->fsuid == dir->i_uid ) { + current->fsuid == dir->i_uid || + fsuser()) { if (empty == 1){ /* We have to removed the EMD file */ ret = msdos_unlink(sdir,UMSDOS_EMD_FILE @@ -948,9 +951,10 @@ if (ret == 0){ PRINTK (("UMSDOS_unlink %s ",info.fake.fname)); /* check sticky bit */ - if ( !(dir->i_mode & S_ISVTX) || fsuser() || + if ( !(dir->i_mode & S_ISVTX) || current->fsuid == info.entry.uid || - current->fsuid == dir->i_uid ) { + current->fsuid == dir->i_uid || + fsuser()) { if (info.entry.flags & UMSDOS_HLINK){ /* #Specification: hard link / deleting a link When we deletes a file, and this file is a link diff -u --recursive --new-file v2.1.74/linux/fs/vfat/namei.c linux/fs/vfat/namei.c --- v2.1.74/linux/fs/vfat/namei.c Thu Dec 4 14:53:56 1997 +++ linux/fs/vfat/namei.c Sat Dec 20 21:33:20 1997 @@ -50,9 +50,29 @@ int long_slots; ino_t ino; int posix; + int anycase; }; void vfat_read_inode(struct inode *inode); +static int vfat_valid_shortname(const char *,int, int, int); +static int vfat_format_name(const char *, int, char *, int, int); +static int vfat_valid_longname(const char *, int, int, int); + +static int strnicmp(const char *s1, const char *s2, int len) +{ + int n = 0; + while (*s1 && *s2 && (tolower(*s1) == tolower(*s2))) { + s1++; s2++; n++; + if (n == len) return 0; + } + if (*s1 == 0 && *s2 == 0) return 0; + if (*s1 && *s2) { + if (*s1 > *s2) return 1; + return -1; + } + if (*s1) return 1; + return -1; +} void vfat_put_super(struct super_block *sb) { @@ -138,6 +158,60 @@ return 1; } +/* + * Compute the hash for the vfat name corresponding to the dentry. + * Note: if the name is invalid, we leave the hash code unchanged so + * that the existing dentry can be used. The vfat fs routines will + * return ENOENT or EINVAL as appropriate. + */ +static int vfat_hash(struct dentry *dentry, struct qstr *qstr) +{ + const char *name; + int len; + char c; + unsigned long hash; + + len = qstr->len; + name = qstr->name; + hash = init_name_hash(); + while (len--) { + c = tolower(*name++); + hash = partial_name_hash(tolower(c), hash); + } + qstr->hash = end_name_hash(hash); + + return 0; +} + +/* + * Compare two vfat names. + */ +static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b) +{ + int alen, blen; + + /* A filename cannot end in '.' or we treat it like it has none */ + alen = a->len; + blen = b->len; + if (alen != blen) { + if (a->name[alen-1] == '.') + alen--; + if (b->name[blen-1] == '.') + blen--; + if (alen != blen) + return 1; + } + + return strnicmp(a->name, b->name, alen); +} + +static struct dentry_operations vfat_dentry_operations = { + NULL, /* d_revalidate */ + vfat_hash, + vfat_cmp, + NULL /* d_delete */ +}; + struct super_block *vfat_read_super(struct super_block *sb,void *data, int silent) { @@ -159,6 +233,9 @@ MOD_DEC_USE_COUNT; } else { MSDOS_SB(sb)->options.dotsOK = 0; + if (MSDOS_SB(sb)->options.name_check != 's') { + sb->s_root->d_op = &vfat_dentry_operations; + } } return res; @@ -245,22 +322,6 @@ int find_long,int new_filename,int is_dir, struct slot_info *sinfo_out); -static int strnicmp(const char *s1, const char *s2, int len) -{ - int n = 0; - while (*s1 && *s2 && (tolower(*s1) == tolower(*s2))) { - s1++; s2++; n++; - if (n == len) return 0; - } - if (*s1 == 0 && *s2 == 0) return 0; - if (*s1 && *s2) { - if (*s1 > *s2) return 1; - return -1; - } - if (*s1) return 1; - return -1; -} - /* Checks the validity of a long MS-DOS filename */ /* Returns negative number on error, 0 for a normal * return, and 1 for . or .. */ @@ -888,7 +949,7 @@ s1 = name; s2 = vf->name; for (i = 0; i < name_len; i++) { - if (vf->new_filename && !vf->posix) { + if (vf->anycase || (vf->new_filename && !vf->posix)) { if (tolower(*s1) != tolower(*s2)) return 0; } else { @@ -933,6 +994,7 @@ vf.new_filename = new_filename; vf.found = 0; vf.posix = MSDOS_SB(sb)->options.posixfs; + vf.anycase = (MSDOS_SB(sb)->options.name_check != 's'); res = fat_readdirx(dir,&fil,(void *)&vf,vfat_readdir_cb,NULL,1,find_long,0); PRINTK(("vfat_find: Debug 1\n")); if (res < 0) goto cleanup; @@ -1038,6 +1100,10 @@ PRINTK (("vfat_lookup: name=%s, len=%d\n", dentry->d_name.name, dentry->d_name.len)); + + if (MSDOS_SB(dir->i_sb)->options.name_check != 's') { + dentry->d_op = &vfat_dentry_operations; + } result = NULL; if ((res = vfat_find(dir,&dentry->d_name,1,0,0,&sinfo)) < 0) { diff -u --recursive --new-file v2.1.74/linux/include/asm-alpha/socket.h linux/include/asm-alpha/socket.h --- v2.1.74/linux/include/asm-alpha/socket.h Wed Dec 10 11:12:45 1997 +++ linux/include/asm-alpha/socket.h Sun Dec 21 17:41:24 1997 @@ -39,13 +39,13 @@ #define SO_PEERCRED 18 #define SO_BINDTODEVICE 25 -#define SO_BINDTODEVICE 25 +/* Socket filtering */ +#define SO_ATTACH_FILTER 26 +#define SO_DETACH_FILTER 27 /* Security levels - as per NRL IPv6 - don't actually do anything */ #define SO_SECURITY_AUTHENTICATION 19 #define SO_SECURITY_ENCRYPTION_TRANSPORT 20 #define SO_SECURITY_ENCRYPTION_NETWORK 21 - -#define SO_BINDTODEVICE 22 #endif /* _ASM_SOCKET_H */ diff -u --recursive --new-file v2.1.74/linux/include/asm-i386/bugs.h linux/include/asm-i386/bugs.h --- v2.1.74/linux/include/asm-i386/bugs.h Mon Nov 17 18:47:22 1997 +++ linux/include/asm-i386/bugs.h Sun Dec 21 17:58:35 1997 @@ -18,12 +18,12 @@ __initfunc(static void no_halt(char *s, int *ints)) { - hlt_works_ok = 0; + boot_cpu_data.hlt_works_ok = 0; } __initfunc(static void no_387(char *s, int *ints)) { - hard_math = 0; + boot_cpu_data.hard_math = 0; __asm__("movl %%cr0,%%eax\n\t" "orl $0xE,%%eax\n\t" "movl %%eax,%%cr0\n\t" : : : "ax"); @@ -49,7 +49,7 @@ { unsigned short control_word; - if (!hard_math) { + if (!boot_cpu_data.hard_math) { #ifndef CONFIG_MATH_EMULATION printk(KERN_EMERG "No coprocessor found and no math emulation present.\n"); printk(KERN_EMERG "Giving up.\n"); @@ -91,9 +91,9 @@ "fistpl %0\n\t" "fwait\n\t" "fninit" - : "=m" (*&fdiv_bug) + : "=m" (*&boot_cpu_data.fdiv_bug) : "m" (*&x), "m" (*&y)); - if (!fdiv_bug) + if (!boot_cpu_data.fdiv_bug) printk("Ok, fpu using exception 16 error reporting.\n"); else printk("Hmm, fpu using exception 16 error reporting with FDIV bug.\n"); @@ -102,7 +102,7 @@ __initfunc(static void check_hlt(void)) { printk(KERN_INFO "Checking 'hlt' instruction... "); - if (!hlt_works_ok) { + if (!boot_cpu_data.hlt_works_ok) { printk("disabled\n"); return; } @@ -117,7 +117,7 @@ * The 386 chips don't support TLB finegrained invalidation. * They will fault when they hit an invlpg instruction. */ - if (x86 == 3) { + if (boot_cpu_data.x86 == 3) { printk(KERN_EMERG "CPU is a 386 and this kernel was compiled for 486 or better.\n"); printk("Giving up.\n"); for (;;) ; @@ -152,17 +152,53 @@ * misexecution of code under Linux. Owners of such processors should * contact AMD for precise details and a CPU swap. * - * See http://www.creaweb.fr/bpc/k6bug_faq.html + * See http://www.chorus.com/~poulot/k6bug.html * http://www.amd.com/K6/k6docs/revgd.html + * + * The following test is erm.. interesting. AMD neglected to up + * the chip setting when fixing the bug but they also tweaked some + * performance at the same time.. */ +extern void vide(void); +__asm__(".align 4\nvide: ret"); + __initfunc(static void check_amd_k6(void)) { - /* B Step AMD K6 */ - if(x86_model==6 && x86_mask==1 && memcmp(x86_vendor_id, "AuthenticAMD", 12)==0) + if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && + boot_cpu_data.x86_model == 6 && + boot_cpu_data.x86_mask == 1) { - printk(KERN_INFO "AMD K6 stepping B detected - system stability may be impaired. Please see.\n"); - printk(KERN_INFO "http://www.creaweb.fr/bpc/k6bug_faq.html"); + int n; + void (*f_vide)(void); + unsigned long d, d2; + + printk(KERN_INFO "AMD K6 stepping B detected - "); + +#define K6_BUG_LOOP 1000000 + + /* + * It looks like AMD fixed the 2.6.2 bug and improved indirect + * calls at the same time. + */ + + n = K6_BUG_LOOP; + f_vide = vide; + __asm__ ("rdtsc" : "=a" (d)); + while (n--) + f_vide(); + __asm__ ("rdtsc" : "=a" (d2)); + d = d2-d; + + /* Knock these two lines out if it debugs out ok */ + printk(KERN_INFO "K6 BUG %ld %d (Report these if test report is incorrect)\n", d, 20*K6_BUG_LOOP); + printk(KERN_INFO "AMD K6 stepping B detected - "); + /* -- cut here -- */ + if (d > 20*K6_BUG_LOOP) + printk(KERN_INFO "system stability may be impaired when more than 32 MB are used.\n"); + else + printk(KERN_INFO "probably OK (after B9730xxxx).\n"); + printk(KERN_INFO "Please see http://www.chorus.com/bpc/k6bug.html\n"); } } @@ -171,30 +207,33 @@ * have the F0 0F bug, which lets nonpriviledged users lock up the system: */ -extern int pentium_f00f_bug; extern void trap_init_f00f_bug(void); - __initfunc(static void check_pentium_f00f(void)) { /* * Pentium and Pentium MMX */ - pentium_f00f_bug = 0; - if (x86==5 && !memcmp(x86_vendor_id, "GenuineIntel", 12)) { + boot_cpu_data.f00f_bug = 0; + if (boot_cpu_data.x86 == 5 && boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) { printk(KERN_INFO "Intel Pentium with F0 0F bug - workaround enabled.\n"); - pentium_f00f_bug = 1; + boot_cpu_data.f00f_bug = 1; trap_init_f00f_bug(); } } __initfunc(static void check_bugs(void)) { +#ifndef __SMP__ + identify_cpu(&boot_cpu_data); + printk("CPU: "); + print_cpu_info(&boot_cpu_data); +#endif check_tlb(); check_fpu(); check_hlt(); check_popad(); check_amd_k6(); check_pentium_f00f(); - system_utsname.machine[1] = '0' + x86; + system_utsname.machine[1] = '0' + boot_cpu_data.x86; } diff -u --recursive --new-file v2.1.74/linux/include/asm-i386/processor.h linux/include/asm-i386/processor.h --- v2.1.74/linux/include/asm-i386/processor.h Fri Dec 19 15:53:02 1997 +++ linux/include/asm-i386/processor.h Sun Dec 21 17:27:18 1997 @@ -12,21 +12,51 @@ #include /* - * System setup and hardware bug flags.. - * [Note we don't test the 386 multiply bug or popad bug] + * CPU type and hardware bug flags. Kept separately for each CPU. + * Members of this structure are referenced in head.S, so think twice + * before touching them. [mj] */ -extern char hard_math; -extern char x86; /* lower 4 bits */ -extern char x86_vendor_id[13]; -extern char x86_model; /* lower 4 bits */ -extern char x86_mask; /* lower 4 bits */ -extern int x86_capability; /* field of flags */ -extern int fdiv_bug; +struct cpuinfo_x86 { + u8 x86; /* CPU family */ + u8 x86_vendor; /* CPU vendor */ + u8 x86_model; + u8 x86_mask; + char wp_works_ok; /* It doesn't on 386's */ + char hlt_works_ok; /* Problems on some 486Dx4's and old 386's */ + char hard_math; + char rfu; + int cpuid_level; /* Maximum supported CPUID level, -1=no CPUID */ + u32 x86_capability; + char x86_vendor_id[16]; + char x86_model_id[64]; + int fdiv_bug; + int f00f_bug; + unsigned long loops_per_sec; +}; + +#define X86_VENDOR_INTEL 0 +#define X86_VENDOR_CYRIX 1 +#define X86_VENDOR_AMD 2 +#define X86_VENDOR_UMC 3 +#define X86_VENDOR_NEXGEN 4 +#define X86_VENDOR_CENTAUR 5 +#define X86_VENDOR_UNKNOWN 0xff + +extern struct cpuinfo_x86 boot_cpu_data; + +#ifdef __SMP__ +extern struct cpuinfo_x86 cpu_data[]; +#define current_cpu_data cpu_data[smp_processor_id()] +#else +#define cpu_data &boot_cpu_data +#define current_cpu_data boot_cpu_data +#endif + extern char ignore_irq13; -extern char wp_works_ok; /* doesn't work on a 386 */ -extern char hlt_works_ok; /* problems on some 486Dx4's and old 386's */ -extern int have_cpuid; /* We have a CPUID */ + +extern void identify_cpu(struct cpuinfo_x86 *); +extern void print_cpu_info(struct cpuinfo_x86 *); /* * Bus types (default is ISA, but people can check others with these..) diff -u --recursive --new-file v2.1.74/linux/include/asm-i386/smp.h linux/include/asm-i386/smp.h --- v2.1.74/linux/include/asm-i386/smp.h Tue May 13 22:41:17 1997 +++ linux/include/asm-i386/smp.h Sun Dec 21 17:27:18 1997 @@ -149,28 +149,6 @@ */ /* - * Per process x86 parameters - */ - -struct cpuinfo_x86 -{ - char hard_math; - char x86; - char x86_model; - char x86_mask; - char x86_vendor_id[16]; - int x86_capability; - int fdiv_bug; - int have_cpuid; - char wp_works_ok; - char hlt_works_ok; - unsigned long udelay_val; -}; - - -extern struct cpuinfo_x86 cpu_data[NR_CPUS]; - -/* * Private routines/data */ diff -u --recursive --new-file v2.1.74/linux/include/asm-i386/socket.h linux/include/asm-i386/socket.h --- v2.1.74/linux/include/asm-i386/socket.h Sat Nov 29 11:25:12 1997 +++ linux/include/asm-i386/socket.h Sun Dec 21 17:41:24 1997 @@ -35,4 +35,8 @@ #define SO_BINDTODEVICE 25 +/* Socket filtering */ +#define SO_ATTACH_FILTER 26 +#define SO_DETACH_FILTER 27 + #endif /* _ASM_SOCKET_H */ diff -u --recursive --new-file v2.1.74/linux/include/asm-i386/uaccess.h linux/include/asm-i386/uaccess.h --- v2.1.74/linux/include/asm-i386/uaccess.h Tue Dec 2 09:49:40 1997 +++ linux/include/asm-i386/uaccess.h Sun Dec 21 17:58:36 1997 @@ -55,7 +55,7 @@ #else #define __access_ok(type,addr,size) \ (__kernel_ok || (__user_ok(addr,size) && \ - ((type) == VERIFY_READ || wp_works_ok || \ + ((type) == VERIFY_READ || boot_cpu_data.wp_works_ok || \ __verify_write((void *)(addr),(size))))) #endif /* CPU */ diff -u --recursive --new-file v2.1.74/linux/include/asm-mips/socket.h linux/include/asm-mips/socket.h --- v2.1.74/linux/include/asm-mips/socket.h Fri Dec 19 15:53:04 1997 +++ linux/include/asm-mips/socket.h Sun Dec 21 17:41:24 1997 @@ -55,6 +55,10 @@ #define SO_BINDTODEVICE 25 +/* Socket filtering */ +#define SO_ATTACH_FILTER 26 +#define SO_DETACH_FILTER 27 + /* Types of sockets. */ #define SOCK_DGRAM 1 /* Connectionless, unreliable datagrams of fixed maximum length. */ diff -u --recursive --new-file v2.1.74/linux/include/linux/arcdevice.h linux/include/linux/arcdevice.h --- v2.1.74/linux/include/linux/arcdevice.h Sun Sep 7 13:10:43 1997 +++ linux/include/linux/arcdevice.h Sun Dec 21 17:27:17 1997 @@ -5,7 +5,7 @@ * * Definitions for the ARCnet handlers. * - * Version: $Id: arcdevice.h,v 1.2 1997/09/05 08:57:56 mj Exp $ + * Version: $Id: arcdevice.h,v 1.3 1997/11/09 11:05:05 mj Exp $ * * Authors: Avery Pennarun * David Woodhouse @@ -74,7 +74,7 @@ /* Display warnings about the driver being an ALPHA version. */ -#define ALPHA_WARNING +#undef ALPHA_WARNING /* New debugging bitflags: each option can be enabled individually. diff -u --recursive --new-file v2.1.74/linux/include/linux/blkdev.h linux/include/linux/blkdev.h --- v2.1.74/linux/include/linux/blkdev.h Fri Dec 19 15:53:04 1997 +++ linux/include/linux/blkdev.h Sun Dec 21 17:58:36 1997 @@ -72,4 +72,15 @@ extern int * max_sectors[MAX_BLKDEV]; +#define MAX_SECTORS 244 /* 254 ? */ + +#define PageAlignSize(size) (((size) + PAGE_SIZE -1) & PAGE_MASK) +#if 0 /* small readahead */ +#define MAX_READAHEAD PageAlignSize(4096*7) +#define MIN_READAHEAD PageAlignSize(4096*2) +#else /* large readahead */ +#define MAX_READAHEAD PageAlignSize(4096*18) +#define MIN_READAHEAD PageAlignSize(4096*3) +#endif + #endif diff -u --recursive --new-file v2.1.74/linux/include/linux/coda.h linux/include/linux/coda.h --- v2.1.74/linux/include/linux/coda.h Wed Dec 10 11:12:46 1997 +++ linux/include/linux/coda.h Sun Dec 21 14:45:14 1997 @@ -1,11 +1,3 @@ -/* - * Venus interface for Coda. - * Original version: (C) 1996 Peter Braam - * Rewritten for Linux 2.1: (C) 1997 Carnegie Mellon University - * - * Carnegie Mellon encourages users of this code to contribute improvements - * to the Coda project. Contact Peter Braam . - */ /* * @@ -21,47 +13,51 @@ /* Catch new _KERNEL defn for NetBSD */ #ifdef __NetBSD__ #include -#ifdef _KERNEL -#define KERNEL -#endif -#endif - -#if 0 -#ifndef _SCALAR_T_ -#define _SCALAR_T_ 1 -typedef unsigned long u_int32_t; -typedef unsigned short u_int16_t; -typedef unsigned char u_int8_t; -#endif #endif #ifdef __linux__ -#ifndef _UQUAD_T_ +#if !defined(_UQUAD_T_) && (!defined(__GLIBC__) || __GLIBC__ < 2) #define _UQUAD_T_ 1 -typedef unsigned long u_quad_t; +typedef unsigned long long u_quad_t; #endif - -#ifdef __KERNEL__ -#define KERNEL -#endif __KERNEL__ #endif + /* * Cfs constants */ -#define CFS_MAXNAMLEN 256 -#define CFS_MAXPATHLEN 256 -#define CODA_MAXSYMLINK 10 +#define CFS_MAXNAMLEN 255 +#define CFS_MAXPATHLEN 1024 +#define CFS_MAXSYMLINK 10 + +/* these are Coda's version of O_RDONLY etc combinations + * to deal with VFS open modes + */ +#define C_O_READ 0x001 +#define C_O_WRITE 0x002 +#define C_O_TRUNC 0x010 +#define C_O_EXCL 0x100 + +/* these are to find mode bits in Venus */ +#define C_M_READ 00400 +#define C_M_WRITE 00200 + +/* for access Venus will use */ +#define C_A_R_OK 4 /* Test for read permission. */ +#define C_A_W_OK 2 /* Test for write permission. */ +#define C_A_X_OK 1 /* Test for execute permission. */ +#define C_A_F_OK 0 /* Test for existence. */ + + -/* types used in kernel and user mode */ #ifndef _VENUS_DIRENT_T_ #define _VENUS_DIRENT_T_ 1 struct venus_dirent { - unsigned long d_fileno; /* file number of entry */ - unsigned short d_reclen; /* length of this record */ - char d_type; /* file type, see below */ - char d_namlen; /* length of string in d_name */ - char d_name[CFS_MAXNAMLEN + 1];/* name must be no longer than this */ + unsigned long d_fileno; /* file number of entry */ + unsigned short d_reclen; /* length of this record */ + char d_type; /* file type, see below */ + char d_namlen; /* length of string in d_name */ + char d_name[CFS_MAXNAMLEN + 1];/* name must be no longer than this */ }; #undef DIRSIZ #define DIRSIZ(dp) ((sizeof (struct venus_dirent) - (CFS_MAXNAMLEN+1)) + \ @@ -105,6 +101,16 @@ } ViceFid; #endif /* VICEFID */ +static inline ino_t coda_f2i(struct ViceFid *fid) +{ + if ( fid ) { + return (fid->Unique + (fid->Vnode << 10) + (fid->Volume << 20)); + } else { + return 0; + } +} + + #ifndef _VUID_T_ #define _VUID_T_ typedef u_long vuid_t; @@ -113,11 +119,9 @@ #ifndef _CODACRED_T_ #define _CODACRED_T_ -#define NGROUPS 32 -struct CodaCred { +struct coda_cred { vuid_t cr_uid, cr_euid, cr_suid, cr_fsuid; /* Real, efftve, set, fs uid*/ vgid_t cr_gid, cr_egid, cr_sgid, cr_fsgid; /* same for groups */ - vgid_t cr_groups[NGROUPS]; /* Group membership for caller */ }; #endif @@ -126,7 +130,7 @@ /* * Vnode types. VNON means no type. */ -enum coda_vtype { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO, VBAD }; +enum coda_vtype { C_VNON, C_VREG, C_VDIR, C_VBLK, C_VCHR, C_VLNK, C_VSOCK, C_VFIFO, C_VBAD }; struct coda_vattr { enum coda_vtype va_type; /* vnode type (for create) */ @@ -134,7 +138,6 @@ short va_nlink; /* number of references to file */ vuid_t va_uid; /* owner user id */ vgid_t va_gid; /* owner group id */ - long va_fsid; /* file system id (dev for now) */ long va_fileid; /* file id */ u_quad_t va_size; /* file size in bytes */ long va_blocksize; /* blocksize preferred for i/o */ @@ -146,17 +149,14 @@ dev_t va_rdev; /* device the special file represents */ u_quad_t va_bytes; /* bytes of disk space held by file */ u_quad_t va_filerev; /* file modification number */ - u_int va_vaflags; /* operations flags, see below */ - long va_spare; /* remain quad aligned */ }; -#define VREAD 00400 -#define VWRITE 00200 #endif /* - * opcode constants + * Kernel <--> Venus communications. */ + #define CFS_ROOT ((u_long) 2) #define CFS_SYNC ((u_long) 3) #define CFS_OPEN ((u_long) 4) @@ -177,8 +177,8 @@ #define CFS_READLINK ((u_long) 19) #define CFS_FSYNC ((u_long) 20) #define CFS_INACTIVE ((u_long) 21) -#define CFS_VGET ((u_long) 22) -#define CFS_SIGNAL ((u_long) 23) +#define CFS_VGET ((u_long) 22) +#define CFS_SIGNAL ((u_long) 23) #define CFS_REPLACE ((u_long) 24) #define CFS_FLUSH ((u_long) 25) #define CFS_PURGEUSER ((u_long) 26) @@ -186,273 +186,423 @@ #define CFS_ZAPDIR ((u_long) 28) #define CFS_ZAPVNODE ((u_long) 29) #define CFS_PURGEFID ((u_long) 30) -#define CFS_RDWR ((u_long) 31) -#define ODY_MOUNT ((u_long) 32) -#define ODY_LOOKUP ((u_long) 33) -#define ODY_EXPAND ((u_long) 34) +#define CFS_NCALLS 31 -#define CFS_NCALLS 35 #define DOWNCALL(opcode) (opcode >= CFS_REPLACE && opcode <= CFS_PURGEFID) +#define VC_MAXDATASIZE 8192 +#define VC_MAXMSGSIZE sizeof(union inputArgs)+sizeof(union outputArgs) +\ + VC_MAXDATASIZE + + + /* * Venus <-> Coda RPC arguments */ +struct cfs_in_hdr { + unsigned long opcode; + unsigned long unique; /* Keep multiple outstanding msgs distinct */ + u_short pid; /* Common to all */ + u_short pgid; /* Common to all */ + u_short sid; /* Common to all */ + struct coda_cred cred; /* Common to all */ +}; -struct inputArgs { - u_long opcode; - u_long unique; /* Keep multiple outstanding msgs distinct */ - u_short pid; /* Common to all */ - u_short pgid; /* Common to all */ - struct CodaCred cred; /* Common to all */ - - union { - /* Nothing needed for cfs_root */ - /* Nothing needed for cfs_sync */ - struct cfs_open_in { - ViceFid VFid; - int flags; - } cfs_open; - struct cfs_close_in { - ViceFid VFid; - int flags; - } cfs_close; - struct cfs_ioctl_in { - ViceFid VFid; - int cmd; - int len; - int rwflag; - char *data; /* Place holder for data. */ - } cfs_ioctl; - struct cfs_getattr_in { - ViceFid VFid; - struct coda_vattr attr; - } cfs_getattr; - struct cfs_setattr_in { - ViceFid VFid; - struct coda_vattr attr; - } cfs_setattr; - struct cfs_access_in { - ViceFid VFid; - int flags; - } cfs_access; - struct cfs_lookup_in { - ViceFid VFid; - char *name; /* Place holder for data. */ - } cfs_lookup; - struct cfs_create_in { - ViceFid VFid; - struct coda_vattr attr; - int excl; - int mode; - char *name; /* Place holder for data. */ - } cfs_create; - struct cfs_remove_in { - ViceFid VFid; - char *name; /* Place holder for data. */ - } cfs_remove; - struct cfs_link_in { - ViceFid sourceFid; /* cnode to link *to* */ - ViceFid destFid; /* Directory in which to place link */ - char *tname; /* Place holder for data. */ - } cfs_link; - struct cfs_rename_in { - ViceFid sourceFid; - char *srcname; - ViceFid destFid; - char *destname; - } cfs_rename; - struct cfs_mkdir_in { - ViceFid VFid; - struct coda_vattr attr; - char *name; /* Place holder for data. */ - } cfs_mkdir; - struct cfs_rmdir_in { - ViceFid VFid; - char *name; /* Place holder for data. */ - } cfs_rmdir; - struct cfs_readdir_in { - ViceFid VFid; - int count; - int offset; - } cfs_readdir; - struct cfs_symlink_in { - ViceFid VFid; /* Directory to put symlink in */ - char *srcname; - struct coda_vattr attr; - char *tname; - } cfs_symlink; - struct cfs_readlink_in { - ViceFid VFid; - } cfs_readlink; - struct cfs_fsync_in { - ViceFid VFid; - } cfs_fsync; - struct cfs_inactive_in { - ViceFid VFid; - } cfs_inactive; - struct cfs_vget_in { - ViceFid VFid; - } cfs_vget; - /* CFS_SIGNAL is out-of-band, doesn't need data. */ - /* CFS_INVALIDATE is a venus->kernel call */ - /* CFS_FLUSH is a venus->kernel call */ - /* CFS_PURGEUSER is a venus->kernel call */ - /* CFS_ZAPFILE is a venus->kernel call */ - /* CFS_ZAPDIR is a venus->kernel call */ - /* CFS_ZAPVNODE is a venus->kernel call */ - /* CFS_PURGEFID is a venus->kernel call */ - struct cfs_rdwr_in { - ViceFid VFid; - int rwflag; - int count; - int offset; - int ioflag; - caddr_t data; /* Place holder for data. */ - } cfs_rdwr; - struct ody_mount_in { - char *name; /* Place holder for data. */ - } ody_mount; - struct ody_lookup_in { - ViceFid VFid; - char *name; /* Place holder for data. */ - } ody_lookup; - struct ody_expand_in { - ViceFid VFid; - int size; /* Size of buffer to return. */ - } ody_expand; - /* CFS_REPLACE is a venus->kernel call */ - } d; -}; - -/* Occasionally, don't cache the fid returned by CFS_LOOKUP. For - * instance, if the fid is inconsistent. This case is handled by - * setting the top bit of the return result parameter. */ -#define CFS_NOCACHE 0x80000000 +/* Really important that opcode and unique are 1st two fields! */ +struct cfs_out_hdr { + unsigned long opcode; + unsigned long unique; + unsigned long result; +}; -#define INIT_OUT(out, opcode, result) \ - out->opcode = (opcode); out->result = (result); +/* cfs_root: NO_IN */ +struct cfs_root_out { + struct cfs_out_hdr oh; + ViceFid VFid; +}; -/* IMPORTANT: opcode and unique must be first two fields! */ -struct outputArgs { - u_long opcode; - u_long unique; /* Keep multiple outstanding msgs distinct */ - u_long result; - union { - struct cfs_root_out { - ViceFid VFid; - } cfs_root; - /* Nothing needed for cfs_sync */ - struct cfs_open_out { - dev_t dev; - ino_t inode; - } cfs_open; - /* Nothing needed for cfs_close */ - struct cfs_ioctl_out { - int len; - caddr_t data; /* Place holder for data. */ - } cfs_ioctl; - struct cfs_getattr_out { - struct coda_vattr attr; - } cfs_getattr; - /* Nothing needed for cfs_setattr */ - /* Nothing needed for cfs_access */ - struct cfs_lookup_out { - ViceFid VFid; - int vtype; - } cfs_lookup; - struct cfs_create_out { - ViceFid VFid; - struct coda_vattr attr; - } cfs_create; - /* Nothing needed for cfs_remove */ - /* Nothing needed for cfs_link */ - /* Nothing needed for cfs_rename */ - struct cfs_mkdir_out { - ViceFid VFid; - struct coda_vattr attr; - } cfs_mkdir; - /* Nothing needed for cfs_rmdir */ - struct cfs_readdir_out { - int size; - caddr_t data; /* Place holder for data. */ - } cfs_readdir; - /* Nothing needed for cfs_symlink */ - struct cfs_readlink_out { - int count; - caddr_t data; /* Place holder for data. */ - } cfs_readlink; - /* Nothing needed for cfs_fsync */ - /* Nothing needed for cfs_inactive */ - struct cfs_vget_out { - ViceFid VFid; - int vtype; - } cfs_vget; - /* CFS_SIGNAL is out-of-band, doesn't need data. */ - /* CFS_INVALIDATE is a venus->kernel call */ - /* CFS_FLUSH is a venus->kernel call */ - struct cfs_purgeuser_out {/* CFS_PURGEUSER is a venus->kernel call */ - struct CodaCred cred; - } cfs_purgeuser; - struct cfs_zapfile_out { /* CFS_ZAPFILE is a venus->kernel call */ - ViceFid CodaFid; - } cfs_zapfile; - struct cfs_zapdir_out { /* CFS_ZAPDIR is a venus->kernel call */ - ViceFid CodaFid; - } cfs_zapdir; - struct cfs_zapvnode_out { /* CFS_ZAPVNODE is a venus->kernel call */ - struct CodaCred cred; - ViceFid VFid; - } cfs_zapvnode; - struct cfs_purgefid_out { /* CFS_PURGEFID is a venus->kernel call */ - ViceFid CodaFid; - } cfs_purgefid; - struct cfs_rdwr_out { - int rwflag; - int count; - caddr_t data; /* Place holder for data. */ - } cfs_rdwr; - struct ody_mount_out { - ViceFid VFid; - } ody_mount; - struct ody_lookup_out { - ViceFid VFid; - } ody_lookup; - struct ody_expand_out { /* Eventually it would be nice to get some */ - char links[sizeof(int)]; /* Place holder for data. */ - } ody_expand; - struct cfs_replace_out { /* cfs_replace is a venus->kernel call */ - ViceFid NewFid; - ViceFid OldFid; - } cfs_replace; - } d; -}; - +struct cfs_root_in { + struct cfs_in_hdr in; +}; + +/* cfs_sync: */ +/* Nothing needed for cfs_sync */ + +/* cfs_open: */ +struct cfs_open_in { + struct cfs_in_hdr ih; + ViceFid VFid; + int flags; +}; + +struct cfs_open_out { + struct cfs_out_hdr oh; + dev_t dev; + ino_t inode; +}; + + +/* cfs_close: */ +struct cfs_close_in { + struct cfs_in_hdr ih; + ViceFid VFid; + int flags; +}; + +struct cfs_close_out { + struct cfs_out_hdr out; +}; + +/* cfs_ioctl: */ +struct cfs_ioctl_in { + struct cfs_in_hdr ih; + ViceFid VFid; + int cmd; + int len; + int rwflag; + char *data; /* Place holder for data. */ +}; + +struct cfs_ioctl_out { + struct cfs_out_hdr oh; + int len; + caddr_t data; /* Place holder for data. */ +}; + + +/* cfs_getattr: */ +struct cfs_getattr_in { + struct cfs_in_hdr ih; + ViceFid VFid; +}; + +struct cfs_getattr_out { + struct cfs_out_hdr oh; + struct coda_vattr attr; +}; + + +/* cfs_setattr: NO_OUT */ +struct cfs_setattr_in { + struct cfs_in_hdr ih; + ViceFid VFid; + struct coda_vattr attr; +}; + +struct cfs_setattr_out { + struct cfs_out_hdr out; +}; + +/* cfs_access: NO_OUT */ +struct cfs_access_in { + struct cfs_in_hdr ih; + ViceFid VFid; + int flags; +}; + +struct cfs_access_out { + struct cfs_out_hdr out; +}; + +/* cfs_lookup: */ +struct cfs_lookup_in { + struct cfs_in_hdr ih; + ViceFid VFid; + int name; /* Place holder for data. */ +}; + +struct cfs_lookup_out { + struct cfs_out_hdr oh; + ViceFid VFid; + int vtype; +}; + + +/* cfs_create: */ +struct cfs_create_in { + struct cfs_in_hdr ih; + ViceFid VFid; + struct coda_vattr attr; + int excl; + int mode; + int name; /* Place holder for data. */ +}; + +struct cfs_create_out { + struct cfs_out_hdr oh; + ViceFid VFid; + struct coda_vattr attr; +}; + + +/* cfs_remove: NO_OUT */ +struct cfs_remove_in { + struct cfs_in_hdr ih; + ViceFid VFid; + int name; /* Place holder for data. */ +}; + +struct cfs_remove_out { + struct cfs_out_hdr out; +}; + +/* cfs_link: NO_OUT */ +struct cfs_link_in { + struct cfs_in_hdr ih; + ViceFid sourceFid; /* cnode to link *to* */ + ViceFid destFid; /* Directory in which to place link */ + int tname; /* Place holder for data. */ +}; + +struct cfs_link_out { + struct cfs_out_hdr out; +}; + + +/* cfs_rename: NO_OUT */ +struct cfs_rename_in { + struct cfs_in_hdr ih; + ViceFid sourceFid; + int srcname; + ViceFid destFid; + int destname; +}; + +struct cfs_rename_out { + struct cfs_out_hdr out; +}; + +/* cfs_mkdir: */ +struct cfs_mkdir_in { + struct cfs_in_hdr ih; + ViceFid VFid; + struct coda_vattr attr; + int name; /* Place holder for data. */ +}; + +struct cfs_mkdir_out { + struct cfs_out_hdr oh; + ViceFid VFid; + struct coda_vattr attr; +}; + + +/* cfs_rmdir: NO_OUT */ +struct cfs_rmdir_in { + struct cfs_in_hdr ih; + ViceFid VFid; + int name; /* Place holder for data. */ +}; + +struct cfs_rmdir_out { + struct cfs_out_hdr out; +}; + +/* cfs_readdir: */ +struct cfs_readdir_in { + struct cfs_in_hdr ih; + ViceFid VFid; + int count; + int offset; +}; + +struct cfs_readdir_out { + struct cfs_out_hdr oh; + int size; + caddr_t data; /* Place holder for data. */ +}; + +/* cfs_symlink: NO_OUT */ +struct cfs_symlink_in { + struct cfs_in_hdr ih; + ViceFid VFid; /* Directory to put symlink in */ + int srcname; + struct coda_vattr attr; + int tname; +}; + +struct cfs_symlink_out { + struct cfs_out_hdr out; +}; + +/* cfs_readlink: */ +struct cfs_readlink_in { + struct cfs_in_hdr ih; + ViceFid VFid; +}; + +struct cfs_readlink_out { + struct cfs_out_hdr oh; + int count; + caddr_t data; /* Place holder for data. */ +}; + + +/* cfs_fsync: NO_OUT */ +struct cfs_fsync_in { + struct cfs_in_hdr ih; + ViceFid VFid; +}; + +struct cfs_fsync_out { + struct cfs_out_hdr out; +}; + +/* cfs_inactive: NO_OUT */ +struct cfs_inactive_in { + struct cfs_in_hdr ih; + ViceFid VFid; +}; + +/* cfs_vget: */ +struct cfs_vget_in { + struct cfs_in_hdr ih; + ViceFid VFid; +}; + +struct cfs_vget_out { + struct cfs_out_hdr oh; + ViceFid VFid; + int vtype; +}; + + +/* CFS_SIGNAL is out-of-band, doesn't need data. */ +/* CFS_INVALIDATE is a venus->kernel call */ +/* CFS_FLUSH is a venus->kernel call */ + +/* cfs_purgeuser: */ +/* CFS_PURGEUSER is a venus->kernel call */ +struct cfs_purgeuser_out { + struct cfs_out_hdr oh; + struct coda_cred cred; +}; + +/* cfs_zapfile: */ +/* CFS_ZAPFILE is a venus->kernel call */ +struct cfs_zapfile_out { + struct cfs_out_hdr oh; + ViceFid CodaFid; +}; + +/* cfs_zapdir: */ +/* CFS_ZAPDIR is a venus->kernel call */ +struct cfs_zapdir_out { + struct cfs_out_hdr oh; + ViceFid CodaFid; +}; + +/* cfs_zapnode: */ +/* CFS_ZAPVNODE is a venus->kernel call */ +struct cfs_zapvnode_out { + struct cfs_out_hdr oh; + struct coda_cred cred; + ViceFid VFid; +}; + +/* cfs_purgefid: */ +/* CFS_PURGEFID is a venus->kernel call */ +struct cfs_purgefid_out { + struct cfs_out_hdr oh; + ViceFid CodaFid; +}; + +/* cfs_rdwr: */ +struct cfs_rdwr_in { + struct cfs_in_hdr ih; + ViceFid VFid; + int rwflag; + int count; + int offset; + int ioflag; + caddr_t data; /* Place holder for data. */ +}; + +struct cfs_rdwr_out { + struct cfs_out_hdr oh; + int rwflag; + int count; + caddr_t data; /* Place holder for data. */ +}; + + +/* cfs_replace: */ +/* CFS_REPLACE is a venus->kernel call */ +struct cfs_replace_out { /* cfs_replace is a venus->kernel call */ + struct cfs_out_hdr oh; + ViceFid NewFid; + ViceFid OldFid; +}; /* - * how big are the inputArgs and outputArgs structures - * for the varying types of calls? + * Occasionally, don't cache the fid returned by CFS_LOOKUP. For instance, if + * the fid is inconsistent. This case is handled by setting the top bit of the + * return result parameter. */ -#define VC_IN_NO_DATA (2 * (int)sizeof(u_long) \ - + 2 * (int)sizeof(u_short) \ - + (int)sizeof(struct CodaCred)) -#define VC_OUT_NO_DATA (3 * (int)sizeof(u_long)) -#define VC_INSIZE(member) (VC_IN_NO_DATA + (int)sizeof(struct member)) -#define VC_OUTSIZE(member) (VC_OUT_NO_DATA + (int)sizeof(struct member)) - -/* Now for venus. C++ doesn't know what struct foo means. */ -#define VC_SIZE(Thing, Member) (VC_OUT_NO_DATA \ - + (int)sizeof((Thing)->d.Member)) - -#define VC_BIGGER_OF_IN_OR_OUT (sizeof(struct outputArgs) \ - > sizeof(struct inputArgs) \ - ? sizeof(struct outputArgs) \ - : sizeof(struct inputArgs)) -#define VC_DATASIZE 8192 -#define VC_MAXMSGSIZE (VC_DATASIZE + VC_BIGGER_OF_IN_OR_OUT) +#define CFS_NOCACHE 0x80000000 + +union inputArgs { + struct cfs_in_hdr ih; /* NB: every struct below begins with an ih */ + struct cfs_open_in cfs_open; + struct cfs_close_in cfs_close; + struct cfs_ioctl_in cfs_ioctl; + struct cfs_getattr_in cfs_getattr; + struct cfs_setattr_in cfs_setattr; + struct cfs_access_in cfs_access; + struct cfs_lookup_in cfs_lookup; + struct cfs_create_in cfs_create; + struct cfs_remove_in cfs_remove; + struct cfs_link_in cfs_link; + struct cfs_rename_in cfs_rename; + struct cfs_mkdir_in cfs_mkdir; + struct cfs_rmdir_in cfs_rmdir; + struct cfs_readdir_in cfs_readdir; + struct cfs_symlink_in cfs_symlink; + struct cfs_readlink_in cfs_readlink; + struct cfs_fsync_in cfs_fsync; + struct cfs_inactive_in cfs_inactive; + struct cfs_vget_in cfs_vget; + struct cfs_rdwr_in cfs_rdwr; +}; + +union outputArgs { + struct cfs_out_hdr oh; /* NB: every struct below begins with an oh */ + struct cfs_root_out cfs_root; + struct cfs_open_out cfs_open; + struct cfs_ioctl_out cfs_ioctl; + struct cfs_getattr_out cfs_getattr; + struct cfs_lookup_out cfs_lookup; + struct cfs_create_out cfs_create; + struct cfs_mkdir_out cfs_mkdir; + struct cfs_readdir_out cfs_readdir; + struct cfs_readlink_out cfs_readlink; + struct cfs_vget_out cfs_vget; + struct cfs_purgeuser_out cfs_purgeuser; + struct cfs_zapfile_out cfs_zapfile; + struct cfs_zapdir_out cfs_zapdir; + struct cfs_zapvnode_out cfs_zapvnode; + struct cfs_purgefid_out cfs_purgefid; + struct cfs_rdwr_out cfs_rdwr; + struct cfs_replace_out cfs_replace; +}; + +union cfs_downcalls { + /* CFS_INVALIDATE is a venus->kernel call */ + /* CFS_FLUSH is a venus->kernel call */ + struct cfs_purgeuser_out purgeuser; + struct cfs_zapfile_out zapfile; + struct cfs_zapdir_out zapdir; + struct cfs_zapvnode_out zapvnode; + struct cfs_purgefid_out purgefid; + struct cfs_replace_out replace; +}; + /* * Used for identifying usage of "Control" and pioctls */ + +#define PIOCPARM_MASK 0x0000ffff struct ViceIoctl { caddr_t in, out; /* Data to be transferred in, or out */ short in_size; /* Size of input buffer <= 2K */ @@ -465,25 +615,17 @@ struct ViceIoctl vi; }; - - - - - #define CFS_CONTROL ".CONTROL" #define CFS_CONTROLLEN 8 #define CTL_VOL -1 #define CTL_VNO -1 #define CTL_UNI -1 #define CTL_INO -1 -#define CTL_FILE "/coda/.CONTROL" -#define IOCPARM_MASK 0x0000ffff +#define CTL_FILE "/coda/.CONTROL" #define IS_CTL_FID(fidp) ((fidp)->Volume == CTL_VOL &&\ (fidp)->Vnode == CTL_VNO &&\ (fidp)->Unique == CTL_UNI) - /*#define ISDIR(fid) ((fid).Vnode & 0x1) */ - #endif diff -u --recursive --new-file v2.1.74/linux/include/linux/coda_cnode.h linux/include/linux/coda_cnode.h --- v2.1.74/linux/include/linux/coda_cnode.h Wed Dec 10 11:12:46 1997 +++ linux/include/linux/coda_cnode.h Sun Dec 21 14:45:14 1997 @@ -7,8 +7,6 @@ * to the Coda project. Contact Peter Braam . */ -/* revamped cnode.h file: platform dependent, kernel only! */ - #ifndef _CNODE_H_ #define _CNODE_H_ @@ -19,29 +17,41 @@ /* defintion of cnode, which combines ViceFid with inode information */ struct cnode { - struct inode *c_vnode; /* linux inode associated with cnode */ - ViceFid c_fid; /* Coda identifier */ - u_short c_flags; /* flags (see below) */ - int c_magic; /* to verify the data structure */ - u_short c_ocount; /* count of openers */ - u_short c_owrite; /* count of open for write */ - u_short c_mmcount; /* count of mmappers */ - struct inode *c_ovp; /* open vnode pointer */ - struct dentry c_odentry; + struct inode *c_vnode; /* inode associated with cnode */ + ViceFid c_fid; /* Coda identifier */ + u_short c_flags; /* flags (see below) */ + int c_magic; /* to verify the data structure */ + u_short c_ocount; /* count of openers */ + u_short c_owrite; /* count of open for write */ + u_short c_mmcount; /* count of mmappers */ + struct inode *c_ovp; /* open vnode pointer */ + struct list_head c_cnhead; /* head of cache entries */ }; /* flags */ #define C_VATTR 0x1 /* Validity of vattr in the cnode */ #define C_SYMLINK 0x2 /* Validity of symlink pointer in the cnode */ -#define C_DYING 0x4 /* Set for outstanding cnodes from venus (which died) */ +#define C_DYING 0x4 /* Set for outstanding cnodes from venus (which died) */ +#define C_ZAPFID 0x8 +#define C_ZAPDIR 0x10 +void coda_cnode_free(struct cnode *); struct cnode *coda_cnode_alloc(void); -void coda_cnode_free(struct cnode *cinode); -int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb); -struct inode *coda_fid2inode(ViceFid *fid, struct super_block *sb); +int coda_cnode_make(struct inode **, struct ViceFid *, struct super_block *); int coda_cnode_makectl(struct inode **inode, struct super_block *sb); +struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb); - +/* inode to cnode */ +static inline struct cnode *ITOC(struct inode *inode) +{ + return ((struct cnode *)inode->u.generic_ip); +} + +/* cnode to inode */ +static inline struct inode *CTOI(struct cnode *cnode) +{ + return (cnode->c_vnode); +} #endif diff -u --recursive --new-file v2.1.74/linux/include/linux/coda_linux.h linux/include/linux/coda_linux.h --- v2.1.74/linux/include/linux/coda_linux.h Tue Dec 2 16:45:20 1997 +++ linux/include/linux/coda_linux.h Sun Dec 21 14:45:14 1997 @@ -41,15 +41,19 @@ extern int coda_debug; extern int coda_print_entry; extern int coda_access_cache; -extern int cfsnc_use; - -/* */ +/* this file: heloers */ char *coda_f2s(ViceFid *f, char *s); int coda_isroot(struct inode *i); -void coda_load_creds(struct CodaCred *cred); - - +int coda_iscontrol(const char *name, size_t length); +void coda_load_creds(struct coda_cred *cred); +int coda_mycred(struct coda_cred *); +void coda_vattr_to_iattr(struct inode *, struct coda_vattr *); +void coda_iattr_to_vattr(struct iattr *, struct coda_vattr *); +unsigned short coda_flags_to_cflags(unsigned short); +void print_vattr( struct coda_vattr *attr ); +int coda_cred_ok(struct coda_cred *cred); +int coda_cred_eq(struct coda_cred *cred1, struct coda_cred *cred2); /* defined in file.c */ void coda_prepare_openfile(struct inode *coda_inode, struct file *coda_file, @@ -58,17 +62,8 @@ void coda_restore_codafile(struct inode *coda_inode, struct file *coda_file, struct inode *open_inode, struct file *open_file); int coda_inode_grab(dev_t dev, ino_t ino, struct inode **ind); -struct super_block *coda_find_super(kdev_t device); - - -#define INIT_IN(in, op) \ - (in)->opcode = (op); \ - (in)->pid = current->pid; \ - (in)->pgid = current->gid; - -/* debugging aids */ -#define coda_panic printk +#define NB_SFS_SIZ 0x895440 /* debugging masks */ #define D_SUPER 1 /* print results returned by Venus */ @@ -81,8 +76,8 @@ #define D_PSDEV 128 #define D_PIOCTL 256 #define D_SPECIAL 512 -/* until we are really good, ... */ -#define coda_panic printk +#define D_TIMING 1024 +#define D_DOWNCALL 2048 #define CDEBUG(mask, format, a...) \ do { \ @@ -98,31 +93,22 @@ if(coda_print_entry) printk("Process %d leaving %s\n",current->pid,__FUNCTION__) -/* inode to cnode */ -#define ITOC(the_inode) ((struct cnode *)(the_inode)->u.generic_ip) -/* cnode to inode */ -#define CTOI(the_cnode) ((the_cnode)->c_vnode) #define CHECK_CNODE(c) \ do { \ - struct cnode *cnode = (c); \ + if ( coda_debug ) {\ + struct cnode *cnode = (c); \ if (!cnode) \ - coda_panic ("%s(%d): cnode is null\n", __FUNCTION__, __LINE__); \ + printk ("%s(%d): cnode is null\n", __FUNCTION__, __LINE__); \ if (cnode->c_magic != CODA_CNODE_MAGIC) \ - coda_panic ("%s(%d): cnode magic wrong\n", __FUNCTION__, __LINE__); \ + printk ("%s(%d): cnode magic wrong\n", __FUNCTION__, __LINE__); \ if (!cnode->c_vnode) \ - coda_panic ("%s(%d): cnode has null inode\n", __FUNCTION__, __LINE__); \ + printk ("%s(%d): cnode has null inode\n", __FUNCTION__, __LINE__); \ if ( (struct cnode *)cnode->c_vnode->u.generic_ip != cnode ) \ - coda_panic("AAooh, %s(%d) cnode doesn't link right!\n", __FUNCTION__,__LINE__);\ -} while (0); + printk("AAooh, %s(%d) cnode doesn't link right!\n", __FUNCTION__,__LINE__);\ +}} while (0); -/* ioctl stuff */ -/* this needs to be sorted out XXXX */ -#ifdef __linux__ -#define IOCPARM_MASK 0x0000ffff -#endif - #define CODA_ALLOC(ptr, cast, size) \ do { \ if (size < 3000) { \ @@ -132,7 +118,7 @@ ptr = (cast)vmalloc((unsigned long) size); \ CDEBUG(D_MALLOC, "vmalloced: %x at %x.\n", (int) size, (int) ptr);}\ if (ptr == 0) { \ - coda_panic("kernel malloc returns 0 at %s:%d\n", __FILE__, __LINE__); \ + printk("kernel malloc returns 0 at %s:%d\n", __FILE__, __LINE__); \ } \ memset( ptr, 0, size ); \ } while (0) @@ -140,48 +126,4 @@ #define CODA_FREE(ptr,size) do {if (size < 3000) { kfree_s((ptr), (size)); CDEBUG(D_MALLOC, "kfreed: %x at %x.\n", (int) size, (int) ptr); } else { vfree((ptr)); CDEBUG(D_MALLOC, "vfreed: %x at %x.\n", (int) size, (int) ptr);} } while (0) - - - -/* - * Macros to manipulate the queue - */ -#define crfree(cred) CODA_FREE( (cred), sizeof(struct ucred)) - -#ifndef INIT_QUEUE - -struct queue { - struct queue *forw, *back; -}; - -#define INIT_QUEUE(head) \ -do { \ - (head).forw = (struct queue *)&(head); \ - (head).back = (struct queue *)&(head); \ -} while (0) - -#define GETNEXT(head) (head).forw - -#define EMPTY(head) ((head).forw == &(head)) - -#define EOQ(el, head) ((struct queue *)(el) == (struct queue *)&(head)) - -#define INSQUE(el, head) \ -do { \ - (el).forw = ((head).back)->forw; \ - (el).back = (head).back; \ - ((head).back)->forw = (struct queue *)&(el); \ - (head).back = (struct queue *)&(el); \ -} while (0) - -#define REMQUE(el) \ -do { \ - ((el).forw)->back = (el).back; \ - (el).back->forw = (el).forw; \ -} while (0) - -#endif INIT_QUEUE - - -#endif _LINUX_CODA_FS - +#endif diff -u --recursive --new-file v2.1.74/linux/include/linux/coda_namecache.h linux/include/linux/coda_namecache.h --- v2.1.74/linux/include/linux/coda_namecache.h Tue Dec 2 16:45:20 1997 +++ linux/include/linux/coda_namecache.h Wed Dec 31 16:00:00 1969 @@ -1,160 +0,0 @@ -/* - * Mach Operating System - * Copyright (c) 1990 Carnegie-Mellon University - * Copyright (c) 1989 Carnegie-Mellon University - * All rights reserved. The CMU software License Agreement specifies - * the terms and conditions for use and redistribution. - */ - -/* - * This code was written for the Coda file system at Carnegie Mellon University. - * Contributers include David Steere, James Kistler, and M. Satyanarayanan. - */ - -/* - * HISTORY - * cfsnc.h,v - * Revision 1.2 1996/01/02 16:57:19 bnoble - * Added support for Coda MiniCache and raw inode calls (final commit) - * - * Revision 1.1.2.1 1995/12/20 01:57:45 bnoble - * Added CFS-specific files - * - * Revision 3.1.1.1 1995/03/04 19:08:22 bnoble - * Branch for NetBSD port revisions - * - * Revision 3.1 1995/03/04 19:08:21 bnoble - * Bump to major revision 3 to prepare for NetBSD port - * - * Revision 2.2 1994/08/28 19:37:39 luqi - * Add a new CFS_REPLACE call to allow venus to replace a ViceFid in the - * mini-cache. - * - * In "cfs.h": - * Add CFS_REPLACE decl. - * - * In "cfs_namecache.c": - * Add routine cfsnc_replace. - * - * In "cfs_subr.c": - * Add case-statement to process CFS_REPLACE. - * - * In "cfsnc.h": - * Add decl for CFSNC_REPLACE. - * - * Revision 2.1 94/07/21 16:25:27 satya - * Conversion to C++ 3.0; start of Coda Release 2.0 - * - * Revision 1.2 92/10/27 17:58:34 lily - * merge kernel/latest and alpha/src/cfs - * - * Revision 2.2 90/07/05 11:27:04 mrt - * Created for the Coda File System. - * [90/05/23 dcs] - * - * Revision 1.4 90/05/31 17:02:12 dcs - * Prepare for merge with facilities kernel. - * - * - */ -#ifndef _CFSNC_HEADER_ -#define _CFSNC_HEADER_ - -#include "coda.h" -#include "coda_cnode.h" - - -/* - * Cfs constants - */ -#define CFSNC_NAMELEN 15 /* longest name stored in cache */ -#define CFSNC_CACHESIZE 256 /* Default cache size */ -#define CFSNC_HASHSIZE 64 /* Must be multiple of 2 */ -/* - * Structure for an element in the CFS Name Cache. - */ - -/* roughly 50 bytes per entry */ -struct cfscache { - struct cfscache *hash_next,*hash_prev; /* Hash list */ - struct cfscache *lru_next, *lru_prev; /* LRU list */ - struct cnode *cp; /* vnode of the file */ - struct cnode *dcp; /* parent's cnode */ - struct CodaCred *cred; /* user credentials */ - char name[CFSNC_NAMELEN]; /* segment name */ - int namelen; /* length of name */ -}; - - - -/* exported */ -void cfsnc_init(void); -void cfsnc_enter(struct cnode *dcp, register const char *name, int namelen, struct cnode *cp); -struct cnode *cfsnc_lookup(struct cnode *dcp, register const char *name, int namelen); -void cfsnc_zapParentfid(ViceFid *fid); -void cfsnc_zapfid(ViceFid *fid); -void cfsnc_zapfile(struct cnode *dcp, register const char *name, int length); -void cfsnc_purge_user(struct CodaCred *cred); -void cfsnc_flush(void); -void cfsnc_replace(ViceFid *f1, ViceFid *f2); -void print_cfsnc(void); -void coda_print_ce(struct cfscache *); -int cfsnc_resize(int hashsize, int heapsize); - - - -/* #define CFSNC_VALID(cncp) ( (cncp->dcp != (struct cnode *)0) && (cncp->cp->c_flags & C_VATTR) ) */ -#define CFSNC_VALID(cncp) (cncp->dcp != (struct cnode *)0) - -#define DATA_PART(cncp) (struct cfscache *) \ - ((char *)cncp + (4*sizeof(struct cfscache *))) -#define DATA_SIZE (sizeof(struct cfscache)-(4*sizeof(struct cfscache *))) - -/* - * Structure to contain statistics on the cache usage - */ - -struct cfsnc_statistics { - unsigned hits; - unsigned misses; - unsigned enters; - unsigned dbl_enters; - unsigned long_name_enters; - unsigned long_name_lookups; - unsigned long_remove; - unsigned lru_rm; - unsigned zapPfids; - unsigned zapFids; - unsigned zapFile; - unsigned zapUsers; - unsigned Flushes; - unsigned Sum_bucket_len; - unsigned Sum2_bucket_len; - unsigned Max_bucket_len; - unsigned Num_zero_len; - unsigned Search_len; -}; - -/* - * Symbols to aid in debugging the namecache code. Assumes the existence - * of the variable cfsnc_debug, which is defined in cfs_namecache.c - */ -extern int cfsnc_debug; -#define CFSNC_DEBUG(N, STMT) { if (cfsnc_debug & (1 <. - */ + + Coda: an Experimental Distributed File System + Release 3.1 + + Copyright (c) 1987-1996 Carnegie Mellon University + All Rights Reserved + +Permission to use, copy, modify and distribute this software and its +documentation is hereby granted, provided that both the copyright +notice and this permission notice appear in all copies of the +software, derivative works or modified versions, and any portions +thereof, and that both notices appear in supporting documentation, and +that credit is given to Carnegie Mellon University in all documents +and publicity pertaining to direct or indirect use of this code or its +derivatives. + +CODA IS AN EXPERIMENTAL SOFTWARE SYSTEM AND IS KNOWN TO HAVE BUGS, +SOME OF WHICH MAY HAVE SERIOUS CONSEQUENCES. CARNEGIE MELLON ALLOWS +FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION. CARNEGIE MELLON +DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER +RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE OR OF +ANY DERIVATIVE WORK. + +Carnegie Mellon encourages users of this software to return any +improvements or extensions that they make, and to grant Carnegie +Mellon the rights to redistribute these changes without encumbrance. +*/ + +static char *rcsid = "$Header: /afs/cs/project/coda-src/cvs/coda/kernel-src/vfs/linux21/linux/coda_opstats.h,v 1.1 1997/12/02 05:35:02 braam Exp $"; +#endif /*_BLURB_*/ /* diff -u --recursive --new-file v2.1.74/linux/include/linux/coda_psdev.h linux/include/linux/coda_psdev.h --- v2.1.74/linux/include/linux/coda_psdev.h Thu Dec 4 14:53:57 1997 +++ linux/include/linux/coda_psdev.h Sun Dec 21 14:45:14 1997 @@ -4,19 +4,49 @@ #define CODA_PSDEV_MAJOR 67 #define MAX_CODADEVS 5 /* how many do we allow */ +#include + extern struct vcomm psdev_vcomm[]; +/* queue stuff; the rest is static to psdev.c */ +struct queue { + struct queue *forw, *back; +}; +void coda_q_insert(struct queue *el, struct queue *q); +void coda_q_remove(struct queue *q); + + #define CODA_SUPER_MAGIC 0x73757245 struct coda_sb_info { - struct inode * sbi_psdev; /* /dev/cfs? Venus/kernel device */ - struct inode * sbi_ctlcp; /* control magic file */ - int sbi_refct; - struct vcomm * sbi_vcomm; - struct inode * sbi_root; + struct inode * sbi_psdev; /* /dev/cfs? Venus/kernel device */ + struct inode * sbi_ctlcp; /* control magic file */ + int sbi_refct; + struct vcomm * sbi_vcomm; + struct inode * sbi_root; + struct list_head sbi_cchead; }; +/* communication pending/processing queues queues */ +struct vcomm { + u_long vc_seq; + struct wait_queue *vc_waitq; /* Venus wait queue */ + struct queue vc_pending; + struct queue vc_processing; + struct super_block *vc_sb; + int vc_inuse; +}; + +static inline int vcomm_open(struct vcomm *vcp) +{ + return ((vcp)->vc_pending.forw != NULL); +} + +static inline void mark_vcomm_closed(struct vcomm *vcp) +{ + (vcp)->vc_pending.forw = NULL; +} static inline struct coda_sb_info *coda_sbp(struct super_block *sb) { @@ -28,22 +58,6 @@ extern void coda_psdev_detach(int unit); extern int init_coda_psdev(void); -/* to aid procedures make upcalls. They must have a - declaration at the top containing: - struct inputArgs *inp; - struct outputArgs *outp; - int error=0; - int size; -*/ - -#define UPARG(bsize, op)\ -do {\ - CODA_ALLOC(inp, struct inputArgs *, (bsize));\ - outp = (struct outputArgs *) (inp);\ - INIT_IN(inp, (op))\ - coda_load_creds(&(inp->cred));\ - size = (bsize);\ -} while (0) /* upcalls */ int venus_rootfid(struct super_block *sb, ViceFid *fidp); @@ -80,40 +94,27 @@ int venus_access(struct super_block *sb, struct ViceFid *fid, int mask); int venus_pioctl(struct super_block *sb, struct ViceFid *fid, unsigned int cmd, struct PioctlData *data); -int coda_downcall(int opcode, struct outputArgs *out); +int coda_downcall(int opcode, union outputArgs *out, struct super_block *sb); int coda_upcall(struct coda_sb_info *mntinfo, int inSize, - int *outSize, struct inputArgs *buffer); + int *outSize, union inputArgs *buffer); +int venus_fsync(struct super_block *sb, struct ViceFid *fid); /* messages between coda filesystem in kernel and Venus */ +extern int coda_hard; +extern unsigned long coda_timeout; struct vmsg { - struct queue vm_chain; - caddr_t vm_data; - u_short vm_flags; - u_short vm_inSize; /* Size is at most 5000 bytes */ - u_short vm_outSize; - u_short vm_opcode; /* copied from data to save lookup */ - int vm_unique; - struct wait_queue *vm_sleep; /* process' wait queue */ -}; - -/* communication pending/processing queues queues */ -struct vcomm { - u_long vc_seq; - struct wait_queue *vc_waitq; /* Venus wait queue */ - struct queue vc_pending; - struct queue vc_processing; + struct queue vm_chain; + caddr_t vm_data; + u_short vm_flags; + u_short vm_inSize; /* Size is at most 5000 bytes */ + u_short vm_outSize; + u_short vm_opcode; /* copied from data to save lookup */ + int vm_unique; + struct wait_queue *vm_sleep; /* process' wait queue */ + unsigned long vm_posttime; }; -static inline int vcomm_open(struct vcomm *vcp) -{ - return ((vcp)->vc_pending.forw != NULL); -} - -static inline void mark_vcomm_closed(struct vcomm *vcp) -{ - (vcp)->vc_pending.forw = NULL; -} /* * Statistics diff -u --recursive --new-file v2.1.74/linux/include/linux/elfcore.h linux/include/linux/elfcore.h --- v2.1.74/linux/include/linux/elfcore.h Thu Feb 6 02:58:52 1997 +++ linux/include/linux/elfcore.h Sat Dec 20 21:52:17 1997 @@ -41,8 +41,8 @@ #endif struct elf_siginfo pr_info; /* Info associated with signal */ short pr_cursig; /* Current signal */ - sigset_t pr_sigpend; /* Set of pending signals */ - sigset_t pr_sighold; /* Set of held signals */ + unsigned long pr_sigpend; /* Set of pending signals */ + unsigned long pr_sighold; /* Set of held signals */ #if 0 struct sigaltstack pr_altstack; /* Alternate stack info */ struct sigaction pr_action; /* Signal action for current sig */ diff -u --recursive --new-file v2.1.74/linux/include/linux/filter.h linux/include/linux/filter.h --- v2.1.74/linux/include/linux/filter.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/filter.h Sun Dec 21 17:41:24 1997 @@ -0,0 +1,107 @@ +/* + * Linux Socket Filter Data Structures + */ + +#ifndef __LINUX_FILTER_H__ +#define __LINUX_FILTER_H__ + +/* + * Current version of the filter code architecture. + */ +#define BPF_MAJOR_VERSION 1 +#define BPF_MINOR_VERSION 1 + +/* + * Try and keep these values and structures similar to BSD, especially + * the BPF code definitions which need to match so you can share filters + */ + +struct sock_filter /* Filter block */ +{ + u16 code; /* Actual filter code */ + u8 jt; /* Jump true */ + u8 jf; /* Jump false */ + u32 k; /* Generic multiuse field */ +}; + +struct sock_fprog /* Required for SO_ATTACH_FILTER. */ +{ + unsigned short len; /* Number of filter blocks */ + struct sock_filter *filter; +}; + +/* + * Instruction classes + */ + +#define BPF_CLASS(code) ((code) & 0x07) +#define BPF_LD 0x00 +#define BPF_LDX 0x01 +#define BPF_ST 0x02 +#define BPF_STX 0x03 +#define BPF_ALU 0x04 +#define BPF_JMP 0x05 +#define BPF_RET 0x06 +#define BPF_MISC 0x07 + +/* ld/ldx fields */ +#define BPF_SIZE(code) ((code) & 0x18) +#define BPF_W 0x00 +#define BPF_H 0x08 +#define BPF_B 0x10 +#define BPF_MODE(code) ((code) & 0xe0) +#define BPF_IMM 0x00 +#define BPF_ABS 0x20 +#define BPF_IND 0x40 +#define BPF_MEM 0x60 +#define BPF_LEN 0x80 +#define BPF_MSH 0xa0 + +/* alu/jmp fields */ +#define BPF_OP(code) ((code) & 0xf0) +#define BPF_ADD 0x00 +#define BPF_SUB 0x10 +#define BPF_MUL 0x20 +#define BPF_DIV 0x30 +#define BPF_OR 0x40 +#define BPF_AND 0x50 +#define BPF_LSH 0x60 +#define BPF_RSH 0x70 +#define BPF_NEG 0x80 +#define BPF_JA 0x00 +#define BPF_JEQ 0x10 +#define BPF_JGT 0x20 +#define BPF_JGE 0x30 +#define BPF_JSET 0x40 +#define BPF_SRC(code) ((code) & 0x08) +#define BPF_K 0x00 +#define BPF_X 0x08 + +/* ret - BPF_K and BPF_X also apply */ +#define BPF_RVAL(code) ((code) & 0x18) +#define BPF_A 0x10 + +/* misc */ +#define BPF_MISCOP(code) ((code) & 0xf8) +#define BPF_TAX 0x00 +#define BPF_TXA 0x80 + +#define BPF_MAXINSNS 512 + +/* + * Macros for filter block array initializers. + */ +#define BPF_STMT(code, k) { (unsigned short)(code), 0, 0, k } +#define BPF_JUMP(code, k, jt, jf) { (unsigned short)(code), jt, jf, k } + +/* + * Number of scratch memory words for: BPF_ST and BPF_STX + */ +#define BPF_MEMWORDS 16 + +#ifdef __KERNEL__ +extern int sk_run_filter(unsigned char *data, int len, struct sock_filter *filter, int flen); +extern int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk); +#endif /* __KERNEL__ */ + +#endif /* __LINUX_FILTER_H__ */ diff -u --recursive --new-file v2.1.74/linux/include/linux/interrupt.h linux/include/linux/interrupt.h --- v2.1.74/linux/include/linux/interrupt.h Tue Dec 2 16:45:20 1997 +++ linux/include/linux/interrupt.h Sun Dec 21 17:46:20 1997 @@ -37,6 +37,7 @@ SPECIALIX_BH, ESP_BH, NET_BH, + SCSI_BH, IMMEDIATE_BH, KEYBOARD_BH, CYCLADES_BH, diff -u --recursive --new-file v2.1.74/linux/include/linux/kernel.h linux/include/linux/kernel.h --- v2.1.74/linux/include/linux/kernel.h Mon Dec 1 12:04:14 1997 +++ linux/include/linux/kernel.h Sun Dec 21 17:41:24 1997 @@ -65,12 +65,6 @@ printk(KERN_INFO fmt,##arg) /* - * "suser()" checks against the effective user id, while "fsuser()" - * is used for file permission checking and checks against the fsuid.. - */ -#define fsuser() (current->fsuid == 0) - -/* * Display an IP address in readable format. */ diff -u --recursive --new-file v2.1.74/linux/include/linux/nfs.h linux/include/linux/nfs.h --- v2.1.74/linux/include/linux/nfs.h Wed Oct 15 16:04:24 1997 +++ linux/include/linux/nfs.h Sat Dec 20 21:06:01 1997 @@ -33,6 +33,7 @@ NFSERR_EAGAIN = 11, NFSERR_ACCES = 13, NFSERR_EXIST = 17, + NFSERR_XDEV = 18, NFSERR_NODEV = 19, NFSERR_NOTDIR = 20, NFSERR_ISDIR = 21, diff -u --recursive --new-file v2.1.74/linux/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h --- v2.1.74/linux/include/linux/nfs_fs.h Fri Dec 19 15:53:04 1997 +++ linux/include/linux/nfs_fs.h Sun Dec 21 17:58:40 1997 @@ -210,14 +210,7 @@ /* NFS root */ -#define NFS_ROOT "/tftpboot/%s" -#define NFS_ROOT_NAME_LEN 256 -#define NFS_ROOT_ADDRS_LEN 128 - extern int nfs_root_mount(struct super_block *sb); -extern int nfs_root_init(char *nfsname, char *nfsaddrs); -extern char nfs_root_name[]; -extern char nfs_root_addrs[]; #endif /* __KERNEL__ */ diff -u --recursive --new-file v2.1.74/linux/include/linux/ntfs_fs.h linux/include/linux/ntfs_fs.h --- v2.1.74/linux/include/linux/ntfs_fs.h Sun Dec 21 16:17:45 1997 +++ linux/include/linux/ntfs_fs.h Sun Dec 21 16:41:26 1997 @@ -1,7 +1,7 @@ #ifndef _LINUX_NTFS_FS_H #define _LINUX_NTFS_FS_H -int init_ntfs_fs(); +int init_ntfs_fs(void); #endif diff -u --recursive --new-file v2.1.74/linux/include/linux/pci.h linux/include/linux/pci.h --- v2.1.74/linux/include/linux/pci.h Fri Dec 19 15:53:04 1997 +++ linux/include/linux/pci.h Sun Dec 21 17:27:18 1997 @@ -926,5 +926,7 @@ extern const char *pci_strdev (unsigned int vendor, unsigned int device); extern int get_pci_list (char *buf); + +extern void pci_quirks_init (void); #endif /* __KERNEL__ */ #endif /* LINUX_PCI_H */ diff -u --recursive --new-file v2.1.74/linux/include/linux/sched.h linux/include/linux/sched.h --- v2.1.74/linux/include/linux/sched.h Wed Dec 10 11:12:46 1997 +++ linux/include/linux/sched.h Sun Dec 21 17:58:36 1997 @@ -513,10 +513,24 @@ * it returns true (to do BSD-style accounting where the process is flagged * if it uses root privs). The implication of this is that you should do * normal permissions checks first, and check suser() last. + * + * [Dec 1997 -- Chris Evans] + * For correctness, the above considerations need to be extended to + * fsuser(). This is done, along with moving fsuser() checks to be + * last. */ extern inline int suser(void) { if (current->euid == 0) { + current->flags |= PF_SUPERPRIV; + return 1; + } + return 0; +} + +extern inline int fsuser(void) +{ + if (current->fsuid == 0) { current->flags |= PF_SUPERPRIV; return 1; } diff -u --recursive --new-file v2.1.74/linux/include/linux/timer.h linux/include/linux/timer.h --- v2.1.74/linux/include/linux/timer.h Sun Jul 27 12:11:01 1997 +++ linux/include/linux/timer.h Sun Dec 21 17:04:50 1997 @@ -23,8 +23,6 @@ * * FLOPPY_TIMER floppy disk timer (not used right now) * - * SCSI_TIMER scsi.c timeout timer - * * NET_TIMER tcp/ip timeout timer * * COPRO_TIMER 387 timeout for buggy hardware.. @@ -44,7 +42,6 @@ #define HD_TIMER 16 #define FLOPPY_TIMER 17 -#define SCSI_TIMER 18 #define NET_TIMER 19 #define SOUND_TIMER 20 #define COPRO_TIMER 21 diff -u --recursive --new-file v2.1.74/linux/include/linux/videodev.h linux/include/linux/videodev.h --- v2.1.74/linux/include/linux/videodev.h Sat Nov 29 11:25:12 1997 +++ linux/include/linux/videodev.h Sun Dec 21 17:41:30 1997 @@ -110,6 +110,7 @@ #define VIDEO_AUDIO_VOLUME 4 #define VIDEO_AUDIO_BASS 8 #define VIDEO_AUDIO_TREBLE 16 + char name[16]; }; struct video_clip @@ -162,6 +163,9 @@ #define VIDIOCSFREQ _IOW('v',15, unsigned long) /* Set tuner */ #define VIDIOCGAUDIO _IOR('v',16, struct video_audio) /* Get audio info */ #define VIDIOCSAUDIO _IOW('v',17, struct video_audio) /* Audio source, mute etc */ + + +#define BASE_VIDIOCPRIVATE 192 /* 192-255 are private */ #define VID_HARDWARE_BT848 1 diff -u --recursive --new-file v2.1.74/linux/include/net/sock.h linux/include/net/sock.h --- v2.1.74/linux/include/net/sock.h Mon Dec 1 12:04:15 1997 +++ linux/include/net/sock.h Sun Dec 21 17:59:15 1997 @@ -77,6 +77,10 @@ #include #endif +#ifdef CONFIG_FILTER +#include +#endif + #include /* @@ -452,10 +456,12 @@ unsigned char localroute; /* Route locally only */ struct ucred peercred; - /* What the user has tried to set with the security API */ - short authentication; - short encryption; - short encrypt_net; +#ifdef CONFIG_FILTER + /* Socket Filtering Instructions */ + int filter; + struct sock_filter *filter_data; +#endif /* CONFIG_FILTER */ + /* * This is where all the private (optional) areas that don't * overlap will eventually live. @@ -832,6 +838,26 @@ extern void sklist_insert_socket(struct sock **list, struct sock *sk); extern void sklist_destroy_socket(struct sock **list, struct sock *sk); +#ifdef CONFIG_FILTER +/* + * Run the filter code and then cut skb->data to correct size returned by + * sk_run_filter. If pkt_len is 0 we toss packet. If skb->len is smaller + * than pkt_len we keep whole skb->data. + */ +extern __inline__ int sk_filter(struct sk_buff *skb, struct sock_filter *filter, int flen) +{ + int pkt_len; + + pkt_len = sk_run_filter(skb->data, skb->len, filter, flen); + if(!pkt_len) + return 1; /* Toss Packet */ + else + skb_trim(skb, pkt_len); + + return 0; +} +#endif /* CONFIG_FILTER */ + /* * Queue a received datagram if it will fit. Stream and sequenced * protocols can't normally use this as they need to fit buffers in @@ -859,8 +885,17 @@ extern __inline__ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) { if (atomic_read(&sk->rmem_alloc) + skb->truesize >= sk->rcvbuf) - return -ENOMEM; - skb_set_owner_r(skb, sk); + return -ENOMEM; + skb_set_owner_r(skb, sk); + +#ifdef CONFIG_FILTER + if (sk->filter) + { + if (sk_filter(skb, sk->filter_data, sk->filter)) + return -1; /* Toss packet */ + } +#endif /* CONFIG_FILTER */ + skb_queue_tail(&sk->receive_queue,skb); if (!sk->dead) sk->data_ready(sk,skb->len); diff -u --recursive --new-file v2.1.74/linux/init/main.c linux/init/main.c --- v2.1.74/linux/init/main.c Fri Dec 19 15:53:05 1997 +++ linux/init/main.c Sun Dec 21 17:50:46 1997 @@ -33,9 +33,6 @@ #include #include #include -#ifdef CONFIG_ROOT_NFS -#include -#endif #include #include @@ -76,6 +73,7 @@ extern void sysctl_init(void); extern void filescache_init(void); extern void signals_init(void); +extern void dquot_init(void); extern void smp_setup(char *str, int *ints); extern void no_scroll(char *str, int *ints); @@ -88,6 +86,9 @@ #ifdef CONFIG_PRINTER extern void lp_setup(char *str, int *ints); #endif +#ifdef CONFIG_JOYSTICK +extern void js_setup(char *str, int *ints); +#endif extern void eth_setup(char *str, int *ints); #ifdef CONFIG_ARCNET_COM20020 extern void com20020_setup(char *str, int *ints); @@ -134,10 +135,8 @@ extern void in2000_setup(char *str, int *ints); extern void NCR53c406a_setup(char *str, int *ints); extern void wd7000_setup(char *str, int *ints); -#ifdef NOTDEF -extern void ppa_setup(char *str, int *ints); -#endif extern void scsi_luns_setup(char *str, int *ints); +extern void scsi_logging_setup(char *str, int *ints); extern void sound_setup(char *str, int *ints); extern void reboot_setup(char *str, int *ints); extern void video_setup(char *str, int *ints); @@ -259,6 +258,12 @@ #ifdef CONFIG_HFMODEM extern void hfmodem_setup(char *str, int *ints); #endif +#ifdef CONFIG_IP_PNP +extern void ip_auto_config_setup(char *str, int *ints); +#endif +#ifdef CONFIG_ROOT_NFS +extern void nfs_root_setup(char *str, int *ints); +#endif #ifdef CONFIG_FTAPE extern void ftape_setup(char *str, int *ints); #endif @@ -297,13 +302,6 @@ int root_mountflags = MS_RDONLY; char *execute_command = NULL; -#ifdef CONFIG_ROOT_NFS -char nfs_root_name[NFS_ROOT_NAME_LEN] = { "default" }; -char nfs_root_addrs[NFS_ROOT_ADDRS_LEN] = { "" }; -#endif - -extern void dquot_init(void); - static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, }; static char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, }; @@ -321,6 +319,7 @@ return(cur); } +#ifdef CONFIG_PROFILE __initfunc(static void profile_setup(char *str, int *ints)) { if (ints[0] > 0) @@ -332,13 +331,129 @@ prof_shift = 2; #endif } +#endif + + +static struct dev_name_struct { + const char *name; + const int num; +} root_dev_names[] __initdata = { +#ifdef CONFIG_ROOT_NFS + { "nfs", 0x00ff }, +#endif +#ifdef CONFIG_BLK_DEV_IDE + { "hda", 0x0300 }, + { "hdb", 0x0340 }, + { "hdc", 0x1600 }, + { "hdd", 0x1640 }, + { "hde", 0x2100 }, + { "hdf", 0x2140 }, + { "hdg", 0x2200 }, + { "hdh", 0x2240 }, +#endif +#ifdef CONFIG_BLK_DEV_SD + { "sda", 0x0800 }, + { "sdb", 0x0810 }, + { "sdc", 0x0820 }, + { "sdd", 0x0830 }, + { "sde", 0x0840 }, +#endif +#ifdef CONFIG_ATARI_ACSI + { "ada", 0x1c00 }, + { "adb", 0x1c10 }, + { "adc", 0x1c20 }, + { "add", 0x1c30 }, + { "ade", 0x1c40 }, +#endif +#ifdef CONFIG_BLK_DEV_FD + { "fd", 0x0200 }, +#endif +#ifdef CONFIG_BLK_DEV_XD + { "xda", 0x0d00 }, + { "xdb", 0x0d40 }, +#endif +#ifdef CONFIG_BLK_DEV_RAM + { "ram", 0x0100 }, +#endif +#ifdef CONFIG_BLK_DEV_SR + { "scd", 0x0b00 }, +#endif +#ifdef CONFIG_MCD + { "mcd", 0x1700 }, +#endif +#ifdef CONFIG_CDU535 + { "cdu535", 0x1800 }, + { "sonycd", 0x1800 }, +#endif +#ifdef CONFIG_AZTCD + { "aztcd", 0x1d00 }, +#endif +#ifdef CONFIG_CM206 + { "cm206cd", 0x2000 }, +#endif +#ifdef CONFIG_GSCD + { "gscd", 0x1000 }, +#endif +#ifdef CONFIG_SBPCD + { "sbpcd", 0x1900 }, +#endif +#ifdef CONFIG_BLK_DEV_PS2 + { "eda", 0x2400 }, + { "eza", 0x2800 }, +#endif +#ifdef CONFIG_BPCD + { "bpcd", 0x2900 }, +#endif +#if CONFIG_APBLOCK + { "apblock", APBLOCK_MAJOR << 8}, +#endif +#if CONFIG_DDV + { "ddv", DDV_MAJOR << 8}, +#endif + { NULL, 0 } +}; + +__initfunc(static void root_dev_setup(char *line, int *num)) +{ + int base = 0; + if (strncmp(line,"/dev/",5) == 0) { + struct dev_name_struct *dev = root_dev_names; + line += 5; + do { + int len = strlen(dev->name); + if (strncmp(line,dev->name,len) == 0) { + line += len; + base = dev->num; + break; + } + dev++; + } while (dev->name); + } + ROOT_DEV = to_kdev_t(base + simple_strtoul(line,NULL,base?10:16)); +} -struct { +/* + * List of kernel command line parameters. The first table lists parameters + * which are subject to values parsing (leading numbers are converted to + * an array of ints and chopped off the string), the second table contains + * the few exceptions which obey their own syntax rules. + */ + +struct kernel_param { const char *str; void (*setup_func)(char *, int *); -} bootsetups[] __initdata = { +}; + +static struct kernel_param cooked_params[] __initdata = { +/* FIXME: make PNP just become reserve_setup */ +#ifndef CONFIG_KERNEL_PNP_RESOURCE { "reserve=", reserve_setup }, +#else + { "reserve=", pnp_reserve_setup }, +#endif +#ifdef CONFIG_PROFILE { "profile=", profile_setup }, +#endif #ifdef __SMP__ { "nosmp", smp_setup }, { "maxcpus=", smp_setup }, @@ -389,8 +504,12 @@ #ifdef CONFIG_PRINTER { "lp=", lp_setup }, #endif +#ifdef CONFIG_JOYSTICK + { "js=", js_setup }, +#endif #ifdef CONFIG_SCSI { "max_scsi_luns=", scsi_luns_setup }, + { "scsi_logging=", scsi_logging_setup }, #endif #ifdef CONFIG_SCSI_ADVANSYS { "advansys=", advansys_setup }, @@ -460,9 +579,6 @@ #ifdef CONFIG_SCSI_7000FASST { "wd7000=", wd7000_setup}, #endif -#ifdef NOTDEF /* CONFIG_SCSI_PPA */ - { "ppa=", ppa_setup }, -#endif #ifdef CONFIG_SCSI_IBMMCA { "ibmmcascsi=", ibmmca_scsi_setup }, #endif @@ -472,7 +588,7 @@ #ifdef CONFIG_BLK_DEV_EZ { "ez=", ez_setup }, #endif -#ifdef CONFIG_BLK_DEV_FD +#if defined(CONFIG_BLK_DEV_FD) || defined(CONFIG_AMIGA_FLOPPY) || defined(CONFIG_ATARI_FLOPPY) { "floppy=", floppy_setup }, #endif #ifdef CONFIG_BLK_DEV_PS2 @@ -599,6 +715,18 @@ { 0, 0 } }; +static struct kernel_param raw_params[] __initdata = { + { "root=", root_dev_setup }, +#ifdef CONFIG_ROOT_NFS + { "nfsroot=", nfs_root_setup }, + { "nfsaddrs=", ip_auto_config_setup }, +#endif +#ifdef CONFIG_IP_PNP + { "ip=", ip_auto_config_setup }, +#endif + { 0, 0 } +}; + #ifdef CONFIG_BLK_DEV_RAM __initfunc(static void ramdisk_start_setup(char *str, int *ints)) { @@ -623,13 +751,11 @@ if (ints[0] > 0 && ints[1] >= 0) rd_size = ints[1]; } - #endif __initfunc(static int checksetup(char *line)) { - int i = 0; - int ints[11]; + int i, ints[11]; #ifdef CONFIG_BLK_DEV_IDE /* ide driver needs the basic string, rather than pre-processed values */ @@ -638,13 +764,19 @@ return 1; } #endif - while (bootsetups[i].str) { - int n = strlen(bootsetups[i].str); - if (!strncmp(line,bootsetups[i].str,n)) { - bootsetups[i].setup_func(get_options(line+n,ints), ints); + for (i=0; raw_params[i].str; i++) { + int n = strlen(raw_params[i].str); + if (!strncmp(line,raw_params[i].str,n)) { + raw_params[i].setup_func(line+n, NULL); + return 1; + } + } + for (i=0; cooked_params[i].str; i++) { + int n = strlen(cooked_params[i].str); + if (!strncmp(line,cooked_params[i].str,n)) { + cooked_params[i].setup_func(get_options(line+n, ints), ints); return 1; } - i++; } return 0; } @@ -665,7 +797,7 @@ loops_per_sec = (1<<12); - printk("Calibrating delay loop.. "); + printk("Calibrating delay loop... "); while (loops_per_sec <<= 1) { /* wait for "start of" clock tick */ ticks = jiffies; @@ -696,84 +828,17 @@ /* finally, adjust loops per second in terms of seconds instead of clocks */ loops_per_sec *= HZ; /* Round the value and print it */ - printk("ok - %lu.%02lu BogoMIPS\n", + printk("%lu.%02lu BogoMIPS\n", (loops_per_sec+2500)/500000, ((loops_per_sec+2500)/5000) % 100); } -__initfunc(static void parse_root_dev(char * line)) -{ - int base = 0; - static struct dev_name_struct { - const char *name; - const int num; - } devices[] = { - { "nfs", 0x00ff }, - { "hda", 0x0300 }, - { "hdb", 0x0340 }, - { "hdc", 0x1600 }, - { "hdd", 0x1640 }, - { "hde", 0x2100 }, - { "hdf", 0x2140 }, - { "hdg", 0x2200 }, - { "hdh", 0x2240 }, - { "sda", 0x0800 }, - { "sdb", 0x0810 }, - { "sdc", 0x0820 }, - { "sdd", 0x0830 }, - { "sde", 0x0840 }, - { "ada", 0x1c00 }, - { "adb", 0x1c10 }, - { "adc", 0x1c20 }, - { "add", 0x1c30 }, - { "ade", 0x1c40 }, - { "fd", 0x0200 }, - { "xda", 0x0d00 }, - { "xdb", 0x0d40 }, - { "ram", 0x0100 }, - { "scd", 0x0b00 }, - { "mcd", 0x1700 }, - { "cdu535", 0x1800 }, - { "aztcd", 0x1d00 }, - { "cm206cd", 0x2000 }, - { "gscd", 0x1000 }, - { "sbpcd", 0x1900 }, - { "sonycd", 0x1800 }, - { "eda", 0x2400 }, - { "eza", 0x2800 }, - { "bpcd", 0x2900 }, -#if CONFIG_APBLOCK - { "apblock", APBLOCK_MAJOR << 8}, -#endif -#if CONFIG_DDV - { "ddv", DDV_MAJOR << 8}, -#endif - { NULL, 0 } - }; - - if (strncmp(line,"/dev/",5) == 0) { - struct dev_name_struct *dev = devices; - line += 5; - do { - int len = strlen(dev->name); - if (strncmp(line,dev->name,len) == 0) { - line += len; - base = dev->num; - break; - } - dev++; - } while (dev->name); - } - ROOT_DEV = to_kdev_t(base + simple_strtoul(line,NULL,base?10:16)); -} - /* * This is a simple kernel command line parsing function: it parses * the command line, and fills in the arguments/environment to init * as appropriate. Any cmd-line option is taken to be an environment * variable if it contains the character '='. * - * * This routine also checks for options meant for the kernel. * These options are not given to init - they are for internal kernel use only. */ @@ -793,33 +858,6 @@ /* * check for kernel options first.. */ - if (!strncmp(line,"root=",5)) { - parse_root_dev(line+5); - continue; - } -#ifdef CONFIG_ROOT_NFS - if (!strncmp(line, "nfsroot=", 8)) { - int n; - line += 8; - ROOT_DEV = MKDEV(UNNAMED_MAJOR, 255); - if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) { - strncpy(nfs_root_name, line, sizeof(nfs_root_name)); - nfs_root_name[sizeof(nfs_root_name)-1] = '\0'; - continue; - } - n = strlen(line) + strlen(NFS_ROOT); - if (n >= sizeof(nfs_root_name)) - line[sizeof(nfs_root_name) - strlen(NFS_ROOT) - 1] = '\0'; - sprintf(nfs_root_name, NFS_ROOT, line); - continue; - } - if (!strncmp(line, "nfsaddrs=", 9)) { - line += 9; - strncpy(nfs_root_addrs, line, sizeof(nfs_root_addrs)); - nfs_root_addrs[sizeof(nfs_root_addrs)-1] = '\0'; - continue; - } -#endif if (!strcmp(line,"ro")) { root_mountflags |= MS_RDONLY; continue; @@ -930,6 +968,7 @@ * Interrupts are still disabled. Do necessary setups, then * enable them */ + printk(linux_banner); setup_arch(&command_line, &memory_start, &memory_end); memory_start = paging_init(memory_start,memory_end); trap_init(); @@ -941,13 +980,6 @@ init_modules(); #endif #ifdef CONFIG_PROFILE - if (!prof_shift) -#ifdef CONFIG_PROFILE_SHIFT - prof_shift = CONFIG_PROFILE_SHIFT; -#else - prof_shift = 2; -#endif -#endif if (prof_shift) { prof_buffer = (unsigned int *) memory_start; /* only text is profiled */ @@ -956,14 +988,18 @@ memory_start += prof_len * sizeof(unsigned int); memset(prof_buffer, 0, prof_len * sizeof(unsigned int)); } +#endif #ifdef CONFIG_SBUS memory_start = sbus_init(memory_start,memory_end); #endif -#ifdef CONFIG_PMAC +#if defined(CONFIG_PMAC) || defined(CONFIG_CHRP) memory_start = powermac_init(memory_start, memory_end); #endif +#if defined(CONFIG_PCI) && defined(CONFIG_PCI_CONSOLE) + memory_start = pci_init(memory_start,memory_end); +#endif memory_start = console_init(memory_start,memory_end); -#ifdef CONFIG_PCI +#if defined(CONFIG_PCI) && !defined(CONFIG_PCI_CONSOLE) memory_start = pci_init(memory_start,memory_end); #endif #ifdef CONFIG_MCA @@ -997,10 +1033,8 @@ ipc_init(); #endif dquot_init(); - sti(); check_bugs(); - printk(linux_banner); printk("POSIX conformance testing by UNIFIX\n"); #ifdef __SMP__ smp_init(); @@ -1130,7 +1164,7 @@ * trying to recover a really broken machine. */ - if(execute_command) + if (execute_command) execve(execute_command,argv_init,envp_init); execve("/sbin/init",argv_init,envp_init); execve("/etc/init",argv_init,envp_init); diff -u --recursive --new-file v2.1.74/linux/ipc/sem.c linux/ipc/sem.c --- v2.1.74/linux/ipc/sem.c Wed Dec 10 11:12:46 1997 +++ linux/ipc/sem.c Sun Dec 21 17:11:08 1997 @@ -403,19 +403,15 @@ seminfo.semusz = used_semids; seminfo.semaem = used_sems; } - err = verify_area(VERIFY_WRITE, tmp, sizeof(struct seminfo)); - if (err) + err = -EFAULT; + if (copy_to_user (tmp, &seminfo, sizeof(struct seminfo))) goto out; - copy_to_user (tmp, &seminfo, sizeof(struct seminfo)); err = max_semid; goto out; } case SEM_STAT: buf = arg.buf; - err = verify_area (VERIFY_WRITE, buf, sizeof (*buf)); - if (err) - goto out; err = -EINVAL; if (semid > max_semid) goto out; @@ -430,8 +426,9 @@ tbuf.sem_otime = sma->sem_otime; tbuf.sem_ctime = sma->sem_ctime; tbuf.sem_nsems = sma->sem_nsems; - copy_to_user (buf, &tbuf, sizeof(*buf)); - err = id; + err = -EFAULT; + if (copy_to_user (buf, &tbuf, sizeof(*buf)) == 0) + err = id; goto out; } @@ -475,9 +472,7 @@ case GETZCNT: return count_semzcnt(sma,semnum); case GETALL: array = arg.array; - err = verify_area (VERIFY_WRITE, array, nsems*sizeof(ushort)); - if (err) - goto out; + break; } break; case SETVAL: @@ -496,9 +491,10 @@ goto out; case SETALL: /* arg is a pointer to an array of ushort */ array = arg.array; - if ((err = verify_area (VERIFY_READ, array, nsems*sizeof(ushort)))) - goto out; - copy_from_user (sem_io, array, nsems*sizeof(ushort)); + err = -EFAULT; + if (copy_from_user (sem_io, array, nsems*sizeof(ushort))) + goto out; + err = 0; for (i = 0; i < nsems; i++) if (sem_io[i] > SEMVMX) { err = -ERANGE; @@ -507,14 +503,12 @@ break; case IPC_STAT: buf = arg.buf; - if ((err = verify_area (VERIFY_WRITE, buf, sizeof(*buf)))) - goto out; break; case IPC_SET: buf = arg.buf; - if ((err = verify_area (VERIFY_READ, buf, sizeof (*buf)))) - goto out; - copy_from_user (&tbuf, buf, sizeof (*buf)); + err = copy_from_user (&tbuf, buf, sizeof (*buf)); + if (err) + err = -EFAULT; break; } @@ -531,7 +525,8 @@ goto out; for (i = 0; i < sma->sem_nsems; i++) sem_io[i] = sma->sem_base[i].semval; - copy_to_user (array, sem_io, nsems*sizeof(ushort)); + if (copy_to_user (array, sem_io, nsems*sizeof(ushort))) + err = -EFAULT; break; case SETVAL: err = -EACCES; @@ -564,7 +559,8 @@ tbuf.sem_otime = sma->sem_otime; tbuf.sem_ctime = sma->sem_ctime; tbuf.sem_nsems = sma->sem_nsems; - copy_to_user (buf, &tbuf, sizeof(*buf)); + if (copy_to_user (buf, &tbuf, sizeof(*buf))) + err = -EFAULT; break; case SETALL: err = -EACCES; @@ -606,9 +602,8 @@ error = -EFAULT; if (!tsops) goto out; - if ((i = verify_area (VERIFY_READ, tsops, nsops * sizeof(*tsops)))) + if (copy_from_user (sops, tsops, nsops * sizeof(*tsops))) goto out; - copy_from_user (sops, tsops, nsops * sizeof(*tsops)); id = (unsigned int) semid % SEMMNI; error = -EINVAL; if ((sma = semary[id]) == IPC_UNUSED || sma == IPC_NOID) diff -u --recursive --new-file v2.1.74/linux/kernel/ksyms.c linux/kernel/ksyms.c --- v2.1.74/linux/kernel/ksyms.c Fri Dec 19 15:53:05 1997 +++ linux/kernel/ksyms.c Sat Dec 20 21:52:17 1997 @@ -241,6 +241,7 @@ EXPORT_SYMBOL(efind_buffer); EXPORT_SYMBOL(init_buffer); EXPORT_SYMBOL(max_sectors); +EXPORT_SYMBOL(max_readahead); /* tty routines */ EXPORT_SYMBOL(tty_hangup); diff -u --recursive --new-file v2.1.74/linux/kernel/printk.c linux/kernel/printk.c --- v2.1.74/linux/kernel/printk.c Fri Dec 19 15:53:05 1997 +++ linux/kernel/printk.c Sun Dec 21 17:02:16 1997 @@ -324,6 +324,8 @@ * that registers here. */ if (selected_console == 0) { + if (console->index < 0) + console->index = 0; if (console->setup == NULL || console->setup(console, NULL) == 0) { console->flags |= CON_ENABLED | CON_FIRST; diff -u --recursive --new-file v2.1.74/linux/kernel/sysctl.c linux/kernel/sysctl.c --- v2.1.74/linux/kernel/sysctl.c Sat Oct 25 02:44:18 1997 +++ linux/kernel/sysctl.c Sun Dec 21 17:27:18 1997 @@ -160,12 +160,6 @@ {KERN_REALROOTDEV, "real-root-dev", &real_root_dev, sizeof(int), 0644, NULL, &proc_dointvec}, #endif -#ifdef CONFIG_ROOT_NFS - {KERN_NFSRNAME, "nfs-root-name", nfs_root_name, NFS_ROOT_NAME_LEN, - 0644, NULL, &proc_dostring, &sysctl_string }, - {KERN_NFSRADDRS, "nfs-root-addrs", nfs_root_addrs, NFS_ROOT_ADDRS_LEN, - 0644, NULL, &proc_dostring, &sysctl_string }, -#endif #ifdef CONFIG_BINFMT_JAVA {KERN_JAVA_INTERPRETER, "java-interpreter", binfmt_java_interpreter, 64, 0644, NULL, &proc_dostring, &sysctl_string }, diff -u --recursive --new-file v2.1.74/linux/mm/filemap.c linux/mm/filemap.c --- v2.1.74/linux/mm/filemap.c Tue Dec 2 09:49:40 1997 +++ linux/mm/filemap.c Sun Dec 21 17:14:45 1997 @@ -436,16 +436,6 @@ * 64k if defined (4K page size assumed). */ -#define PageAlignSize(size) (((size) + PAGE_SIZE -1) & PAGE_MASK) - -#if 0 /* small readahead */ -#define MAX_READAHEAD PageAlignSize(4096*7) -#define MIN_READAHEAD PageAlignSize(4096*2) -#else /* large readahead */ -#define MAX_READAHEAD PageAlignSize(4096*18) -#define MIN_READAHEAD PageAlignSize(4096*3) -#endif - static inline int get_max_readahead(struct inode * inode) { if (!inode->i_dev || !max_readahead[MAJOR(inode->i_dev)]) diff -u --recursive --new-file v2.1.74/linux/net/Config.in linux/net/Config.in --- v2.1.74/linux/net/Config.in Fri Dec 19 15:53:05 1997 +++ linux/net/Config.in Sun Dec 21 17:41:30 1997 @@ -16,6 +16,7 @@ fi fi bool 'Network aliasing' CONFIG_NET_ALIAS +bool 'Socket Filtering' CONFIG_FILTER tristate 'Unix domain sockets' CONFIG_UNIX bool 'TCP/IP networking' CONFIG_INET if [ "$CONFIG_INET" = "y" ]; then diff -u --recursive --new-file v2.1.74/linux/net/core/Makefile linux/net/core/Makefile --- v2.1.74/linux/net/core/Makefile Mon Dec 1 12:04:15 1997 +++ linux/net/core/Makefile Sun Dec 21 17:41:30 1997 @@ -16,6 +16,10 @@ O_OBJS += sysctl_net_core.o endif +ifdef CONFIG_FILTER +O_OBJS += filter.o +endif + ifdef CONFIG_NET O_OBJS += dev.o dev_mcast.o diff -u --recursive --new-file v2.1.74/linux/net/core/filter.c linux/net/core/filter.c --- v2.1.74/linux/net/core/filter.c Wed Dec 31 16:00:00 1969 +++ linux/net/core/filter.c Sun Dec 21 17:41:30 1997 @@ -0,0 +1,366 @@ +/* + * Linux Socket Filter - Kernel level socket filtering + * + * Author: + * Jay Schulist + * + * Based on the design of: + * - The Berkely Packet Filter + * + * 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. + */ + +#include +#if defined(CONFIG_FILTER) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Decode and apply filter instructions to the skb->data. + * Return length to keep, 0 for none. skb is the data we are + * filtering, filter is the array of filter instructions, and + * len is the number of filter blocks in the array. + */ + +int sk_run_filter(unsigned char *data, int len, struct sock_filter *filter, int flen) +{ + struct sock_filter *fentry; /* We walk down these */ + u32 A = 0; /* Accumulator */ + u32 X = 0; /* Index Register */ + u32 mem[BPF_MEMWORDS]; /* Scratch Memory Store */ + int k; + int pc; + int *t; + + /* + * Process array of filter instructions. + */ + + for(pc = 0; pc < flen; pc++) + { + fentry = &filter[pc]; + if(fentry->code & BPF_X) + t=&X; + else + t=&fentry->k; + + switch(fentry->code) + { + case BPF_ALU|BPF_ADD|BPF_X: + case BPF_ALU|BPF_ADD|BPF_K: + A += *t; + continue; + + case BPF_ALU|BPF_SUB|BPF_X: + case BPF_ALU|BPF_SUB|BPF_K: + A -= *t; + continue; + + case BPF_ALU|BPF_MUL|BPF_X: + case BPF_ALU|BPF_MUL|BPF_K: + A *= *t; + continue; + + case BPF_ALU|BPF_DIV|BPF_X: + case BPF_ALU|BPF_DIV|BPF_K: + if(*t == 0) + return (0); + A /= *t; + continue; + + case BPF_ALU|BPF_AND|BPF_X: + case BPF_ALU|BPF_AND|BPF_K: + A &= *t; + continue; + + case BPF_ALU|BPF_OR|BPF_X: + case BPF_ALU|BPF_OR|BPF_K: + A |= *t; + continue; + + case BPF_ALU|BPF_LSH|BPF_X: + case BPF_ALU|BPF_LSH|BPF_K: + A <<= *t; + continue; + + case BPF_ALU|BPF_RSH|BPF_X: + case BPF_ALU|BPF_RSH|BPF_K: + A >>= *t; + continue; + + case BPF_ALU|BPF_NEG: + A = -A; + continue; + + case BPF_JMP|BPF_JA: + pc += fentry->k; + continue; + + case BPF_JMP|BPF_JGT|BPF_K: + pc += (A > fentry->k) ? fentry->jt : fentry->jf; + continue; + + case BPF_JMP|BPF_JGE|BPF_K: + pc += (A >= fentry->k) ? fentry->jt : fentry->jf; + continue; + + case BPF_JMP|BPF_JEQ|BPF_K: + pc += (A == fentry->k) ? fentry->jt : fentry->jf; + continue; + + case BPF_JMP|BPF_JSET|BPF_K: + pc += (A & fentry->k) ? fentry->jt : fentry->jf; + continue; + + case BPF_JMP|BPF_JGT|BPF_X: + pc += (A > X) ? fentry->jt : fentry->jf; + continue; + + case BPF_JMP|BPF_JGE|BPF_X: + pc += (A >= X) ? fentry->jt : fentry->jf; + continue; + + case BPF_JMP|BPF_JEQ|BPF_X: + pc += (A == X) ? fentry->jt : fentry->jf; + continue; + + case BPF_JMP|BPF_JSET|BPF_X: + pc += (A & X) ? fentry->jt : fentry->jf; + continue; + case BPF_LD|BPF_W|BPF_ABS: + k = fentry->k; + if(k + sizeof(long) > len) + return (0); + A = ntohl(*(long*)&data[k]); + continue; + + case BPF_LD|BPF_H|BPF_ABS: + k = fentry->k; + if(k + sizeof(short) > len) + return (0); + A = ntohs(*(short*)&data[k]); + continue; + + case BPF_LD|BPF_B|BPF_ABS: + k = fentry->k; + if(k >= len) + return (0); + A = data[k]; + continue; + + case BPF_LD|BPF_W|BPF_LEN: + A = len; + continue; + + case BPF_LDX|BPF_W|BPF_LEN: + X = len; + continue; + + case BPF_LD|BPF_W|BPF_IND: + k = X + fentry->k; + if(k + sizeof(u32) > len) + return (0); + A = ntohl(*(u32 *)&data[k]); + continue; + + case BPF_LD|BPF_H|BPF_IND: + k = X + fentry->k; + if(k + sizeof(u16) > len) + return (0); + A = ntohs(*(u16*)&data[k]); + continue; + + case BPF_LD|BPF_B|BPF_IND: + k = X + fentry->k; + if(k >= len) + return (0); + A = data[k]; + continue; + + case BPF_LDX|BPF_B|BPF_MSH: + /* + * Hack for BPF to handle TOS etc + */ + k = fentry->k; + if(k >= len) + return (0); + X = (data[fentry->k] & 0xf) << 2; + continue; + + case BPF_LD|BPF_IMM: + A = fentry->k; + continue; + + case BPF_LDX|BPF_IMM: + X = fentry->k; + continue; + + case BPF_LD|BPF_MEM: + A = mem[fentry->k]; + continue; + + case BPF_LDX|BPF_MEM: + X = mem[fentry->k]; + continue; + + case BPF_MISC|BPF_TAX: + X = A; + continue; + + case BPF_MISC|BPF_TXA: + A = X; + continue; + + case BPF_RET|BPF_K: + return ((unsigned int)fentry->k); + + case BPF_RET|BPF_A: + return ((unsigned int)A); + + case BPF_ST: + mem[fentry->k] = A; + continue; + + case BPF_STX: + mem[fentry->k] = X; + continue; + + + + default: + /* Invalid instruction counts as RET */ + return (0); + } + } + + printk(KERN_ERR "Filter ruleset ran off the end.\n"); + return (0); +} + +/* + * Check the user's filter code. If we let some ugly + * filter code slip through kaboom! + */ + +int sk_chk_filter(struct sock_filter *filter, int flen) +{ + struct sock_filter *ftest; + int pc; + + /* + * Check the filter code now. + */ + for(pc = 0; pc < flen; pc++) + { + /* + * All jumps are forward as they are not signed + */ + + ftest = &filter[pc]; + if(BPF_CLASS(ftest->code) == BPF_JMP) + { + /* + * But they mustn't jump off the end. + */ + if(BPF_OP(ftest->code) == BPF_JA) + { + if(pc + ftest->k + 1>= (unsigned)flen) + return (-EINVAL); + } + else + { + /* + * For conditionals both must be safe + */ + if(pc + ftest->jt +1 >= flen || pc + ftest->jf +1 >= flen) + return (-EINVAL); + } + } + + /* + * Check that memory operations use valid addresses. + */ + + if(ftest->k <0 || ftest->k >= BPF_MEMWORDS) + { + /* + * But it might not be a memory operation... + */ + + if (BPF_CLASS(ftest->code) == BPF_ST) + return -EINVAL; + if((BPF_CLASS(ftest->code) == BPF_LD) && + (BPF_MODE(ftest->code) == BPF_MEM)) + return (-EINVAL); + } + } + + /* + * The program must end with a return. We don't care where they + * jumped within the script (its always forwards) but in the + * end they _will_ hit this. + */ + + return (BPF_CLASS(filter[flen - 1].code) == BPF_RET); +} + +/* + * Attach the user's filter code. We first run some sanity checks on + * it to make sure it does not explode on us later. + */ + +int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) +{ + struct sock_filter *fp, *old_filter; + int fsize = sizeof(struct sock_filter) * fprog->len; + int err; + + /* Make sure new filter is there and in the right amounts. */ + if(fprog->filter == NULL || fprog->len == 0 || fsize > BPF_MAXINSNS) + return (-EINVAL); + + if((err = sk_chk_filter(fprog->filter, fprog->len))) + { + /* If existing filter, remove it first */ + if(sk->filter) + { + old_filter = sk->filter_data; + kfree_s(old_filter, (sizeof(old_filter) * sk->filter)); + sk->filter_data = NULL; + } + + fp = (struct sock_filter *)kmalloc(fsize, GFP_KERNEL); + if(fp == NULL) + return (-ENOMEM); + + memset(fp,0,sizeof(*fp)); + memcpy(fp, fprog->filter, fsize); /* Copy instructions */ + + sk->filter = fprog->len; /* Number of filter blocks */ + sk->filter_data = fp; /* Filter instructions */ + } + + return (err); +} +#endif /* CONFIG_FILTER */ diff -u --recursive --new-file v2.1.74/linux/net/core/sock.c linux/net/core/sock.c --- v2.1.74/linux/net/core/sock.c Wed Dec 10 11:12:46 1997 +++ linux/net/core/sock.c Sun Dec 21 17:41:30 1997 @@ -76,6 +76,7 @@ * Steve Whitehouse: Added various other default routines * common to several socket families. * Chris Evans : Call suser() check last on F_SETOWN + * Jay Schulist : Added SO_ATTACH_FILTER and SO_DETACH_FILTER. * * To Fix: * @@ -122,6 +123,10 @@ #include #include +#ifdef CONFIG_FILTER +#include +#endif + #define min(a,b) ((a)<(b)?(a):(b)) /* Run time adjustable parameters. */ @@ -147,6 +152,10 @@ struct linger ling; struct ifreq req; int ret = 0; + +#ifdef CONFIG_FILTER + struct sock_fprog fprog; +#endif /* * Options without arguments @@ -278,48 +287,6 @@ break; -#ifdef CONFIG_NET_SECURITY - /* - * FIXME: make these error things that are not - * available! - */ - - case SO_SECURITY_AUTHENTICATION: - if(val<=IPSEC_LEVEL_DEFAULT) - { - sk->authentication=val; - return 0; - } - if(net_families[sock->ops->family]->authentication) - sk->authentication=val; - else - return -EINVAL; - break; - - case SO_SECURITY_ENCRYPTION_TRANSPORT: - if(val<=IPSEC_LEVEL_DEFAULT) - { - sk->encryption=val; - return 0; - } - if(net_families[sock->ops->family]->encryption) - sk->encryption = val; - else - return -EINVAL; - break; - - case SO_SECURITY_ENCRYPTION_NETWORK: - if(val<=IPSEC_LEVEL_DEFAULT) - { - sk->encrypt_net=val; - return 0; - } - if(net_families[sock->ops->family]->encrypt_net) - sk->encrypt_net = val; - else - return -EINVAL; - break; -#endif case SO_BINDTODEVICE: /* Bind this socket to a particular device like "eth0", * as specified in an ifreq structure. If the device @@ -359,6 +326,33 @@ } return 0; +#ifdef CONFIG_FILTER + case SO_ATTACH_FILTER: + if(optlen < sizeof(struct sock_fprog)) + return -EINVAL; + + if(copy_from_user(&fprog, optval, sizeof(fprog))) + { + ret = -EFAULT; + break; + } + + ret = sk_attach_filter(&fprog, sk); + break; + + case SO_DETACH_FILTER: + if(sk->filter) + { + fprog.filter = sk->filter_data; + kfree_s(fprog.filter, (sizeof(fprog.filter) * sk->filter)); + sk->filter_data = NULL; + sk->filter = 0; + return 0; + } + else + return -EINVAL; + break; +#endif /* We implement the SO_SNDLOWAT etc to not be settable (1003.1g 5.3) */ diff -u --recursive --new-file v2.1.74/linux/net/ipv4/fib_semantics.c linux/net/ipv4/fib_semantics.c --- v2.1.74/linux/net/ipv4/fib_semantics.c Mon Dec 1 12:04:16 1997 +++ linux/net/ipv4/fib_semantics.c Sun Dec 21 17:41:30 1997 @@ -120,6 +120,7 @@ for_nexthops(fi) { if (nh->nh_oif != onh->nh_oif || nh->nh_gw != onh->nh_gw || + nh->nh_scope != onh->nh_scope || #ifdef CONFIG_IP_ROUTE_MULTIPATH nh->nh_weight != onh->nh_weight || #endif diff -u --recursive --new-file v2.1.74/linux/net/ipv4/ip_forward.c linux/net/ipv4/ip_forward.c --- v2.1.74/linux/net/ipv4/ip_forward.c Fri Dec 19 15:53:06 1997 +++ linux/net/ipv4/ip_forward.c Sun Dec 21 17:41:30 1997 @@ -184,7 +184,8 @@ { #endif maddr = inet_select_addr(dev2, rt->rt_gateway, RT_SCOPE_UNIVERSE); - if (fw_res = ip_fw_masq_icmp(&skb, maddr) < 0) { + fw_res = ip_fw_masq_icmp(&skb, maddr); + if (fw_res < 0) { kfree_skb(skb, FREE_READ); return -1; } diff -u --recursive --new-file v2.1.74/linux/net/netlink/af_netlink.c linux/net/netlink/af_netlink.c --- v2.1.74/linux/net/netlink/af_netlink.c Tue Dec 2 09:49:40 1997 +++ linux/net/netlink/af_netlink.c Sun Dec 21 17:41:30 1997 @@ -315,7 +315,7 @@ return -EINVAL; /* Only superuser is allowed to send multicasts */ - if (!suser() && nladdr->nl_groups) + if (nladdr->nl_groups && !suser()) return -EPERM; sk->protinfo.af_netlink.dst_pid = nladdr->nl_pid; diff -u --recursive --new-file v2.1.74/linux/net/netsyms.c linux/net/netsyms.c --- v2.1.74/linux/net/netsyms.c Thu Dec 4 14:53:57 1997 +++ linux/net/netsyms.c Sun Dec 21 17:52:06 1997 @@ -44,6 +44,8 @@ #endif +#include + #include #include @@ -78,6 +80,13 @@ EXPORT_SYMBOL(sock_unregister); /* Socket layer support routines */ +EXPORT_SYMBOL(sk_alloc); +EXPORT_SYMBOL(sk_free); + +#ifdef CONFIG_FILTER +EXPORT_SYMBOL(sk_run_filter); +#endif + EXPORT_SYMBOL(memcpy_fromiovec); EXPORT_SYMBOL(sock_create); EXPORT_SYMBOL(sock_alloc); @@ -86,8 +95,6 @@ EXPORT_SYMBOL(sock_getsockopt); EXPORT_SYMBOL(sock_sendmsg); EXPORT_SYMBOL(sock_recvmsg); -EXPORT_SYMBOL(sk_alloc); -EXPORT_SYMBOL(sk_free); EXPORT_SYMBOL(sock_wake_async); EXPORT_SYMBOL(sock_alloc_send_skb); EXPORT_SYMBOL(sock_init_data); diff -u --recursive --new-file v2.1.74/linux/net/sunrpc/sched.c linux/net/sunrpc/sched.c --- v2.1.74/linux/net/sunrpc/sched.c Sun Dec 21 16:17:45 1997 +++ linux/net/sunrpc/sched.c Sun Dec 21 16:15:02 1997 @@ -395,7 +395,7 @@ /* When the task received a signal, remove from * any queues etc, and make runnable again. */ - if (signalled()) + if (0 && signalled()) __rpc_wake_up(task); dprintk("RPC: %4d sync task resuming\n", @@ -521,6 +521,7 @@ if (flags & RPC_TASK_ASYNC) return NULL; current->timeout = jiffies + (HZ >> 4); + current->state = TASK_INTERRUPTIBLE; schedule(); } while (!signalled()); diff -u --recursive --new-file v2.1.74/linux/scripts/Menuconfig linux/scripts/Menuconfig --- v2.1.74/linux/scripts/Menuconfig Wed Dec 10 11:12:46 1997 +++ linux/scripts/Menuconfig Sun Dec 21 17:41:30 1997 @@ -445,7 +445,7 @@ # Semantics of + and ? in GNU expr changed, so # we avoid them: - if expr "$answer" : '0$\|\(-[1-9]\|[1-9]\)[0-9]*$' >/dev/null + if expr "$answer" : '0$\|\(-[1-9]\|[0-9]\)[0-9]*$' >/dev/null then eval $2="$answer" else diff -u --recursive --new-file v2.1.74/linux/scripts/tkcond.c linux/scripts/tkcond.c --- v2.1.74/linux/scripts/tkcond.c Mon Jun 16 16:36:02 1997 +++ linux/scripts/tkcond.c Sun Dec 21 17:04:50 1997 @@ -101,7 +101,7 @@ struct condition * cond; struct condition * cond2; struct kconfig * cfg; - char tmpbuf[10]; + char tmpbuf[255]; for(cfg = config;cfg != NULL; cfg = cfg->next) {