diff -u --recursive --new-file v2.1.58/linux/Makefile linux/Makefile --- v2.1.58/linux/Makefile Wed Oct 15 16:04:23 1997 +++ linux/Makefile Wed Oct 15 21:09:09 1997 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 1 -SUBLEVEL = 58 +SUBLEVEL = 59 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/) diff -u --recursive --new-file v2.1.58/linux/arch/alpha/kernel/irq.c linux/arch/alpha/kernel/irq.c --- v2.1.58/linux/arch/alpha/kernel/irq.c Wed Apr 16 14:14:59 1997 +++ linux/arch/alpha/kernel/irq.c Fri Oct 17 14:02:01 1997 @@ -37,6 +37,18 @@ # error Unable to handle more than 64 irq levels. #endif +/* PROBE_MASK is the bitset of irqs that we consider for autoprobing: */ +#if defined(CONFIG_ALPHA_P2K) + /* always mask out unused timer irq 0 and RTC irq 8 */ +# define PROBE_MASK (((1UL << NR_IRQS) - 1) & ~0x101UL) +#elif defined(CONFIG_ALPHA_ALCOR) + /* always mask out unused timer irq 0, "irqs" 20-30, and the EISA cascade: */ +# define PROBE_MASK (((1UL << NR_IRQS) - 1) & ~0xfff000000001UL) +#else + /* always mask out unused timer irq 0: */ +# define PROBE_MASK (((1UL << NR_IRQS) - 1) & ~1UL) +#endif + /* Reserved interrupts. These must NEVER be requested by any driver! */ #define IS_RESERVED_IRQ(irq) ((irq)==2) /* IRQ 2 used by hw cascade */ @@ -644,10 +656,13 @@ unsigned int i; for (i = NR_IRQS - 1; i > 0; i--) { + if (!(PROBE_MASK & (1UL << i))) { + continue; + } action = irq_action[i]; if (!action) { enable_irq(i); - irqs |= (1 << i); + irqs |= (1UL << i); } } /* @@ -668,14 +683,10 @@ */ int probe_irq_off(unsigned long irqs) { + unsigned long delay; int i; - /* as irq 0 & 8 handling don't use this function, i didn't - * bother changing the following: */ - irqs &= irq_mask & ~1; /* always mask out irq 0---it's the unused timer */ -#ifdef CONFIG_ALPHA_P2K - irqs &= ~(1 << 8); /* mask out irq 8 since that's the unused RTC input to PIC */ -#endif + irqs &= irq_mask; if (!irqs) return 0; i = ffz(~irqs); diff -u --recursive --new-file v2.1.58/linux/arch/i386/kernel/i386_ksyms.c linux/arch/i386/kernel/i386_ksyms.c --- v2.1.58/linux/arch/i386/kernel/i386_ksyms.c Mon Aug 18 18:19:42 1997 +++ linux/arch/i386/kernel/i386_ksyms.c Fri Oct 17 13:59:30 1997 @@ -63,6 +63,7 @@ #ifdef CONFIG_MCA /* Adapter probing and info methods. */ +EXPORT_SYMBOL(machine_id); EXPORT_SYMBOL(mca_find_adapter); EXPORT_SYMBOL(mca_write_pos); EXPORT_SYMBOL(mca_read_pos); diff -u --recursive --new-file v2.1.58/linux/drivers/block/ide-probe.c linux/drivers/block/ide-probe.c --- v2.1.58/linux/drivers/block/ide-probe.c Tue Sep 23 16:48:47 1997 +++ linux/drivers/block/ide-probe.c Fri Oct 17 15:51:08 1997 @@ -176,7 +176,7 @@ int rc; ide_ioreg_t hd_status; unsigned long timeout; - int irqs = 0; + unsigned long irqs = 0; if (!HWIF(drive)->irq) { /* already got an IRQ? */ probe_irq_off(probe_irq_on()); /* clear dangling irqs */ @@ -235,7 +235,7 @@ (void) GET_STAT(); /* clear drive IRQ */ } else { /* Mmmm.. multiple IRQs.. don't know which was ours */ - printk("%s: IRQ probe failed (%d)\n", drive->name, irqs); + printk("%s: IRQ probe failed (%ld)\n", drive->name, irqs); #ifdef CONFIG_BLK_DEV_CMD640 #ifdef CMD640_DUMP_REGS if (HWIF(drive)->chipset == ide_cmd640) { diff -u --recursive --new-file v2.1.58/linux/drivers/net/8390.h linux/drivers/net/8390.h --- v2.1.58/linux/drivers/net/8390.h Sun Feb 2 05:18:35 1997 +++ linux/drivers/net/8390.h Fri Oct 17 14:02:01 1997 @@ -46,8 +46,8 @@ #ifndef HAVE_AUTOIRQ /* From auto_irq.c */ extern struct device *irq2dev_map[16]; -extern int autoirq_setup(int waittime); -extern int autoirq_report(int waittime); +extern void autoirq_setup(int waittime); +extern unsigned long autoirq_report(int waittime); #endif /* Most of these entries should be in 'struct device' (or most of the diff -u --recursive --new-file v2.1.58/linux/drivers/net/auto_irq.c linux/drivers/net/auto_irq.c --- v2.1.58/linux/drivers/net/auto_irq.c Thu Feb 29 21:50:43 1996 +++ linux/drivers/net/auto_irq.c Fri Oct 17 14:02:01 1997 @@ -39,76 +39,18 @@ #include #include -struct device *irq2dev_map[16] = {0, 0, /* ... zeroed */}; +struct device *irq2dev_map[NR_IRQS] = {0, 0, /* ... zeroed */}; -unsigned long irqs_busy = 0x2147; /* The set of fixed IRQs (keyboard, timer, etc) */ -unsigned long irqs_used = 0x0001; /* The set of fixed IRQs sometimes enabled. */ -unsigned long irqs_reserved = 0x0000; /* An advisory "reserved" table. */ -unsigned long irqs_shared = 0x0000; /* IRQ lines "shared" among conforming cards.*/ +static unsigned long irqs; -static volatile unsigned long irq_bitmap; /* The irqs we actually found. */ -static unsigned long irq_handled; /* The irq lines we have a handler on. */ -static volatile int irq_number; /* The latest irq number we actually found. */ - -static void autoirq_probe(int irq, void *dev_id, struct pt_regs * regs) +void autoirq_setup(int waittime) { - irq_number = irq; - set_bit(irq, (void *)&irq_bitmap); /* irq_bitmap |= 1 << irq; */ - disable_irq(irq); - return; -} - -int autoirq_setup(int waittime) -{ - int i, mask; - int timeout = jiffies + waittime; - int boguscount = (waittime*loops_per_sec) / 100; - - irq_handled = 0; - for (i = 0; i < 16; i++) { - if (test_bit(i, &irqs_busy) == 0 - && request_irq(i, autoirq_probe, SA_INTERRUPT, "irq probe", NULL) == 0) - set_bit(i, (void *)&irq_handled); /* irq_handled |= 1 << i;*/ - } - /* Update our USED lists. */ - irqs_used |= ~irq_handled; - irq_number = 0; - irq_bitmap = 0; - - /* Hang out at least jiffies waiting for bogus IRQ hits. */ - while (timeout > jiffies && --boguscount > 0) - ; - - for (i = 0, mask = 0x01; i < 16; i++, mask <<= 1) { - if (irq_bitmap & irq_handled & mask) { - irq_handled &= ~mask; -#ifdef notdef - printk(" Spurious interrupt on IRQ %d\n", i); -#endif - free_irq(i, NULL); - } - } - return irq_handled; + irqs = probe_irq_on(); } int autoirq_report(int waittime) { - int i; - int timeout = jiffies+waittime; - int boguscount = (waittime*loops_per_sec) / 100; - - /* Hang out at least jiffies waiting for the IRQ. */ - - while (timeout > jiffies && --boguscount > 0) - if (irq_number) - break; - - /* Retract the irq handlers that we installed. */ - for (i = 0; i < 16; i++) { - if (test_bit(i, (void *)&irq_handled)) - free_irq(i, NULL); - } - return irq_number; + return probe_irq_off(irqs); } /* diff -u --recursive --new-file v2.1.58/linux/drivers/net/ne.c linux/drivers/net/ne.c --- v2.1.58/linux/drivers/net/ne.c Mon Aug 11 14:47:04 1997 +++ linux/drivers/net/ne.c Fri Oct 17 14:04:26 1997 @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -408,6 +409,7 @@ outb_p(0x00, ioaddr + EN0_RCNTLO); outb_p(0x00, ioaddr + EN0_RCNTHI); outb_p(E8390_RREAD+E8390_START, ioaddr); /* Trigger it... */ + udelay(10000); /* wait 10ms for interrupt to propagate */ outb_p(0x00, ioaddr + EN0_IMR); /* Mask it again. */ dev->irq = autoirq_report(0); if (ei_debug > 2) diff -u --recursive --new-file v2.1.58/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in --- v2.1.58/linux/drivers/scsi/Config.in Tue Sep 23 16:48:48 1997 +++ linux/drivers/scsi/Config.in Fri Oct 17 13:59:30 1997 @@ -82,7 +82,10 @@ fi fi if [ "$CONFIG_MCA" = "y" ]; then - dep_tristate 'IBMMCA SCSI support' CONFIG_SCSI_IBMMCA $CONFIG_SCSI + dep_tristate 'IBMMCA SCSI support' CONFIG_SCSI_IBMMCA $CONFIG_SCSI + if [ "$CONFIG_SCSI_IBMMCA" != "n" ]; then + bool ' reset SCSI-devices while booting' SCSI_IBMMCA_DEV_RESET + fi fi if [ "$CONFIG_PARPORT" != "n" ]; then dep_tristate 'IOMEGA Parallel Port ZIP drive SCSI support' CONFIG_SCSI_PPA $CONFIG_SCSI $CONFIG_PARPORT diff -u --recursive --new-file v2.1.58/linux/drivers/scsi/ibmmca.c linux/drivers/scsi/ibmmca.c --- v2.1.58/linux/drivers/scsi/ibmmca.c Mon Apr 14 16:28:16 1997 +++ linux/drivers/scsi/ibmmca.c Fri Oct 17 13:59:30 1997 @@ -7,6 +7,7 @@ /* Update history: Jan 15 1996: First public release. + - Martin Kolinek Jan 23 1996: Scrapped code which reassigned scsi devices to logical device numbers. Instead, the existing assignment (created @@ -16,15 +17,18 @@ and also the hard disks are ordered under Linux the same way as they are under dos (i.e., C: disk is sda, D: disk is sdb, etc.). + - Martin Kolinek I think that the CD-ROM is now detected only if a CD is inside CD_ROM while Linux boots. This can be fixed later, once the driver works on all types of PS/2's. + - Martin Kolinek Feb 7 1996: Modified biosparam function. Fixed the CD-ROM detection. For now, devices other than harddisk and CD_ROM are ignored. Temporarily modified abort() function - to behave like reset(). + to behave like reset(). + - Martin Kolinek Mar 31 1996: The integrated scsi subsystem is correctly found in PS/2 models 56,57, but not in model 76. Therefore @@ -32,21 +36,200 @@ This function allows the user to force detection of scsi subsystem. The kernel option has format ibmmcascsi=n - where n is the scsi_id (pun) of the subsystem. Most - likely, n is 7. + where n is the scsi_id (pun) of the subsystem. Most likely, n is 7. + - Martin Kolinek Aug 21 1996: Modified the code which maps ldns to (pun,0). It was insufficient for those of us with CD-ROM changers. - Chris Beauregard - - Mar 16 1997: Modified driver to run as a module and to support - multiple adapters. + + Dec 14 1996: More improvements to the ldn mapping. See check_devices + for details. Did more fiddling with the integrated SCSI detection, + but I think it's ultimately hopeless without actually testing the + model of the machine. The 56, 57, 76 and 95 (ultimedia) all have + different integrated SCSI register configurations. However, the 56 + and 57 are the only ones that have problems with forced detection. + - Chris Beauregard + + Mar 8-16 1997: Modified driver to run as a module and to support + multiple adapters. A structure, called ibmmca_hostdata, is now + present, containing all the variables, that were once only + available for one single adapter. The find_subsystem-routine has vanished. + The hardware recognition is now done in ibmmca_detect directly. + This routine checks for presence of MCA-bus, checks the interrupt + level and continues with checking the installed hardware. + Certain PS/2-models do not recognize a SCSI-subsystem automatically. + Hence, the setup defined by command-line-parameters is checked first. + Thereafter, the routine probes for an integrated SCSI-subsystem. + Finally, adapters are checked. This method has the advantage to cover all + possible combinations of multiple SCSI-subsystems on one MCA-board. Up to + eight SCSI-subsystems can be recognized and announced to the upper-level + drivers with this improvement. A set of defines made changes to other + routines as small as possible. - Klaus Kudielka - */ + + May 30 1997: (v1.5b) + 1) SCSI-command capability enlarged by the recognition of MODE_SELECT. + This needs the RD-Bit to be disabled on IM_OTHER_SCSI_CMD_CMD which + allows data to be written from the system to the device. It is a + necessary step to be allowed to set blocksize of SCSI-tape-drives and + the tape-speed, whithout confusing the SCSI-Subsystem. + 2) The recognition of a tape is included in the check_devices routine. + This is done by checking for TYPE_TAPE, that is already defined in + the kernel-scsi-environment. The markup of a tape is done in the + global ldn_is_tape[] array. If the entry on index ldn + is 1, there is a tapedrive connected. + 3) The ldn_is_tape[] array is necessary to distinguish between tape- and + other devices. Fixed blocklength devices should not cause a problem + with the SCB-command for read and write in the ibmmca_queuecommand + subroutine. Therefore, I only derivate the READ_XX, WRITE_XX for + the tape-devices, as recommended by IBM in this Technical Reference, + mentioned below. (IBM recommends to avoid using the read/write of the + subsystem, but the fact was, that read/write causes a command error from + the subsystem and this causes kernel-panic.) + 4) In addition, I propose to use the ldn instead of a fix char for the + display of PS2_DISK_LED_ON(). On 95, one can distinguish between the + devices that are accessed. It shows activity and easyfies debugging. + The tape-support has been tested with a SONY SDT-5200 and a HP DDS-2 + (I do not know yet the type). Optimization and CD-ROM audio-support, + I am working on ... + - Michael Lang + + June 19 1997: (v1.6b) + 1) Submitting the extra-array ldn_is_tape[] -> to the local ld[] + device-array. + 2) CD-ROM Audio-Play seems to work now. + 3) When using DDS-2 (120M) DAT-Tapes, mtst shows still density-code + 0x13 for ordinary DDS (61000 BPM) instead 0x24 for DDS-2. This appears + also on Adaptec 2940 adaptor in a PCI-System. Therefore, I assume that + the problem is independent of the low-level-driver/bus-architecture. + 4) Hexadecimal ldn on PS/2-95 LED-display. + 5) Fixing of the PS/2-LED on/off that it works right with tapedrives and + does not confuse the disk_rw_in_progress counter. + - Michael Lang + + June 21 1997: (v1.7b) + 1) Adding of a proc_info routine to inform in /proc/scsi/ibmmca/ the + outer-world about operational load statistics on the different ldns, + seen by the driver. Everybody that has more than one IBM-SCSI should + test this, because I only have one and cannot see what happens with more + than one IBM-SCSI hosts. + 2) Definition of a driver version-number to have a better recognition of + the source when there are existing too much releases that may confuse + the user, when reading about release-specific problems. Up to know, + I calculated the version-number to be 1.7. Because we are in BETA-test + yet, it is today 1.7b. + 3) Sorry for the heavy bug I programmed on June 19 1997! After that, the + CD-ROM did not work any more! The C7-command was a fake impression + I got while programming. Now, the READ and WRITE commands for CD-ROM are + no longer running over the subsystem, but just over + IM_OTHER_SCSI_CMD_CMD. On my observations (PS/2-95), now CD-ROM mounts + much faster(!) and hopefully all fancy multimedia-functions, like direct + digital recording from audio-CDs also work. (I tried it with cdda2wav + from the cdwtools-package and it filled up the harddisk immediately :-).) + To easify boolean logics, a further local device-type in ld[], called + is_cdrom has been included. + 4) If one uses a SCSI-device of unsupported type/commands, one + immediately runs into a kernel-panic caused by Command Error. To better + understand which SCSI-command caused the problem, I extended this + specific panic-message slightly. + - Michael Lang + + June 25 1997: (v1.8b) + 1) Some cosmetical changes for the handling of SCSI-device-types. + Now, also CD-Burners / WORMs and SCSI-scanners should work. For + MO-drives I have no experience, therefore not yet supported. + In logical_devices I changed from different type-variables to one + called 'device_type' where the values, corresponding to scsi.h, + of a SCSI-device are stored. + 2) There existed a small bug, that maps a device, coming after a SCSI-tape + wrong. Therefore, e.g. a CD-ROM changer would have been mapped wrong + -> problem removed. + 3) Extension of the logical_device structure. Now it contains also device, + vendor and revision-level of a SCSI-device for internal usage. + - Michael Lang + + June 26-29 1997: (v2.0b) + 1) The release number 2.0b is necessary because of the completely new done + recognition and handling of SCSI-devices with the adapter. As I got + from Chris the hint, that the subsystem can reassign ldns dynamically, + I remembered this immediate_assign-command, I found once in the handbook. + Now, the driver first kills all ldn assignments that are set by default + on the SCSI-subsystem. After that, it probes on all puns and luns for + devices by going through all combinations with immediate_assign and + probing for devices, using device_inquiry. The found physical(!) pun,lun + structure is stored in get_scsi[][] as device types. This is followed + by the assignment of all ldns to existing SCSI-devices. If more ldns + than devices are available, they are assigned to non existing pun,lun + combinations to satisfy the adapter. With this, the dynamical mapping + was possible to implement. (For further info see the text in the + source-code and in the description below. Read the description + below BEFORE installing this driver on your system!) + 2) Changed the name IBMMCA_DRIVER_VERSION to IBMMCA_SCSI_DRIVER_VERSION. + 3) The LED-display shows on PS/2-95 no longer the ldn, but the SCSI-ID + (pun) of the accessed SCSI-device. This is now senseful, because the + pun known within the driver is exactly the pun of the physical device + and no longer a fake one. + 4) The /proc/scsi/ibmmca/ consists now of the first part, where + hit-statistics of ldns is shown and a second part, where the maps of + physical and logical SCSI-devices are displayed. This could be very + interesting, when one is using more than 15 SCSI-devices in order to + follow the dynamical remapping of ldns. + - Michael Lang + + June 26-29 1997: (v2.0b-1) + 1) I forgot to switch the local_checking_phase_flag to 1 and back to 0 + in the dynamical remapping part in ibmmca_queuecommand for the + device_exist routine. Sorry. + - Michael Lang + + July 1-13 1997: (v3.0b,c) + 1) Merging of the driver-developments of Klaus Kudielka and Michael Lang + in order to get a optimum and unified driver-release for the + IBM-SCSI-Subsystem-Adapter(s). + For people, using the Kernel-release >=2.1.0, module-support should + be no problem. For users, running under <2.1.0, module-support may not + work, because the methods have changed between 2.0.x and 2.1.x. + 2) Added some more effective statistics for /proc-output. + 3) Change typecasting at necessary points from (unsigned long) to + virt_to_bus(). + 4) Included #if... at special points to have specific adaption of the + driver to kernel 2.0.x and 2.1.x. It should therefore also run with + later releases. + 5) Magneto-Optical drives and medium-changers are also recognized, now. + Therefore, we have a completely gapfree recognition of all SCSI- + device-types, that are known by Linux up to kernel 2.1.31. + 6) The flag SCSI_IBMMCA_DEV_RESET has been inserted. If it is set within + the configuration, each connected SCSI-device will get a reset command + during boottime. This can be necessary for some special SCSI-devices. + This flag should be included in Config.in. + (See also the new Config.in file.) + Probable next improvement: bad disk handler. + - Michael Lang + + Sept 14 1997: (v3.0c) + 1) Some debugging and speed optimization applied. + - Michael Lang + + + + TODO: + + - It seems that the handling of bad disks is really bad - + non-existent, in fact. + - More testing of the full driver-controlled dynamical ldn + (re)mapping for up to 56 SCSI-devices. + - Support more SCSI-device-types, if Linux defines more. + - Support more of the SCSI-command set. + - Support some of the caching abilities, particularly Read Prefetch. + This fetches data into the cache, which later gets hit by the + regular Read Data. + - Abort and Reset functions still slightly buggy. Especially when + floppydisk(!) operations report errors. -#include +******************************************************************************/ -#include +#include #include #include #include @@ -65,8 +248,13 @@ #include "hosts.h" #include "ibmmca.h" +#include /* for CONFIG_SCSI_IBMMCA etc. */ + /*--------------------------------------------------------------------*/ +/* current version of this driver-source: */ +#define IBMMCA_SCSI_DRIVER_VERSION "3.0d" + /* Driver Description @@ -115,6 +303,47 @@ ldn -> (ldn/8,ldn%8). We end up with a real mishmash of puns and luns, but it all seems to work. - Chris Beaurgard + And that last paragraph is also no longer correct. It uses a + slightly more complex mapping that will always map hard disks to + (x,0), for some x, and consecutive none disk devices will usually + share puns. + + Again, the last paragraphs are no longer correct. Now, the physical + SCSI-devices on the SCSI-bus are probed via immediate_assign- and + device_inquiry-commands. This delivers a exact map of the physical + SCSI-world that is now stored in the get_scsi[][]-array. This means, + that the once hidden pun,lun assignment is now known to this driver. + It no longer believes in default-settings of the subsystem and maps all + ldns to existing pun,lun by foot. This assures full control of the ldn + mapping and allows dynamical remapping of ldns to different pun,lun, if + there are more SCSI-devices installed than ldns available (n>15). The + ldns from 0 to 6 get 'hardwired' by this driver to puns 0 to 7 at lun=0, + excluding the pun of the subsystem. This assures, that at least simple + SCSI-installations have optimum access-speed and are not touched by + dynamical remapping. The ldns 7 to 14 are put to existing devices with + lun>0 or to non-existing devices, in order to satisfy the subsystem, if + there are less than 15 SCSI-devices connected. In the case of more than 15 + devices, the dynamical mapping goes active. If the get_scsi[][] reports a + device to be existant, but it has no ldn assigned, it gets a ldn out of 7 + to 14. The numbers are assigned in cyclic order. Therefore it takes 8 + dynamical assignments on SCSI-devices, until a certain device + looses its ldn again. This assures, that dynamical remapping is avoided + during intense I/O between up to eight SCSI-devices (means pun,lun + combinations). A further advantage of this method is, that people who + build their kernel without probing on all luns will get what they expect. + + IMPORTANT: Because of the now correct recognition of physical pun,lun, and + their report to mid-level- and higher-level-drivers, the new reported puns + can be different from the old, faked puns. Therefore, Linux will eventually + change /dev/sdXXX assignments and prompt you for corrupted superblock + repair on boottime. In this case DO NOT PANIC, YOUR DISKS ARE STILL OK!!! + You have to reboot (CTRL-D) with a old kernel and set the /etc/fstab-file + entries right. After that, the system should come up as errorfree as before. + If your boot-partition is not coming up, also edit the /etc/lilo.conf-file + in a Linux session booted on old kernel and run lilo before reboot. Check + lilo.conf anyway to get boot on other partitions with foreign OSes right + again. + (C) Regular Processing Only three functions get involved: ibmmca_queuecommand(), issue_cmd(), and interrupt_handler(). @@ -123,7 +352,8 @@ ibmmca_queuecommand(). This function fills a "subsystem control block" (scb) and calls a local function issue_cmd(), which writes a scb command into subsystem I/O ports. Once the scb command is carried out, - interrupt_handler() is invoked. + interrupt_handler() is invoked. If a device is determined to be existant + and it has not assigned any ldn, it gets one dynamically. (D) Abort, Reset. These are implemented with busy waiting for interrupt to arrive. @@ -137,12 +367,24 @@ 100% sure that it is correct for larger disks. (F) Kernel Boot Option - The function ibmmca_scsi_setup() is called if option ibmmcascsi=... + The function ibmmca_scsi_setup() is called if option ibmmcascsi=n is passed to the kernel. See file linux/init/main.c for details. + + (G) Driver Module Support + Is implemented and tested by K. Kudielka. This could probably not work + on kernels <2.1.0. + + (H) Multiple Hostadapter Support + This driver supports up to eight interfaces of type IBM-SCSI-Subsystem. + Integrated-, and MCA-adapters are automatically recognized. Unrecognizable + IBM-SCSI-Subsystem interfaces can be specified as kernel-parameters. + + (I) /proc-Filesystem Information + Information about the driver condition is given in + /proc/scsi/ibmmca/. ibmmca_proc_info provides this information. */ /*--------------------------------------------------------------------*/ - /* Here are the values and structures specific for the subsystem. * The source of information is "Update for the PS/2 Hardware * Interface Technical Reference, Common Interfaces", September 1991, @@ -152,26 +394,57 @@ * In addition to SCSI subsystem, this update contains fairly detailed * (at hardware register level) sections on diskette controller, * keyboard controller, serial port controller, VGA, and XGA. + * + * Additional information from "Personal System/2 Micro Channel SCSI + * Adapter with Cache Technical Reference", March 1990, PN 68X2365, + * probably available from the same source (or possibly found buried + * in officemates desk). + * + * Further literature/program-sources referred for this driver: + * + * Friedhelm Schmidt, "SCSI-Bus und IDE-Schnittstelle - Moderne Peripherie- + * Schnittstellen: Hardware, Protokollbeschreibung und Anwendung", 2. Aufl. + * Addison Wesley, 1996. + * + * Michael K. Johnson, "The Linux Kernel Hackers' Guide", Version 0.6, Chapel + * Hill - North Carolina, 1995 + * + * Andreas Kaiser, "SCSI TAPE BACKUP for OS/2 2.0", Version 2.12, Stuttgart + * 1993 */ +/*--------------------------------------------------------------------*/ + /* driver configuration */ #define IM_MAX_HOSTS 8 /* maximum number of host adapters */ #define IM_RESET_DELAY 10 /* seconds allowed for a reset */ /* driver debugging - #undef all for normal operation */ -#undef IM_DEBUG_TIMEOUT 50 /* if defined: count interrupts - and ignore this special one */ -#undef IM_DEBUG_INT /* verbose interrupt */ -#undef IM_DEBUG_CMD /* verbose queuecommand */ - -/* addresses of hardware registers on the subsystem */ -#define IM_CMD_REG (shpnt->io_port) /*Command Interface, (4 bytes long) */ -#define IM_ATTN_REG (shpnt->io_port+4) /*Attention (1 byte) */ -#define IM_CTR_REG (shpnt->io_port+5) /*Basic Control (1 byte) */ -#define IM_INTR_REG (shpnt->io_port+6) /*Interrupt Status (1 byte, r/o) */ -#define IM_STAT_REG (shpnt->io_port+7) /*Basic Status (1 byte, read only) */ +/* if defined: count interrupts and ignore this special one: */ +#undef IM_DEBUG_TIMEOUT 50 +/* verbose interrupt: */ +#undef IM_DEBUG_INT +/* verbose queuecommand: */ +#undef IM_DEBUG_CMD +/* verbose queucommand for specific SCSI-device type: */ +#undef IM_DEBUG_CMD_SPEC_DEV +/* verbose device probing */ +#undef IM_DEBUG_PROBE + +/* device type that shall be displayed on syslog (only during debugging): */ +#define IM_DEBUG_CMD_DEVICE TYPE_TAPE + +/* relative addresses of hardware registers on a subsystem */ +#define IM_CMD_REG (shpnt->io_port) /*Command Interface, (4 bytes long) */ +#define IM_ATTN_REG (shpnt->io_port+4) /*Attention (1 byte) */ +#define IM_CTR_REG (shpnt->io_port+5) /*Basic Control (1 byte) */ +#define IM_INTR_REG (shpnt->io_port+6) /*Interrupt Status (1 byte, r/o) */ +#define IM_STAT_REG (shpnt->io_port+7) /*Basic Status (1 byte, read only) */ + +/* basic I/O-port of first adapter */ #define IM_IO_PORT 0x3540 +/* maximum number of hosts that can be find */ #define IM_N_IO_PORT 8 /*requests going into the upper nibble of the Attention register */ @@ -204,11 +477,11 @@ /*immediate commands (word written into low 2 bytes of command reg) */ #define IM_RESET_IMM_CMD 0x0400 -#define IM_FORMAT_PREP_IMM_CMD 0x0417 #define IM_FEATURE_CTR_IMM_CMD 0x040c #define IM_DMA_PACING_IMM_CMD 0x040d #define IM_ASSIGN_IMM_CMD 0x040e #define IM_ABORT_IMM_CMD 0x040f +#define IM_FORMAT_PREP_IMM_CMD 0x0417 /*SCB (Subsystem Control Block) structure */ struct im_scb @@ -257,6 +530,13 @@ #define IM_DEVICE_INQUIRY_CMD 0x1c0b #define IM_OTHER_SCSI_CMD_CMD 0x241f +/* unused, but supported, SCB commands */ +#define IM_GET_COMMAND_COMPLETE_STATUS_CMD 0x1c07 /* command status */ +#define IM_GET_POS_INFO_CMD 0x1c0a /* returns neat stuff */ +#define IM_READ_PREFETCH_CMD 0x1c31 /* caching controller only */ +#define IM_FOMAT_UNIT_CMD 0x1c16 /* format unit */ +#define IM_REASSIGN_BLOCK_CMD 0x1c18 /* in case of error */ + /*values to set bits in the enable word of SCB */ #define IM_READ_CONTROL 0x8000 #define IM_REPORT_TSB_ONLY_ON_ERROR 0x4000 @@ -286,10 +566,41 @@ /*subsystem uses interrupt request level 14 */ #define IM_IRQ 14 -/*PS2 disk led is turned on/off by bits 6,7 of system control port */ -#define PS2_SYS_CTR 0x92 -#define PS2_DISK_LED_ON() outb(inb(PS2_SYS_CTR) | 0xc0, PS2_SYS_CTR) -#define PS2_DISK_LED_OFF() outb(inb(PS2_SYS_CTR) & 0x3f, PS2_SYS_CTR) +/*--------------------------------------------------------------------*/ +/* + The model 95 doesn't have a standard activity light. Instead it + has a row of LEDs on the front. We use the last one as the activity + indicator if we think we're on a model 95. I suspect the model id + check will be either too narrow or too general, and some machines + won't have an activity indicator. Oh well... + + The regular PS/2 disk led is turned on/off by bits 6,7 of system + control port. +*/ + +/* LED display-port (actually, last LED on display) */ +#define MOD95_LED_PORT 0x108 +/* system-control-register of PS/2s with diskindicator */ +#define PS2_SYS_CTR 0x92 + +/* The SCSI-ID(!) of the accessed SCSI-device is shown on PS/2-95 machines' LED + displays. ldn is no longer displayed here, because the ldn mapping is now + done dynamically and the ldn <-> pun,lun maps can be looked-up at boottime + or during uptime in /proc/scsi/ibmmca/ in case of trouble, + interest, debugging or just for having fun. The left number gives the + host-adapter number and the right shows the accessed SCSI-ID. */ + +#define PS2_DISK_LED_ON(ad,id) {\ + if( machine_id == 0xf8 ) { outb((char)(id+48), MOD95_LED_PORT ); \ + outb((char)(ad+48), MOD95_LED_PORT+1); } \ + else outb(inb(PS2_SYS_CTR) | 0xc0, PS2_SYS_CTR); \ +} + +#define PS2_DISK_LED_OFF() {\ + if( machine_id == 0xf8 ) { outb( ' ', MOD95_LED_PORT ); \ + outb(' ', MOD95_LED_PORT+1); } \ + else outb(inb(PS2_SYS_CTR) | 0x3f, PS2_SYS_CTR); \ +} /*--------------------------------------------------------------------*/ @@ -315,7 +626,8 @@ S_IFDIR | S_IRUGO | S_IXUGO, 2 }; -/*max number of logical devices (can be up to 15) */ +/* Max number of logical devices (can be up from 0 to 14). 15 is the address +of the adapter itself. */ #define MAX_LOG_DEV 15 /*local data for a logical device */ @@ -324,50 +636,108 @@ struct im_scb scb; struct im_tsb tsb; struct im_sge sge[16]; - Scsi_Cmnd *cmd; - int is_disk; + Scsi_Cmnd *cmd; /* SCSI-command that is currently in progress */ + + int device_type; /* type of the SCSI-device. See include/scsi/scsi.h + for interpreation of the possible values */ int block_length; }; +/* statistics of the driver during operations (for proc_info) */ +struct Driver_Statistics + { + /* SCSI statistics on the adapter */ + int ldn_access[MAX_LOG_DEV+1]; /* total accesses on a ldn */ + int ldn_read_access[MAX_LOG_DEV+1]; /* total read-access on a ldn */ + int ldn_write_access[MAX_LOG_DEV+1]; /* total write-access on a ldn */ + int total_accesses; /* total accesses on all ldns */ + int total_interrupts; /* total interrupts (should be + same as total_accesses) */ + /* dynamical assignment statistics */ + int total_scsi_devices; /* number of physical pun,lun */ + int dyn_flag; /* flag showing dynamical mode */ + int dynamical_assignments; /* number of remappings of ldns */ + int ldn_assignments[MAX_LOG_DEV+1]; /* number of remappings of each + ldn */ + }; + /* data structure for each host adapter */ struct ibmmca_hostdata - { - /* array of logical devices */ +{ + /* array of logical devices */ struct logical_device _ld[MAX_LOG_DEV]; - /* array to convert (pun, lun) into logical device number */ + /* array to convert (pun, lun) into logical device number */ unsigned char _get_ldn[8][8]; - /* used only when checking logical devices */ + /*array that contains the information about the physical SCSI-devices + attached to this host adapter */ + unsigned char _get_scsi[8][8]; + /* used only when checking logical devices */ int _local_checking_phase_flag; int _got_interrupt; int _stat_result; - /* reset status (used only when doing reset) */ + /* reset status (used only when doing reset) */ int _reset_status; - }; - -/* reset status values */ -#define IM_RESET_NOT_IN_PROGRESS 0 -#define IM_RESET_IN_PROGRESS 1 -#define IM_RESET_FINISHED_OK 2 -#define IM_RESET_FINISHED_FAIL 3 + /* code of the last SCSI command (needed for panic info) */ + int _last_scsi_command; + /* counter that points on next reassignable ldn for dynamical remapping */ + /* The default value is 7, that is the first reassignable number in + the list on startup. */ + int _next_ldn; + /* Statistics for this IBM-SCSI-host */ + struct Driver_Statistics _IBM_DS; +}; /* macros to access host data structure */ #define HOSTDATA(shpnt) ((struct ibmmca_hostdata *) shpnt->hostdata) #define subsystem_pun (shpnt->this_id) #define ld (HOSTDATA(shpnt)->_ld) #define get_ldn (HOSTDATA(shpnt)->_get_ldn) +#define get_scsi (HOSTDATA(shpnt)->_get_scsi) #define local_checking_phase_flag (HOSTDATA(shpnt)->_local_checking_phase_flag) #define got_interrupt (HOSTDATA(shpnt)->_got_interrupt) #define stat_result (HOSTDATA(shpnt)->_stat_result) #define reset_status (HOSTDATA(shpnt)->_reset_status) +#define last_scsi_command (HOSTDATA(shpnt)->_last_scsi_command) +#define next_ldn (HOSTDATA(shpnt)->_next_ldn) +#define IBM_DS (HOSTDATA(shpnt)->_IBM_DS) + +/* Define a arbitrary number as subsystem-marker-type. This number is, as + described in the SCSI-Standard, not occupied by other device-types. */ +#define TYPE_IBM_SCSI_ADAPTER 0x2F + +/* Define 0xFF for no device type, because this type is not defined within + the SCSI-standard, therefore, it can be used and should not cause any + harm. */ +#define TYPE_NO_DEVICE 0xFF + +/* define medium-changer. If this is not defined previously, define + this type here. */ +#ifndef TYPE_MEDIUM_CHANGER +#define TYPE_MEDIUM_CHANGER 0x08 +#endif -/*--------------------------------------------------------------------*/ +/* define operations for immediate_assign */ +#define SET_LDN 0 +#define REMOVE_LDN 1 + +/* reset status flag contents */ +#define IM_RESET_NOT_IN_PROGRESS 0 +#define IM_RESET_IN_PROGRESS 1 +#define IM_RESET_FINISHED_OK 2 +#define IM_RESET_FINISHED_FAIL 3 + +/*-----------------------------------------------------------------------*/ /* if this is nonzero, ibmmcascsi option has been passed to the kernel */ -static int io_port[IM_MAX_HOSTS] = { 0 }; -static int scsi_id[IM_MAX_HOSTS] = { 7 }; +static int io_port[IM_MAX_HOSTS] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +static int scsi_id[IM_MAX_HOSTS] = { 7, 7, 7, 7, 7, 7, 7, 7 }; +/* fill module-parameters only, when this define is present. + (that is kernel >=2.1.0) */ +#ifdef MODULE_PARM MODULE_PARM(io_port, "1-" __MODULE_STRING(IM_MAX_HOSTS) "i"); -MODULE_PARM(scsi_id, "1-" __MODULE_STRING(IM_MAX_HOSTS) "i"); +MODULE_PARM(scsi_id, "1-" __MODULE_STRING(IM_MAX_HOSTS) "i"); +#endif /*counter of concurrent disk read/writes, to turn on/off disk led */ static int disk_rw_in_progress = 0; @@ -375,26 +745,34 @@ /* host information */ static int found = 0; static struct Scsi_Host *hosts[IM_MAX_HOSTS+1] = { NULL }; +/*-----------------------------------------------------------------------*/ -/*--------------------------------------------------------------------*/ - -/*local functions */ -static void interrupt_handler (int irq, void *dev_id, - struct pt_regs *regs); -static void issue_cmd (struct Scsi_Host *shpnt, unsigned long cmd_reg, - unsigned char attn_reg); +/*local functions in forward declaration */ +static void interrupt_handler (int irq, void *dev_id, struct pt_regs *regs); +static void issue_cmd (struct Scsi_Host *shpnt, unsigned long cmd_reg, + unsigned char attn_reg); static void internal_done (Scsi_Cmnd * cmd); static void check_devices (struct Scsi_Host *shpnt); -static int device_exists (struct Scsi_Host *shpnt, int ldn, int *is_disk, - int *block_length); -static struct Scsi_Host *ibmmca_register(Scsi_Host_Template * template, +static int immediate_assign(struct Scsi_Host *shpnt, unsigned int pun, + unsigned int lun, unsigned int ldn, + unsigned int operation); +static int device_inquiry(struct Scsi_Host *shpnt, int ldn, + unsigned char *buf); +static char *ti_p(int value); +static char *ti_l(int value); +static int device_exists (struct Scsi_Host *shpnt, int ldn, int *block_length, + int *device_type); +static struct Scsi_Host *ibmmca_register(Scsi_Host_Template * template, int port, int id); +/* local functions needed for proc_info */ +static int ldn_access_load(struct Scsi_Host *shpnt, int ldn); +static int ldn_access_total_read_write(struct Scsi_Host *shpnt); + /*--------------------------------------------------------------------*/ static void -interrupt_handler (int irq, void *dev_id, - struct pt_regs *regs) +interrupt_handler (int irq, void *dev_id, struct pt_regs *regs) { int i = 0; struct Scsi_Host *shpnt; @@ -402,21 +780,24 @@ unsigned int cmd_result; unsigned int ldn; - do shpnt = hosts[i++]; + /* search for one adapter-response on shared interrupt */ + do + shpnt = hosts[i++]; while (shpnt && !(inb(IM_STAT_REG) & IM_INTR_REQUEST)); + + /* return if some other device on this IRQ caused the interrupt */ if (!shpnt) return; /*get command result and logical device */ - intr_reg = inb(IM_INTR_REG); + intr_reg = inb (IM_INTR_REG); cmd_result = intr_reg & 0xf0; ldn = intr_reg & 0x0f; /*must wait for attention reg not busy, then send EOI to subsystem */ - while (1) - { + while (1) { cli (); - if (!(inb (IM_STAT_REG) & IM_BUSY)) - break; + if (!(inb (IM_STAT_REG) & IM_BUSY)) + break; sti (); } outb (IM_EOI | ldn, IM_ATTN_REG); @@ -424,17 +805,24 @@ /*these should never happen (hw fails, or a local programming bug) */ if (cmd_result == IM_ADAPTER_HW_FAILURE) - panic ("IBM MCA SCSI: subsystem hardware failure.\n"); + panic ("IBM MCA SCSI: subsystem hardware failure. Last SCSI_CMD=0x%X. \n", + last_scsi_command); if (cmd_result == IM_CMD_ERROR) - panic ("IBM MCA SCSI: command error.\n"); + panic ("IBM MCA SCSI: command error. Last SCSI_CMD=0x%X. \n", + last_scsi_command); if (cmd_result == IM_SOFTWARE_SEQUENCING_ERROR) - panic ("IBM MCA SCSI: software sequencing error.\n"); + panic ("IBM MCA SCSI: software sequencing error. Last SCSI_CMD=0x%X. \n", + last_scsi_command); + /* if no panic appeared, increase the interrupt-counter */ + IBM_DS.total_interrupts++; + /*only for local checking phase */ if (local_checking_phase_flag) { stat_result = cmd_result; got_interrupt = 1; + reset_status = IM_RESET_FINISHED_OK; return; } @@ -454,6 +842,9 @@ } else { + /*reset disk led counter, turn off disk led */ + disk_rw_in_progress = 0; + PS2_DISK_LED_OFF (); reset_status = IM_RESET_FINISHED_OK; } return; @@ -464,12 +855,12 @@ #ifdef IM_DEBUG_TIMEOUT { - static int count = 0; + static int count = 0; - if (++count == IM_DEBUG_TIMEOUT) { - printk("IBM MCA SCSI: Ignoring interrupt.\n"); - return; - } + if (++count == IM_DEBUG_TIMEOUT) { + printk("IBM MCA SCSI: Ignoring interrupt.\n"); + return; + } } #endif @@ -481,25 +872,28 @@ #ifdef IM_DEBUG_INT printk("cmd=%02x ireg=%02x ds=%02x cs=%02x de=%02x ce=%02x\n", - cmd->cmnd[0], intr_reg, - ld[ldn].tsb.dev_status, ld[ldn].tsb.cmd_status, - ld[ldn].tsb.dev_error, ld[ldn].tsb.cmd_error); + cmd->cmnd[0], intr_reg, + ld[ldn].tsb.dev_status, ld[ldn].tsb.cmd_status, + ld[ldn].tsb.dev_error, ld[ldn].tsb.cmd_error); #endif - /*if this is end of disk read/write, may turn off PS/2 disk led */ - if (ld[ldn].is_disk) - { + /*if this is end of media read/write, may turn off PS/2 disk led */ + if ((ld[ldn].device_type!=TYPE_NO_LUN)&& + (ld[ldn].device_type!=TYPE_NO_DEVICE)) + { /* only access this, if there was a valid device addressed */ switch (cmd->cmnd[0]) { case READ_6: case WRITE_6: case READ_10: case WRITE_10: + case READ_12: + case WRITE_12: if (--disk_rw_in_progress == 0) PS2_DISK_LED_OFF (); } } - + /*write device status into cmd->result, and call done function */ if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE) cmd->result = ld[ldn].tsb.dev_status & 0x1e; @@ -512,8 +906,8 @@ /*--------------------------------------------------------------------*/ static void -issue_cmd (struct Scsi_Host *shpnt, unsigned long cmd_reg, - unsigned char attn_reg) +issue_cmd (struct Scsi_Host *shpnt, unsigned long cmd_reg, + unsigned char attn_reg) { /*must wait for attention reg not busy */ while (1) @@ -540,51 +934,23 @@ /*--------------------------------------------------------------------*/ -static int -ibmmca_getinfo (char *buf, int slot, void *dev) +static int ibmmca_getinfo (char *buf, int slot, void *dev) { struct Scsi_Host *shpnt = dev; int len = 0; len += sprintf (buf + len, "Subsystem PUN: %d\n", subsystem_pun); - len += sprintf (buf + len, "I/O base address: 0x%x\n", shpnt->io_port); + len += sprintf (buf + len, "I/O base address: 0x%x\n", IM_CMD_REG); return len; } /*--------------------------------------------------------------------*/ -static void -check_devices(struct Scsi_Host *shpnt) -{ - int is_disk, block_length; - int ldn; - int num_ldn = 0; - - /* check ldn's from 0 to MAX_LOG_DEV to find which devices exist */ - for (ldn = 0; ldn < MAX_LOG_DEV; ldn++) - { - if (device_exists(shpnt, ldn, &is_disk, &block_length)) - { - printk("IBM MCA SCSI: logical device found at ldn=%d.\n", ldn); - ld[ldn].is_disk = is_disk; - ld[ldn].block_length = block_length; - get_ldn[num_ldn / 8][num_ldn % 8] = ldn; - num_ldn++; - } - } - - return; -} - -/*--------------------------------------------------------------------*/ - -static int -device_exists(struct Scsi_Host *shpnt, int ldn, int *is_disk, - int *block_length) -{ +/* SCSI-SCB-command for device_inquiry */ +static int device_inquiry(struct Scsi_Host *shpnt, int ldn, unsigned char *buf) +{ struct im_scb scb; struct im_tsb tsb; - unsigned char buf[256]; int retries; for (retries = 0; retries < 3; retries++) @@ -592,7 +958,6 @@ /*fill scb with inquiry command */ scb.command = IM_DEVICE_INQUIRY_CMD; scb.enable = IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT; - /* I think this virt_to_bus is needed.. ??? AC */ scb.sys_buf_adr = virt_to_bus(buf); scb.sys_buf_length = 255; scb.tsb_adr = virt_to_bus(&tsb); @@ -611,19 +976,373 @@ /*if all three retries failed, return "no device at this ldn" */ if (retries >= 3) return 0; + else + return 1; +} + +/* SCSI-immediate-command for assign. This functions maps/unmaps specific + ldn-numbers on SCSI (PUN,LUN). It is needed for presetting of the + subsystem and for dynamical remapping od ldns. */ +static int immediate_assign(struct Scsi_Host *shpnt, unsigned int pun, + unsigned int lun, unsigned int ldn, + unsigned int operation) +{ + int retries; + unsigned long imm_command; + + for (retries=0; retries<3; retries ++) + { + imm_command = inl(IM_CMD_REG); + imm_command &= (unsigned long)(0xF8000000); /* keep reserved bits */ + imm_command |= (unsigned long)(IM_ASSIGN_IMM_CMD); + imm_command |= (unsigned long)((lun & 7) << 24); + imm_command |= (unsigned long)((operation & 1) << 23); + imm_command |= (unsigned long)((pun & 7) << 20); + imm_command |= (unsigned long)((ldn & 15) << 16); + + got_interrupt = 0; + issue_cmd (shpnt, (unsigned long)(imm_command), IM_IMM_CMD | 0xf); + while (!got_interrupt) + barrier (); + + /*if command succesful, break */ + if (stat_result == IM_IMMEDIATE_CMD_COMPLETED) + break; + } + + if (retries >= 3) + return 0; + else + return 1; +} + +/* type-interpreter for physical device numbers */ +static char *ti_p(int value) +{ + switch (value) + { + case TYPE_IBM_SCSI_ADAPTER: return("A"); break; + case TYPE_DISK: return("D"); break; + case TYPE_TAPE: return("T"); break; + case TYPE_PROCESSOR: return("P"); break; + case TYPE_WORM: return("W"); break; + case TYPE_ROM: return("R"); break; + case TYPE_SCANNER: return("S"); break; + case TYPE_MOD: return("M"); break; + case TYPE_MEDIUM_CHANGER: return("C"); break; + case TYPE_NO_LUN: return("+"); break; /* show NO_LUN */ + case TYPE_NO_DEVICE: + default: return("-"); break; + } + return("-"); +} + +/* type-interpreter for logical devices + (A bit stupid, but it was necessary to get the '-' and the Hex-codes + into one type.) */ +static char *ti_l(int value) +{ + switch (value) + { + case 0: return("0"); break; case 1: return("1"); break; + case 2: return("2"); break; case 3: return("3"); break; + case 4: return("4"); break; case 5: return("5"); break; + case 6: return("6"); break; case 7: return("7"); break; + case 8: return("8"); break; case 9: return("9"); break; + case 10: return("a"); break; case 11: return("b"); break; + case 12: return("c"); break; case 13: return("d"); break; + case 14: return("e"); break; case 15: return("f"); break; + default: return("-"); break; + } + return("-"); +} + +/* + The following routine probes the SCSI-devices in four steps: + 1. The current ldn -> pun,lun mapping is removed on the SCSI-adapter. + 2. ldn 0 is used to go through all possible combinations of pun,lun and + a device_inquiry is done to fiddle out whether there is a device + responding or not. This physical map is stored in get_scsi[][]. + 3. The 15 available ldns (0-14) are mapped to existing pun,lun. + If there are more devices than ldns, it stops at 14 for the boot + time. Dynamical remapping will be done in ibmmca_queuecommand. + 4. If there are less than 15 valid pun,lun, the remaining ldns are + mapped to NON-existing pun,lun to satisfy the adapter. Information + about pun,lun -> ldn is stored as before in get_ldn[][]. + This method leads to the result, that the SCSI-pun,lun shown to Linux + mid-level- and higher-level-drivers is exactly corresponding to the + physical reality on the SCSI-bus. Therefore, it is possible that users + of older releases of this driver have to rewrite their fstab-file, because + the /dev/sdXXX could have changed due to the right pun,lun report, now. + The assignment of ALL ldns avoids dynamical remapping by the adapter + itself. + */ +static void check_devices (struct Scsi_Host *shpnt) +{ + int id, lun, ldn; + unsigned char buf[256]; + int count_devices = 0; /* local counter for connected device */ + + /* assign default values to certain variables */ + + IBM_DS.dyn_flag = 0; /* normally no need for dynamical ldn management */ + next_ldn = 7; /* next ldn to be assigned is 7, because 0-6 is 'hardwired'*/ + last_scsi_command = 0; /* emptify last SCSI-command storage */ + + /* initialize the very important driver-informational arrays/structs */ + memset (ld, 0, sizeof ld); + memset (get_ldn, TYPE_NO_DEVICE, sizeof get_ldn); /* this is essential ! */ + memset (get_scsi, TYPE_NO_DEVICE, sizeof get_scsi); /* this is essential ! */ + + for (lun=0; lun<=7; lun++) /* mark the adapter at its pun on all luns*/ + { + get_scsi[subsystem_pun][lun] = TYPE_IBM_SCSI_ADAPTER; + get_ldn[subsystem_pun][lun] = MAX_LOG_DEV; /* make sure, the subsystem + ldn is active for all + luns. */ + } + + /* STEP 1: */ + printk("IBM MCA SCSI: Removing current logical SCSI-device mapping."); + for (ldn=0; ldn 0) + { + /* remove mapping */ + get_ldn[id][lun]=TYPE_NO_DEVICE; + immediate_assign(shpnt,0,0,ldn,REMOVE_LDN); + } + else ldn++; + } + } + else if (lun == 0) + { + /* map lun == 0, even if no device exists */ + immediate_assign(shpnt,id,lun,ldn,SET_LDN); + get_ldn[id][lun]=ldn; /* map ldn */ + ldn++; + } + } + } + + /* STEP 4: */ + + /* map remaining ldns to non-existing devices */ + for (lun=1; lun<=7 && ldn=MAX_LOG_DEV) + IBM_DS.dyn_flag = 1; /* dynamical assignment is necessary */ + else + IBM_DS.dyn_flag = 0; /* dynamical assignment is not necessary */ + + /* If no SCSI-devices are assigned, return 1 in order to cause message. */ + if (ldn == 0) + printk("IBM MCA SCSI: Warning: No SCSI-devices found/assignable!\n"); + + /* reset the counters for statistics on the current adapter */ + IBM_DS.total_accesses = 0; + IBM_DS.total_interrupts = 0; + IBM_DS.dynamical_assignments = 0; + memset (IBM_DS.ldn_access, 0x0, sizeof (IBM_DS.ldn_access)); + memset (IBM_DS.ldn_read_access, 0x0, sizeof (IBM_DS.ldn_read_access)); + memset (IBM_DS.ldn_write_access, 0x0, sizeof (IBM_DS.ldn_write_access)); + memset (IBM_DS.ldn_assignments, 0x0, sizeof (IBM_DS.ldn_assignments)); + + return; +} + +/*--------------------------------------------------------------------*/ + +static int +device_exists (struct Scsi_Host *shpnt, int ldn, int *block_length, + int *device_type) +{ + struct im_scb scb; + struct im_tsb tsb; + unsigned char buf[256]; + int retries; + + /* if no valid device found, return immediately with 0 */ + if (!(device_inquiry(shpnt, ldn, buf))) return 0; + /*if device is CD_ROM, assume block size 2048 and return */ if (buf[0] == TYPE_ROM) { - *is_disk = 0; + *device_type = TYPE_ROM; + *block_length = 2048; /* (standard blocksize for yellow-/red-book) */ + return 1; + } + + if (buf[0] == TYPE_WORM) /* CD-burner, WORM, Linux handles this as CD-ROM + therefore, the block_length is also 2048. */ + { + *device_type = TYPE_WORM; *block_length = 2048; return 1; } - - /*if device is disk, use "read capacity" to find its block size */ + + /* if device is disk, use "read capacity" to find its block size */ if (buf[0] == TYPE_DISK) { - *is_disk = 1; + *device_type = TYPE_DISK; + + for (retries = 0; retries < 3; retries++) + { + /*fill scb with read capacity command */ + scb.command = IM_READ_CAPACITY_CMD; + scb.enable = IM_READ_CONTROL; + scb.sys_buf_adr = virt_to_bus(buf); + scb.sys_buf_length = 8; + scb.tsb_adr = virt_to_bus(&tsb); + + /*issue scb to passed ldn, and busy wait for interrupt */ + got_interrupt = 0; + issue_cmd (shpnt, virt_to_bus(&scb), IM_SCB | ldn); + while (!got_interrupt) + barrier (); + + /*if got capacity, get block length and return one device found */ + if (stat_result == IM_SCB_CMD_COMPLETED) + { + *block_length = buf[7] + (buf[6] << 8) + (buf[5] << 16) + (buf[4] << 24); + return 1; + } + } + + /*if all three retries failed, return "no device at this ldn" */ + if (retries >= 3) + return 0; + } + + /* if this is a magneto-optical drive, treat it like a harddisk */ + if (buf[0] == TYPE_MOD) + { + *device_type = TYPE_MOD; for (retries = 0; retries < 3; retries++) { @@ -651,9 +1370,39 @@ /*if all three retries failed, return "no device at this ldn" */ if (retries >= 3) return 0; + } + + if (buf[0] == TYPE_TAPE) /* TAPE-device found */ + { + *device_type = TYPE_TAPE; + *block_length = 0; /* not in use (setting by mt and mtst in op.) */ + return 1; } - /*for now, ignore tape and other devices - return 0 */ + if (buf[0] == TYPE_PROCESSOR) /* HP-Scanners, diverse SCSI-processing units*/ + { + *device_type = TYPE_PROCESSOR; + *block_length = 0; /* they set their stuff on drivers */ + return 1; + } + + if (buf[0] == TYPE_SCANNER) /* other SCSI-scanners */ + { + *device_type = TYPE_SCANNER; + *block_length = 0; /* they set their stuff on drivers */ + return 1; + } + + if (buf[0] == TYPE_MEDIUM_CHANGER) /* Medium-Changer */ + { + *device_type = TYPE_MEDIUM_CHANGER; + *block_length = 0; /* One never knows, what to expect on a medium + changer device. */ + return 1; + } + + /* Up to now, no SCSI-devices that are known up to kernel 2.1.31 are + ignored! MO-drives are now supported and treated as harddisk. */ return 0; } @@ -664,19 +1413,17 @@ void ibmmca_scsi_setup (char *str, int *ints) { - int i; + int i; - for (i = 0; i < IM_MAX_HOSTS && i < ints[0]; i++) - { + for (i = 0; i < IM_MAX_HOSTS && i < ints[0]; i++) io_port[i] = ints[i+1]; - } } #endif /*--------------------------------------------------------------------*/ -int +int ibmmca_detect (Scsi_Host_Template * template) { struct Scsi_Host *shpnt; @@ -697,11 +1444,11 @@ /* if ibmmcascsi setup option was passed to kernel, return "found" */ for (i = 0; i < IM_MAX_HOSTS; i++) if (io_port[i] > 0 && scsi_id[i] >= 0 && scsi_id[i] < 8) - { + { printk("IBM MCA SCSI: forced detection, io=0x%x, scsi id=%d.\n", - io_port[i], scsi_id[i]); + io_port[i], scsi_id[i]); ibmmca_register(template, io_port[i], scsi_id[i]); - } + } if (found) return found; /* first look for the SCSI integrated on the motherboard */ @@ -712,13 +1459,13 @@ port = IM_IO_PORT + ((pos2 & 0x0e) << 2); id = (pos3 & 0xe0) >> 5; printk("IBM MCA SCSI: integrated SCSI found, io=0x%x, scsi id=%d.\n", - port, id); + port, id); if ((shpnt = ibmmca_register(template, port, id))) - { - mca_set_adapter_name(MCA_INTEGSCSI, "PS/2 Integrated SCSI"); - mca_set_adapter_procfn(MCA_INTEGSCSI, (MCA_ProcFn) ibmmca_getinfo, - shpnt); - } + { + mca_set_adapter_name(MCA_INTEGSCSI, "PS/2 Integrated SCSI"); + mca_set_adapter_procfn(MCA_INTEGSCSI, (MCA_ProcFn) ibmmca_getinfo, + shpnt); + } } /* now look for other adapters */ @@ -727,22 +1474,22 @@ { slot = 0; while ((slot = mca_find_adapter(subsys_list[i].mca_id, slot)) - != MCA_NOTFOUND) - { - pos2 = mca_read_stored_pos(slot, 2); - pos3 = mca_read_stored_pos(slot, 3); - port = IM_IO_PORT + ((pos2 & 0x0e) << 2); - id = (pos3 & 0xe0) >> 5; - printk ("IBM MCA SCSI: %s found in slot %d, io=0x%x, scsi id=%d.\n", - subsys_list[i].description, slot + 1, port, id); - if ((shpnt = ibmmca_register(template, port, id))) - { - mca_set_adapter_name (slot, subsys_list[i].description); - mca_set_adapter_procfn (slot, (MCA_ProcFn) ibmmca_getinfo, - shpnt); - } - slot++; - } + != MCA_NOTFOUND) + { + pos2 = mca_read_stored_pos(slot, 2); + pos3 = mca_read_stored_pos(slot, 3); + port = IM_IO_PORT + ((pos2 & 0x0e) << 2); + id = (pos3 & 0xe0) >> 5; + printk ("IBM MCA SCSI: %s found in slot %d, io=0x%x, scsi id=%d.\n", + subsys_list[i].description, slot + 1, port, id); + if ((shpnt = ibmmca_register(template, port, id))) + { + mca_set_adapter_name (slot, subsys_list[i].description); + mca_set_adapter_procfn (slot, (MCA_ProcFn) ibmmca_getinfo, + shpnt); + } + slot++; + } } if (!found) { @@ -753,8 +1500,6 @@ return found; } -/*--------------------------------------------------------------------*/ - static struct Scsi_Host * ibmmca_register(Scsi_Host_Template * template, int port, int id) { @@ -765,7 +1510,7 @@ if (check_region(port, IM_N_IO_PORT)) { printk("IBM MCA SCSI: Unable to get I/O region 0x%x-0x%x.\n", - port, port + IM_N_IO_PORT); + port, port + IM_N_IO_PORT); return NULL; } @@ -785,6 +1530,9 @@ shpnt->io_port = port; shpnt->n_io_port = IM_N_IO_PORT; shpnt->this_id = id; + + reset_status = IM_RESET_NOT_IN_PROGRESS; + for (i = 0; i < 8; i++) for (j = 0; j < 8; j++) get_ldn[i][j] = MAX_LOG_DEV; @@ -800,17 +1548,6 @@ /*--------------------------------------------------------------------*/ -int -ibmmca_release(struct Scsi_Host *shpnt) -{ - release_region(shpnt->io_port, shpnt->n_io_port); - if (!(--found)) - free_irq(shpnt->irq, hosts); - return 0; -} - -/*--------------------------------------------------------------------*/ - int ibmmca_command (Scsi_Cmnd * cmd) { @@ -823,21 +1560,134 @@ /*--------------------------------------------------------------------*/ -int -ibmmca_queuecommand (Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) +int +ibmmca_release(struct Scsi_Host *shpnt) +{ + release_region(shpnt->io_port, shpnt->n_io_port); + if (!(--found)) + free_irq(shpnt->irq, hosts); + return 0; +} + +/*--------------------------------------------------------------------*/ + +/* The following routine is the SCSI command queue. The old edition is + now improved by dynamical reassignment of ldn numbers that are + currently not assigned. The mechanism works in a way, that first + the physical structure is checked. If at a certain pun,lun a device + should be present, the routine proceeds to the ldn check from + get_ldn. An answer of 0xff would show-up, that the aimed device is + currently not assigned any ldn. At this point, the dynamical + remapping algorithm is called. It works in a way, that it goes in + cyclic order through the ldns from 7 to 14. If a ldn is assigned, + it takes 8 dynamical reassignment calls, until a device looses its + ldn again. With this method it is assured, that while doing + intense I/O between up to eight devices, no dynamical remapping is + done there. ldns 0 through 6(!) are left untouched, which means, that + puns 0 through 7(!) on lun=0 are always accessible without remapping. + These ldns are statically assigned by this driver. The subsystem always + occupies at least one pun, therefore 7 ldns (at lun=0) for other devices + are sufficient. (The adapter uses always ldn=15, at whatever pun it is.) */ +int ibmmca_queuecommand (Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) { unsigned int ldn; unsigned int scsi_cmd; struct im_scb *scb; struct Scsi_Host *shpnt = cmd->host; + + int current_ldn; + int id,lun; - /*if (target,lun) unassigned, return error */ + /*if (target,lun) is NO LUN or not existing at all, return error */ + if ((get_scsi[cmd->target][cmd->lun] == TYPE_NO_LUN)|| + (get_scsi[cmd->target][cmd->lun] == TYPE_NO_DEVICE)) + { + cmd->result = DID_NO_CONNECT << 16; + done (cmd); + return 0; + } + + /*if (target,lun) unassigned, do further checks... */ ldn = get_ldn[cmd->target][cmd->lun]; - if (ldn >= MAX_LOG_DEV) + if (ldn >= MAX_LOG_DEV) /* on invalid ldn do special stuff */ { - cmd->result = DID_NO_CONNECT << 16; - done (cmd); - return 0; + if (ldn > MAX_LOG_DEV) /* dynamical remapping if ldn unassigned */ + { + current_ldn = next_ldn; /* stop-value for one circle */ + while (ld[next_ldn].cmd) /* search for a occupied, but not in */ + { /* command-processing ldn. */ + next_ldn ++; + if (next_ldn>=MAX_LOG_DEV) + next_ldn = 7; + if (current_ldn == next_ldn) /* One circle done ? */ + { /* no non-processing ldn found */ + printk("IBM MCA SCSI: Cannot assign SCSI-device dynamically!\n"); + printk(" On ldn 7-14 SCSI-commands everywhere in progress.\n"); + printk(" Reporting DID_NO_CONNECT for device (%d,%d).\n", + cmd->target, cmd->lun); + cmd->result = DID_NO_CONNECT << 16;/* return no connect*/ + done (cmd); + return 0; + } + } + + /* unmap non-processing ldn */ + for (id=0; id<=7; id ++) + for (lun=0; lun<=7; lun++) + { + if (get_ldn[id][lun] == next_ldn) + { + get_ldn[id][lun] = TYPE_NO_DEVICE; /* unmap entry */ + goto DYN_ASSIGN; /* jump out as fast as possible */ + } + } + +DYN_ASSIGN: + /* unassign found ldn (pun,lun does not matter for remove) */ + immediate_assign(shpnt,0,0,next_ldn,REMOVE_LDN); + /* assign found ldn to aimed pun,lun */ + immediate_assign(shpnt,cmd->target,cmd->lun,next_ldn,SET_LDN); + /* map found ldn to pun,lun */ + get_ldn[cmd->target][cmd->lun] = next_ldn; + /* change ldn to the right value, that is now next_ldn */ + ldn = next_ldn; + /* set reduced interrupt_handler-mode for checking */ + local_checking_phase_flag = 1; + /* get device information for ld[ldn] */ + if (device_exists (shpnt, ldn, &ld[ldn].block_length, + &ld[ldn].device_type)) + { + ld[ldn].cmd = 0; /* To prevent panic set 0, because + devices that were not assigned, + should have nothing in progress. */ + + /* increase assignment counters for statistics in /proc */ + IBM_DS.dynamical_assignments++; + IBM_DS.ldn_assignments[ldn]++; + } + else + /* panic here, because a device, found at boottime has + vanished */ + panic("IBM MCA SCSI: ldn=0x%x, SCSI-device on (%d,%d) vanished!\n", + ldn, cmd->target, cmd->lun); + + /* set back to normal interrupt_handling */ + local_checking_phase_flag = 0; + + /* Information on syslog terminal */ + printk("IBM MCA SCSI: ldn=0x%x dynamically reassigned to (%d,%d).\n", + ldn, cmd->target, cmd->lun); + + /* increase next_ldn for next dynamical assignment */ + next_ldn ++; + if (next_ldn>=MAX_LOG_DEV) next_ldn = 7; + } + else + { /* wall against Linux accesses to the subsystem adapter */ + cmd->result = DID_NO_CONNECT << 16; + done (cmd); + return 0; + } } /*verify there is no command already in progress for this log dev */ @@ -875,71 +1725,149 @@ /*fill scb information dependent on scsi command */ scsi_cmd = cmd->cmnd[0]; + #ifdef IM_DEBUG_CMD printk("issue scsi cmd=%02x to ldn=%d\n", scsi_cmd, ldn); #endif + + /* for specific device debugging: */ +#ifdef IM_DEBUG_CMD_SPEC_DEV + if (ld[ldn].device_type==IM_DEBUG_CMD_DEVICE) + printk("(SCSI-device-type=0x%x) issue scsi cmd=%02x to ldn=%d\n", + ld[ldn].device_type, scsi_cmd, ldn); +#endif + + /* for possible panics store current command */ + last_scsi_command = scsi_cmd; + + /* update statistical info */ + IBM_DS.total_accesses++; + IBM_DS.ldn_access[ldn]++; + switch (scsi_cmd) { case READ_6: case WRITE_6: case READ_10: case WRITE_10: - if (scsi_cmd == READ_6 || scsi_cmd == READ_10) - { - scb->command = IM_READ_DATA_CMD; - scb->enable |= IM_READ_CONTROL; - } - else - { - scb->command = IM_WRITE_DATA_CMD; - } - if (scsi_cmd == READ_6 || scsi_cmd == WRITE_6) - { - scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[3]) << 0) | - (((unsigned) cmd->cmnd[2]) << 8) | - ((((unsigned) cmd->cmnd[1]) & 0x1f) << 16); - scb->u2.blk.count = (unsigned) cmd->cmnd[4]; - } - else - { - scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[5]) << 0) | - (((unsigned) cmd->cmnd[4]) << 8) | - (((unsigned) cmd->cmnd[3]) << 16) | - (((unsigned) cmd->cmnd[2]) << 24); - scb->u2.blk.count = (((unsigned) cmd->cmnd[8]) << 0) | - (((unsigned) cmd->cmnd[7]) << 8); - } - scb->u2.blk.length = ld[ldn].block_length; - if (ld[ldn].is_disk) - { - if (++disk_rw_in_progress == 1) - PS2_DISK_LED_ON (); - } + case READ_12: + case WRITE_12: + /* statistics for proc_info */ + if ((scsi_cmd == READ_6)||(scsi_cmd == READ_10)||(scsi_cmd == READ_12)) + IBM_DS.ldn_read_access[ldn]++; /* increase READ-access on ldn stat. */ + else if ((scsi_cmd == WRITE_6)||(scsi_cmd == WRITE_10)|| + (scsi_cmd == WRITE_12)) + IBM_DS.ldn_write_access[ldn]++; /* increase write-count on ldn stat.*/ + + /* Distinguish between disk and other devices. Only disks (that are the + most frequently accessed devices) should be supported by the + IBM-SCSI-Subsystem commands. */ + switch (ld[ldn].device_type) + { + case TYPE_DISK: /* for harddisks enter here ... */ + case TYPE_MOD: /* ... try it also for MO-drives (send flames as */ + /* you like, if this won't work.) */ + if (scsi_cmd == READ_6 || scsi_cmd == READ_10 || + scsi_cmd == READ_12) + { + scb->command = IM_READ_DATA_CMD; + scb->enable |= IM_READ_CONTROL; + } + else + { + scb->command = IM_WRITE_DATA_CMD; + } + if (scsi_cmd == READ_6 || scsi_cmd == WRITE_6) + { + scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[3]) << 0) | + (((unsigned) cmd->cmnd[2]) << 8) | + ((((unsigned) cmd->cmnd[1]) & 0x1f) << 16); + scb->u2.blk.count = (unsigned) cmd->cmnd[4]; + } + else + { + scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[5]) << 0) | + (((unsigned) cmd->cmnd[4]) << 8) | + (((unsigned) cmd->cmnd[3]) << 16) | + (((unsigned) cmd->cmnd[2]) << 24); + scb->u2.blk.count = (((unsigned) cmd->cmnd[8]) << 0) | + (((unsigned) cmd->cmnd[7]) << 8); + } + scb->u2.blk.length = ld[ldn].block_length; + if (++disk_rw_in_progress == 1) + PS2_DISK_LED_ON (shpnt->host_no, cmd->target); + break; + + /* for other devices, enter here. Other types are not known by + Linux! TYPE_NO_LUN is forbidden as valid device. */ + case TYPE_ROM: + case TYPE_TAPE: + case TYPE_PROCESSOR: + case TYPE_WORM: + case TYPE_SCANNER: + case TYPE_MEDIUM_CHANGER: + + /* If there is a sequential-device, IBM recommends to use + IM_OTHER_SCSI_CMD_CMD instead of subsystem READ/WRITE. + Good/modern CD-ROM-drives are capable of + reading sequential AND random-access. This leads to the problem, + that random-accesses are covered by the subsystem, but + sequentials are not, as like for tape-drives. Therefore, it is + the easiest way to use IM_OTHER_SCSI_CMD_CMD for all read-ops + on CD-ROM-drives in order not to run into timing problems and + to have a stable state. In addition, data-access on CD-ROMs + works faster like that. Strange, but obvious. */ + + scb->command = IM_OTHER_SCSI_CMD_CMD; + if (scsi_cmd == READ_6 || scsi_cmd == READ_10 || + scsi_cmd == READ_12) /* enable READ */ + scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT; + else + scb->enable |= IM_SUPRESS_EXCEPTION_SHORT; /* assume WRITE */ + + scb->u1.scsi_cmd_length = cmd->cmd_len; + memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len); + + /* Read/write on this non-disk devices is also displayworthy, + so flash-up the LED/display. */ + if (++disk_rw_in_progress == 1) + PS2_DISK_LED_ON (shpnt->host_no, cmd->target); + break; + } break; - case INQUIRY: scb->command = IM_DEVICE_INQUIRY_CMD; - scb->enable |= IM_READ_CONTROL | - IM_SUPRESS_EXCEPTION_SHORT; + scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT; break; case READ_CAPACITY: scb->command = IM_READ_CAPACITY_CMD; scb->enable |= IM_READ_CONTROL; - /*the length of system memory buffer must be exactly 8 bytes */ + /* the length of system memory buffer must be exactly 8 bytes */ if (scb->sys_buf_length >= 8) scb->sys_buf_length = 8; break; + /* Commands that need read-only-mode (system <- device): */ case REQUEST_SENSE: scb->command = IM_REQUEST_SENSE_CMD; scb->enable |= IM_READ_CONTROL; break; - + + /* Commands that need write-only-mode (system -> device): */ + case MODE_SELECT: + case MODE_SELECT_10: + scb->command = IM_OTHER_SCSI_CMD_CMD; + scb->enable |= IM_SUPRESS_EXCEPTION_SHORT; /*Select needs WRITE-enabled*/ + scb->u1.scsi_cmd_length = cmd->cmd_len; + memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len); + break; + + /* For other commands, read-only is useful. Most other commands are + running without an input-data-block. */ default: scb->command = IM_OTHER_SCSI_CMD_CMD; - scb->enable |= IM_READ_CONTROL | - IM_SUPRESS_EXCEPTION_SHORT; + scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT; scb->u1.scsi_cmd_length = cmd->cmd_len; memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len); break; @@ -958,10 +1886,10 @@ /* The code below doesn't work right now, so we tell the upper layer that we can't abort. This eventually causes a reset. */ - return SCSI_ABORT_SNOOZE; + return SCSI_ABORT_SNOOZE ; #if 0 - struct Scsi_Host *shpnt = cmd->host; + struct Scsi_host *shpnt = cmd->host; unsigned int ldn; void (*saved_done) (Scsi_Cmnd *); @@ -986,7 +1914,7 @@ saved_done = cmd->scsi_done; cmd->scsi_done = internal_done; cmd->SCp.Status = 0; - issue_cmd (shpnt, IM_ABORT_IMM_CMD, IM_IMM_CMD | ldn); + issue_cmd (shpnt, T_IMM_CMD, IM_IMM_CMD | ldn); while (!cmd->SCp.Status) barrier (); @@ -1004,7 +1932,7 @@ /*--------------------------------------------------------------------*/ -int +int ibmmca_reset (Scsi_Cmnd * cmd, unsigned int reset_flags) { struct Scsi_Host *shpnt = cmd->host; @@ -1024,15 +1952,14 @@ udelay(1000000/HZ); barrier(); } - /* if reset did not complete, just return an error*/ if (!ticks) { printk("IBM MCA SCSI: reset did not complete within %d seconds.\n", - IM_RESET_DELAY); + IM_RESET_DELAY); reset_status = IM_RESET_FINISHED_FAIL; return SCSI_RESET_ERROR; } - + /* if reset failed, just return an error */ if (reset_status == IM_RESET_FINISHED_FAIL) { printk("IBM MCA SCSI: reset failed.\n"); @@ -1045,13 +1972,13 @@ int i; for (i = 0; i < MAX_LOG_DEV; i++) { - Scsi_Cmnd *cmd = ld[i].cmd; - if (cmd && cmd->scsi_done) - { - ld[i].cmd = 0; - cmd->result = DID_RESET; - (cmd->scsi_done) (cmd); - } + Scsi_Cmnd *cmd = ld[i].cmd; + if (cmd && cmd->scsi_done) + { + ld[i].cmd = 0; + cmd->result = DID_RESET; + (cmd->scsi_done) (cmd); + } } } return SCSI_RESET_SUCCESS; @@ -1082,7 +2009,114 @@ return 0; } -/*--------------------------------------------------------------------*/ +/* calculate percentage of total accesses on a ldn */ +static int ldn_access_load(struct Scsi_Host *shpnt, int ldn) +{ + if (IBM_DS.total_accesses == 0) return (0); + if (IBM_DS.ldn_access[ldn] == 0) return (0); + return((int)(((float)IBM_DS.ldn_access[ldn]/(float)IBM_DS.total_accesses)*(float)100.000)); +} + +/* calculate total amount of r/w-accesses */ +static int ldn_access_total_read_write(struct Scsi_Host *shpnt) +{ + int a = 0; + int i; + + for (i=0; i<=MAX_LOG_DEV; i++) + a+=IBM_DS.ldn_read_access[i]+IBM_DS.ldn_write_access[i]; + return(a); +} + +/* routine to display info in the proc-fs-structure (a deluxe feature) */ +int ibmmca_proc_info (char *buffer, char **start, off_t offset, int length, + int hostno, int inout) +{ + int len=0; + int i,id; + struct Scsi_Host *shpnt; + + for (i = 0; hosts[i] && hosts[i]->host_no != hostno; i++); + shpnt = hosts[i]; + if (!shpnt) { + len += sprintf(buffer+len, "\nCan't find adapter for host number %d\n", hostno); + return len; + } + + cli(); + + len += sprintf(buffer+len, "\n IBM-SCSI-Subsystem-Linux-Driver, Version %s\n\n\n", + IBMMCA_SCSI_DRIVER_VERSION); + len += sprintf(buffer+len, " SCSI Access-Statistics:\n"); +#ifdef CONFIG_SCSI_MULTI_LUN + len += sprintf(buffer+len, " Multiple LUN probing.....: Yes\n"); +#else + len += sprintf(buffer+len, " Multiple LUN probing.....: No\n"); +#endif + len += sprintf(buffer+len, " This Hostnumber..........: %d\n", + hostno); + len += sprintf(buffer+len, " Base I/O-Port............: 0x%x\n", + IM_CMD_REG); + len += sprintf(buffer+len, " (Shared) IRQ.............: %d\n", + IM_IRQ); + len += sprintf(buffer+len, " Total Interrupts.........: %d\n", + IBM_DS.total_interrupts); + len += sprintf(buffer+len, " Total SCSI Accesses......: %d\n", + IBM_DS.total_accesses); + len += sprintf(buffer+len, " Total SCSI READ/WRITE..: %d\n", + ldn_access_total_read_write(shpnt)); + len += sprintf(buffer+len, " Total SCSI other cmds..: %d\n\n", + IBM_DS.total_accesses - ldn_access_total_read_write(shpnt)); + + len += sprintf(buffer+len, " Logical-Device-Number (LDN) Access-Statistics:\n"); + len += sprintf(buffer+len, " LDN | Accesses [%%] | READ | WRITE | ASSIGNMENTS\n"); + len += sprintf(buffer+len, " -----|--------------|-----------|-----------|--------------\n"); + for (i=0; i<=MAX_LOG_DEV; i++) + len += sprintf(buffer+len, " %2X | %3d | %8d | %8d | %8d\n", + i, ldn_access_load(shpnt, i), IBM_DS.ldn_read_access[i], + IBM_DS.ldn_write_access[i], IBM_DS.ldn_assignments[i]); + len += sprintf(buffer+len, " -----------------------------------------------------------\n\n"); + + len += sprintf(buffer+len, " Dynamical-LDN-Assignment-Statistics:\n"); + len += sprintf(buffer+len, " Number of physical SCSI-devices..: %d (+ Adapter)\n", + IBM_DS.total_scsi_devices); + len += sprintf(buffer+len, " Dynamical Assignment necessaray..: %s\n", + IBM_DS.dyn_flag ? "Yes" : "No "); + len += sprintf(buffer+len, " Next LDN to be assigned..........: 0x%x\n", + next_ldn); + len += sprintf(buffer+len, " Dynamical assignments done yet...: %d\n", + IBM_DS.dynamical_assignments); + + len += sprintf(buffer+len, "\n Current SCSI-Device-Mapping:\n"); + len += sprintf(buffer+len, " Physical SCSI-Device Map Logical SCSI-Device Map\n"); + len += sprintf(buffer+len, " ID\\LUN 0 1 2 3 4 5 6 7 ID\\LUN 0 1 2 3 4 5 6 7\n"); + for (id=0; id<=7; id++) + { + len += sprintf(buffer+len, " %2d %2s %2s %2s %2s %2s %2s %2s %2s", + id, ti_p(get_scsi[id][0]), ti_p(get_scsi[id][1]), + ti_p(get_scsi[id][2]), ti_p(get_scsi[id][3]), + ti_p(get_scsi[id][4]), ti_p(get_scsi[id][5]), + ti_p(get_scsi[id][6]), ti_p(get_scsi[id][7])); + len += sprintf(buffer+len, " %2d %2s %2s %2s %2s %2s %2s %2s %2s\n", + id, ti_l(get_ldn[id][0]), ti_l(get_ldn[id][1]), + ti_l(get_ldn[id][2]), ti_l(get_ldn[id][3]), + ti_l(get_ldn[id][4]), ti_l(get_ldn[id][5]), + ti_l(get_ldn[id][6]), ti_l(get_ldn[id][7])); + } + + len += sprintf(buffer+len, "(A = IBM-Subsystem, D = Harddisk, T = Tapedrive, P = Processor, W = WORM,\n"); + len += sprintf(buffer+len, " R = CD-ROM, S = Scanner, M = MO-Drive, C = Medium-Changer, + = unprovided LUN,\n"); + len += sprintf(buffer+len, " - = nothing found)\n\n"); + + *start = buffer + offset; + len -= offset; + if (len > length) + len = length; + + sti(); + + return len; +} #ifdef MODULE /* Eventually this will go into an include file, but this will be later */ @@ -1091,3 +2125,4 @@ #include "scsi_module.c" #endif +/*--------------------------------------------------------------------*/ diff -u --recursive --new-file v2.1.58/linux/drivers/scsi/ibmmca.h linux/drivers/scsi/ibmmca.h --- v2.1.58/linux/drivers/scsi/ibmmca.h Fri Apr 4 08:52:23 1997 +++ linux/drivers/scsi/ibmmca.h Fri Oct 17 13:59:30 1997 @@ -1,12 +1,12 @@ - #ifndef _IBMMCA_H #define _IBMMCA_H -/* +/* * Low Level Driver for the IBM Microchannel SCSI Subsystem */ /*services provided to the higher level of Linux SCSI driver */ +int ibmmca_proc_info (char *, char **, off_t, int, int, int); int ibmmca_detect (Scsi_Host_Template *); int ibmmca_release (struct Scsi_Host *); int ibmmca_command (Scsi_Cmnd *); @@ -21,9 +21,9 @@ /*initialization for Scsi_host_template type */ #define IBMMCA { \ NULL, /*next*/ \ - NULL, /*module*/ \ + NULL, /*usage_count*/ \ &proc_scsi_ibmmca, /*proc_dir*/ \ - NULL, /*proc info fn*/ \ + ibmmca_proc_info, /*proc info fn*/ \ "IBMMCA", /*name*/ \ ibmmca_detect, /*detect fn*/ \ ibmmca_release, /*release fn*/ \ diff -u --recursive --new-file v2.1.58/linux/fs/ncpfs/dir.c linux/fs/ncpfs/dir.c --- v2.1.58/linux/fs/ncpfs/dir.c Thu Sep 11 09:02:23 1997 +++ linux/fs/ncpfs/dir.c Thu Oct 16 08:10:14 1997 @@ -216,7 +216,7 @@ int result = 0; int i = 0; int index = 0; - struct inode *inode = file->f_dentry->d_inode; + struct inode *inode = filp->f_dentry->d_inode; struct ncp_dirent *entry = NULL; struct ncp_server *server = NCP_SERVER(inode); struct ncp_inode_info *dir = NCP_INOP(inode); diff -u --recursive --new-file v2.1.58/linux/fs/nfs/dir.c linux/fs/nfs/dir.c --- v2.1.58/linux/fs/nfs/dir.c Wed Oct 15 16:04:23 1997 +++ linux/fs/nfs/dir.c Wed Oct 15 21:08:46 1997 @@ -180,7 +180,7 @@ goto again; } - if (ismydir && cache->mtime != NFS_OLDMTIME(inode)) + if (ismydir && cache->mtime != inode->i_mtime) cache->valid = 0; if (!cache->valid || cache->age < dead) { @@ -207,7 +207,7 @@ if (j < cache->size - 1) { index = j + 1; entry = this_ent + 3; - } else if (*(this_ent+2) >> 16) { + } else if (*(this_ent+2) & (1 << 15)) { /* eof */ return 0; } @@ -253,7 +253,7 @@ cache->valid = 1; entry = cache->entry + (index = 0); } - cache->mtime = NFS_OLDMTIME(inode); + cache->mtime = inode->i_mtime; cache->age = jiffies; /* @@ -926,99 +926,6 @@ /* Update the dcache */ d_move(old_dentry, new_dentry); } - return error; -} - -/* - * Many nfs protocol calls return the new file attributes after - * an operation. Here we update the inode to reflect the state - * of the server's inode. - */ - -int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) -{ - int error = -EIO; - - dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d)\n", - inode->i_dev, inode->i_ino, inode->i_count); - - if (!inode || !fattr) { - printk("nfs_refresh_inode: inode or fattr is NULL\n"); - goto out; - } - if (inode->i_ino != fattr->fileid) { - printk("nfs_refresh_inode: inode number mismatch\n"); - goto out; - } - /* - * Check whether the mode has been set, as we only want to - * do this once. (We don't allow inodes to change types.) - */ - if (inode->i_mode == 0) { - inode->i_mode = fattr->mode; - if (S_ISREG(inode->i_mode)) - inode->i_op = &nfs_file_inode_operations; - else if (S_ISDIR(inode->i_mode)) - inode->i_op = &nfs_dir_inode_operations; - else if (S_ISLNK(inode->i_mode)) - inode->i_op = &nfs_symlink_inode_operations; - else if (S_ISCHR(inode->i_mode)) { - inode->i_op = &chrdev_inode_operations; - inode->i_rdev = to_kdev_t(fattr->rdev); - } else if (S_ISBLK(inode->i_mode)) { - inode->i_op = &blkdev_inode_operations; - inode->i_rdev = to_kdev_t(fattr->rdev); - } else if (S_ISFIFO(inode->i_mode)) - init_fifo(inode); - else - inode->i_op = NULL; - } else if ((inode->i_mode & S_IFMT) == (fattr->mode & S_IFMT)) { - inode->i_mode = fattr->mode; - } else { - mode_t old_mode; - /* - * Big trouble! The inode has become a different object. - */ -#if 1 -printk("nfs_refresh_inode: mode changed, %07o to %07o\n", -inode->i_mode, fattr->mode); -#endif - old_mode = inode->i_mode; /* save mode */ - make_bad_inode(inode); - inode->i_mode = old_mode; /* restore mode */ - inode->i_nlink = 0; - /* - * No need to worry about unhashing the dentry, as the - * lookup validation will know that the inode is bad. - * (But we do want to invalidate the caches.) - */ - invalidate_inode_pages(inode); - nfs_invalidate_dircache(inode); - goto out; - } - - inode->i_nlink = fattr->nlink; - inode->i_uid = fattr->uid; - inode->i_gid = fattr->gid; - - /* Size changed from outside: invalidate caches on next read */ - if (inode->i_size != fattr->size) { - dfprintk(PAGECACHE, "NFS: cacheinv(%x/%ld)\n", - inode->i_dev, inode->i_ino); - NFS_CACHEINV(inode); - } - if (NFS_OLDMTIME(inode) != fattr->mtime.seconds) { - dfprintk(PAGECACHE, "NFS: mtime change on %x/%ld\n", - inode->i_dev, inode->i_ino); - NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); - } - inode->i_size = fattr->size; - inode->i_blocks = fattr->blocks; - inode->i_atime = fattr->atime.seconds; - inode->i_mtime = fattr->mtime.seconds; - inode->i_ctime = fattr->ctime.seconds; - error = 0; -out: return error; } diff -u --recursive --new-file v2.1.58/linux/fs/nfs/inode.c linux/fs/nfs/inode.c --- v2.1.58/linux/fs/nfs/inode.c Wed Oct 15 16:04:23 1997 +++ linux/fs/nfs/inode.c Wed Oct 15 21:08:46 1997 @@ -33,6 +33,7 @@ #include #define NFSDBG_FACILITY NFSDBG_VFS +#define NFS_PARANOIA 1 extern void nfs_invalidate_dircache_sb(struct super_block *); @@ -72,6 +73,7 @@ inode->i_rdev = 0; inode->i_op = NULL; NFS_CACHEINV(inode); + NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); } static void @@ -356,14 +358,48 @@ printk("nfs_fhget: iget failed\n"); return NULL; } - if (inode->i_dev == sb->s_dev) { - if (inode->i_ino != fattr->fileid) { - printk("nfs_fhget: unexpected inode from iget\n"); - return inode; - } - *NFS_FH(inode) = *fhandle; - nfs_refresh_inode(inode, fattr); +#ifdef NFS_PARANOIA +if (inode->i_dev != sb->s_dev) +printk("nfs_fhget: impossible\n"); +#endif + + if (inode->i_ino != fattr->fileid) { + printk("nfs_fhget: unexpected inode from iget\n"); + return inode; + } + + /* + * Check whether the mode has been set, as we only want to + * do this once. (We don't allow inodes to change types.) + */ + if (inode->i_mode == 0) { + inode->i_mode = fattr->mode; + if (S_ISREG(inode->i_mode)) + inode->i_op = &nfs_file_inode_operations; + else if (S_ISDIR(inode->i_mode)) + inode->i_op = &nfs_dir_inode_operations; + else if (S_ISLNK(inode->i_mode)) + inode->i_op = &nfs_symlink_inode_operations; + else if (S_ISCHR(inode->i_mode)) { + inode->i_op = &chrdev_inode_operations; + inode->i_rdev = to_kdev_t(fattr->rdev); + } else if (S_ISBLK(inode->i_mode)) { + inode->i_op = &blkdev_inode_operations; + inode->i_rdev = to_kdev_t(fattr->rdev); + } else if (S_ISFIFO(inode->i_mode)) + init_fifo(inode); + else + inode->i_op = NULL; + /* + * Preset the size and mtime, as there's no need + * to invalidate the caches. + */ + inode->i_size = fattr->size; + inode->i_mtime = fattr->mtime.seconds; + NFS_OLDMTIME(inode) = fattr->mtime.seconds; } + *NFS_FH(inode) = *fhandle; + nfs_refresh_inode(inode, fattr); dprintk("NFS: fhget(%x/%ld ct=%d)\n", inode->i_dev, inode->i_ino, inode->i_count); @@ -378,6 +414,17 @@ struct nfs_fattr fattr; int error; + /* + * Make sure the inode is up-to-date. + */ + error = nfs_revalidate(inode); + if (error) { +#ifdef NFS_PARANOIA +printk("nfs_notify_change: revalidate failed, error=%d\n", error); +#endif + goto out; + } + sattr.mode = (u32) -1; if (attr->ia_valid & ATTR_MODE) sattr.mode = attr->ia_mode; @@ -390,7 +437,6 @@ if (attr->ia_valid & ATTR_GID) sattr.gid = attr->ia_gid; - sattr.size = (u32) -1; if ((attr->ia_valid & ATTR_SIZE) && S_ISREG(inode->i_mode)) sattr.size = attr->ia_size; @@ -408,11 +454,25 @@ } error = nfs_proc_setattr(NFS_SERVER(inode), NFS_FH(inode), - &sattr, &fattr); - if (!error) { + &sattr, &fattr); + if (error) + goto out; + /* + * If we changed the size or mtime, update the inode + * now to avoid invalidating the page cache. + */ + if (sattr.size != (u32) -1) { + if (sattr.size != fattr.size) + printk("nfs_notify_change: sattr=%d, fattr=%d??\n", + sattr.size, fattr.size); nfs_truncate_dirty_pages(inode, sattr.size); - nfs_refresh_inode(inode, &fattr); + inode->i_size = sattr.size; + inode->i_mtime = fattr.mtime.seconds; } + if (sattr.mtime.seconds != (u32) -1) + inode->i_mtime = fattr.mtime.seconds; + error = nfs_refresh_inode(inode, &fattr); +out: return error; } @@ -428,54 +488,152 @@ /* * This function is called whenever some part of NFS notices that * the cached attributes have to be refreshed. - * - * This is a bit tricky because we have to make sure all dirty pages - * have been sent off to the server before calling invalidate_inode_pages. - * To make sure no other process adds more write requests while we try - * our best to flush them, we make them sleep during the attribute refresh. - * - * A very similar scenario holds for the dir cache. */ int _nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) { struct nfs_fattr fattr; - int status; + int status = 0; if (jiffies - NFS_READTIME(inode) < NFS_ATTRTIMEO(inode)) - return 0; + goto out; dfprintk(PAGECACHE, "NFS: revalidating %x/%ld inode\n", inode->i_dev, inode->i_ino); - NFS_READTIME(inode) = jiffies; - if ((status = nfs_proc_getattr(server, NFS_FH(inode), &fattr)) < 0) + status = nfs_proc_getattr(server, NFS_FH(inode), &fattr); + if (status) { +#ifdef NFS_PARANOIA +printk("nfs_revalidate_inode: getattr failed, error=%d\n", status); +#endif goto done; + } - nfs_refresh_inode(inode, &fattr); - if (fattr.mtime.seconds != NFS_OLDMTIME(inode)) { - if (!S_ISDIR(inode->i_mode)) { - /* This sends off all dirty pages off to the server. - * Note that this function must not sleep. */ - nfs_invalidate_pages(inode); - invalidate_inode_pages(inode); - } else { - nfs_invalidate_dircache(inode); - } - - NFS_OLDMTIME(inode) = fattr.mtime.seconds; - NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); - } else { + status = nfs_refresh_inode(inode, &fattr); + if (status) + goto done; + if (fattr.mtime.seconds == NFS_OLDMTIME(inode)) { /* Update attrtimeo value */ if ((NFS_ATTRTIMEO(inode) <<= 1) > NFS_MAXATTRTIMEO(inode)) NFS_ATTRTIMEO(inode) = NFS_MAXATTRTIMEO(inode); } - status = 0; + NFS_OLDMTIME(inode) = fattr.mtime.seconds; done: dfprintk(PAGECACHE, "NFS: inode %x/%ld revalidation complete (status %d).\n", inode->i_dev, inode->i_ino, status); +out: return status; +} + +/* + * Many nfs protocol calls return the new file attributes after + * an operation. Here we update the inode to reflect the state + * of the server's inode. + * + * This is a bit tricky because we have to make sure all dirty pages + * have been sent off to the server before calling invalidate_inode_pages. + * To make sure no other process adds more write requests while we try + * our best to flush them, we make them sleep during the attribute refresh. + * + * A very similar scenario holds for the dir cache. + */ +int +nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) +{ + int invalid = 0; + int error = -EIO; + + dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d)\n", + inode->i_dev, inode->i_ino, inode->i_count); + + if (!inode || !fattr) { + printk("nfs_refresh_inode: inode or fattr is NULL\n"); + goto out; + } + if (inode->i_ino != fattr->fileid) { + printk("nfs_refresh_inode: inode number mismatch\n"); + goto out; + } + + /* + * Make sure the inode's type hasn't changed. + */ + if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) + goto out_changed; + + /* + * If the size or mtime changed from outside, we want + * to invalidate the local caches immediately. + */ + if (inode->i_size != fattr->size) { +#ifdef NFS_DEBUG_VERBOSE +printk("NFS: size change on %x/%ld\n", inode->i_dev, inode->i_ino); +#endif + invalid = 1; + } + if (inode->i_mtime != fattr->mtime.seconds) { +#ifdef NFS_DEBUG_VERBOSE +printk("NFS: mtime change on %x/%ld\n", inode->i_dev, inode->i_ino); +#endif + invalid = 1; + } + + inode->i_mode = fattr->mode; + inode->i_nlink = fattr->nlink; + inode->i_uid = fattr->uid; + inode->i_gid = fattr->gid; + + inode->i_size = fattr->size; + inode->i_blocks = fattr->blocks; + inode->i_atime = fattr->atime.seconds; + inode->i_mtime = fattr->mtime.seconds; + inode->i_ctime = fattr->ctime.seconds; + /* + * Update the read time so we don't revalidate too often. + */ + NFS_READTIME(inode) = jiffies; + error = 0; + if (invalid) + goto out_invalid; +out: + return error; + +out_changed: + /* + * Big trouble! The inode has become a different object. + */ +#ifdef NFS_PARANOIA +printk("nfs_refresh_inode: mode changed, %07o to %07o\n", +inode->i_mode, fattr->mode); +#endif + fattr->mode = inode->i_mode; /* save mode */ + make_bad_inode(inode); + inode->i_mode = fattr->mode; /* restore mode */ + inode->i_nlink = 0; + /* + * No need to worry about unhashing the dentry, as the + * lookup validation will know that the inode is bad. + * (But we fall through to invalidate the caches.) + */ + +out_invalid: + /* + * Invalidate the local caches + */ +#ifdef NFS_DEBUG_VERBOSE +printk("nfs_refresh_inode: invalidating %ld pages\n", inode->i_nrpages); +#endif + if (!S_ISDIR(inode->i_mode)) { + /* This sends off all dirty pages off to the server. + * Note that this function must not sleep. */ + nfs_invalidate_pages(inode); + invalidate_inode_pages(inode); + } else + nfs_invalidate_dircache(inode); + NFS_CACHEINV(inode); + NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); + goto out; } /* diff -u --recursive --new-file v2.1.58/linux/fs/nfs/write.c linux/fs/nfs/write.c --- v2.1.58/linux/fs/nfs/write.c Tue Sep 23 16:48:49 1997 +++ linux/fs/nfs/write.c Wed Oct 15 21:08:46 1997 @@ -196,22 +196,44 @@ clear_bit(PG_uptodate, &page->flags); goto io_error; } + if (result != wsize) + printk("NFS: short write, wsize=%u, result=%d\n", + wsize, result); refresh = 1; buffer += wsize; offset += wsize; written += wsize; count -= wsize; + /* + * If we've extended the file, update the inode + * now so we don't invalidate the cache. + */ + if (offset > inode->i_size) + inode->i_size = offset; } while (count); io_error: + /* N.B. do we want to refresh if there was an error?? (fattr valid?) */ if (refresh) { /* See comments in nfs_wback_result */ + /* N.B. I don't think this is right -- sync writes in order */ if (fattr.size < inode->i_size) fattr.size = inode->i_size; + if (fattr.mtime.seconds < inode->i_mtime) + printk("nfs_writepage_sync: prior time??\n"); /* Solaris 2.5 server seems to send garbled * fattrs occasionally */ - if (inode->i_ino == fattr.fileid) + if (inode->i_ino == fattr.fileid) { + /* + * We expect the mtime value to change, and + * don't want to invalidate the caches. + */ + inode->i_mtime = fattr.mtime.seconds; nfs_refresh_inode(inode, &fattr); + } + else + printk("nfs_writepage_sync: inode %ld, got %u?\n", + inode->i_ino, fattr.fileid); } nfs_unlock_page(page); @@ -327,7 +349,7 @@ wreq = (struct nfs_wreq *) kmalloc(sizeof(*wreq), GFP_USER); if (!wreq) - return NULL; + goto out_fail; memset(wreq, 0, sizeof(*wreq)); task = &wreq->wb_task; @@ -336,11 +358,8 @@ task->tk_action = nfs_wback_lock; rpcauth_lookupcred(task); /* Obtain user creds */ - if (task->tk_status < 0) { - rpc_release_task(task); - kfree(wreq); - return NULL; - } + if (task->tk_status < 0) + goto out_req; /* Put the task on inode's writeback request list. */ wreq->wb_inode = inode; @@ -357,6 +376,12 @@ rpc_wake_up_next(&write_queue); return wreq; + +out_req: + rpc_release_task(task); + kfree(wreq); +out_fail: + return NULL; } /* @@ -423,6 +448,8 @@ } remove_wait_queue(&page->wait, &wait); current->state = TASK_RUNNING; +if (atomic_read(&page->count) == 1) +printk("NFS: lost a page\n"); atomic_dec(&page->count); return retval; } @@ -808,15 +835,29 @@ } clear_bit(PG_uptodate, &page->flags); } else if (!WB_CANCELLED(req)) { + struct nfs_fattr *fattr = req->wb_fattr; /* Update attributes as result of writeback. * Beware: when UDP replies arrive out of order, we * may end up overwriting a previous, bigger file size. */ - if (req->wb_fattr->size < inode->i_size) - req->wb_fattr->size = inode->i_size; - /* possible Solaris 2.5 server bug workaround */ - if (inode->i_ino == req->wb_fattr->fileid) - nfs_refresh_inode(inode, req->wb_fattr); + if (fattr->mtime.seconds >= inode->i_mtime) { + if (fattr->size < inode->i_size) + fattr->size = inode->i_size; + + /* possible Solaris 2.5 server bug workaround */ + if (inode->i_ino == fattr->fileid) { + /* + * We expect these values to change, and + * don't want to invalidate the caches. + */ + inode->i_size = fattr->size; + inode->i_mtime = fattr->mtime.seconds; + nfs_refresh_inode(inode, fattr); + } + else + printk("nfs_wback_result: inode %ld, got %u?\n", + inode->i_ino, fattr->fileid); + } } rpc_release_task(task); diff -u --recursive --new-file v2.1.58/linux/fs/super.c linux/fs/super.c --- v2.1.58/linux/fs/super.c Wed Oct 15 16:04:23 1997 +++ linux/fs/super.c Thu Oct 16 08:11:24 1997 @@ -840,6 +840,13 @@ int retval; struct vfsmount *vfsmnt; + /* + * Invalidate the inodes, as some mount options may be changed. + * N.B. If we are changing media, we should check the return + * from invalidate_inodes ... can't allow _any_ open files. + */ + invalidate_inodes(sb); + if (!(flags & MS_RDONLY) && sb->s_dev && is_read_only(sb->s_dev)) return -EACCES; /*flags |= MS_RDONLY;*/ @@ -870,8 +877,14 @@ struct super_block * sb = dentry->d_inode->i_sb; retval = -EINVAL; - if (dentry == sb->s_root) + if (dentry == sb->s_root) { + /* + * Shrink the dcache and sync the device. + */ + shrink_dcache_sb(sb); + fsync_dev(sb->s_dev); retval = do_remount_sb(sb, flags, data); + } dput(dentry); } return retval; diff -u --recursive --new-file v2.1.58/linux/include/linux/ioport.h linux/include/linux/ioport.h --- v2.1.58/linux/include/linux/ioport.h Mon Apr 14 16:28:26 1997 +++ linux/include/linux/ioport.h Fri Oct 17 14:02:01 1997 @@ -30,7 +30,7 @@ #define HAVE_AUTOIRQ extern void *irq2dev_map[16]; /* Use only if you own the IRQ. */ -extern int autoirq_setup(int waittime); +extern void autoirq_setup(int waittime); extern int autoirq_report(int waittime); #endif /* _LINUX_PORTIO_H */ diff -u --recursive --new-file v2.1.58/linux/include/linux/kernel.h linux/include/linux/kernel.h --- v2.1.58/linux/include/linux/kernel.h Sun Sep 7 13:10:43 1997 +++ linux/include/linux/kernel.h Fri Oct 17 15:43:49 1997 @@ -33,6 +33,12 @@ # define ATTRIB_NORET __attribute__((noreturn)) # define NORET_AND noreturn, +#ifdef __i386__ +#define FASTCALL(x) x __attribute__((regparm(3))) +#else +#define FASTCALL(x) x +#endif + extern void math_error(void); NORET_TYPE void panic(const char * fmt, ...) __attribute__ ((NORET_AND format (printf, 1, 2))); diff -u --recursive --new-file v2.1.58/linux/include/linux/mm.h linux/include/linux/mm.h --- v2.1.58/linux/include/linux/mm.h Wed Oct 15 16:04:24 1997 +++ linux/include/linux/mm.h Fri Oct 17 15:49:57 1997 @@ -241,7 +241,7 @@ */ #define __get_free_page(priority) __get_free_pages((priority),0,0) #define __get_dma_pages(priority, order) __get_free_pages((priority),(order),1) -extern unsigned long __get_free_pages(int priority, unsigned long gfporder, int dma); +extern unsigned long FASTCALL(__get_free_pages(int priority, unsigned long gfporder, int dma)); extern inline unsigned long get_free_page(int priority) { @@ -256,8 +256,8 @@ /* memory.c & swap.c*/ #define free_page(addr) free_pages((addr),0) -extern void free_pages(unsigned long addr, unsigned long order); -extern void __free_page(struct page *); +extern void FASTCALL(free_pages(unsigned long addr, unsigned long order)); +extern void FASTCALL(__free_page(struct page *)); extern void show_free_areas(void); extern unsigned long put_dirty_page(struct task_struct * tsk,unsigned long page, diff -u --recursive --new-file v2.1.58/linux/include/linux/sched.h linux/include/linux/sched.h --- v2.1.58/linux/include/linux/sched.h Wed Sep 24 20:05:48 1997 +++ linux/include/linux/sched.h Fri Oct 17 15:49:57 1997 @@ -439,11 +439,11 @@ #define CURRENT_TIME (xtime.tv_sec) -extern void sleep_on(struct wait_queue ** p); -extern void interruptible_sleep_on(struct wait_queue ** p); -extern void wake_up(struct wait_queue ** p); -extern void wake_up_interruptible(struct wait_queue ** p); -extern void wake_up_process(struct task_struct * tsk); +extern void FASTCALL(sleep_on(struct wait_queue ** p)); +extern void FASTCALL(interruptible_sleep_on(struct wait_queue ** p)); +extern void FASTCALL(wake_up(struct wait_queue ** p)); +extern void FASTCALL(wake_up_interruptible(struct wait_queue ** p)); +extern void FASTCALL(wake_up_process(struct task_struct * tsk)); extern void notify_parent(struct task_struct * tsk, int signal); extern void force_sig(unsigned long sig,struct task_struct * p); diff -u --recursive --new-file v2.1.58/linux/include/linux/swap.h linux/include/linux/swap.h --- v2.1.58/linux/include/linux/swap.h Thu Jul 17 10:06:09 1997 +++ linux/include/linux/swap.h Fri Oct 17 15:49:15 1997 @@ -73,7 +73,7 @@ extern struct swap_info_struct swap_info[]; void si_swapinfo(struct sysinfo *); unsigned long get_swap_page(void); -extern void swap_free(unsigned long); +extern void FASTCALL(swap_free(unsigned long)); /* * vm_ops not present page codes for shared memory. diff -u --recursive --new-file v2.1.58/linux/kernel/sched.c linux/kernel/sched.c --- v2.1.58/linux/kernel/sched.c Wed Sep 24 20:05:48 1997 +++ linux/kernel/sched.c Fri Oct 17 13:26:57 1997 @@ -104,7 +104,7 @@ static inline void add_to_runqueue(struct task_struct * p) { - if (p->counter > current->counter + 3) + if (p->policy != SCHED_OTHER || p->counter > current->counter + 3) need_resched = 1; nr_running++; (p->prev_run = init_task.prev_run)->next_run = p; @@ -649,14 +649,14 @@ } -static inline void __sleep_on(struct wait_queue **p, int state) +static void FASTCALL(__sleep_on(struct wait_queue **p, int state)); +static void __sleep_on(struct wait_queue **p, int state) { unsigned long flags; - struct wait_queue wait = { current, NULL }; + struct wait_queue wait; - if (!p) - return; current->state = state; + wait.task = current; write_lock_irqsave(&waitqueue_lock, flags); __add_wait_queue(p, &wait); write_unlock(&waitqueue_lock);