E: rth@twiddle.net
E: rth@cygnus.com
D: Alpha hacker, kernel and userland
-S: 50 E. Middlefield #10
-S: Mountain View, California 94043
+S: 1668 California St.
+S: Mountain View, California 94041
S: USA
N: Benjamin Herrenschmidt
D: 6pack driver for AX.25
N: Harald Koerfgen
-E: harald@unix-ag.org
-D: DECstation port
+E: hkoerfg@web.de
+D: Linux/MIPS kernel hacks and fixes,
+D: DECstation port, Sharp Mobilon port
S: D-50931 Koeln
S: Germany
This enables support for the VR5000-based NEC DDB Vrc-5074
evaluation board.
+Support for NEC DDB Vrc-5476
+CONFIG_DDB5476
+ This enables support for the R5432-based NEC DDB Vrc-5476
+ evaluation board.
+
+ Features : kernel debugging, serial terminal, NFS root fs, on-board
+ ether port (with a patch to tulip driver), IDE controller, PS2 keyboard
+ PS2 mouse, etc.
+
+ TODO : USB, Compact-PCI interface.
+
+Support for MIPS Atlas board
+CONFIG_MIPS_ATLAS
+ This enables support for the QED R5231-based MIPS Atlas evaluation
+ board.
+
+Support for MIPS Malta board
+CONFIG_MIPS_MALTA
+ This enables support for the VR5000-based MIPS Malta evaluation
+ board.
+
Support for Mips Magnum 4000
CONFIG_MIPS_MAGNUM_4000
This is a machine with a R4000 100 MHz CPU. To compile a Linux
4000, Acer PICA, Olivetti M700-10 and a few other identical OEM
systems.
+Kernel floating-point instruction emulation
+CONFIG_MIPS_FPU_EMULATOR
+ This option enables the MIPS software floatingpoint support. Due to the
+ way floatingpoint works you should always enable this option unless
+ you exactly know what you're doing.
+
PCMCIA SCSI adapter support
CONFIG_SCSI_PCMCIA
Say Y here if you intend to attach a PCMCIA or CardBus card to your
certain types of data to get through the socket. Linux Socket
Filtering works on all socket types except TCP for now. See the text
file Documentation/networking/filter.txt for more information.
+
+ You need to say Y here if you want to use PPP packet filtering
+ (see the CONFIG_PPP_FILTER option below).
+
If unsure, say N.
Network packet filtering
of those special I/O ports.
SGI PROM Console Support
-CONFIG_SGI_PROM_CONSOLE
+CONFIG_ARC_CONSOLE
Say Y here if you want to use the PROMs for console I/O.
SGI Zilog85C30 serial support
If unsure, say N.
+PPP filtering (EXPERIMENTAL)
+CONFIG_PPP_FILTER
+ Say Y here if you want to be able to filter the packets passing over
+ PPP interfaces. This allows you to control which packets count as
+ activity (i.e. which packets will reset the idle timer or bring up
+ a demand-dialled link) and which packets are to be dropped entirely.
+ You need to say Y here if you wish to use the pass-filter and
+ active-filter options to pppd.
+
+ If unsure, say N.
+
PPP support for async serial ports
CONFIG_PPP_ASYNC
Say Y (or M) here if you want to be able to use PPP over standard
how to pass options to the kernel at boot time.) The syntax of the
"lp" command line option can be found in drivers/char/lp.c.
- If you have more than 3 printers, you need to increase the LP_NO
- variable in lp.c.
+ If you have more than 8 printers, you need to increase the LP_NO
+ macro in lp.c and the PARPORT_MAX macro in parport.h.
Support for console on line printer
CONFIG_LP_CONSOLE
Enable support for the AVM T1 T1B card.
Note: This is a PRI card and handle 30 B-channels.
-AVM C4 support
+AVM C4/C2 support
CONFIG_ISDN_DRV_AVMB1_C4
- Enable support for the AVM C4 PCI card.
- This card handle 4 BRI ISDN lines (8 channels).
+ Enable support for the AVM C4/C2 PCI cards.
+ These cards handle 4/2 BRI ISDN lines (8/4 channels).
Verbose reason code reporting (kernel size +=7K)
CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON
ext2 was originally released in January 1993. Written by R\'emy Card,
Theodore Ts'o and Stephen Tweedie, it was a major rewrite of the
-Extended Filesystem. It is currently (February 1999) the predominant
+Extended Filesystem. It is currently still (April 2001) the predominant
filesystem in use by Linux. There are also implementations available
for NetBSD, FreeBSD, the GNU HURD, Windows 95/98/NT, OS/2 and RISC OS.
bsddf (*) Makes `df' act like BSD.
minixdf Makes `df' act like Minix.
-check=none, nocheck Perform no checks upon the filesystem.
-check=normal (*) Perform normal checks on the filesystem.
-check=strict Perform extra checks on the filesystem.
+check=none, nocheck (*) Don't do extra checking of bitmaps on mount
+ (check=normal and check=strict options removed)
-debug For developers only.
+debug Extra debugging information is sent to the
+ kernel syslog. Useful for developers.
errors=continue (*) Keep going on a filesystem error.
errors=remount-ro Remount the filesystem read-only on an error.
grpid, bsdgroups Give objects the same group ID as their parent.
nogrpid, sysvgroups (*) New objects have the group ID of their creator.
-resuid=n The user which may use the reserved blocks.
-resgid=n The group which may use the reserved blocks.
+resuid=n The user ID which may use the reserved blocks.
+resgid=n The group ID which may use the reserved blocks.
sb=n Use alternate superblock at this location.
------
The space in the device or file is split up into blocks. These are
-a fixed size, of 1024, 2048 or 4096 bytes, which is decided when the
-filesystem is created. Smaller blocks mean less wasted space per file,
-but require slightly more accounting overhead.
+a fixed size, of 1024, 2048 or 4096 bytes (8192 bytes on Alpha systems),
+which is decided when the filesystem is created. Smaller blocks mean
+less wasted space per file, but require slightly more accounting overhead,
+and also impose other limits on the size of files and the filesystem.
+
+Block Groups
+------------
Blocks are clustered into block groups in order to reduce fragmentation
-and minimise the amount of head seeking when reading a large amount of
-consecutive data. Each block group has a descriptor and the array of
-descriptors is stored immediately after the superblock. Two blocks at
-the start of each group are reserved for the block usage bitmap and
-the inode usage bitmap which show which blocks and inodes are used.
-Since each bitmap fits in a block, this means that the maximum size of
-a block group is 8 times the size of a block.
-
-The first (non-reserved) blocks in the block group are designated as
-the inode table for the block and the remainder are the data blocks.
-The block allocation algorithm attempts to allocate data blocks in the
-same block group as the inode which contains them.
+and minimise the amount of head seeking when reading a large amount
+of consecutive data. Information about each block group is kept in a
+descriptor table stored in the block(s) immediately after the superblock.
+Two blocks near the start of each group are reserved for the block usage
+bitmap and the inode usage bitmap which show which blocks and inodes
+are in use. Since each bitmap is limited to a single block, this means
+that the maximum size of a block group is 8 times the size of a block.
+
+The block(s) following the bitmaps in each block group are designated
+as the inode table for that block group and the remainder are the data
+blocks. The block allocation algorithm attempts to allocate data blocks
+in the same block group as the inode which contains them.
The Superblock
--------------
The superblock contains all the information about the configuration of
-the filing system. It is stored in block 1 of the filesystem (numbering
-from 0) and it is essential to mounting it. Since it is so important,
-backup copies of the superblock are stored in block groups throughout
-the filesystem. The first revision of ext2 stores a copy at the start
-of every block group. Later revisions can store a copy in only some
-block groups to reduce the amount of redundancy on large filesystems.
-The groups chosen are 0, 1 and powers of 3, 5 and 7.
-
-The information in the superblock contains fields such as how many
-inodes and blocks are in the filesystem and how many are unused, how
-many inodes and blocks are in a block group, when the filesystem was
-mounted, when it was modified, what version of the filesystem it is
-(see the Revisions section below) and which OS created it.
-
-If the revision of the filesystem is recent enough then there are extra
-fields, such as a volume name, a unique identifier, the inode size,
-support for compression, block preallocation and creating fewer backup
-superblocks.
+the filing system. The primary copy of the superblock is stored at an
+offset of 1024 bytes from the start of the device, and it is essential
+to mounting the filesystem. Since it is so important, backup copies of
+the superblock are stored in block groups throughout the filesystem.
+The first version of ext2 (revision 0) stores a copy at the start of
+every block group, along with backups of the group descriptor block(s).
+Because this can consume a considerable amount of space for large
+filesystems, later revisions can optionally reduce the number of backup
+copies by only putting backups in specific groups (this is the sparse
+superblock feature). The groups chosen are 0, 1 and powers of 3, 5 and 7.
+
+The information in the superblock contains fields such as the total
+number of inodes and blocks in the filesystem and how many are free,
+how many inodes and blocks are in each block group, when the filesystem
+was mounted (and if it was cleanly unmounted), when it was modified,
+what version of the filesystem it is (see the Revisions section below)
+and which OS created it.
+
+If the filesystem is revision 1 or higher, then there are extra fields,
+such as a volume name, a unique identification number, the inode size,
+and space for optional filesystem features to store configuration info.
All fields in the superblock (as in all other ext2 structures) are stored
on the disc in little endian format, so a filesystem is portable between
Inodes
------
-The inode (index node) is the fundamental concept in the ext2 filesystem.
+The inode (index node) is a fundamental concept in the ext2 filesystem.
Each object in the filesystem is represented by an inode. The inode
structure contains pointers to the filesystem blocks which contain the
data held in the object and all of the metadata about an object except
its name. The metadata about an object includes the permissions, owner,
group, flags, size, number of blocks used, access time, change time,
modification time, deletion time, number of links, fragments, version
-(for NFS) and ACLs.
-
-There are several reserved fields which are currently unused in the inode
-structure and several which are overloaded. One field is used for the
-directory ACL if the inode is a directory and for the top 32 bits of
-the file size if the inode is a regular file. The translator field is
-unused under Linux, but is used by the HURD to reference the inode of
-a program which will be used to interpret this object. The HURD also
-has larger permissions, owner and group fields, so it uses some of the
-other unused by Linux fields to store the extra bits.
+(for NFS) and extended attributes (EAs) and/or Access Control Lists (ACLs).
+
+There are some reserved fields which are currently unused in the inode
+structure and several which are overloaded. One field is reserved for the
+directory ACL if the inode is a directory and alternately for the top 32
+bits of the file size if the inode is a regular file (allowing file sizes
+larger than 2GB). The translator field is unused under Linux, but is used
+by the HURD to reference the inode of a program which will be used to
+interpret this object. Most of the remaining reserved fields have been
+used up for both Linux and the HURD for larger owner and group fields,
+The HURD also has a larger mode field so it uses another of the remaining
+fields to store the extra more bits.
There are pointers to the first 12 blocks which contain the file's data
in the inode. There is a pointer to an indirect block (which contains
trebly-indirect block (which contains pointers to doubly-indirect blocks).
The flags field contains some ext2-specific flags which aren't catered
-for by the standard chmod flags. These flags can be listed with
-lsattr and changed with the chattr command. There are flags for secure
-deletion, undeletable, compression, synchronous updates, immutability,
-append-only, dumpable, no-atime, and btree directories. Not all of
-these are supported yet.
+for by the standard chmod flags. These flags can be listed with lsattr
+and changed with the chattr command, and allow specific filesystem
+behaviour on a per-file basis. There are flags for secure deletion,
+undeletable, compression, synchronous updates, immutability, append-only,
+dumpable, no-atime, indexed directories, and data-journaling. Not all
+of these are supported yet.
Directories
-----------
It is a specially formatted file containing records which associate
each name with an inode number. Later revisions of the filesystem also
encode the type of the object (file, directory, symlink, device, fifo,
-socket) in the directory entry for speed. The current implementation
-of ext2 uses a linked list in directories; a planned enhancement will
-use btrees instead. The current implementation also never shrinks
-directories once they have grown to accommodate more files.
+socket) to avoid the need to check the inode itself for this information
+(support for taking advantage of this feature does not yet exist in
+Glibc 2.2).
+
+The inode allocation code tries to assign inodes which are in the same
+block group as the directory in which they are first created.
+
+The current implementation of ext2 uses a singly-linked list to store
+the filenames in the directory; a pending enhancement uses hashing of the
+filenames to allow lookup without the need to scan the entire directory.
+
+The current implementation never removes empty directory blocks once they
+have been allocated to hold more files.
Special files
-------------
Symbolic links are also filesystem objects with inodes. They deserve
special mention because the data for them is stored within the inode
itself if the symlink is less than 60 bytes long. It uses the fields
-which would normally be used to store the pointers to blocks to store
-the data. This is a worthwhile optimisation to make as it does not then
-take up a block, and most symlinks are less than 60 characters long.
+which would normally be used to store the pointers to data blocks.
+This is a worthwhile optimisation as it we avoid allocating a full
+block for the symlink, and most symlinks are less than 60 characters long.
Character and block special devices never have data blocks assigned to
them. Instead, their device number is stored in the inode, again reusing
-the fields which would be used to point to the blocks.
-
-Revisions
----------
-
-The revisioning mechanism used in ext2 is sophisticated. The revisioning
-mechanism is not supported by version 0 (EXT2_GOOD_OLD_REV) of ext2 but
-was introduced in version 1. There are three 32-bit fields, one for
-compatible features, one for read-only compatible features and one for
-incompatible features.
+the fields which would be used to point to the data blocks.
Reserved Space
--------------
In ext2, there is a mechanism for reserving a certain number of blocks
for a particular user (normally the super-user). This is intended to
-allow for the system to continue functioning even if a user fills up
-all the available space. It also keeps the filesystem from filling up
-entirely which helps combat fragmentation.
+allow for the system to continue functioning even if non-priveleged users
+fill up all the space available to them (this is independent of filesystem
+quotas). It also keeps the filesystem from filling up entirely which
+helps combat fragmentation.
Filesystem check
----------------
filesystems. The superblock of the ext2 filesystem contains several
fields which indicate whether fsck should actually run (since checking
the filesystem at boot can take a long time if it is large). fsck will
-run if the filesystem was not unmounted without errors, if the maximum
-mount count has been exceeded or if the maximum time between checks has
-been exceeded.
+run if the filesystem was not cleanly unmounted, if the maximum mount
+count has been exceeded or if the maximum time between checks has been
+exceeded.
+
+Feature Compatibility
+---------------------
+
+The compatibility feature mechanism used in ext2 is sophisticated.
+It safely allows features to be added to the filesystem, without
+unnecessarily sacrificing compatibility with older versions of the
+filesystem code. The feature compatibility mechanism is not supported by
+the original revision 0 (EXT2_GOOD_OLD_REV) of ext2, but was introduced in
+revision 1. There are three 32-bit fields, one for compatible features
+(COMPAT), one for read-only compatible (RO_COMPAT) features and one for
+incompatible (INCOMPAT) features.
+
+These feature flags have specific meanings for the kernel as follows:
+
+A COMPAT flag indicates that a feature is present in the filesystem,
+but the on-disk format is 100% compatible with older on-disk formats, so
+a kernel which didn't know anything about this feature could read/write
+the filesystem without any chance of corrupting the filesystem (or even
+making it inconsistent). This is essentially just a flag which says
+"this filesystem has a (hidden) feature" that the kernel or e2fsck may
+want to be aware of (more on e2fsck and feature flags later). The ext3
+HAS_JOURNAL feature is a COMPAT flag because the ext3 journal is simply
+a regular file with data blocks in it so the kernel does not need to
+take any special notice of it if it doesn't understand ext3 journaling.
+
+An RO_COMPAT flag indicates that the on-disk format is 100% compatible
+with older on-disk formats for reading (i.e. the feature does not change
+the visible on-disk format). However, an old kernel writing to such a
+filesystem would/could corrupt the filesystem, so this is prevented. The
+most common such feature, SPARSE_SUPER, is an RO_COMPAT feature because
+sparse groups allow file data blocks where superblock/group descriptor
+backups used to live, and ext2_free_blocks() refuses to free these blocks,
+which would leading to inconsistent bitmaps. An old kernel would also
+get an error if it tried to free a series of blocks which crossed a group
+boundary, but this is a legitimate layout in a SPARSE_SUPER filesystem.
+
+An INCOMPAT flag indicates the on-disk format has changed in some
+way that makes it unreadable by older kernels, or would otherwise
+cause a problem if an old kernel tried to mount it. FILETYPE is an
+INCOMPAT flag because older kernels would think a filename was longer
+than 256 characters, which would lead to corrupt directory listings.
+The COMPRESSION flag is an obvious INCOMPAT flag - if the kernel
+doesn't understand compression, you would just get garbage back from
+read() instead of it automatically decompressing your data. The ext3
+RECOVER flag is needed to prevent a kernel which does not understand the
+ext3 journal from mounting the filesystem without replaying the journal.
+
+For e2fsck, it needs to be more strict with the handling of these
+flags than the kernel. If it doesn't understand ANY of the COMPAT,
+RO_COMPAT, or INCOMPAT flags it will refuse to check the filesystem,
+because it has no way of verifying whether a given feature is valid
+or not. Allowing e2fsck to succeed on a filesystem with an unknown
+feature is a false sense of security for the user. Refusing to check
+a filesystem with unknown features is a good incentive for the user to
+update to the latest e2fsck. This also means that anyone adding feature
+flags to ext2 also needs to update e2fsck to verify these features.
Metadata
--------
respective fsck programs.
If you're exceptionally paranoid, there are 3 ways of making metadata
-writes synchronous:
+writes synchronous on ext2:
-per-file if you have the source: use the O_SYNC argument to open()
-per-file if you don't have the source: use chattr +S
-per-filesystem: mount -o sync
+per-file if you have the program source: use the O_SYNC flag to open()
+per-file if you don't have the source: use "chattr +S" on the file
+per-filesystem: add the "sync" option to mount (or in /etc/fstab)
the first and last are not ext2 specific but do force the metadata to
-be written synchronously.
+be written synchronously. See also Journaling below.
+
+Limitations
+-----------
+
+There are various limits imposed by the on-disk layout of ext2. Other
+limits are imposed by the current implementation of the kernel code.
+Many of the limits are determined at the time the filesystem is first
+created, and depend upon the block size chosen. The ratio of inodes to
+data blocks is fixed at filesystem creation time, so the only way to
+increase the number of inodes is to increase the size of the filesystem.
+No tools currently exist which can change the ratio of inodes to blocks.
+
+Most of these limits could be overcome with slight changes in the on-disk
+format and using a compatibility flag to signal the format change (at
+the expense of some compatibility).
+
+Filesystem block size: 1kB 2kB 4kB 8kB
+
+File size limit: 16GB 256GB 2048GB 2048GB
+Filesystem size limit: 2047GB 8192GB 16384GB 32768GB
+
+There is a 2.4 kernel limit of 2048GB for a single block device, so no
+filesystem larger than that can be created at this time. There is also
+an upper limit on the block size imposed by the page size of the kernel,
+so 8kB blocks are only allowed on Alpha systems (and other architectures
+which support larger pages).
+
+There is a "soft" upper limit of about 10-15k files in a single directory
+with the current linear linked-list directory implementation. This limit
+stems from performance problems when creating and deleting (and also
+finding) files in such large directories. Using a hashed directory index
+(under development) allows 100k-1M+ files in a single directory without
+performance problems (although RAM size becomes an issue at this point).
+
+The (meaningless) absolute upper limit of files in a single directory
+(imposed by the file size, the realistic limit is obviously much less)
+is over 130 trillion files. It would be higher except there are not
+enough 4-character names to make up unique directory entries, so they
+have to be 8 character filenames, even then we are fairly close to
+running out of unique filenames.
+
+Journaling
+----------
+
+A journaling extension to the ext2 code has been developed by Stephen
+Tweedie. It avoids the risks of metadata corruption and the need to
+wait for e2fsck to complete after a crash, without requiring a change
+to the on-disk ext2 layout. In a nutshell, the journal is a regular
+file which stores whole metadata (and optionally data) blocks that have
+been modified, prior to writing them into the filesystem. This means
+it is possible to add a journal to an existing ext2 filesystem without
+the need for data conversion.
+
+When changes to the filesystem (e.g. a file is renamed) they are stored in
+a transaction in the journal and can either be complete or incomplete at
+the time of a crash. If a transaction is complete at the time of a crash
+(or in the normal case where the system does not crash), then any blocks
+in that transaction are guaranteed to represent a valid filesystem state,
+and are copied into the filesystem. If a transaction is incomplete at
+the time of the crash, then there is no guarantee of consistency for
+the blocks in that transaction so they are discarded (which means any
+filesystem changes they represent are also lost).
+
+The ext3 code is currently (Apr 2001) available for 2.2 kernels only,
+and not yet available for 2.4 kernels.
References
==========
The kernel source file:/usr/src/linux/fs/ext2/
-Design & Implementation http://khg.redhat.com/HyperNews/get/fs/ext2intro.html
-Compression http://debs.fuller.edu/e2compr/
-ACL support ftp://tsx-11.mit.edu/pub/linux/ALPHA/ext2fs
-updated ACL work http://aerobee.informatik.uni-bremen.de/acl_eng.html
-e2fsprogs ftp://tsx-11.mit.edu/pub/linux/packages/ext2fs
+e2fsprogs (e2fsck) http://e2fsprogs.sourceforge.net/
+Design & Implementation http://e2fsprogs.sourceforge.net/ext2intro.html
+Journaling (ext3) ftp://ftp.uk.linux.org/pub/linux/sct/fs/jfs/
+Hashed Directories http://kernelnewbies.org/~phillips/htree/
+Filesystem Resizing http://ext2resize.sourceforge.net/
+Extended Attributes &
+Access Control Lists http://acl.bestbits.at/
+Compression (*) http://www.netspace.net.au/~reiter/e2compr/
Implementations for:
+Windows 95/98/NT/2000 http://uranus.it.swin.edu.au/~jn/linux/Explore2fs.htm
+Windows 95 (*) http://www.yipton.demon.co.uk/content.html#FSDEXT2
+DOS client (*) ftp://metalab.unc.edu/pub/Linux/system/filesystems/ext2/
OS/2 http://perso.wanadoo.fr/matthieu.willm/ext2-os2/
-Windows 95 http://www.yipton.demon.co.uk/
-Windows NT http://www.cyco.nl/~andreys/ext2fsnt/
- http://uranus.it.swin.edu.au/~jn/linux/Explore2fs.htm
-DOS client ftp://metalab.unc.edu/pub/Linux/system/filesystems/ext2/
RISC OS client ftp://ftp.barnet.ac.uk/pub/acorn/armlinux/iscafs/
+
+(*) no longer actively developed/supported (as of Apr 2001)
--- /dev/null
+README for arch/mips/gt64120 directory and subdirectories
+
+Jun Sun, jsun@mvista.com or jsun@junsun.net
+01/27, 2001
+
+MOTIVATION
+----------
+
+Many MIPS boards share the same system controller (or CPU companian chip),
+such as GT-64120. It is highly desirable to let these boards share
+the same controller code instead of duplicating them.
+
+This directory is meant to hold all MIPS boards that use GT-64120 or GT-64120A.
+
+
+HOW TO ADD A BOARD
+------------------
+
+. Create a subdirectory include/asm/gt64120/<board>.
+
+. Create a file called gt64120_dep.h under that directory.
+
+. Modify include/asm/gt64120/gt64120.h file to include the new gt64120_dep.h
+ based on config options. The board-dep section is at the end of
+ include/asm/gt64120/gt64120.h file. There you can find all required
+ definitions include/asm/gt64120/<board>/gt64120_dep.h file must supply.
+
+. Create a subdirectory arch/mips/gt64120/<board> directory to hold
+ board specific routines.
+
+. The GT-64120 common code is supplied under arch/mips/gt64120/common directory.
+ It includes:
+ 1) arch/mips/gt64120/pci.c -
+ common PCI routine, include the top-level pcibios_init()
+ 2) arch/mips/gt64120/irq.c -
+ common IRQ routine, include the top-level do_IRQ()
+ [This part really belongs to arch/mips/kernel. jsun]
+ 3) arch/mips/gt64120/gt_irq.c -
+ common IRQ routines for GT-64120 chip. Currently it only handles
+ the timer interrupt.
+
+. Board-specific routines are supplied under arch/mips/gt64120/<board> dir.
+ 1) arch/mips/gt64120/<board>/pci.c - it provides bus fixup routine
+ 2) arch/mips/gt64120/<board>/irq.c - it provides enable/disable irqs
+ and board irq setup routine (irq_setup)
+ 3) arch/mips/gt64120/<board>/int-handler.S -
+ The first-level interrupt dispatching routine.
+ 4) a bunch of other "normal" stuff (setup, prom, dbg_io, reset, etc)
+
+. Follow other "normal" procedure to modify configuration files, etc.
+
+
+TO-DO LIST
+----------
+
+. Expand arch/mips/gt64120/gt_irq.c to handle all GT-64120 interrupts.
+ We probably need to introduce GT_IRQ_BASE in board-dep header file,
+ which is used the starting irq_nr for all GT irqs.
+
+ A function, gt64120_handle_irq(), will be added so that the first-level
+ irq dispatcher will call this function if it detects an interrupt
+ from GT-64120.
+
+. More support for GT-64120 PCI features (2nd PCI bus, perhaps)
+
--- /dev/null
+README for MIPS time services
+
+Jun Sun
+jsun@mvista.com or jsun@junsun.net
+
+
+ABOUT
+-----
+This file describes the new arch/mips/kernel/time.c, related files and the
+services they provide.
+
+If you are short in patience and just want to know how to use time.c for a
+new board or convert an existing board, go to the last section.
+
+
+FILES, COMPATABILITY AND CONFIGS
+---------------------------------
+
+The old arch/mips/kernel/time.c is renamed to old-time.c.
+
+A new time.c is put there, together with include/asm-mips/time.h.
+
+Two configs variables are introduced, CONFIG_OLD_TIME_C and CONFIG_NEW_TIME_C.
+So we allow boards using
+
+ 1) old time.c (CONFIG_OLD_TIME_C)
+ 2) new time.c (CONFIG_NEW_TIME_C)
+ 3) neither (their own private time.c)
+
+However, it is expected every board will move to the new time.c in the near
+future.
+
+
+WHAT THE NEW CODE PROVIDES?
+---------------------------
+
+The new time code provide the following services:
+
+ a) Implements functions required by Linux common code:
+ time_init
+ do_gettimeofday
+ do_settimeofday
+
+ b) provides an abstraction of RTC and null RTC implementation as default.
+ extern unsigned long (*rtc_get_time)(void);
+ extern int (*rtc_set_time)(unsigned long);
+
+ c) a set of gettimeoffset functions for different CPUs and different
+ needs.
+
+ d) high-level and low-level timer interrupt routines where the timer
+ interrupt source may or may not be the CPU timer. The high-level
+ routine is dispatched through do_IRQ() while the low-level is
+ dispatched in assemably code (usually int-handler.S)
+
+
+WHAT THE NEW CODE REQUIRES?
+---------------------------
+
+For the new code to work properly, each board implementation needs to supply
+the following functions or values:
+
+ a) board_time_init - a function pointer. Invoked at the beginnig of
+ time_init(). It is optional.
+ 1. (optional) set up RTC routines
+ 2. (optional) calibrate and set the mips_counter_frequency
+
+ b) board_timer_setup - a function pointer. Invoked at the end of time_init()
+ 1. (optional) over-ride any decisions made in time_init()
+ 2. set up the irqaction for timer interrupt.
+ 3. enable the timer interrupt
+
+ c) (optional) board-specific RTC routines.
+
+ d) (optional) mips_counter_frequency - It must be definied if the board
+ is using CPU counter for timer interrupt or it is using fixed rate
+ gettimeoffset().
+
+
+PORTING GUIDE
+-------------
+
+Step 1: decide how you like to implement the time services.
+
+ a) does this board have a RTC? If yes, implement the two RTC funcs.
+
+ b) does the CPU have counter/compare registers?
+
+ If the answer is no, you need a timer to provide the timer interrupt
+ at 100 HZ speed.
+
+ You cannot use the fast gettimeoffset functions, i.e.,
+
+ unsigned long fixed_rate_gettimeoffset(void);
+ unsigned long calibrate_div32_gettimeoffset(void);
+ unsigned long calibrate_div64_gettimeoffset(void);
+
+ You can use null_gettimeoffset() will gives the same time resolution as
+ jiffy. Or you can implement your own gettimeoffset (probably based on
+ some ad hoc hardware on your machine.)
+
+ c) The following sub steps assume your CPU has counter register.
+ Do you plan to use the CPU counter register as the timer interrupt
+ or use an exnternal timer?
+
+ In order to CPU counter register as the timer interrupt source, you must
+ know the counter speed (mips_counter_frequency). It is usually the
+ same as the CPU speed (Or it is ALWAYS the same?)
+
+ d) decide on whether you want to use high-level or low-level timer
+ interrupt routines. The low-level one is presumably faster, but should
+ not make too mcuh difference.
+
+
+Step 2: the machine setup() function
+
+ If you supply board_time_init(), set the function poointer.
+
+ Set the function pointer board_timer_setup() (mandatory)
+
+
+Step 3: implement rtc routines, board_time_init() and board_timer_setup()
+ if needed.
+
+ board_time_init() -
+ a) (optional) set up RTC routines,
+ b) (optional) calibrate and set the mips_counter_frequency
+ (only needed if you intended to use fixed_rate_gettimeoffset
+ or use cpu counter as timer interrupt source)
+
+ board_timer_setup() -
+ a) (optional) over-write any choices made above by time_init().
+ b) machine specific code should setup the timer irqaction.
+ c) enable the timer interrupt
+
+
+ If the RTC chip is a common chip, I suggest the routines are put under
+ arch/mips/libs. For example, for DS1386 chip, one would create
+ rtc-ds1386.c under arch/mips/lib directory. Add the following line to
+ the arch/mips/lib/Makefile:
+
+ obj-$(CONFIG_DDB5476) += rtc-ds1386.o
+
+Step 4: if you are using low-level timer interrupt, change your interrupt
+ dispathcing code to check for timer interrupt and jump to
+ ll_timer_interrupt() directly if one is detected.
+
+Step 5: Modify arch/mips/config.in and add CONFIG_NEW_TIME_C to your machine.
+ Modify the appropriate defconfig if applicable.
+
+Final notes:
+
+For some tricky cases, you may need to add your own wrapper functions
+for some of the functions in time.c.
+
+For example, you may define your own timer interrupt routine, which does
+its own processing and in turn calls timer_interrupt().
+
+You can also over-ride any of the built-in functions (gettimeoffset,
+RTC routines and/or timer interrupt routine).
+
VERSION = 2
PATCHLEVEL = 4
SUBLEVEL = 4
-EXTRAVERSION =-pre5
+EXTRAVERSION =-pre6
KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
EXPORT_SYMBOL(down_interruptible);
EXPORT_SYMBOL(down_trylock);
EXPORT_SYMBOL(up);
-EXPORT_SYMBOL(__down_read_failed);
-EXPORT_SYMBOL(__down_write_failed);
-EXPORT_SYMBOL(__rwsem_wake);
EXPORT_SYMBOL(down_read);
EXPORT_SYMBOL(down_write);
EXPORT_SYMBOL(up_read);
define_bool CONFIG_SBUS n
define_bool CONFIG_UID16 y
-define_bool CONFIG_RWSEM_GENERIC_SPINLOCK n
-define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM y
mainmenu_option next_comment
comment 'Code maturity level options'
define_bool CONFIG_X86_CMPXCHG n
define_bool CONFIG_X86_XADD n
define_int CONFIG_X86_L1_CACHE_SHIFT 4
+ define_bool CONFIG_RWSEM_GENERIC_SPINLOCK y
+ define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM n
else
define_bool CONFIG_X86_WP_WORKS_OK y
define_bool CONFIG_X86_INVLPG y
define_bool CONFIG_X86_XADD y
define_bool CONFIG_X86_BSWAP y
define_bool CONFIG_X86_POPAD_OK y
+ define_bool CONFIG_RWSEM_GENERIC_SPINLOCK n
+ define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM y
fi
if [ "$CONFIG_M486" = "y" ]; then
define_int CONFIG_X86_L1_CACHE_SHIFT 4
rep
movsl
1:
-#ifdef CONFIG_SMP
checkCPUtype:
-#endif
movl $-1,X86_CPUID # -1 for no CPUID initially
orl $2,%eax # set MP
2: movl %eax,%cr0
call check_x87
-#ifdef CONFIG_SMP
incb ready
-#endif
lgdt gdt_descr
lidt idt_descr
ljmp $(__KERNEL_CS),$1f
jmp L6 # main should never return here, but
# just in case, we know what happens.
-#ifdef CONFIG_SMP
ready: .byte 0
-#endif
/*
* We depend on ET to be correct. This checks for 287/387.
EXPORT_SYMBOL_NOVERS(__down_failed_interruptible);
EXPORT_SYMBOL_NOVERS(__down_failed_trylock);
EXPORT_SYMBOL_NOVERS(__up_wakeup);
+#ifdef CONFIG_RWSEM_XCHGADD_ALGORITHM
EXPORT_SYMBOL_NOVERS(__rwsem_down_write_failed);
EXPORT_SYMBOL_NOVERS(__rwsem_down_read_failed);
EXPORT_SYMBOL_NOVERS(__rwsem_wake);
+#endif
/* Networking helper routines. */
EXPORT_SYMBOL(csum_partial_copy_generic);
/* Delay loops */
d->irq = 9;
}
-static void __init pci_fixup_vt8363(struct pci_dev *d)
-{
- /*
- * The VIA bridge will corrupt disks without these settings.
- */
- u8 tmp;
- pci_read_config_byte(d, 0x54, &tmp);
- if(tmp & (1<<2)) {
- printk("PCI: Bus master Pipeline request disabled\n");
- pci_write_config_byte(d, 0x54, tmp & ~(1<<2));
- }
- pci_read_config_byte(d, 0x70, &tmp);
- if(tmp & (1<<3)) {
- printk("PCI: Disabled enhanced CPU to PCI writes\n");
- pci_write_config_byte(d, 0x70, tmp & ~(1<<3));
- }
- pci_read_config_byte(d, 0x71, &tmp);
- if((tmp & (1<<3)) == 0) {
- printk("PCI: Bursting cornercase bug worked around\n");
- pci_write_config_byte(d, 0x71, tmp | (1<<3));
- }
- pci_read_config_byte(d, 0x76, &tmp);
- if(tmp & (1<<7)) {
- printk("PCI: Post Write Fail set to Retry\n");
- pci_write_config_byte(d, 0x76, tmp & ~(1<<7));
- }
-}
-
static void __init pci_fixup_via691(struct pci_dev *d)
{
/*
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5598, pci_fixup_latency },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3, pci_fixup_via_acpi },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4, pci_fixup_via_acpi },
- { PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8363_0, pci_fixup_vt8363 },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C691, pci_fixup_via691 },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C598_1, pci_fixup_via691_2 },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, pci_fixup_piix4_acpi },
* Zap initial low-memory mappings.
*
* Note that "pgd_clear()" doesn't do it for
- * us in this case, because pgd_clear() is a
- * no-op in the 2-level case (pmd_clear() is
- * the thing that clears the page-tables in
- * that case).
+ * us, because pgd_clear() is a no-op on i386.
*/
for (i = 0; i < USER_PTRS_PER_PGD; i++)
#if CONFIG_X86_PAE
- pgd_clear(swapper_pg_dir+i);
+ set_pgd(swapper_pg_dir+i, __pgd(1 + __pa(empty_zero_page)));
#else
set_pgd(swapper_pg_dir+i, __pgd(0));
#endif
#if defined(CONFIG_S390_TAPE) && defined(CONFIG_S390_TAPE_CHAR)
tapechar_init();
#endif
-#if defined(CONFIG_S390_TAPE) && defined(CONFIG_S390_TAPE_CHAR)
- tapechar_init();
-#endif
#if defined(CONFIG_ADB)
adbdev_init();
#endif
* Can we trust the reported IRQ?
*/
pciirq = dev->irq;
+
+ if (dev->class >> 8 == PCI_CLASS_STORAGE_RAID)
+ {
+ /* By rights we want to ignore these, but the Promise Fastrak
+ people have some strange ideas about proprietary so we have
+ to act otherwise on those. The supertrak however we need
+ to skip */
+ if (IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20265))
+ {
+ printk(KERN_INFO "ide: Found promise 20265 in RAID mode.\n");
+ if(dev->bus->self->vendor == PCI_VENDOR_ID_INTEL &&
+ dev->bus->self->device == PCI_DEVICE_ID_INTEL_I960)
+ {
+ printk(KERN_INFO "ide: Skipping Promise PDC20265 attached to I2O RAID controller.\n");
+ return;
+ }
+ }
+ /* Its attached to something else, just a random bridge.
+ Suspect a fastrak and fall through */
+ }
if ((dev->class & ~(0xfa)) != ((PCI_CLASS_STORAGE_IDE << 8) | 5)) {
printk("%s: not 100%% native mode: will probe irqs later\n", d->name);
/*
"QUANTUM FIREBALLlct08 08",
"QUANTUM FIREBALLP KA6.4",
"QUANTUM FIREBALLP LM20.4",
+ "QUANTUM FIREBALLP KX20.5",
"QUANTUM FIREBALLP LM20.5",
NULL
};
dep_tristate ' AVM B1/M1/M2 PCMCIA support' CONFIG_ISDN_DRV_AVMB1_B1PCMCIA $CONFIG_ISDN_CAPI
dep_tristate ' AVM B1/M1/M2 PCMCIA cs module' CONFIG_ISDN_DRV_AVMB1_AVM_CS $CONFIG_ISDN_DRV_AVMB1_B1PCMCIA $CONFIG_PCMCIA
dep_tristate ' AVM T1/T1-B PCI support' CONFIG_ISDN_DRV_AVMB1_T1PCI $CONFIG_ISDN_CAPI $CONFIG_PCI
- dep_tristate ' AVM C4 support' CONFIG_ISDN_DRV_AVMB1_C4 $CONFIG_ISDN_CAPI $CONFIG_PCI
+ dep_tristate ' AVM C4/C2 support' CONFIG_ISDN_DRV_AVMB1_C4 $CONFIG_ISDN_CAPI $CONFIG_PCI
fi
# HYSDN
/*
- * $Id: avmcard.h,v 1.8 2000/10/10 17:44:19 kai Exp $
+ * $Id: avmcard.h,v 1.8.6.1 2001/04/20 02:41:59 keil Exp $
*
* Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
*
* $Log: avmcard.h,v $
+ * Revision 1.8.6.1 2001/04/20 02:41:59 keil
+ * changes from mainstream
+ *
* Revision 1.8 2000/10/10 17:44:19 kai
* changes from/for 2.2.18
*
avm_m2,
avm_t1isa,
avm_t1pci,
- avm_c4
+ avm_c4,
+ avm_c2
};
typedef struct avmcard_dmainfo {
/*
- * $Id: b1.c,v 1.20.6.3 2001/03/21 08:52:20 kai Exp $
+ * $Id: b1.c,v 1.20.6.4 2001/04/20 02:41:59 keil Exp $
*
* Common module for AVM B1 cards.
*
* (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
*
* $Log: b1.c,v $
+ * Revision 1.20.6.4 2001/04/20 02:41:59 keil
+ * changes from mainstream
+ *
* Revision 1.20.6.3 2001/03/21 08:52:20 kai
* merge from main branch: fix buffer for revision string (calle)
*
#include "capicmd.h"
#include "capiutil.h"
-static char *revision = "$Revision: 1.20.6.3 $";
+static char *revision = "$Revision: 1.20.6.4 $";
/* ------------------------------------------------------------- */
case avm_t1isa: s = "T1 ISA (HEMA)"; break;
case avm_t1pci: s = "T1 PCI"; break;
case avm_c4: s = "C4"; break;
+ case avm_c2: s = "C2"; break;
default: s = "???"; break;
}
len += sprintf(page+len, "%-16s %s\n", "type", s);
/*
- * $Id: b1dma.c,v 1.11.6.3 2001/03/21 08:52:21 kai Exp $
+ * $Id: b1dma.c,v 1.11.6.4 2001/04/20 02:41:59 keil Exp $
*
* Common module for AVM B1 cards that support dma with AMCC
*
* (c) Copyright 2000 by Carsten Paeth (calle@calle.in-berlin.de)
*
* $Log: b1dma.c,v $
+ * Revision 1.11.6.4 2001/04/20 02:41:59 keil
+ * changes from mainstream
+ *
* Revision 1.11.6.3 2001/03/21 08:52:21 kai
* merge from main branch: fix buffer for revision string (calle)
*
#include "capicmd.h"
#include "capiutil.h"
-static char *revision = "$Revision: 1.11.6.3 $";
+static char *revision = "$Revision: 1.11.6.4 $";
/* ------------------------------------------------------------- */
case avm_t1isa: s = "T1 ISA (HEMA)"; break;
case avm_t1pci: s = "T1 PCI"; break;
case avm_c4: s = "C4"; break;
+ case avm_c2: s = "C2"; break;
default: s = "???"; break;
}
len += sprintf(page+len, "%-16s %s\n", "type", s);
/*
- * $Id: b1pci.c,v 1.29.6.2 2001/03/21 08:52:21 kai Exp $
+ * $Id: b1pci.c,v 1.29.6.3 2001/04/20 02:41:59 keil Exp $
*
* Module for AVM B1 PCI-card.
*
* (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
*
* $Log: b1pci.c,v $
+ * Revision 1.29.6.3 2001/04/20 02:41:59 keil
+ * changes from mainstream
+ *
* Revision 1.29.6.2 2001/03/21 08:52:21 kai
* merge from main branch: fix buffer for revision string (calle)
*
#include "capilli.h"
#include "avmcard.h"
-static char *revision = "$Revision: 1.29.6.2 $";
+static char *revision = "$Revision: 1.29.6.3 $";
/* ------------------------------------------------------------- */
/*
- * $Id: c4.c,v 1.20.6.5 2001/03/21 08:52:21 kai Exp $
+ * $Id: c4.c,v 1.20.6.6 2001/04/20 02:41:59 keil Exp $
*
* Module for AVM C4 card.
*
* (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
*
* $Log: c4.c,v $
+ * Revision 1.20.6.6 2001/04/20 02:41:59 keil
+ * changes from mainstream
+ *
* Revision 1.20.6.5 2001/03/21 08:52:21 kai
* merge from main branch: fix buffer for revision string (calle)
*
#include "capilli.h"
#include "avmcard.h"
-static char *revision = "$Revision: 1.20.6.5 $";
+static char *revision = "$Revision: 1.20.6.6 $";
#undef CONFIG_C4_DEBUG
#undef CONFIG_C4_POLLDEBUG
/* ------------------------------------------------------------- */
+#ifndef PCI_DEVICE_ID_AVM_C2
+#define PCI_DEVICE_ID_AVM_C2 0x1100
+#endif
+/* ------------------------------------------------------------- */
static int suppress_pollack;
static struct pci_device_id c4_pci_tbl[] __initdata = {
{ PCI_VENDOR_ID_DEC,PCI_DEVICE_ID_DEC_21285, PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C4 },
+ { PCI_VENDOR_ID_DEC,PCI_DEVICE_ID_DEC_21285, PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C2 },
{ } /* Terminating entry */
};
MsgLen = _get_slice(&p, card->msgbuf);
DataB3Len = _get_slice(&p, card->databuf);
cidx = CAPIMSG_CONTROLLER(card->msgbuf)-card->cardnr;
- if (cidx > 3) cidx = 0;
+ if (cidx >= card->nlogcontr) cidx = 0;
ctrl = card->ctrlinfo[cidx].capi_ctrl;
if (MsgLen < 30) { /* not CAPI 64Bit */
ApplId = (unsigned) _get_word(&p);
MsgLen = _get_slice(&p, card->msgbuf);
cidx = CAPIMSG_CONTROLLER(card->msgbuf)-card->cardnr;
- if (cidx > 3) cidx = 0;
+ if (cidx >= card->nlogcontr) cidx = 0;
ctrl = card->ctrlinfo[cidx].capi_ctrl;
if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
NCCI = _get_word(&p);
WindowSize = _get_word(&p);
cidx = (NCCI&0x7f) - card->cardnr;
- if (cidx > 3) cidx = 0;
+ if (cidx >= card->nlogcontr) cidx = 0;
ctrl = card->ctrlinfo[cidx].capi_ctrl;
ctrl->new_ncci(ctrl, ApplId, NCCI, WindowSize);
if (NCCI != 0xffffffff) {
cidx = (NCCI&0x7f) - card->cardnr;
- if (cidx > 3) cidx = 0;
+ if (cidx >= card->nlogcontr) cidx = 0;
ctrl = card->ctrlinfo[cidx].capi_ctrl;
- ctrl->free_ncci(ctrl, ApplId, NCCI);
+ if (ctrl)
+ ctrl->free_ncci(ctrl, ApplId, NCCI);
} else {
for (cidx=0; cidx < 4; cidx++) {
ctrl = card->ctrlinfo[cidx].capi_ctrl;
- ctrl->appl_released(ctrl, ApplId);
+ if (ctrl)
+ ctrl->appl_released(ctrl, ApplId);
}
}
break;
queue_pollack(card);
for (cidx=0; cidx < 4; cidx++) {
ctrl = card->ctrlinfo[cidx].capi_ctrl;
- ctrl->resume_output(ctrl);
+ if (ctrl)
+ ctrl->resume_output(ctrl);
}
break;
case RECEIVE_STOP:
for (cidx=0; cidx < 4; cidx++) {
ctrl = card->ctrlinfo[cidx].capi_ctrl;
- ctrl->suspend_output(ctrl);
+ if (ctrl)
+ ctrl->suspend_output(ctrl);
}
break;
case RECEIVE_INIT:
- cidx = card->nlogcontr++;
+ cidx = card->nlogcontr;
+ if (cidx >= 4 || !card->ctrlinfo[cidx].capi_ctrl) {
+ printk(KERN_ERR "%s: card with %d controllers ??\n",
+ card->name, cidx+1);
+ break;
+ }
+ card->nlogcontr++;
cinfo = &card->ctrlinfo[cidx];
ctrl = cinfo->capi_ctrl;
cinfo->versionlen = _get_slice(&p, cinfo->versionbuf);
if (status & DBELL_RESET_HOST) {
int i;
c4outmeml(card->mbase+PCI_OUT_INT_MASK, 0x0c);
+ if (card->nlogcontr == 0)
+ return;
printk(KERN_ERR "%s: unexpected reset\n", card->name);
for (i=0; i < 4; i++) {
avmctrl_info *cinfo = &card->ctrlinfo[i];
if (cinfo->capi_ctrl)
cinfo->capi_ctrl->reseted(cinfo->capi_ctrl);
}
+ card->nlogcontr = 0;
return;
}
if (cinfo->capi_ctrl)
cinfo->capi_ctrl->reseted(cinfo->capi_ctrl);
}
+ card->nlogcontr = 0;
}
static void c4_remove_ctr(struct capi_ctr *ctrl)
case avm_t1isa: s = "T1 ISA (HEMA)"; break;
case avm_t1pci: s = "T1 PCI"; break;
case avm_c4: s = "C4"; break;
+ case avm_c2: s = "C2"; break;
default: s = "???"; break;
}
len += sprintf(page+len, "%-16s %s\n", "type", s);
/* ------------------------------------------------------------- */
-static int c4_add_card(struct capi_driver *driver, struct capicardparams *p)
+static int c4_add_card(struct capi_driver *driver,
+ struct capicardparams *p,
+ int nr)
{
avmctrl_info *cinfo;
avmcard *card;
cinfo = &card->ctrlinfo[i];
cinfo->card = card;
}
- sprintf(card->name, "c4-%x", p->port);
+ sprintf(card->name, "%s-%x", driver->name, p->port);
card->port = p->port;
card->irq = p->irq;
card->membase = p->membase;
- card->cardtype = avm_c4;
+ card->cardtype = nr == 4 ? avm_c4 : avm_c2;
if (check_region(card->port, AVMB1_PORTLEN)) {
printk(KERN_WARNING
return -EBUSY;
}
- for (i=0; i < 4; i++) {
+ for (i=0; i < nr ; i++) {
cinfo = &card->ctrlinfo[i];
cinfo->card = card;
cinfo->capi_ctrl = di->attach_ctr(driver, card->name, cinfo);
skb_queue_head_init(&card->dma->send_queue);
printk(KERN_INFO
- "%s: AVM C4 at i/o %#x, irq %d, mem %#lx\n",
- driver->name, card->port, card->irq, card->membase);
+ "%s: AVM C%d at i/o %#x, irq %d, mem %#lx\n",
+ driver->name, nr, card->port, card->irq, card->membase);
return 0;
}
/* ------------------------------------------------------------- */
+static struct capi_driver c2_driver = {
+ name: "c2",
+ revision: "0.0",
+ load_firmware: c4_load_firmware,
+ reset_ctr: c4_reset_ctr,
+ remove_ctr: c4_remove_ctr,
+ register_appl: c4_register_appl,
+ release_appl: c4_release_appl,
+ send_message: c4_send_message,
+
+ procinfo: c4_procinfo,
+ ctr_read_proc: c4_read_proc,
+ driver_read_proc: 0, /* use standard driver_read_proc */
+
+ add_card: 0, /* no add_card function */
+};
+
static struct capi_driver c4_driver = {
name: "c4",
revision: "0.0",
static int ncards = 0;
-static int __init c4_init(void)
+static int c4_attach_driver (struct capi_driver * driver)
{
- struct capi_driver *driver = &c4_driver;
- struct pci_dev *dev = NULL;
char *p;
- int retval;
-
- MOD_INC_USE_COUNT;
-
if ((p = strchr(revision, ':')) != 0 && p[1]) {
strncpy(driver->revision, p + 2, sizeof(driver->revision));
driver->revision[sizeof(driver->revision)-1] = 0;
MOD_DEC_USE_COUNT;
return -ENODEV;
}
+ return 0;
+}
+
+static int __init search_cards(struct capi_driver * driver,
+ int pci_id, int nr)
+{
+ struct pci_dev * dev = NULL;
+ int retval = 0;
while ((dev = pci_find_subsys(
PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285,
- PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C4, dev))) {
+ PCI_VENDOR_ID_AVM, pci_id, dev))) {
struct capicardparams param;
if (pci_enable_device(dev) < 0) {
- printk(KERN_ERR "%s: failed to enable AVM-C4\n",
- driver->name);
+ printk(KERN_ERR "%s: failed to enable AVM-C%d\n",
+ driver->name, nr);
continue;
}
pci_set_master(dev);
param.membase = pci_resource_start(dev, 0);
printk(KERN_INFO
- "%s: PCI BIOS reports AVM-C4 at i/o %#x, irq %d, mem %#x\n",
- driver->name, param.port, param.irq, param.membase);
- retval = c4_add_card(driver, ¶m);
+ "%s: PCI BIOS reports AVM-C%d at i/o %#x, irq %d, mem %#x\n",
+ driver->name, nr, param.port, param.irq, param.membase);
+ retval = c4_add_card(driver, ¶m, nr);
if (retval != 0) {
printk(KERN_ERR
- "%s: no AVM-C4 at i/o %#x, irq %d detected, mem %#x\n",
- driver->name, param.port, param.irq, param.membase);
+ "%s: no AVM-C%d at i/o %#x, irq %d detected, mem %#x\n",
+ driver->name, nr, param.port, param.irq, param.membase);
continue;
}
ncards++;
}
+ return retval;
+}
+
+static int __init c4_init(void)
+{
+ int retval;
+
+ MOD_INC_USE_COUNT;
+
+ retval = c4_attach_driver (&c4_driver);
+ if (retval) {
+ MOD_DEC_USE_COUNT;
+ return retval;
+ }
+
+ retval = c4_attach_driver (&c2_driver);
+ if (retval) {
+ MOD_DEC_USE_COUNT;
+ return retval;
+ }
+
+ retval = search_cards(&c4_driver, PCI_DEVICE_ID_AVM_C4, 4);
+ if (retval && ncards == 0) {
+ detach_capi_driver(&c2_driver);
+ detach_capi_driver(&c4_driver);
+ MOD_DEC_USE_COUNT;
+ return retval;
+ }
+ retval = search_cards(&c2_driver, PCI_DEVICE_ID_AVM_C2, 2);
+ if (retval && ncards == 0) {
+ detach_capi_driver(&c2_driver);
+ detach_capi_driver(&c4_driver);
+ MOD_DEC_USE_COUNT;
+ return retval;
+ }
+
if (ncards) {
- printk(KERN_INFO "%s: %d C4 card(s) detected\n",
- driver->name, ncards);
+ printk(KERN_INFO "%s: %d C4/C2 card(s) detected\n",
+ c4_driver.name, ncards);
MOD_DEC_USE_COUNT;
return 0;
}
- printk(KERN_ERR "%s: NO C4 card detected\n", driver->name);
- detach_capi_driver(driver);
+ printk(KERN_ERR "%s: NO C4/C2 card detected\n", c4_driver.name);
+ detach_capi_driver(&c4_driver);
+ detach_capi_driver(&c2_driver);
MOD_DEC_USE_COUNT;
return -ENODEV;
}
static void __exit c4_exit(void)
{
+ detach_capi_driver(&c2_driver);
detach_capi_driver(&c4_driver);
}
# cards in the moment.
# Read ../../../Documentation/isdn/HiSax.cert for more informations.
#
-ca7bd9bac39203f3074f3f093948cc3c isac.c
-a2ad619fd404b3149099a2984de9d23c isdnl1.c
-d2a78e407f3d94876deac160c6f9aae6 isdnl2.c
-e7932ca7ae39c497c17f13a2e1434fcd isdnl3.c
-afb5f2f4ac296d6de45c856993b161e1 tei.c
-00023e2a482cb86a26ea870577ade5d6 callc.c
-a1834e9b2ec068440cff2e899eff4710 cert.c
-1551f78b3cd01097ecd586b5c96d0765 l3dss1.c
-89aecf3a80489c723dc885fcaa4aba1b l3_1tr6.c
-1685c1ddfecf3e1b88ae5f3d7202ce69 elsa.c
-6d7056d1558bf6cc57dd89b7b260dc27 diva.c
-4398918680d45c4618bb48108ea0c282 sedlbauer.c
+9663cc9f4374c361197d394f6d27c459 isac.c
+9666c672c0fa0e65d5cc5b322f10a18c isdnl1.c
+9250f15b932dfc36855aa120b896ed0b isdnl2.c
+0cc2ef892bdb4a2be473e00eb1398950 isdnl3.c
+cac9c32fff889c57ff50d59823053019 tei.c
+665044a72334336533ac79da1a831b17 callc.c
+e592db58630c1f1029cc064110108156 cert.c
+fadeb3b85bb23bc1ac48470c0848d6fa l3dss1.c
+cf7dec9fac6283716904d26b99188476 l3_1tr6.c
+65d9e5471bc129624f858ebcf0743525 elsa.c
+b4cf8a4dceed9ea6dcba65a85b4eecc7 diva.c
+99e67bea8f6945fa0d4e0aded5bf0fa0 sedlbauer.c
# end of md5sums
-----BEGIN PGP SIGNATURE-----
Version: 2.6.3i
Charset: noconv
-iQCVAwUBOlxeLTpxHvX/mS9tAQH6RwP8DhyvqAnXFV6WIGi16iQ3vKikkPoqnDQs
-GEn5uCW0dPYKlwthD2Grj/JbMYZhOmCFuDxF7ufJnjTSDe/D8XNe2wngxzAiwcIe
-WjCrT8X95cuP3HZHscbFTEinVV0GAnoI0ZEgs5eBDhVHDqILLYMaTFBQaRH3jgXc
-i5VH88jPfUM=
-=qc+J
+iQCVAwUBOt+j/jpxHvX/mS9tAQGXwAP/U4voKzXAcTfo9CqJhHN92GRxunj6mlvn
+H+1pxSe0GdtC7BlrPhrokB5dNSwewk89Z5t7kTD76kx2FFuTcXBJxbgH7LZVF3ga
+JX92bOWQekHMH7Hk12Qc7zpeTmPzY02pvVc37Eo614BCvJMCk02cpQyo8a5wWRKH
+8vpQkQKiSyY=
+=FFLG
-----END PGP SIGNATURE-----
-/* $Id: icn.c,v 1.65.6.3 2001/02/16 16:43:31 kai Exp $
+/* $Id: icn.c,v 1.65.6.4 2001/04/20 02:42:01 keil Exp $
* ISDN low-level module for the ICN active ISDN-Card.
*
#undef MAP_DEBUG
static char
-*revision = "$Revision: 1.65.6.3 $";
+*revision = "$Revision: 1.65.6.4 $";
static int icn_addcard(int, char *, char *);
card->other->rvalid = 1;
}
if (!dev.mvalid) {
- if (check_shmem((ulong) dev.shmem, 0x4000)) {
+ if (check_mem_region(dev.memaddr, 0x4000)) {
printk(KERN_WARNING
- "icn: memory at 0x%08lx in use.\n",
- (ulong) dev.shmem);
+ "icn: memory at 0x%08lx in use.\n", dev.memaddr);
restore_flags(flags);
return -EBUSY;
}
- request_shmem((ulong) dev.shmem, 0x4000, "icn");
+ request_mem_region(dev.memaddr, 0x4000, "icn-isdn (all cards)");
+ dev.shmem = ioremap(dev.memaddr, 0x4000);
dev.mvalid = 1;
}
restore_flags(flags);
OUTB_P(0, ICN_RUN); /* Reset Controller */
OUTB_P(0, ICN_MAPRAM); /* Disable RAM */
icn_shiftout(ICN_CFG, 0x0f, 3, 4); /* Windowsize= 16k */
- icn_shiftout(ICN_CFG, (unsigned long) dev.shmem, 23, 10); /* Set RAM-Addr. */
+ icn_shiftout(ICN_CFG, dev.memaddr, 23, 10); /* Set RAM-Addr. */
#ifdef BOOT_DEBUG
- printk(KERN_DEBUG "shmem=%08lx\n", (ulong) dev.shmem);
+ printk(KERN_DEBUG "shmem=%08lx\n", dev.memaddr);
#endif
SLEEP(1);
#ifdef BOOT_DEBUG
memcpy(&a, c->parm.num, sizeof(ulong));
switch (c->arg) {
case ICN_IOCTL_SETMMIO:
- if ((unsigned long) dev.shmem != (a & 0x0ffc000)) {
- if (check_shmem((ulong) (a & 0x0ffc000), 0x4000)) {
+ if (dev.memaddr != (a & 0x0ffc000)) {
+ if (check_mem_region(a & 0x0ffc000, 0x4000)) {
printk(KERN_WARNING
"icn: memory at 0x%08lx in use.\n",
- (ulong) (a & 0x0ffc000));
+ a & 0x0ffc000);
return -EINVAL;
}
icn_stopallcards();
save_flags(flags);
cli();
- if (dev.mvalid)
- release_shmem((ulong) dev.shmem, 0x4000);
+ if (dev.mvalid) {
+ iounmap(dev.shmem);
+ release_mem_region(dev.memaddr, 0x4000);
+ }
dev.mvalid = 0;
- dev.shmem = (icn_shmem *) (a & 0x0ffc000);
+ dev.memaddr = a & 0x0ffc000;
restore_flags(flags);
printk(KERN_INFO
"icn: (%s) mmio set to 0x%08lx\n",
CID,
- (unsigned long) dev.shmem);
+ dev.memaddr);
}
break;
case ICN_IOCTL_GETMMIO:
- return (long) dev.shmem;
+ return (long) dev.memaddr;
case ICN_IOCTL_SETPORT:
if (a == 0x300 || a == 0x310 || a == 0x320 || a == 0x330
|| a == 0x340 || a == 0x350 || a == 0x360 ||
if (ints[0])
portbase = ints[1];
if (ints[0] > 1)
- membase = ints[2];
+ membase = (unsigned long)ints[2];
if (str && *str) {
strcpy(sid, str);
icn_id = sid;
char rev[10];
memset(&dev, 0, sizeof(icn_dev));
- dev.shmem = (icn_shmem *) ((unsigned long) membase & 0x0ffc000);
+ dev.memaddr = (membase & 0x0ffc000);
dev.channel = -1;
dev.mcard = NULL;
dev.firstload = 1;
} else
strcpy(rev, " ??? ");
printk(KERN_NOTICE "ICN-ISDN-driver Rev%smem=0x%08lx\n", rev,
- (ulong) dev.shmem);
+ dev.memaddr);
return (icn_addcard(portbase, icn_id, icn_id2));
}
card = card->next;
kfree(last);
}
- if (dev.mvalid)
- release_shmem((ulong) dev.shmem, 0x4000);
+ if (dev.mvalid) {
+ iounmap(dev.shmem);
+ release_mem_region(dev.memaddr, 0x4000);
+ }
printk(KERN_NOTICE "ICN-ISDN-driver unloaded\n");
}
-/* $Id: icn.h,v 1.30.6.2 2001/02/16 16:43:31 kai Exp $
+/* $Id: icn.h,v 1.30.6.3 2001/04/20 02:42:01 keil Exp $
* ISDN lowlevel-module for the ICN active ISDN-Card.
*
* Main driver data
*/
typedef struct icn_dev {
+ unsigned long memaddr; /* Address of memory mapped buffers */
icn_shmem *shmem; /* Pointer to memory-mapped-buffers */
int mvalid; /* IO-shmem has been requested */
int channel; /* Currently mapped channel */
* integers.
*/
static int portbase = ICN_BASEADDR;
-static int membase = ICN_MEMADDR;
+static unsigned long membase = ICN_MEMADDR;
static char *icn_id = "\0";
static char *icn_id2 = "\0";
MODULE_AUTHOR("Fritz Elfert");
MODULE_PARM(portbase, "i");
MODULE_PARM_DESC(portbase, "Port address of first card");
-MODULE_PARM(membase, "i");
+MODULE_PARM(membase, "l");
MODULE_PARM_DESC(membase, "Shared memory address of all cards");
MODULE_PARM(icn_id, "s");
MODULE_PARM_DESC(icn_id, "ID-String of first card");
#define MIN(a,b) ((a<b)?a:b)
#define MAX(a,b) ((a>b)?a:b)
-/* Hopefully, a separate resource-registration-scheme for shared-memory
- * will be introduced into the kernel. Until then, we use the normal
- * routines, designed for port-registration.
- */
-#define check_shmem check_region
-#define release_shmem release_region
-#define request_shmem request_region
-
#endif /* defined(__KERNEL__) || defined(__DEBUGVAR__) */
#endif /* icn_h */
int rw,
struct buffer_head *bh)
{
- int ret = lvm_map(bh, rw);
- if (ret < 0)
- buffer_IO_error(bh);
- return ret;
+ if (lvm_map(bh, rw) >= 0)
+ return 1;
+
+ buffer_IO_error(bh);
+ return 0;
}
tristate 'PPP (point-to-point protocol) support' CONFIG_PPP
if [ ! "$CONFIG_PPP" = "n" ]; then
dep_bool ' PPP multilink support (EXPERIMENTAL)' CONFIG_PPP_MULTILINK $CONFIG_EXPERIMENTAL
+ bool ' PPP filtering' CONFIG_PPP_FILTER
dep_tristate ' PPP support for async serial ports' CONFIG_PPP_ASYNC $CONFIG_PPP
dep_tristate ' PPP support for sync tty ports' CONFIG_PPP_SYNC_TTY $CONFIG_PPP
dep_tristate ' PPP Deflate compression' CONFIG_PPP_DEFLATE $CONFIG_PPP
int __init atarilance_probe( struct net_device *dev )
{
- int i;
- static int found = 0;
+ int i;
+ static int found;
SET_MODULE_OWNER(dev);
struct lance_private *lp;
struct lance_ioreg *IO;
int i;
- static int did_version = 0;
+ static int did_version;
unsigned short save1, save2;
PROBE_PRINT(( "Probing for Lance card at mem %#lx io %#lx\n",
{
struct net_device *dev;
DFX_board_t *bp; /* board pointer */
- static int version_disp;
int err;
- if (!version_disp) /* display version info if adapter is found */
+#ifndef MODULE
+ static int version_disp;
+
+ if (!version_disp) /* display version info if adapter is found */
{
- version_disp = 1; /* set display flag to TRUE so that */
- printk(version); /* we only display this string ONCE */
+ version_disp = 1; /* set display flag to TRUE so that */
+ printk(version); /* we only display this string ONCE */
}
+#endif
/*
* init_fddidev() allocates a device structure with private data, clears the device structure and private data,
{
int rc_pci, rc_eisa;
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+ printk(version);
+#endif
+
rc_pci = pci_module_init(&dfx_driver);
if (rc_pci >= 0) dfx_have_pci = 1;
Resource usage cleanups.
Report driver version to user.
+ Tobias Ringstr÷m <zajbot@goteborg.utfors.se> :
+ Added additional proper locking
+ Rewrote the transmit code to actually use the ring buffer,
+ and to generate a lot fewer interrupts.
+
+ Frank Davis <fdavis112@juno.com>
+ Added SMP-safe locking mechanisms in addition to Andrew Morton's work
+
TODO
Implement pci_driver::suspend() and pci_driver::resume()
*/
-#define DMFE_VERSION "1.30 (June 11, 2000)"
+#define DMFE_VERSION "1.30p3 (April 17, 2001)"
#include <linux/module.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/delay.h>
+#include <linux/spinlock.h>
#include <asm/processor.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/dma.h>
+static char version[] __initdata =
+KERN_INFO "Davicom DM91xx net driver, version " DMFE_VERSION "\n";
+
/* Board/System/Debug information/definition ---------------- */
#define TX_MAX_SEND_CNT 0x1 /* Maximum tx packet per time */
#define TX_DESC_CNT 0x10 /* Allocated Tx descriptors */
#define RX_DESC_CNT 0x10 /* Allocated Rx descriptors */
+#define TX_IRQ_THR 12
#define DESC_ALL_CNT TX_DESC_CNT+RX_DESC_CNT
#define TX_BUF_ALLOC 0x600
#define RX_ALLOC_SIZE 0x620
u32 chip_id; /* Chip vendor/Device ID */
u32 chip_revision; /* Chip revision */
struct net_device *next_dev; /* next device */
-
+ spinlock_t lock; /* Spinlock */
struct pci_dev *net_dev; /* PCI device */
unsigned long ioaddr; /* I/O base address */
struct rx_desc *first_rx_desc;
struct rx_desc *rx_insert_ptr;
struct rx_desc *rx_ready_ptr; /* packet come pointer */
- u32 tx_packet_cnt; /* transmitted packet count */
- u32 tx_queue_cnt; /* wait to send packet count */
+ int tx_int_pkt_num; /* number of packets to transmit until
+ * a transmit interrupt is requested */
+ u32 tx_live_cnt; /* number of used/live tx slots */
u32 rx_avail_cnt; /* available rx descriptor count */
u32 interval_rx_cnt; /* rx packet count a callback time */
u8 link_failed; /* Ever link failed */
u8 wait_reset; /* Hardware failed, need to reset */
u8 in_reset_state; /* Now driver in reset routine */
- u8 rx_error_cnt; /* recievd abnormal case count */
+ u8 rx_error_cnt; /* recieved abnormal case count */
u8 dm910x_chk_mode; /* Operating mode check */
struct timer_list timer;
struct net_device_stats stats; /* statistic counter */
};
/* Global variable declaration ----------------------------- */
-static int dmfe_debug = 0;
+static int dmfe_debug;
static unsigned char dmfe_media_mode = 8;
-static u32 dmfe_cr6_user_set = 0;
+static u32 dmfe_cr6_user_set;
/* For module input parameter */
-static int debug = 0;
-static u32 cr6set = 0;
+static int debug;
+static u32 cr6set;
static unsigned char mode = 8;
static u8 chkmode = 1;
u32 dev_rev;
u16 pci_command;
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+ static int printed_version;
+ if (!printed_version++)
+ printk(version);
+#endif
+
DMFE_DBUG(0, "dmfe_probe()", 0);
/* Enable Master/IO access, Disable memory access */
/* Interrupt check */
if (pci_irqline == 0) {
- printk(KERN_ERR "dmfe: Interrupt wrong : IRQ=%d\n",
- pci_irqline);
+ printk(KERN_ERR "dmfe(%s): Interrupt wrong : IRQ=%d\n",
+ pdev->slot_name, pci_irqline);
goto err_out;
}
/* iobase check */
- if (pci_iobase == 0) {
- printk(KERN_ERR "dmfe: I/O base is zero\n");
+ if (pci_iobase == 0 || pci_resource_len(pdev, 0) == 0) {
+ printk(KERN_ERR "dmfe(%s): I/O base is zero\n", pdev->slot_name);
goto err_out;
}
pci_read_config_dword(pdev, PCI_REVISION_ID, &dev_rev);
/* Init network device */
- dev = init_etherdev(NULL, sizeof(*db));
+ dev = alloc_etherdev(sizeof(*db));
if (dev == NULL)
goto err_out;
SET_MODULE_OWNER(dev);
- /* IO range check */
- if (!request_region(pci_iobase, CHK_IO_SIZE(pdev, dev_rev), dev->name)) {
- printk(KERN_ERR "dmfe: I/O conflict : IO=%lx Range=%x\n",
- pci_iobase, CHK_IO_SIZE(pdev, dev_rev));
+ if (pci_request_regions(pdev, "dmfe"))
goto err_out_netdev;
- }
db = dev->priv;
pci_set_drvdata(pdev, dev);
+ spin_lock_init(&db->lock);
db->chip_id = ent->driver_data;
db->ioaddr = pci_iobase;
for (i = 0; i < 64; i++)
((u16 *) db->srom)[i] = read_srom_word(pci_iobase, i);
+ printk(KERN_INFO "%s: Davicom DM%04lx at 0x%lx,",
+ dev->name,
+ ent->driver_data >> 16,
+ pci_iobase);
+
/* Set Node address */
- for (i = 0; i < 6; i++)
+ for (i = 0; i < 6; i++) {
dev->dev_addr[i] = db->srom[20 + i];
+ printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]);
+ }
+
+ printk(", IRQ %d\n", pci_irqline);
+
+ i = register_netdev(dev);
+ if (i)
+ goto err_out_res;
return 0;
+err_out_res:
+ pci_release_regions(pdev);
err_out_netdev:
- unregister_netdev(dev);
kfree(dev);
err_out:
return -ENODEV;
db = dev->priv;
unregister_netdev(dev);
- release_region(dev->base_addr, CHK_IO_SIZE(pdev, db->chip_revision));
+ pci_release_regions(pdev);
kfree(dev); /* free board information */
pci_set_drvdata(pdev, NULL);
/* system variable init */
db->cr6_data = CR6_DEFAULT | dmfe_cr6_user_set;
- db->tx_packet_cnt = 0;
- db->tx_queue_cnt = 0;
+ db->tx_int_pkt_num = TX_IRQ_THR;
+ db->tx_live_cnt = 0;
db->rx_avail_cnt = 0;
db->link_failed = 0;
db->wait_reset = 0;
{
struct dmfe_board_info *db = dev->priv;
u32 ioaddr = db->ioaddr;
-
+
DMFE_DBUG(0, "dmfe_init_dm910x()", 0);
/* Reset DM910x board : need 32 PCI clock to complete */
{
struct dmfe_board_info *db = dev->priv;
struct tx_desc *txptr;
+ unsigned long flags;
DMFE_DBUG(0, "dmfe_start_xmit", 0);
-
- netif_stop_queue(dev);
+ spin_lock_irqsave(&db->lock, flags);
- /* Too large packet check */
- if (skb->len > MAX_PACKET_SIZE) {
- printk(KERN_ERR "%s: oversized frame (%d bytes) for transmit.\n", dev->name, (u16) skb->len);
- dev_kfree_skb(skb);
- return 0;
- }
- /* No Tx resource check, it never happen nromally */
- if (db->tx_packet_cnt >= TX_FREE_DESC_CNT) {
- return 1;
- }
-
/* transmit this packet */
txptr = db->tx_insert_ptr;
memcpy((char *) txptr->tx_buf_ptr, (char *) skb->data, skb->len);
- txptr->tdes1 = 0xe1000000 | skb->len;
+ if (--db->tx_int_pkt_num < 0)
+ {
+ txptr->tdes1 = 0xe1000000 | skb->len;
+ db->tx_int_pkt_num = TX_IRQ_THR;
+ }
+ else
+ txptr->tdes1 = 0x61000000 | skb->len;
+
+ /* Transmit Packet Process */
+ txptr->tdes0 = 0x80000000; /* set owner bit to DM910X */
+ outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling command */
+ dev->trans_start = jiffies; /* saved the time stamp */
/* Point to next transmit free descriptor */
- db->tx_insert_ptr = (struct tx_desc *) txptr->next_tx_desc;
+ txptr = (struct tx_desc *)txptr->next_tx_desc;
- /* Transmit Packet Process */
- if (db->tx_packet_cnt < TX_MAX_SEND_CNT) {
- txptr->tdes0 = 0x80000000; /* set owner bit to DM910X */
- db->tx_packet_cnt++; /* Ready to send count */
- outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling command */
- } else {
- db->tx_queue_cnt++; /* queue the tx packet */
- outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling command */
- }
+ if (txptr->tdes0 & 0x80000000)
+ netif_stop_queue(dev);
- /* Tx resource check */
- if (db->tx_packet_cnt < TX_FREE_DESC_CNT)
- netif_wake_queue(dev);
+ db->tx_insert_ptr = txptr;
+ db->tx_live_cnt++;
+
+ spin_unlock_irqrestore(&db->lock, flags);
/* free this SKB */
dev_kfree_skb(skb);
+
return 0;
}
/* deleted timer */
del_timer_sync(&db->timer);
-
+
/* free interrupt */
free_irq(dev->irq, dev);
DMFE_DBUG(0, "dmfe_interrupt()", 0);
+ spin_lock_irq(&db->lock);
+
/* Disable all interrupt in CR7 to solve the interrupt edge problem */
outl(0, ioaddr + DCR7);
netif_stop_queue(dev);
db->wait_reset = 1; /* Need to RESET */
outl(0, ioaddr + DCR7); /* disable all interrupt */
+ spin_unlock_irq(&db->lock);
return;
}
+
/* Free the transmitted descriptor */
txptr = db->tx_remove_ptr;
- while (db->tx_packet_cnt) {
+ while (db->tx_live_cnt > 0 && (txptr->tdes0 & 0x80000000) == 0)
+ {
/* printk("tdes0=%x\n", txptr->tdes0); */
- if (txptr->tdes0 & 0x80000000)
- break;
db->stats.tx_packets++;
if ((txptr->tdes0 & TDES0_ERR_MASK) && (txptr->tdes0 != 0x7fffffff)) {
db->stats.tx_errors++;
}
txptr = (struct tx_desc *) txptr->next_tx_desc;
- db->tx_packet_cnt--;
+ db->tx_live_cnt--;
}
/* Update TX remove pointer to next */
db->tx_remove_ptr = (struct tx_desc *) txptr;
- /* Send the Tx packet in queue */
- if ((db->tx_packet_cnt < TX_MAX_SEND_CNT) && db->tx_queue_cnt) {
- txptr->tdes0 = 0x80000000; /* set owner bit to DM910X */
- db->tx_packet_cnt++; /* Ready to send count */
- outl(0x1, ioaddr + DCR1); /* Issue Tx polling command */
- dev->trans_start = jiffies; /* saved the time stamp */
- db->tx_queue_cnt--;
- }
- /* Resource available check */
- if (db->tx_packet_cnt < TX_FREE_DESC_CNT)
+ if ((db->tx_insert_ptr->tdes0 & 0x80000000) == 0)
netif_wake_queue(dev);
/* Received the coming packet */
else
db->cr7_data = 0x1a2cd;
outl(db->cr7_data, ioaddr + DCR7);
+
+ spin_unlock_irq(&db->lock);
}
/*
/*
Process the upper socket ioctl command
*/
+
+/*
+ * The following function just returns 0. Shouldn't it do more?
+ */
+
static int dmfe_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
DMFE_DBUG(0, "dmfe_do_ioctl()", 0);
db->interval_rx_cnt = 0;
- if (db->wait_reset | (db->tx_packet_cnt &&
- ((jiffies - dev->trans_start) > DMFE_TX_TIMEOUT)) | (db->rx_error_cnt > 3)) {
+ if (db->wait_reset ||
+ (db->tx_live_cnt > 0 &&
+ ((jiffies - dev->trans_start) > DMFE_TX_TIMEOUT)) ||
+ (db->rx_error_cnt > 3)) {
/*
printk("wait_reset %x, tx cnt %x, rx err %x, time %x\n", db->wait_reset, db->tx_packet_cnt, db->rx_error_cnt, jiffies-dev->trans_start);
*/
- DMFE_DBUG(0, "Warn!! Warn!! Tx/Rx moniotr step1", db->tx_packet_cnt);
+ DMFE_DBUG(0, "Warn!! Warn!! Tx/Rx monitor step1", 0);
dmfe_dynamic_reset(dev);
db->timer.expires = jiffies + DMFE_TIMER_WUT;
add_timer(&db->timer);
dmfe_free_rxbuffer(db);
/* system variable init */
- db->tx_packet_cnt = 0;
- db->tx_queue_cnt = 0;
+ db->tx_int_pkt_num = TX_IRQ_THR;
+ db->tx_live_cnt = 0;
db->rx_avail_cnt = 0;
db->link_failed = 0;
db->wait_reset = 0;
struct dmfe_board_info *db = dev->priv;
struct dev_mc_list *mcptr;
struct tx_desc *txptr;
+ unsigned long flags;
u16 *addrptr;
u32 *suptr;
int i;
- DMFE_DBUG(0, "send_filetr_frame()", 0);
+ DMFE_DBUG(0, "send_filter_frame()", 0);
+ spin_lock_irqsave(&db->lock, flags);
txptr = db->tx_insert_ptr;
suptr = (u32 *) txptr->tx_buf_ptr;
+ if (txptr->tdes0 & 0x80000000) {
+ spin_unlock_irqrestore(&db->lock, flags);
+ printk(KERN_WARNING "%s: Too busy to send filter frame\n",
+ dev->name);
+ return;
+ }
+
/* Node address */
addrptr = (u16 *) dev->dev_addr;
*suptr++ = addrptr[0];
*suptr++ = addrptr[2];
}
- for (; i < 14; i++) {
+ for (i=0; i < 14; i++)
*suptr++ = 0xffff;
*suptr++ = 0xffff;
*suptr++ = 0xffff;
- }
+
/* prepare the setup frame */
db->tx_insert_ptr = (struct tx_desc *) txptr->next_tx_desc;
+ db->tx_live_cnt++;
txptr->tdes1 = 0x890000c0;
- /* Resource Check and Send the setup packet */
- if (!db->tx_packet_cnt) {
- /* Resource Empty */
- db->tx_packet_cnt++;
- txptr->tdes0 = 0x80000000;
- update_cr6(db->cr6_data | 0x2000, dev->base_addr);
- outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling command */
- update_cr6(db->cr6_data, dev->base_addr);
- } else {
- /* Put into TX queue */
- db->tx_queue_cnt++;
- }
+ /* Send the setup packet */
+ txptr->tdes0 = 0x80000000;
+ update_cr6(db->cr6_data | 0x2000, dev->base_addr);
+ outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling command */
+ update_cr6(db->cr6_data, dev->base_addr);
+ spin_unlock_irqrestore(&db->lock, flags);
}
/*
{
int rc;
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+ printk(version);
+#endif
+
DMFE_DBUG(0, "init_module() ", debug);
if (debug)
if (rc < 0)
return rc;
- printk (KERN_INFO "Davicom DM91xx net driver loaded, version "
- DMFE_VERSION "\n");
return 0;
}
*/
-/* These identify the driver base version and may not be removed. */
-static const char version[] =
-"epic100.c:v1.11 1/7/2001 Written by Donald Becker <becker@scyld.com>\n";
-static const char version2[] =
-" http://www.scyld.com/network/epic100.html\n";
-static const char version3[] =
-" (unofficial 2.4.x kernel port, version 1.1.6, January 11, 2001)\n";
-
/* The user-configurable values.
These may be modified when a driver module is loaded.*/
#include <asm/bitops.h>
#include <asm/io.h>
+/* These identify the driver base version and may not be removed. */
+static char version[] __devinitdata =
+"epic100.c:v1.11 1/7/2001 Written by Donald Becker <becker@scyld.com>\n";
+static char version2[] __devinitdata =
+" http://www.scyld.com/network/epic100.html\n";
+static char version3[] __devinitdata =
+" (unofficial 2.4.x kernel port, version 1.1.7, April 17, 2001)\n";
+
MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
MODULE_DESCRIPTION("SMC 83c170 EPIC series Ethernet driver");
MODULE_PARM(debug, "i");
const struct pci_device_id *ent)
{
static int card_idx = -1;
- static int printed_version;
long ioaddr;
int chip_idx = (int) ent->driver_data;
int irq;
void *ring_space;
dma_addr_t ring_dma;
- card_idx++;
-
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+ static int printed_version;
if (!printed_version++)
printk (KERN_INFO "%s" KERN_INFO "%s" KERN_INFO "%s",
version, version2, version3);
+#endif
+
+ card_idx++;
i = pci_enable_device(pdev);
if (i)
static int __init epic_init (void)
{
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+ printk (KERN_INFO "%s" KERN_INFO "%s" KERN_INFO "%s",
+ version, version2, version3);
+#endif
+
return pci_module_init (&epic_driver);
}
*/
-static const char *version =
-"hamachi.c:v1.01 5/16/2000 Written by Donald Becker\n"
-" Some modifications by Eric kasten <kasten@nscl.msu.edu>\n"
-" Further modifications by Keith Underwood <keithu@parl.clemson.edu>\n"
-" Support by many others\n"
-" http://www.scyld.com/network/hamachi.html\n"
-" or\n"
-" http://www.parl.clemson.edu/~keithu/drivers/hamachi.html\n";
-
-
/* A few user-configurable values. */
static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */
#include <linux/ip.h>
#include <linux/delay.h>
+static char version[] __initdata =
+KERN_INFO "hamachi.c:v1.01 5/16/2000 Written by Donald Becker\n"
+KERN_INFO " Some modifications by Eric kasten <kasten@nscl.msu.edu>\n"
+KERN_INFO " Further modifications by Keith Underwood <keithu@parl.clemson.edu>\n";
+
+
/* IP_MF appears to be only defined in <netinet/ip.h>, however,
we need it for hardware checksumming support. FYI... some of
the definitions in <netinet/ip.h> conflict/duplicate those in
static int __init hamachi_init_one (struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- static int did_version; /* Already printed version info. */
struct hamachi_private *hmp;
int option, i, rx_int_var, tx_int_var, boguscnt;
int chip_id = ent->driver_data;
static int card_idx;
struct net_device *dev;
- if (hamachi_debug > 0 && did_version++ == 0)
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+ static int printed_version;
+ if (!printed_version++)
printk(version);
+#endif
if (pci_enable_device(pdev))
return -EIO;
static int __init hamachi_init (void)
{
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+ printk(version);
+#endif
if (pci_register_driver(&hamachi_driver) > 0)
return 0;
pci_unregister_driver(&hamachi_driver);
*/
-/* These identify the driver base version and may not be removed. */
-static const char version1[] =
-"natsemi.c:v1.07 1/9/2001 Written by Donald Becker <becker@scyld.com>\n";
-static const char version2[] =
-" http://www.scyld.com/network/natsemi.html\n";
-static const char version3[] =
-" (unofficial 2.4.x kernel port, version 1.0.4, February 26, 2001 Jeff Garzik, Tjeerd Mulder)\n";
/* Updated to recommendations in pci-skeleton v2.03. */
/* Automatically extracted configuration info:
#include <asm/bitops.h>
#include <asm/io.h>
+/* These identify the driver base version and may not be removed. */
+static char version[] __devinitdata =
+KERN_INFO "natsemi.c:v1.07 1/9/2001 Written by Donald Becker <becker@scyld.com>\n"
+KERN_INFO " http://www.scyld.com/network/natsemi.html\n"
+KERN_INFO " (unofficial 2.4.x kernel port, version 1.0.5, April 17, 2001 Jeff Garzik, Tjeerd Mulder)\n";
+
/* Condensed operations for readability. */
#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
struct netdev_private *np;
int i, option, irq, chip_idx = ent->driver_data;
static int find_cnt = -1;
- static int printed_version;
unsigned long ioaddr, iosize;
const int pcibar = 1; /* PCI base address register */
int prev_eedata;
u32 tmp;
- if ((debug <= 1) && !printed_version++)
- printk(KERN_INFO "%s" KERN_INFO "%s" KERN_INFO "%s",
- version1, version2, version3);
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+ static int printed_version;
+ if (!printed_version++)
+ printk(version);
+#endif
i = pci_enable_device(pdev);
if (i) return i;
static int __init natsemi_init_mod (void)
{
- if (debug > 1)
- printk(KERN_INFO "%s" KERN_INFO "%s" KERN_INFO "%s",
- version1, version2, version3);
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+ printk(version);
+#endif
return pci_module_init (&natsemi_driver);
}
Limited full-duplex support.
*/
-/* These identify the driver base version and may not be removed. */
-static const char version1[] =
-"ne2k-pci.c:v1.02 10/19/2000 D. Becker/P. Gortmaker\n";
-static const char version2[] =
-" http://www.scyld.com/network/ne2k-pci.html\n";
-
/* The user-configurable values.
These may be modified when a driver module is loaded.*/
#include <linux/etherdevice.h>
#include "8390.h"
+/* These identify the driver base version and may not be removed. */
+static char version[] __devinitdata =
+KERN_INFO "ne2k-pci.c:v1.02 10/19/2000 D. Becker/P. Gortmaker\n"
+KERN_INFO " http://www.scyld.com/network/ne2k-pci.html\n";
+
#if defined(__powerpc__)
#define inl_le(addr) le32_to_cpu(inl(addr))
#define inw_le(addr) le16_to_cpu(inw(addr))
long ioaddr;
int flags = pci_clone_list[chip_idx].flags;
- if (fnd_cnt++ == 0)
- printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2);
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+ static int printed_version;
+ if (!printed_version++)
+ printk(version);
+#endif
+
+ fnd_cnt++;
i = pci_enable_device (pdev);
if (i)
static int __init ne2k_pci_init(void)
{
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+ printk(version);
+#endif
return pci_module_init (&ne2k_driver);
}
IVc. Errata
-1) The RTL-8139 has a serious problem with motherboards which do
-posted MMIO writes to PCI space. This driver works around the
-problem by having an MMIO register write be immediately followed by
-an MMIO register read.
-
-2) The RTL-8129 is only supported in Donald Becker's rtl8139 driver.
-
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <asm/io.h>
-
#define NETDRV_VERSION "1.0.0"
#define MODNAME "netdrv"
#define NETDRV_DRIVER_LOAD_MSG "MyVendor Fast Ethernet driver " NETDRV_VERSION " loaded"
#define PFX MODNAME ": "
+static char version[] __devinitdata =
+KERN_INFO NETDRV_DRIVER_LOAD_MSG "\n"
+KERN_INFO " Support available from http://foo.com/bar/baz.html\n";
/* define to 1 to enable PIO instead of MMIO */
#undef USE_IO_OPS
int i, addr_len, option;
void *ioaddr = NULL;
static int board_idx = -1;
- static int printed_version;
u8 tmp;
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+ static int printed_version;
+ if (!printed_version++)
+ printk(version);
+#endif
+
DPRINTK ("ENTER\n");
assert (pdev != NULL);
board_idx++;
- if (!printed_version) {
- printk (KERN_INFO NETDRV_DRIVER_LOAD_MSG "\n");
- printed_version = 1;
- }
-
i = netdrv_init_board (pdev, &dev, &ioaddr);
if (i < 0) {
DPRINTK ("EXIT, returning %d\n", i);
static int __init netdrv_init_module (void)
{
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+ printk(version);
+#endif
return pci_module_init (&netdrv_pci_driver);
}
{ /* SCM Ethernet */ 0x0ff0, 0x00, 0x20, 0xcb, 0 },
{ /* Socket EA */ 0x4000, 0x00, 0xc0, 0x1b,
DELAY_OUTPUT | HAS_MISC_REG | USE_BIG_BUF },
+ { /* Socket LP-E CF+ */ 0x01c0, 0x00, 0xc0, 0x1b, 0 },
{ /* SuperSocket RE450T */ 0x0110, 0x00, 0xe0, 0x98, 0 },
{ /* Volktek NPL-402CT */ 0x0060, 0x00, 0x40, 0x05, 0 },
{ /* NEC PC-9801N-J12 */ 0x0ff0, 0x00, 0x00, 0x4c, 0 },
pcnet32_dwio_reset(ioaddr);
pcnet32_wio_reset(ioaddr);
- /* Important to do the check for dwio mode first. */
- if (pcnet32_dwio_read_csr(ioaddr, 0) == 4 && pcnet32_dwio_check(ioaddr)) {
- a = &pcnet32_dwio;
+ /* NOTE: 16-bit check is first, otherwise some older PCnet chips fail */
+ if (pcnet32_wio_read_csr (ioaddr, 0) == 4 && pcnet32_wio_check (ioaddr)) {
+ a = &pcnet32_wio;
} else {
- if (pcnet32_wio_read_csr(ioaddr, 0) == 4 &&
- pcnet32_wio_check(ioaddr)) {
- a = &pcnet32_wio;
+ if (pcnet32_dwio_read_csr (ioaddr, 0) == 4 && pcnet32_dwio_check(ioaddr)) {
+ a = &pcnet32_dwio;
} else
return -ENODEV;
}
promaddr[i] = inb(ioaddr + i);
}
if( memcmp( promaddr, dev->dev_addr, 6) )
- printk(" warning: PROM address does not match CSR address");
+ {
+ printk(" warning PROM address does not match CSR address");
+#if defined(__i386__)
+ printk(KERN_WARNING "%s: Probably a Compaq, using the PROM address of", dev->name);
+ memcpy(dev->dev_addr, promaddr, 6);
+#endif
+ }
}
/* if the ethernet address is not valid, force to 00:00:00:00:00:00 */
if( !is_valid_ether_addr(dev->dev_addr) )
err = 0;
break;
- case PPPIOCATTACH:
- case PPPIOCDETACH:
- err = ppp_channel_ioctl(&ap->chan, cmd, arg);
- break;
-
default:
err = -ENOIOCTLCMD;
}
* PPP driver, written by Michael Callahan and Al Longyear, and
* subsequently hacked by Paul Mackerras.
*
- * ==FILEVERSION 20000417==
+ * ==FILEVERSION 20000902==
*/
#include <linux/config.h>
#include <linux/netdevice.h>
#include <linux/poll.h>
#include <linux/ppp_defs.h>
+#include <linux/filter.h>
#include <linux/if_ppp.h>
#include <linux/ppp_channel.h>
#include <linux/ppp-comp.h>
struct sk_buff_head mrq; /* MP: receive reconstruction queue */
#endif /* CONFIG_PPP_MULTILINK */
struct net_device_stats stats; /* statistics */
+#ifdef CONFIG_PPP_FILTER
+ struct sock_fprog pass_filter; /* filter for packets to pass */
+ struct sock_fprog active_filter;/* filter for pkts to reset idle */
+#endif /* CONFIG_PPP_FILTER */
};
/*
err = 0;
break;
+#ifdef CONFIG_PPP_FILTER
+ case PPPIOCSPASS:
+ case PPPIOCSACTIVE:
+ {
+ struct sock_fprog uprog, *filtp;
+ struct sock_filter *code = NULL;
+ int len;
+
+ if (copy_from_user(&uprog, (void *) arg, sizeof(uprog)))
+ break;
+ if (uprog.len > 0) {
+ err = -ENOMEM;
+ len = uprog.len * sizeof(struct sock_filter);
+ code = kmalloc(len, GFP_KERNEL);
+ if (code == 0)
+ break;
+ err = -EFAULT;
+ if (copy_from_user(code, uprog.filter, len))
+ break;
+ err = sk_chk_filter(code, uprog.len);
+ if (err) {
+ kfree(code);
+ break;
+ }
+ }
+ filtp = (cmd == PPPIOCSPASS)? &ppp->pass_filter: &ppp->active_filter;
+ ppp_lock(ppp);
+ if (filtp->filter)
+ kfree(filtp->filter);
+ filtp->filter = code;
+ filtp->len = uprog.len;
+ ppp_unlock(ppp);
+ err = 0;
+ break;
+ }
+#endif /* CONFIG_PPP_FILTER */
+
#ifdef CONFIG_PPP_MULTILINK
case PPPIOCSMRRU:
if (get_user(val, (int *) arg))
int len;
unsigned char *cp;
+ if (proto < 0x8000) {
+#ifdef CONFIG_PPP_FILTER
+ /* check if we should pass this packet */
+ /* the filter instructions are constructed assuming
+ a four-byte PPP header on each packet */
+ *skb_push(skb, 2) = 1;
+ if (ppp->pass_filter.filter
+ && sk_run_filter(skb, ppp->pass_filter.filter,
+ ppp->pass_filter.len) == 0) {
+ if (ppp->debug & 1) {
+ printk(KERN_DEBUG "PPP: outbound frame not passed\n");
+ kfree_skb(skb);
+ return;
+ }
+ }
+ /* if this packet passes the active filter, record the time */
+ if (!(ppp->active_filter.filter
+ && sk_run_filter(skb, ppp->active_filter.filter,
+ ppp->active_filter.len) == 0))
+ ppp->last_xmit = jiffies;
+ skb_pull(skb, 2);
+#else
+ /* for data packets, record the time */
+ ppp->last_xmit = jiffies;
+#endif /* CONFIG_PPP_FILTER */
+ }
+
++ppp->stats.tx_packets;
ppp->stats.tx_bytes += skb->len - 2;
}
}
- /* for data packets, record the time */
- if (proto < 0x8000)
- ppp->last_xmit = jiffies;
-
/*
* If we are waiting for traffic (demand dialling),
* queue it up for pppd to receive.
} else {
/* network protocol frame - give it to the kernel */
+
+#ifdef CONFIG_PPP_FILTER
+ /* check if the packet passes the pass and active filters */
+ /* the filter instructions are constructed assuming
+ a four-byte PPP header on each packet */
+ *skb_push(skb, 2) = 0;
+ if (ppp->pass_filter.filter
+ && sk_run_filter(skb, ppp->pass_filter.filter,
+ ppp->pass_filter.len) == 0) {
+ if (ppp->debug & 1)
+ printk(KERN_DEBUG "PPP: inbound frame not passed\n");
+ kfree_skb(skb);
+ return;
+ }
+ if (!(ppp->active_filter.filter
+ && sk_run_filter(skb, ppp->active_filter.filter,
+ ppp->active_filter.len) == 0))
+ ppp->last_recv = jiffies;
+ skb_pull(skb, 2);
+#else
ppp->last_recv = jiffies;
+#endif /* CONFIG_PPP_FILTER */
+
if ((ppp->dev->flags & IFF_UP) == 0
|| ppp->npmode[npi] != NPMODE_PASS) {
kfree_skb(skb);
}
/*
- * This is basically temporary compatibility stuff.
- */
-ssize_t
-ppp_channel_read(struct ppp_channel *chan, struct file *file,
- char *buf, size_t count)
-{
- struct channel *pch = chan->ppp;
-
- if (pch == 0)
- return -ENXIO;
- return ppp_file_read(&pch->file, file, buf, count);
-}
-
-ssize_t
-ppp_channel_write(struct ppp_channel *chan, const char *buf, size_t count)
-{
- struct channel *pch = chan->ppp;
-
- if (pch == 0)
- return -ENXIO;
- return ppp_file_write(&pch->file, buf, count);
-}
-
-/* No kernel lock - fine */
-unsigned int
-ppp_channel_poll(struct ppp_channel *chan, struct file *file, poll_table *wait)
-{
- unsigned int mask;
- struct channel *pch = chan->ppp;
-
- mask = POLLOUT | POLLWRNORM;
- if (pch != 0) {
- poll_wait(file, &pch->file.rwait, wait);
- if (skb_peek(&pch->file.rq) != 0)
- mask |= POLLIN | POLLRDNORM;
- }
- return mask;
-}
-
-int ppp_channel_ioctl(struct ppp_channel *chan, unsigned int cmd,
- unsigned long arg)
-{
- struct channel *pch = chan->ppp;
- int err = -ENOTTY;
- int unit;
-
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
- if (pch == 0)
- return -EINVAL;
- switch (cmd) {
- case PPPIOCATTACH:
- if (get_user(unit, (int *) arg))
- break;
- err = ppp_connect_channel(pch, unit);
- break;
- case PPPIOCDETACH:
- err = ppp_disconnect_channel(pch);
- break;
- }
- return err;
-}
-
-/*
* Compression control.
*/
ppp->file.index = unit;
ppp->mru = PPP_MRU;
init_ppp_file(&ppp->file, INTERFACE);
+ ppp->file.hdrlen = PPP_HDRLEN - 2; /* don't count proto bytes */
for (i = 0; i < NUM_NP; ++i)
ppp->npmode[i] = NPMODE_PASS;
INIT_LIST_HEAD(&ppp->channels);
#ifdef CONFIG_PPP_MULTILINK
skb_queue_purge(&ppp->mrq);
#endif /* CONFIG_PPP_MULTILINK */
+#ifdef CONFIG_PPP_FILTER
+ if (ppp->pass_filter.filter) {
+ kfree(ppp->pass_filter.filter);
+ ppp->pass_filter.filter = NULL;
+ }
+ if (ppp->active_filter.filter) {
+ kfree(ppp->active_filter.filter);
+ ppp->active_filter.filter = 0;
+ }
+#endif /* CONFIG_PPP_FILTER */
dev = ppp->dev;
ppp->dev = 0;
ppp_unlock(ppp);
EXPORT_SYMBOL(ppp_output_wakeup);
EXPORT_SYMBOL(ppp_register_compressor);
EXPORT_SYMBOL(ppp_unregister_compressor);
-EXPORT_SYMBOL(ppp_channel_read);
-EXPORT_SYMBOL(ppp_channel_write);
-EXPORT_SYMBOL(ppp_channel_poll);
-EXPORT_SYMBOL(ppp_channel_ioctl);
EXPORT_SYMBOL(all_ppp_units); /* for debugging */
EXPORT_SYMBOL(all_channels); /* for debugging */
}
/*
- * Read a PPP frame, for compatibility until pppd is updated.
+ * Read does nothing - no data is ever available this way.
+ * Pppd reads and writes packets via /dev/ppp instead.
*/
static ssize_t
ppp_sync_read(struct tty_struct *tty, struct file *file,
unsigned char *buf, size_t count)
{
- struct syncppp *ap = tty->disc_data;
-
- if (ap == 0)
- return -ENXIO;
- return ppp_channel_read(&ap->chan, file, buf, count);
+ return -EAGAIN;
}
/*
- * Write a ppp frame, for compatibility until pppd is updated.
+ * Write on the tty does nothing, the packets all come in
+ * from the ppp generic stuff.
*/
static ssize_t
ppp_sync_write(struct tty_struct *tty, struct file *file,
const unsigned char *buf, size_t count)
{
- struct syncppp *ap = tty->disc_data;
-
- if (ap == 0)
- return -ENXIO;
- return ppp_channel_write(&ap->chan, buf, count);
+ return -EAGAIN;
}
static int
err = 0;
break;
- /*
- * Compatibility calls until pppd is updated.
- */
- case PPPIOCGFLAGS:
- case PPPIOCSFLAGS:
- case PPPIOCGASYNCMAP:
- case PPPIOCSASYNCMAP:
- case PPPIOCGRASYNCMAP:
- case PPPIOCSRASYNCMAP:
- case PPPIOCGXASYNCMAP:
- case PPPIOCSXASYNCMAP:
- case PPPIOCGMRU:
- case PPPIOCSMRU:
- err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
- break;
- err = ppp_sync_ioctl(&ap->chan, cmd, arg);
- break;
-
- case PPPIOCATTACH:
- case PPPIOCDETACH:
- err = ppp_channel_ioctl(&ap->chan, cmd, arg);
- break;
-
default:
err = -ENOIOCTLCMD;
}
static unsigned int
ppp_sync_poll(struct tty_struct *tty, struct file *file, poll_table *wait)
{
- struct syncppp *ap = tty->disc_data;
- unsigned int mask;
-
- mask = POLLOUT | POLLWRNORM;
- /* compatibility for old pppd */
- if (ap != 0)
- mask |= ppp_channel_poll(&ap->chan, file, wait);
- if (test_bit(TTY_OTHER_CLOSED, &tty->flags) || tty_hung_up_p(file))
- mask |= POLLHUP;
- return mask;
+ return 0;
}
static int
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/ioport.h>
-#include <linux/malloc.h>
+#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
#include "sis900.h"
-static const char *version =
-"sis900.c: v1.07.11 4/10/2001\n";
+static char version[] __devinitdata =
+KERN_INFO "sis900.c: v1.07.11 4/10/2001\n";
static int max_interrupt_work = 40;
static int multicast_filter_limit = 128;
u8 revision;
char *card_name = card_names[pci_id->driver_data];
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+ static int printed_version;
+ if (!printed_version++)
+ printk(version);
+#endif
+
/* setup various bits in PCI command register */
ret = pci_enable_device(pci_dev);
if(ret) return ret;
static int __init sis900_init_module(void)
{
- printk(KERN_INFO "%s", version);
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+ printk(version);
+#endif
return pci_module_init(&sis900_pci_driver);
}
- Merge Becker version 1.03
*/
-/* These identify the driver base version and may not be removed. */
-static const char version1[] =
-"starfire.c:v1.03 7/26/2000 Written by Donald Becker <becker@scyld.com>\n";
-static const char version2[] =
-" Updates and info at http://www.scyld.com/network/starfire.html\n";
-
-static const char version3[] =
-" (unofficial 2.4.x kernel port, version 1.1.4, August 10, 2000)\n";
-
/* The user-configurable values.
These may be modified when a driver module is loaded.*/
#include <asm/bitops.h>
#include <asm/io.h>
+/* These identify the driver base version and may not be removed. */
+static char version[] __devinitdata =
+KERN_INFO "starfire.c:v1.03 7/26/2000 Written by Donald Becker <becker@scyld.com>\n"
+KERN_INFO " Updates and info at http://www.scyld.com/network/starfire.html\n"
+KERN_INFO " (unofficial 2.4.x kernel port, version 1.1.4a, April 17, 2001)\n";
+
MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
MODULE_DESCRIPTION("Adaptec Starfire Ethernet driver");
MODULE_PARM(max_interrupt_work, "i");
int i, irq, option, chip_idx = ent->driver_data;
struct net_device *dev;
static int card_idx = -1;
- static int printed_version;
long ioaddr;
int drv_flags, io_size = netdrv_tbl[chip_idx].io_size;
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+ static int printed_version;
+ if (!printed_version++)
+ printk(version);
+#endif
+
card_idx++;
option = card_idx < MAX_UNITS ? options[card_idx] : 0;
- if (!printed_version++)
- printk(KERN_INFO "%s" KERN_INFO "%s" KERN_INFO "%s",
- version1, version2, version3);
-
if (pci_enable_device (pdev))
return -EIO;
static int __init starfire_init (void)
{
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+ printk(version);
+#endif
return pci_module_init (&starfire_driver);
}
return ret;
}
init_timer(&bp->bigmac_timer);
- return bigmac_init(bp, 0);
+ ret = bigmac_init(bp, 0);
+ if (ret)
+ free_irq(dev->irq, bp);
+ return ret;
}
static int bigmac_close(struct net_device *dev)
bigmac_stop(bp);
bigmac_clean_rings(bp);
- free_irq(dev->irq, (void *)bp);
+ free_irq(dev->irq, bp);
return 0;
}
struct net_device *dev = NULL;
struct sbus_bus *sbus;
struct sbus_dev *sdev = 0;
- static int called = 0;
+ static int called;
int cards = 0, v;
root_bigmac_dev = NULL;
http://www.scyld.com/network/sundance.html
*/
-/* These identify the driver base version and may not be removed. */
-static const char version1[] =
-"sundance.c:v1.01 4/09/00 Written by Donald Becker\n";
-static const char version2[] =
-" http://www.scyld.com/network/sundance.html\n";
-
/* The user-configurable values.
These may be modified when a driver module is loaded.*/
#include <linux/spinlock.h>
+/* These identify the driver base version and may not be removed. */
+static char version[] __devinitdata =
+KERN_INFO "sundance.c:v1.01 4/09/00 Written by Donald Becker\n"
+KERN_INFO " http://www.scyld.com/network/sundance.html\n";
/* Condensed operations for readability. */
#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
int i, option = card_idx < MAX_UNITS ? options[card_idx] : 0;
long ioaddr;
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+ static int printed_version;
+ if (!printed_version++)
+ printk(version);
+#endif
+
if (pci_enable_device(pdev))
return -EIO;
pci_set_master(pdev);
ioaddr = pci_resource_start(pdev, 1);
ioaddr = (long) ioremap (ioaddr, pci_id_tbl[chip_idx].io_size);
if (!ioaddr)
- goto err_out_iomem;
+ goto err_out_res;
#endif
for (i = 0; i < 3; i++)
dev->do_ioctl = &mii_ioctl;
dev->tx_timeout = &tx_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
+ pci_set_drvdata(pdev, dev);
if (mtu)
dev->mtu = mtu;
pci_set_drvdata(pdev, NULL);
#ifndef USE_IO_OPS
iounmap((void *)ioaddr);
-err_out_iomem:
+err_out_res:
#endif
pci_release_regions(pdev);
err_out_netdev:
static int __init sundance_init(void)
{
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+ printk(version);
+#endif
return pci_module_init(&sundance_driver);
}
* 16-Jan-00 AF Created
*
*/
-static const char *version = "madgemc.c: v0.91 23/01/2000 by Adam Fritzler\n";
+static const char version[] = "madgemc.c: v0.91 23/01/2000 by Adam Fritzler\n";
#include <linux/module.h>
#include <linux/mca.h>
* TODO:
* 1. Add support for Proteon TR ISA adapters (1392, 1392+)
*/
-static const char *version = "tmsisa.c: v1.00 14/01/2001 by Jochen Friedrich\n";
+static const char version[] = "tmsisa.c: v1.00 14/01/2001 by Jochen Friedrich\n";
#include <linux/module.h>
#include <linux/kernel.h>
0
};
-static char *isa_cardname = "SK NET TR 4/16 ISA\0";
+static char isa_cardname[] = "SK NET TR 4/16 ISA\0";
int tms_isa_probe(struct net_device *dev);
static int tms_isa_open(struct net_device *dev);
{
/* Enlist in the card list */
card = kmalloc(sizeof(struct tms_isa_card), GFP_KERNEL);
+ if (!card) {
+ unregister_trdev(dev);
+ release_region(dev->base_addr, TMS_ISA_IO_EXTENT);
+ free_irq(dev->irq, dev);
+ free_dma(dev->dma);
+ tmsdev_term(dev);
+ return -1;
+ }
card->next = tms_isa_card_list;
tms_isa_card_list = card;
card->dev = dev;
2001-04-03 Jeff Garzik <jgarzik@mandrakesoft.com>
+ * tulip_core.c: Now that dev->name is only available late
+ in the probe, insert a hack to replace a not-evaluated
+ "eth%d" string with an evaluated "tulip%d" string.
+ Also, remove obvious comment and an indentation cleanup.
+
+2001-04-03 Jeff Garzik <jgarzik@mandrakesoft.com>
+
+ * tulip_core.c: If we are a module, always print out the
+ version string. If we are built into the kernel, only print
+ the version string if at least one tulip is detected.
+
+2001-04-03 Jeff Garzik <jgarzik@mandrakesoft.com>
+
+ Merged from Becker's tulip.c 0.92t:
+
+ * tulip_core.c: Add support for Conexant LANfinity.
+
+2001-04-03 Jeff Garzik <jgarzik@mandrakesoft.com>
+
+ * tulip_core.c: Only suspend/resume if the interface
+ is up and running. Use alloc_etherdev and pci_request_regions.
+ Spelling fix.
+
+2001-04-03 Jeff Garzik <jgarzik@mandrakesoft.com>
+
* tulip_core.c: Remove code that existed when one or more of
the following defines existed. These defines were never used
by normal users in practice: TULIP_FULL_DUPLEX,
(tp->link_change)(dev, csr5);
}
if (csr5 & SytemError) {
- printk(KERN_ERR "%s: (%lu) System Error occured\n", dev->name, tp->nir);
+ int error = (csr5 >> 23) & 7;
+ /* oops, we hit a PCI error. The code produced corresponds
+ * to the reason:
+ * 0 - parity error
+ * 1 - master abort
+ * 2 - target abort
+ * Note that on parity error, we should do a software reset
+ * of the chip to get it back into a sane state (according
+ * to the 21142/3 docs that is).
+ * -- rmk
+ */
+ printk(KERN_ERR "%s: (%lu) System Error occured (%d)\n",
+ dev->name, tp->nir, error);
}
/* Clear all error sources, included undocumented ones! */
outl(0x0800f7ba, ioaddr + CSR5);
COMPEX9881,
I21145,
DM910X,
+ CONEXANT,
};
#include <asm/unaligned.h>
static char version[] __devinitdata =
- "Linux Tulip driver version 0.9.14d (April 3, 2001)\n";
+ "Linux Tulip driver version 0.9.14e (April 20, 2001)\n";
/* A few user-configurable values. */
{ "Davicom DM9102/DM9102A", 128, 0x0001ebef,
HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | HAS_ACPI,
tulip_timer },
+
+ /* CONEXANT */
+ { "Conexant LANfinity", 0x100, 0x0001ebef,
+ HAS_MII | HAS_ACPI,
+ tulip_timer },
};
{ 0x1113, 0x1216, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
{ 0x1113, 0x1217, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98715 },
{ 0x1113, 0x9511, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
- {0, }
+ { 0x14f1, 0x1803, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CONEXANT },
+ { } /* terminate list */
};
MODULE_DEVICE_TABLE(pci, tulip_pci_tbl);
static int __devinit tulip_init_one (struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- static int did_version; /* Already printed version info. */
struct tulip_private *tp;
/* See note below on the multiport cards. */
static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'};
static int last_irq;
- static int multiport_cnt; /* For four-port boards w/one EEPROM */
+ static int multiport_cnt; /* For four-port boards w/one EEPROM */
u8 chip_rev;
int i, irq;
unsigned short sum;
long ioaddr;
static int board_idx = -1;
int chip_idx = ent->driver_data;
+ int t2104x_mode = 0;
+ int eeprom_missing = 0;
- board_idx++;
-
+#ifndef MODULE
+ static int did_version; /* Already printed version info. */
if (tulip_debug > 0 && did_version++ == 0)
printk (KERN_INFO "%s", version);
+#endif
+
+ board_idx++;
/*
* Lan media wire a tulip chip to a wan interface. Needs a very
irq = pdev->irq;
/* init_etherdev ensures aligned and zeroed private structures */
- dev = init_etherdev (NULL, sizeof (*tp));
+ dev = alloc_etherdev (sizeof (*tp));
if (!dev) {
printk (KERN_ERR PFX "ether device alloc failed, aborting\n");
return -ENOMEM;
if (pci_resource_len (pdev, 0) < tulip_tbl[chip_idx].io_size) {
printk (KERN_ERR PFX "%s: I/O region (0x%lx@0x%lx) too small, "
- "aborting\n", dev->name, pci_resource_len (pdev, 0),
+ "aborting\n", pdev->slot_name,
+ pci_resource_len (pdev, 0),
pci_resource_start (pdev, 0));
goto err_out_free_netdev;
}
/* grab all resources from both PIO and MMIO regions, as we
* don't want anyone else messing around with our hardware */
- if (!request_region (pci_resource_start (pdev, 0),
- pci_resource_len (pdev, 0),
- dev->name)) {
- printk (KERN_ERR PFX "%s: I/O region (0x%lx@0x%lx) unavailable, "
- "aborting\n", dev->name, pci_resource_len (pdev, 0),
- pci_resource_start (pdev, 0));
+ if (pci_request_regions (pdev, "tulip"))
goto err_out_free_netdev;
- }
- if (!request_mem_region (pci_resource_start (pdev, 1),
- pci_resource_len (pdev, 1),
- dev->name)) {
- printk (KERN_ERR PFX "%s: MMIO region (0x%lx@0x%lx) unavailable, "
- "aborting\n", dev->name, pci_resource_len (pdev, 1),
- pci_resource_start (pdev, 1));
- goto err_out_free_pio_res;
- }
pci_set_master(pdev);
pci_read_config_byte (pdev, PCI_REVISION_ID, &chip_rev);
/*
- * initialize priviate data structure 'tp'
+ * initialize private data structure 'tp'
* it is zeroed and aligned in init_etherdev
*/
tp = dev->priv;
sizeof(struct tulip_tx_desc) * TX_RING_SIZE,
&tp->rx_ring_dma);
if (!tp->rx_ring)
- goto err_out_free_mmio_res;
+ goto err_out_free_res;
tp->tx_ring = (struct tulip_tx_desc *)(tp->rx_ring + RX_RING_SIZE);
tp->tx_ring_dma = tp->rx_ring_dma + sizeof(struct tulip_rx_desc) * RX_RING_SIZE;
dev->base_addr = ioaddr;
dev->irq = irq;
- pci_set_drvdata(pdev, dev);
-
- printk(KERN_INFO "%s: %s rev %d at %#3lx,",
- dev->name, tulip_tbl[chip_idx].chip_name, chip_rev, ioaddr);
/* bugfix: the ASIX must have a burst limit or horrible things happen. */
if (chip_idx == AX88140) {
if (chip_idx == DC21041) {
if (inl(ioaddr + CSR9) & 0x8000) {
- printk(" 21040 compatible mode,");
chip_idx = DC21040;
+ t2104x_mode = 1;
} else {
- printk(" 21041 mode,");
+ t2104x_mode = 2;
}
}
Many PCI BIOSes also incorrectly report the IRQ line, so we correct
that here as well. */
if (sum == 0 || sum == 6*0xff) {
- printk(" EEPROM not present,");
+ eeprom_missing = 1;
for (i = 0; i < 5; i++)
dev->dev_addr[i] = last_phys_addr[i];
dev->dev_addr[i] = last_phys_addr[i] + 1;
}
for (i = 0; i < 6; i++)
- printk("%c%2.2X", i ? ':' : ' ', last_phys_addr[i] = dev->dev_addr[i]);
- printk(", IRQ %d.\n", irq);
+ last_phys_addr[i] = dev->dev_addr[i];
last_irq = irq;
/* The lower four bits are the media type. */
tp->default_port = dev->mem_start & MEDIA_MASK;
if (tp->default_port) {
printk(KERN_INFO "%s: Transceiver selection forced to %s.\n",
- dev->name, medianame[tp->default_port & MEDIA_MASK]);
+ pdev->slot_name, medianame[tp->default_port & MEDIA_MASK]);
tp->medialock = 1;
if (tulip_media_cap[tp->default_port] & MediaAlwaysFD)
tp->full_duplex = 1;
else
tp->to_advertise = 0x01e1;
- /* This is logically part of _init_one(), but too complex to write inline. */
if (tp->flags & HAS_MEDIA_TABLE) {
memcpy(tp->eeprom, ee_data, sizeof(tp->eeprom));
+
+ sprintf(dev->name, "tulip%d", board_idx); /* hack */
tulip_parse_eeprom(dev);
+ strcpy(dev->name, "eth%d"); /* un-hack */
}
if ((tp->flags & ALWAYS_CHECK_MII) ||
tp->advertising[phy_idx++] = reg4;
printk(KERN_INFO "%s: MII transceiver #%d "
"config %4.4x status %4.4x advertising %4.4x.\n",
- dev->name, phy, mii_reg0, mii_status, mii_advert);
+ pdev->slot_name, phy, mii_reg0, mii_status, mii_advert);
/* Fixup for DLink with miswired PHY. */
if (mii_advert != reg4) {
printk(KERN_DEBUG "%s: Advertising %4.4x on PHY %d,"
" previously advertising %4.4x.\n",
- dev->name, reg4, phy, mii_advert);
+ pdev->slot_name, reg4, phy, mii_advert);
printk(KERN_DEBUG "%s: Advertising %4.4x (to advertise"
" is %4.4x).\n",
- dev->name, reg4, tp->to_advertise);
+ pdev->slot_name, reg4, tp->to_advertise);
tulip_mdio_write(dev, phy, 4, reg4);
}
/* Enable autonegotiation: some boards default to off. */
tp->mii_cnt = phy_idx;
if (tp->mtable && tp->mtable->has_mii && phy_idx == 0) {
printk(KERN_INFO "%s: ***WARNING***: No MII transceiver found!\n",
- dev->name);
+ pdev->slot_name);
tp->phys[0] = 1;
}
}
dev->do_ioctl = private_ioctl;
dev->set_multicast_list = set_rx_mode;
+ if (register_netdev(dev))
+ goto err_out_mtable;
+
+ printk(KERN_INFO "%s: %s rev %d at %#3lx,",
+ dev->name, tulip_tbl[chip_idx].chip_name, chip_rev, ioaddr);
+ pci_set_drvdata(pdev, dev);
+
+ if (t2104x_mode == 1)
+ printk(" 21040 compatible mode,");
+ else if (t2104x_mode == 2)
+ printk(" 21041 mode,");
+ if (eeprom_missing)
+ printk(" EEPROM not present,");
+ for (i = 0; i < 6; i++)
+ printk("%c%2.2X", i ? ':' : ' ', dev->dev_addr[i]);
+ printk(", IRQ %d.\n", irq);
+
if ((tp->flags & HAS_NWAY) || tp->chip_id == DC21041)
tp->link_change = t21142_lnk_change;
else if (tp->flags & HAS_PNICNWAY)
return 0;
-err_out_free_mmio_res:
- release_mem_region (pci_resource_start (pdev, 1),
- pci_resource_len (pdev, 1));
-err_out_free_pio_res:
- release_region (pci_resource_start (pdev, 0),
- pci_resource_len (pdev, 0));
+err_out_mtable:
+ if (tp->mtable)
+ kfree (tp->mtable);
+err_out_free_res:
+ pci_release_regions (pdev);
err_out_free_netdev:
unregister_netdev (dev);
kfree (dev);
{
struct net_device *dev = pci_get_drvdata(pdev);
- if (dev && netif_device_present (dev)) {
+ if (dev && netif_running (dev) && netif_device_present (dev)) {
netif_device_detach (dev);
tulip_down (dev);
/* pci_power_off(pdev, -1); */
{
struct net_device *dev = pci_get_drvdata(pdev);
+ if (dev && netif_running (dev) && !netif_device_present (dev)) {
#if 1
- pci_enable_device (pdev);
+ pci_enable_device (pdev);
#endif
- if (dev && !netif_device_present (dev)) {
/* pci_power_on(pdev); */
tulip_up (dev);
netif_device_attach (dev);
sizeof (struct tulip_tx_desc) * TX_RING_SIZE,
tp->rx_ring, tp->rx_ring_dma);
unregister_netdev (dev);
- release_mem_region (pci_resource_start (pdev, 1),
- pci_resource_len (pdev, 1));
- release_region (pci_resource_start (pdev, 0),
- pci_resource_len (pdev, 0));
if (tp->mtable)
kfree (tp->mtable);
kfree (dev);
-
+ pci_release_regions (pdev);
pci_set_drvdata (pdev, NULL);
/* pci_power_off (pdev, -1); */
static int __init tulip_init (void)
{
+#ifdef MODULE
+ printk (KERN_INFO "%s", version);
+#endif
+
/* copy module parms into globals */
tulip_rx_copybreak = rx_copybreak;
tulip_max_interrupt_work = max_interrupt_work;
#include <asm/irq.h>
/* These identify the driver base version and may not be removed. */
-static char version1[] __devinitdata =
-"via-rhine.c:v1.08b-LK1.1.7 8/9/2000 Written by Donald Becker\n";
-static char version2[] __devinitdata =
-" http://www.scyld.com/network/via-rhine.html\n";
+static char version[] __devinitdata =
+KERN_INFO "via-rhine.c:v1.08b-LK1.1.8 4/17/2000 Written by Donald Becker\n"
+KERN_INFO " http://www.scyld.com/network/via-rhine.html\n";
int i, option;
int chip_id = (int) ent->driver_data;
static int card_idx = -1;
- static int did_version;
long ioaddr;
int io_size;
int pci_flags;
- /* print version once and once only */
- if (! did_version++) {
- printk (KERN_INFO "%s", version1);
- printk (KERN_INFO "%s", version2);
- }
-
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+ static int printed_version;
+ if (!printed_version++)
+ printk(version);
+#endif
+
card_idx++;
option = card_idx < MAX_UNITS ? options[card_idx] : 0;
io_size = via_rhine_chip_info[chip_id].io_size;
static int __init via_rhine_init (void)
{
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+ printk(version);
+#endif
return pci_module_init (&via_rhine_driver);
}
#include <net/sock.h>
-static const char *devname = "dlci";
-static const char *version = "DLCI driver v0.35, 4 Jan 1997, mike.mclagan@linux.org";
+static const char devname[] = "dlci";
+static const char version[] = "DLCI driver v0.35, 4 Jan 1997, mike.mclagan@linux.org";
static struct net_device *open_dev[CONFIG_DLCI_COUNT];
#include <linux/hdlc.h>
/* Version */
-static const char * version = "$Id: dscc4.c,v 1.130 2001/02/25 15:27:34 romieu Exp $\n";
+static const char version[] = "$Id: dscc4.c,v 1.130 2001/02/25 15:27:34 romieu Exp $\n";
static int debug;
dev->tx_queue_len = 100;
/* Initialize socket buffers */
-#if defined(LINUX_2_1) || defined(LINUX_2_4)
- dev_init_buffers(dev);
-#else
+#if !defined(LINUX_2_1) && !defined(LINUX_2_4)
for (i = 0; i < DEV_NUMBUFFS; ++i)
skb_queue_head_init(&dev->buffs[i]);
#endif
dev->tx_queue_len = 100;
/* Initialize socket buffers */
-#if defined(LINUX_2_1) || defined(LINUX_2_4)
- dev_init_buffers(dev);
-#else
+#if !defined(LINUX_2_1) && !defined(LINUX_2_4)
for (i = 0; i < DEV_NUMBUFFS; ++i)
skb_queue_head_init(&dev->buffs[i]);
#endif
dev->tx_queue_len = 100;
/* Initialize socket buffers */
-#if defined(LINUX_2_1) || defined(LINUX_2_4)
- dev_init_buffers(dev);
-#else
+ #if !defined(LINUX_2_1) && !defined(LINUX_2_4)
for (i = 0; i < DEV_NUMBUFFS; ++i)
skb_queue_head_init(&dev->buffs[i]);
#endif
dev->tx_queue_len = 100;
/* Initialize socket buffers */
-#if defined(LINUX_2_1) || defined(LINUX_2_4)
- dev_init_buffers(dev);
-#else
+#if !defined(LINUX_2_1) && !defined(LINUX_2_4)
for (i = 0; i < DEV_NUMBUFFS; ++i)
skb_queue_head_init(&dev->buffs[i]);
#endif
dev->tx_queue_len = 100;
/* Initialize socket buffers */
- #if defined(LINUX_2_1) || defined(LINUX_2_4)
- #else
+ #if !defined(LINUX_2_1) && !defined(LINUX_2_4)
for (i = 0; i < DEV_NUMBUFFS; ++i)
skb_queue_head_init(&dev->buffs[i]);
#endif
descriptors. Remove cpu_to_le32, enable BE descriptors.
*/
-/* These identify the driver base version and may not be removed. */
-static const char version1[] =
-"winbond-840.c:v1.01 (2.4 port) 5/15/2000 Donald Becker <becker@scyld.com>\n";
-static const char version2[] =
-" http://www.scyld.com/network/drivers.html\n";
-
/* Automatically extracted configuration info:
probe-func: winbond840_probe
config-in: tristate 'Winbond W89c840 Ethernet support' CONFIG_WINBOND_840
#include <asm/bitops.h>
#include <asm/io.h>
+/* These identify the driver base version and may not be removed. */
+static char version[] __devinitdata =
+KERN_INFO "winbond-840.c:v1.01 (2.4 port) 5/15/2000 Donald Becker <becker@scyld.com>\n"
+KERN_INFO " http://www.scyld.com/network/drivers.html\n";
+
MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
MODULE_DESCRIPTION("Winbond W89c840 Ethernet driver");
MODULE_PARM(max_interrupt_work, "i");
static int __init w840_init(void)
{
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+ printk(version);
+#endif
return pci_module_init(&w840_driver);
}
*/
-/* These identify the driver base version and may not be removed. */
-static const char version1[] =
-"yellowfin.c:v1.05 1/09/2001 Written by Donald Becker <becker@scyld.com>\n";
-static const char version2[] =
-" http://www.scyld.com/network/yellowfin.html\n";
-static const char version3[] =
-" (unofficial 2.4.x port, LK1.1.2, January 11, 2001)\n";
-
/* The user-configurable values.
These may be modified when a driver module is loaded.*/
#include <asm/bitops.h>
#include <asm/io.h>
+/* These identify the driver base version and may not be removed. */
+static char version[] __devinitdata =
+KERN_INFO "yellowfin.c:v1.05 1/09/2001 Written by Donald Becker <becker@scyld.com>\n"
+KERN_INFO " http://www.scyld.com/network/yellowfin.html\n"
+KERN_INFO " (unofficial 2.4.x port, LK1.1.2, January 11, 2001)\n";
+
/* Condensed operations for readability. */
#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
int i, option = find_cnt < MAX_UNITS ? options[find_cnt] : 0;
int drv_flags = pci_id_tbl[chip_idx].drv_flags;
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+ static int printed_version;
+ if (!printed_version++)
+ printk(version);
+#endif
+
i = pci_enable_device(pdev);
if (i) return i;
static int __init yellowfin_init (void)
{
- if (debug) /* Emit version even if no cards detected. */
- printk(KERN_INFO "%s" KERN_INFO "%s" KERN_INFO "%s",
- version1, version2, version3);
-
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+ printk(version);
+#endif
return pci_module_init (&yellowfin_driver);
}
+2001-04-20 Paul Gortmaker <p_gortmaker@yahoo.com>
+
+ * parport_pc.c: Cut down the size quite a bit (more than 4k off
+ the object, about 1k off the zImage) for the older non-PCI
+ machines which are typically resource starved anyway...
+
+2001-03-26 R Horn <rjh@world.std.com>
+
+ * parport_pc.c: Some commentary changes.
+
+2001-04-19 Tim Waugh <twaugh@redhat.com>
+
+ * parport_pc.c (parport_pc_probe_port): Remove __devinit
+ attribute. Export unconditionally.
+
2001-04-14 Jeff Garzik <jgarzik@mandrakesoft.com>
Merged: 2001-03-30 Tim Waugh <twaugh@redhat.com>
* Cleaned up include files - Russell King <linux@arm.uk.linux.org>
* DMA support - Bert De Jonghe <bert@sophis.be>
* Many ECP bugs fixed. Fred Barnes & Jamie Lokier, 1999
+ * More PCI support now conditional on CONFIG_PCI, 03/2001, Paul G.
*/
/* This driver should work with any hardware that is broadly compatible
/* Can't yield the port. */
schedule ();
- /* At this point, the FIFO may already be full.
- * Ideally, we'd be able to tell the port to hold on
- * for a second while we empty the FIFO, and we'd be
- * able to ensure that no data is lost. I'm not sure
- * that's the case. :-( It might be that you can play
- * games with STB, as in the forward case; someone should
- * look at a datasheet. */
+ /* At this point, the FIFO may already be full. In
+ * that case ECP is already holding back the
+ * peripheral (assuming proper design) with a delayed
+ * handshake. Work fast to avoid a peripheral
+ * timeout. */
if (ecrval & 0x01) {
/* FIFO is empty. Wait for interrupt. */
goto false_alarm;
}
+ /* Depending on how the FIFO threshold was
+ * set, how long interrupt service took, and
+ * how fast the peripheral is, we might be
+ * lucky and have a just filled FIFO. */
continue;
}
continue;
}
+ /* FIFO not filled. We will cycle this loop for a while
+ * and either the peripheral will fill it faster,
+ * tripping a fast empty with insb, or we empty it. */
*bufp++ = inb (fifo);
left--;
}
/* --- Initialisation code -------------------------------- */
-struct parport *__devinit parport_pc_probe_port (unsigned long int base,
- unsigned long int base_hi,
- int irq, int dma,
- struct pci_dev *dev)
+struct parport *parport_pc_probe_port (unsigned long int base,
+ unsigned long int base_hi,
+ int irq, int dma,
+ struct pci_dev *dev)
{
struct parport_pc_private *priv;
struct parport_operations *ops;
}
+#ifdef CONFIG_PCI
/* Via support maintained by Jeff Garzik <jgarzik@mandrakesoft.com> */
static int __devinit sio_via_686a_probe (struct pci_dev *pdev, int autoirq,
int autodma)
static int __init parport_pc_init_superio (int autoirq, int autodma)
{
-#ifdef CONFIG_PCI
const struct pci_device_id *id;
struct pci_dev *pdev;
return parport_pc_superio_info[id->driver_data].probe
(pdev, autoirq, autodma);
}
-#endif /* CONFIG_PCI */
return 0; /* zero devices found */
}
+#else
+static struct pci_driver parport_pc_pci_driver;
+static int __init parport_pc_init_superio(void) {return 0;}
+#endif /* CONFIG_PCI */
/* This is called by parport_pc_find_nonpci_ports (in asm/parport.h) */
static int __init __attribute__((unused))
}
/* Exported symbols. */
-#ifdef CONFIG_PARPORT_PC_PCMCIA
-
-/* parport_cs needs this in order to dyncamically get us to find ports. */
EXPORT_SYMBOL (parport_pc_probe_port);
-#else
-
-EXPORT_NO_SYMBOLS;
-
-#endif
-
#ifdef MODULE
static int io[PARPORT_PC_MAX_PORTS+1] = { [0 ... PARPORT_PC_MAX_PORTS] = 0 };
static int io_hi[PARPORT_PC_MAX_PORTS+1] = { [0 ... PARPORT_PC_MAX_PORTS] = 0 };
}
/*
+ * VIA Apollo KT133 needs PCI latency patch
+ * Made according to a windows driver based patch by George E. Breese
+ * see PCI Latency Adjust on http://www.viahardware.com/download/viatweak.shtm
+ */
+static void __init quirk_vialatency(struct pci_dev *dev)
+{
+ u8 r70;
+
+ printk(KERN_INFO "Applying VIA PCI latency patch.\n");
+ /*
+ * In register 0x70, mask off bit 2 (PCI Master read caching)
+ * and 1 (Delay Transaction)
+ */
+ pci_read_config_byte(dev, 0x70, &r70);
+ r70 &= 0xf9;
+ pci_write_config_byte(dev, 0x70, r70);
+ /*
+ * Turn off PCI Latency timeout (set to 0 clocks)
+ */
+ pci_write_config_byte(dev, 0x75, 0x80);
+}
+
+/*
* VIA Apollo VP3 needs ETBF on BT848/878
*/
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_2, quirk_natoma },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5597, quirk_nopcipci },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_496, quirk_nopcipci },
+ { PCI_FIXUP_FINAL, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8363_0, quirk_vialatency },
{ PCI_FIXUP_FINAL, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C597_0, quirk_viaetbf },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C597_0, quirk_vt82c598_id },
{ PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3, quirk_vt82c586_acpi },
codec->private_data = card;
codec->codec_read = m3_ac97_read;
codec->codec_write = m3_ac97_write;
+ /* someday we should support secondary codecs.. */
+ codec->id = 0;
if (ac97_probe_codec(codec) == 0) {
printk(KERN_ERR PFX "codec probe failed\n");
if (!pci_register_driver(&m3_pci_driver)) {
pci_unregister_driver(&m3_pci_driver);
+ unregister_reboot_notifier(&m3_reboot_nb);
return -ENODEV;
}
return 0;
last allocated block is not cleared. Well,
this fs never claimed to be Posix conformant.
-Please direct bug reports to: hjw@zvw.de
+Please direct bug reports to: zippel@linux-m68k.org
+
+Version 3.14
+------------
+
+- correctly cut off long file names for compares
+- correctly initialize s_last_bmap
+
+Version 3.13
+------------
+
+Major cleanup for 2.4 [Roman Zippel]
+- new extended block handling
+- new bitmap allocation functions
+- locking should be safe for the future
+- cleanup of some interfaces
+
+Version 3.12
+------------
+
+more 2.4 fixes: [Roman Zippel]
+- s_lock changes
+- increased getblock mess
+- clear meta blocks
Version 3.11
------------
#
# Note 2! The CFLAGS definitions are now in the main makefile.
+#EXTRA_CFLAGS=-DDEBUG=1
+
O_TARGET := affs.o
obj-y := super.o namei.o inode.o file.o dir.o amigaffs.o bitmap.o symlink.o
* Please send bug reports to: hjw@zvw.de
*/
-#define DEBUG 0
#include <stdarg.h>
#include <linux/stat.h>
#include <linux/sched.h>
* Functions for accessing Amiga-FFS structures.
*/
-/* Set *NAME to point to the file name in a file header block in memory
- pointed to by FH_DATA. The length of the name is returned. */
-int
-affs_get_file_name(int bsize, void *fh_data, unsigned char **name)
-{
- struct file_end *file_end;
-
- file_end = GET_END_PTR(struct file_end, fh_data, bsize);
- if (file_end->file_name[0] == 0
- || file_end->file_name[0] > 30) {
- printk(KERN_WARNING "AFFS: bad filename (length=%d chars)\n",
- file_end->file_name[0]);
- *name = "***BAD_FILE***";
- return 14;
- }
- *name = (unsigned char *)&file_end->file_name[1];
- return file_end->file_name[0];
-}
-
-/* Insert a header block (file) into the directory (next).
- * This routine assumes that the caller has the superblock locked.
+/* Insert a header block bh into the directory dir
+ * caller must hold AFFS_DIR->i_hash_lock!
*/
int
-affs_insert_hash(unsigned long next, struct buffer_head *file, struct inode *inode)
+affs_insert_hash(struct inode *dir, struct buffer_head *bh)
{
- struct buffer_head *bh;
- s32 ino;
- int offset;
+ struct super_block *sb = dir->i_sb;
+ struct buffer_head *dir_bh;
+ u32 ino, hash_ino;
+ int offset;
- offset = affs_hash_name(FILE_END(file->b_data,inode)->file_name+1,
- FILE_END(file->b_data,inode)->file_name[0],
- AFFS_I2FSTYPE(inode),AFFS_I2HSIZE(inode)) + 6;
- ino = be32_to_cpu(((struct dir_front *)file->b_data)->own_key);
+ ino = bh->b_blocknr;
+ offset = affs_hash_name(sb, AFFS_TAIL(sb, bh)->name + 1, AFFS_TAIL(sb, bh)->name[0]);
- pr_debug("AFFS: insert_hash(dir_ino=%lu,ino=%d)\n",next,ino);
+ pr_debug("AFFS: insert_hash(dir=%u, ino=%d)\n", (u32)dir->i_ino, ino);
- FILE_END(file->b_data,inode)->parent = cpu_to_be32(next);
+ dir_bh = affs_bread(sb, dir->i_ino);
+ if (!dir_bh)
+ return -EIO;
- while (1) {
- if (!(bh = affs_bread(inode->i_dev,next,AFFS_I2BSIZE(inode))))
+ hash_ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[offset]);
+ while (hash_ino) {
+ affs_brelse(dir_bh);
+ dir_bh = affs_bread(sb, hash_ino);
+ if (!dir_bh)
return -EIO;
- next = be32_to_cpu(((s32 *)bh->b_data)[offset]);
- if (!next || next > ino)
- break;
- offset = AFFS_I2BSIZE(inode) / 4 - 4;
- affs_brelse(bh);
+ hash_ino = be32_to_cpu(AFFS_TAIL(sb, dir_bh)->hash_chain);
}
+ AFFS_TAIL(sb, bh)->parent = cpu_to_be32(dir->i_ino);
+ AFFS_TAIL(sb, bh)->hash_chain = 0;
- DIR_END(file->b_data,inode)->hash_chain = cpu_to_be32(next);
- ((s32 *)bh->b_data)[offset] = cpu_to_be32(ino);
- affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
- mark_buffer_dirty(bh);
- affs_brelse(bh);
+ if (dir->i_ino == dir_bh->b_blocknr)
+ AFFS_HEAD(dir_bh)->table[offset] = cpu_to_be32(ino);
+ else
+ AFFS_TAIL(sb, dir_bh)->hash_chain = cpu_to_be32(ino);
+
+ affs_adjust_checksum(dir_bh, ino);
+ mark_buffer_dirty(dir_bh);
+ affs_brelse(dir_bh);
+
+ dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+ dir->i_version = ++event;
+ mark_inode_dirty(dir);
return 0;
}
-/* Remove a header block from its hash table (directory).
- * 'inode' may be any inode on the partition, it's only
- * used for calculating the block size and superblock
- * reference.
+
+/* Remove a header block from its directory.
+ * caller must hold AFFS_DIR->i_hash_lock!
*/
int
-affs_remove_hash(struct buffer_head *dbh, struct inode *inode)
+affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh)
{
- s32 ownkey;
- s32 key;
- s32 ptype;
- s32 stype;
- int offset;
- int retval;
- struct buffer_head *bh;
-
- ownkey = be32_to_cpu(((struct dir_front *)dbh->b_data)->own_key);
- key = be32_to_cpu(FILE_END(dbh->b_data,inode)->parent);
- offset = affs_hash_name(FILE_END(dbh->b_data,inode)->file_name+1,
- FILE_END(dbh->b_data,inode)->file_name[0],
- AFFS_I2FSTYPE(inode),AFFS_I2HSIZE(inode)) + 6;
- pr_debug("AFFS: remove_hash(dir=%d, ino=%d, hashval=%d)\n",key,ownkey,offset-6);
- retval = -ENOENT;
+ struct super_block *sb;
+ struct buffer_head *bh;
+ u32 rem_ino, hash_ino, ino;
+ int offset, retval;
+
+ sb = dir->i_sb;
+ rem_ino = rem_bh->b_blocknr;
+ offset = affs_hash_name(sb, AFFS_TAIL(sb, rem_bh)->name+1, AFFS_TAIL(sb, rem_bh)->name[0]);
+ pr_debug("AFFS: remove_hash(dir=%d, ino=%d, hashval=%d)\n", (u32)dir->i_ino, rem_ino, offset);
+
+ bh = affs_bread(sb, dir->i_ino);
+ if (!bh)
+ return -EIO;
- lock_super(inode->i_sb);
- while (key) {
- if (!(bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)))) {
- retval = -EIO;
- break;
- }
- if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype)
- || ptype != T_SHORT || (stype != ST_FILE && stype != ST_USERDIR &&
- stype != ST_LINKFILE && stype != ST_LINKDIR &&
- stype != ST_ROOT && stype != ST_SOFTLINK)) {
- affs_error(inode->i_sb,"affs_remove_hash",
- "Bad block in hash chain (key=%d, ptype=%d, stype=%d, ownkey=%d)",
- key,ptype,stype,ownkey);
- affs_brelse(bh);
- retval = -EINVAL;
- break;
- }
- key = be32_to_cpu(((s32 *)bh->b_data)[offset]);
- if (ownkey == key) {
- ((s32 *)bh->b_data)[offset] = FILE_END(dbh->b_data,inode)->hash_chain;
- affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+ retval = -ENOENT;
+ hash_ino = be32_to_cpu(AFFS_HEAD(bh)->table[offset]);
+ while (hash_ino) {
+ if (hash_ino == rem_ino) {
+ ino = AFFS_TAIL(sb, rem_bh)->hash_chain;
+ if (dir->i_ino == bh->b_blocknr)
+ AFFS_HEAD(bh)->table[offset] = ino;
+ else
+ AFFS_TAIL(sb, bh)->hash_chain = ino;
+ affs_adjust_checksum(bh, be32_to_cpu(ino) - hash_ino);
mark_buffer_dirty(bh);
- affs_brelse(bh);
retval = 0;
break;
}
affs_brelse(bh);
- offset = AFFS_I2BSIZE(inode) / 4 - 4;
+ bh = affs_bread(sb, hash_ino);
+ if (!bh)
+ return -EIO;
+ hash_ino = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain);
}
- unlock_super(inode->i_sb);
+
+ affs_brelse(bh);
+
+ dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+ dir->i_version = ++event;
+ mark_inode_dirty(dir);
return retval;
}
+static void
+affs_fix_dcache(struct dentry *dentry, u32 entry_ino)
+{
+ struct inode *inode = dentry->d_inode;
+ void *data = dentry->d_fsdata;
+ struct list_head *head, *next;
+
+ spin_lock(&dcache_lock);
+ head = &inode->i_dentry;
+ next = head->next;
+ while (next != head) {
+ dentry = list_entry(next, struct dentry, d_alias);
+ if (entry_ino == (u32)dentry->d_fsdata) {
+ dentry->d_fsdata = data;
+ break;
+ }
+ next = next->next;
+ }
+ spin_unlock(&dcache_lock);
+}
+
+
/* Remove header from link chain */
-int
-affs_remove_link(struct buffer_head *dbh, struct inode *inode)
+static int
+affs_remove_link(struct dentry *dentry)
{
- int retval;
- s32 key;
- s32 ownkey;
- s32 ptype;
- s32 stype;
- struct buffer_head *bh;
-
- ownkey = be32_to_cpu((DIR_FRONT(dbh)->own_key));
- key = be32_to_cpu(FILE_END(dbh->b_data,inode)->original);
- retval = -ENOENT;
-
- pr_debug("AFFS: remove_link(link=%d, original=%d)\n",ownkey,key);
+ struct inode *dir, *inode = dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *bh = NULL, *link_bh = NULL;
+ u32 link_ino, ino;
+ int retval;
+
+ pr_debug("AFFS: remove_link(key=%ld)\n", inode->i_ino);
+ down(&AFFS_INODE->i_link_lock);
+ retval = -EIO;
+ bh = affs_bread(sb, inode->i_ino);
+ if (!bh)
+ goto done;
+
+ link_ino = (u32)dentry->d_fsdata;
+ if (inode->i_ino == link_ino) {
+ /* we can't remove the head of the link, as its blocknr is still used as ino,
+ * so we remove the block of the first link instead.
+ */
+ link_ino = be32_to_cpu(AFFS_TAIL(sb, bh)->link_chain);
+ link_bh = affs_bread(sb, link_ino);
+ if (!link_bh)
+ goto done;
+
+ dir = iget(sb, be32_to_cpu(AFFS_TAIL(sb, link_bh)->parent));
+ if (!dir)
+ goto done;
+
+ down(&AFFS_DIR->i_hash_lock);
+ affs_fix_dcache(dentry, link_ino);
+ retval = affs_remove_hash(dir, link_bh);
+ if (retval)
+ goto done;
+
+ memcpy(AFFS_TAIL(sb, bh)->name, AFFS_TAIL(sb, link_bh)->name, 32);
+ retval = affs_insert_hash(dir, bh);
+ if (retval)
+ goto done;
+
+ up(&AFFS_DIR->i_hash_lock);
+ iput(dir);
+ } else {
+ link_bh = affs_bread(sb, link_ino);
+ if (!link_bh)
+ goto done;
+ }
- lock_super(inode->i_sb);
- while (key) {
- if (!(bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)))) {
- retval = -EIO;
- break;
- }
- if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype)) {
- affs_error(inode->i_sb,"affs_remove_link","Checksum error (block %d)",key);
- affs_brelse(bh);
- retval = -EINVAL;
- break;
- }
- key = be32_to_cpu(FILE_END(bh->b_data,inode)->link_chain);
- if (ownkey == key) {
- FILE_END(bh->b_data,inode)->link_chain =
- FILE_END(dbh->b_data,inode)->link_chain;
- affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+ while ((ino = be32_to_cpu(AFFS_TAIL(sb, bh)->link_chain))) {
+ if (ino == link_ino) {
+ ino = AFFS_TAIL(sb, link_bh)->link_chain;
+ AFFS_TAIL(sb, bh)->link_chain = ino;
+ affs_adjust_checksum(bh, be32_to_cpu(ino) - link_ino);
mark_buffer_dirty(bh);
- affs_brelse(bh);
retval = 0;
- break;
+ /* Fix the link count, if bh is a normal header block without links */
+ switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
+ case ST_LINKDIR:
+ case ST_LINKFILE:
+ break;
+ default:
+ if (!AFFS_TAIL(sb, bh)->link_chain)
+ inode->i_nlink = 1;
+ }
+ affs_free_block(sb, link_ino);
+ goto done;
}
affs_brelse(bh);
+ bh = affs_bread(sb, ino);
+ if (!bh)
+ goto done;
}
- unlock_super(inode->i_sb);
+ retval = -ENOENT;
+done:
+ affs_brelse(link_bh);
+ affs_brelse(bh);
+ up(&AFFS_INODE->i_link_lock);
+ return retval;
+}
+
+static int
+affs_empty_dir(struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *bh;
+ int retval, size;
+
+ retval = -EIO;
+ bh = affs_bread(sb, inode->i_ino);
+ if (!bh)
+ goto done;
+
+ retval = -ENOTEMPTY;
+ for (size = AFFS_SB->s_hashsize - 1; size >= 0; size--)
+ if (AFFS_HEAD(bh)->table[size])
+ goto not_empty;
+ retval = 0;
+not_empty:
+ affs_brelse(bh);
+done:
return retval;
}
+
/* Remove a filesystem object. If the object to be removed has
* links to it, one of the links must be changed to inherit
* the file or directory. As above, any inode will do.
*/
int
-affs_remove_header(struct buffer_head *bh, struct inode *inode)
+affs_remove_header(struct dentry *dentry)
{
- struct buffer_head *link_bh;
- struct inode *dir;
- unsigned long link_ino;
- unsigned long orig_ino;
- unsigned int dir_ino;
- int error;
-
- pr_debug("AFFS: remove_header(key=%ld)\n",be32_to_cpu(DIR_FRONT(bh)->own_key));
-
- /* Mark directory as changed. We do this before anything else,
- * as it must be done anyway and doesn't hurt even if an
- * error occurs later.
- */
- dir = iget(inode->i_sb,be32_to_cpu(FILE_END(bh->b_data,inode)->parent));
- if (!dir)
- return -EIO;
- dir->i_ctime = dir->i_mtime = CURRENT_TIME;
- dir->i_version++;
- mark_inode_dirty(dir);
- iput(dir);
-
- orig_ino = be32_to_cpu(FILE_END(bh->b_data,inode)->original);
- if (orig_ino) { /* This is just a link. Nothing much to do. */
- pr_debug("AFFS: Removing link.\n");
- if ((error = affs_remove_link(bh,inode)))
- return error;
- if ((error = affs_remove_hash(bh,inode)))
- return error;
- affs_free_block(inode->i_sb,be32_to_cpu(DIR_FRONT(bh)->own_key));
- return 1;
- }
-
- link_ino = be32_to_cpu(FILE_END(bh->b_data,inode)->link_chain);
- if (link_ino) { /* This is the complicated case. Yuck. */
- pr_debug("AFFS: Removing original with links to it.\n");
- /* Unlink the object and its first link from their directories. */
- if ((error = affs_remove_hash(bh,inode)))
- return error;
- if (!(link_bh = affs_bread(inode->i_dev,link_ino,AFFS_I2BSIZE(inode))))
- return -EIO;
- if ((error = affs_remove_hash(link_bh,inode))) {
- affs_brelse(link_bh);
- return error;
- }
- /* Fix link chain. */
- if ((error = affs_remove_link(link_bh,inode))) {
- affs_brelse(link_bh);
- return error;
- }
- /* Rename link to object. */
- memcpy(FILE_END(bh->b_data,inode)->file_name,
- FILE_END(link_bh->b_data,inode)->file_name,32);
- /* Insert object into dir the link was in. */
- dir_ino = be32_to_cpu(FILE_END(link_bh->b_data,inode)->parent);
- if ((error = affs_insert_hash(dir_ino,bh,inode))) {
- affs_brelse(link_bh);
- return error;
- }
- affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
- mark_buffer_dirty(bh);
- affs_brelse(link_bh);
- affs_free_block(inode->i_sb,link_ino);
- /* Mark the link's parent dir as changed, too. */
- if (!(dir = iget(inode->i_sb,dir_ino)))
- return -EIO;
- dir->i_ctime = dir->i_mtime = CURRENT_TIME;
- dir->i_version++;
- mark_inode_dirty(dir);
- iput(dir);
- return 1;
+ struct super_block *sb;
+ struct inode *inode, *dir;
+ struct buffer_head *bh = NULL;
+ int retval;
+
+ dir = dentry->d_parent->d_inode;
+ sb = dir->i_sb;
+
+ retval = -ENOENT;
+ inode = dentry->d_inode;
+ if (!inode)
+ goto done;
+
+ pr_debug("AFFS: remove_header(key=%ld)\n", inode->i_ino);
+ down(&AFFS_DIR->i_hash_lock);
+ if (S_ISDIR(inode->i_mode)) {
+ retval = affs_empty_dir(inode);
+ if (retval)
+ goto done_unlock;
}
- /* Plain file/dir. This is the simplest case. */
- pr_debug("AFFS: Removing plain file/dir.\n");
- if ((error = affs_remove_hash(bh,inode)))
- return error;
- return 0;
-}
+ retval = -EIO;
+ bh = affs_bread(sb, inode->i_ino);
+ if (!bh)
+ goto done;
+
+ retval = affs_remove_hash(dir, bh);
+ if (retval)
+ goto done_unlock;
+
+ up(&AFFS_DIR->i_hash_lock);
+
+ if (inode->i_nlink > 1)
+ retval = affs_remove_link(dentry);
+ else
+ inode->i_nlink = 0;
+ inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+
+done:
+ affs_brelse(bh);
+ return retval;
+
+done_unlock:
+ up(&AFFS_DIR->i_hash_lock);
+ goto done;
+}
/* Checksum a block, do various consistency checks and optionally return
the blocks type number. DATA points to the block. If their pointers
Returns non-zero if the block is not consistent. */
u32
-affs_checksum_block(int bsize, void *data, s32 *ptype, s32 *stype)
+affs_checksum_block(struct super_block *sb, struct buffer_head *bh)
{
- u32 sum;
- u32 *p;
-
- bsize /= 4;
- if (ptype)
- *ptype = be32_to_cpu(((s32 *)data)[0]);
- if (stype)
- *stype = be32_to_cpu(((s32 *)data)[bsize - 1]);
-
- sum = 0;
- p = data;
- while (bsize--)
- sum += be32_to_cpu(*p++);
+ u32 *ptr = (u32 *)bh->b_data;
+ u32 sum;
+ int bsize;
+
+ sum = 0;
+ for (bsize = sb->s_blocksize / sizeof(u32); bsize > 0; bsize--)
+ sum += be32_to_cpu(*ptr++);
return sum;
}
*/
void
-affs_fix_checksum(int bsize, void *data, int cspos)
+affs_fix_checksum(struct super_block *sb, struct buffer_head *bh)
{
- u32 ocs;
- u32 cs;
-
- cs = affs_checksum_block(bsize,data,NULL,NULL);
- ocs = be32_to_cpu(((u32 *)data)[cspos]);
- ocs -= cs;
- ((u32 *)data)[cspos] = be32_to_cpu(ocs);
+ int cnt = sb->s_blocksize / sizeof(u32);
+ u32 *ptr = (u32 *)bh->b_data;
+ u32 checksum, *checksumptr;
+
+ checksumptr = ptr + 5;
+ *checksumptr = 0;
+ for (checksum = 0; cnt > 0; ptr++, cnt--)
+ checksum += be32_to_cpu(*ptr);
+ *checksumptr = cpu_to_be32(-checksum);
}
void
-secs_to_datestamp(time_t secs, struct DateStamp *ds)
+secs_to_datestamp(time_t secs, struct affs_date *ds)
{
u32 days;
u32 minute;
minute = secs / 60;
secs -= minute * 60;
- ds->ds_Days = be32_to_cpu(days);
- ds->ds_Minute = be32_to_cpu(minute);
- ds->ds_Tick = be32_to_cpu(secs * 50);
+ ds->days = be32_to_cpu(days);
+ ds->mins = be32_to_cpu(minute);
+ ds->ticks = be32_to_cpu(secs * 50);
}
-int
+mode_t
prot_to_mode(u32 prot)
{
- int mode = 0;
+ int mode = 0;
- if (AFFS_UMAYWRITE(prot))
+ if (!(prot & FIBF_NOWRITE))
mode |= S_IWUSR;
- if (AFFS_UMAYREAD(prot))
+ if (!(prot & FIBF_NOREAD))
mode |= S_IRUSR;
- if (AFFS_UMAYEXECUTE(prot))
+ if (!(prot & FIBF_NOEXECUTE))
mode |= S_IXUSR;
- if (AFFS_GMAYWRITE(prot))
+ if (prot & FIBF_GRP_WRITE)
mode |= S_IWGRP;
- if (AFFS_GMAYREAD(prot))
+ if (prot & FIBF_GRP_READ)
mode |= S_IRGRP;
- if (AFFS_GMAYEXECUTE(prot))
+ if (prot & FIBF_GRP_EXECUTE)
mode |= S_IXGRP;
- if (AFFS_OMAYWRITE(prot))
+ if (prot & FIBF_OTR_WRITE)
mode |= S_IWOTH;
- if (AFFS_OMAYREAD(prot))
+ if (prot & FIBF_OTR_READ)
mode |= S_IROTH;
- if (AFFS_OMAYEXECUTE(prot))
+ if (prot & FIBF_OTR_EXECUTE)
mode |= S_IXOTH;
-
+
return mode;
}
-u32
-mode_to_prot(int mode)
+void
+mode_to_prot(struct inode *inode)
{
- u32 prot = 0;
-
- if (mode & S_IXUSR)
- prot |= FIBF_SCRIPT;
- if (mode & S_IRUSR)
- prot |= FIBF_READ;
- if (mode & S_IWUSR)
- prot |= FIBF_WRITE | FIBF_DELETE;
+ u32 prot = AFFS_INODE->i_protect;
+ mode_t mode = inode->i_mode;
+
+ if (!(mode & S_IXUSR))
+ prot |= FIBF_NOEXECUTE;
+ if (!(mode & S_IRUSR))
+ prot |= FIBF_NOREAD;
+ if (!(mode & S_IWUSR))
+ prot |= FIBF_NOWRITE;
+ if (mode & S_IXGRP)
+ prot |= FIBF_GRP_EXECUTE;
if (mode & S_IRGRP)
prot |= FIBF_GRP_READ;
if (mode & S_IWGRP)
prot |= FIBF_GRP_WRITE;
+ if (mode & S_IXOTH)
+ prot |= FIBF_OTR_EXECUTE;
if (mode & S_IROTH)
prot |= FIBF_OTR_READ;
if (mode & S_IWOTH)
prot |= FIBF_OTR_WRITE;
-
- return prot;
+
+ AFFS_INODE->i_protect = prot;
}
void
vsprintf(ErrorBuffer,fmt,args);
va_end(args);
- printk(KERN_CRIT "AFFS error (device %s): %s(): %s\n",kdevname(sb->s_dev),
+ printk(KERN_CRIT "AFFS error (device %s): %s(): %s\n", bdevname(sb->s_dev),
function,ErrorBuffer);
if (!(sb->s_flags & MS_RDONLY))
printk(KERN_WARNING "AFFS: Remounting filesystem read-only\n");
sb->s_flags |= MS_RDONLY;
- sb->u.affs_sb.s_flags |= SF_READONLY; /* Don't allow to remount rw */
+ AFFS_SB->s_flags |= SF_READONLY; /* Don't allow to remount rw */
}
void
vsprintf(ErrorBuffer,fmt,args);
va_end(args);
- printk(KERN_WARNING "AFFS warning (device %s): %s(): %s\n",kdevname(sb->s_dev),
+ printk(KERN_WARNING "AFFS warning (device %s): %s(): %s\n", bdevname(sb->s_dev),
function,ErrorBuffer);
}
*/
int
-affs_copy_name(unsigned char *bstr, const unsigned char *name)
+affs_copy_name(unsigned char *bstr, struct dentry *dentry)
{
- int len;
+ int len = MIN(dentry->d_name.len, 30);
- for (len = 0; len < 30; len++) {
- bstr[len + 1] = name[len];
- if (name[len] == '\0')
- break;
- }
- bstr[0] = len;
+ *bstr++ = len;
+ memcpy(bstr, dentry->d_name.name, len);
return len;
}
* block allocation, deallocation, calculation of free space.
*/
-#define DEBUG 0
#include <linux/sched.h>
#include <linux/affs_fs.h>
#include <linux/stat.h>
#include <linux/kernel.h>
+#include <linux/slab.h>
#include <linux/string.h>
#include <linux/locks.h>
#include <linux/amigaffs.h>
static int nibblemap[] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
-int
-affs_count_free_bits(int blocksize, const char *data)
+u32
+affs_count_free_bits(u32 blocksize, const void *data)
{
- int free;
- int i;
+ const u32 *map;
+ u32 free;
+ u32 tmp;
- free = 0;
- for (i = 0; i < blocksize; i++) {
- free += nibblemap[data[i] & 0xF] + nibblemap[(data[i]>>4) & 0xF];
- }
+ map = data;
+ free = 0;
+ for (blocksize /= 4; blocksize > 0; blocksize--) {
+ tmp = *map++;
+ while (tmp) {
+ free += nibblemap[tmp & 0xf];
+ tmp >>= 4;
+ }
+ }
- return free;
+ return free;
}
-int
-affs_count_free_blocks(struct super_block *s)
+u32
+affs_count_free_blocks(struct super_block *sb)
{
- int free;
- int i;
+ struct affs_bm_info *bm;
+ u32 free;
+ int i;
pr_debug("AFFS: count_free_blocks()\n");
+ if (sb->s_flags & MS_RDONLY)
+ return 0;
+
+ down(&AFFS_SB->s_bmlock);
+
+ bm = AFFS_SB->s_bitmap;
free = 0;
- if (s->u.affs_sb.s_flags & SF_BM_VALID) {
- for (i = 0; i < s->u.affs_sb.s_num_az; i++) {
- free += s->u.affs_sb.s_alloc[i].az_free;
- }
- }
+ for (i = AFFS_SB->s_bmap_count; i > 0; bm++, i--)
+ free += bm->bm_free;
+
+ up(&AFFS_SB->s_bmlock);
+
return free;
}
void
-affs_free_block(struct super_block *sb, s32 block)
+affs_free_block(struct super_block *sb, u32 block)
{
- int bmap;
- int bit;
- int blk;
- int zone_no;
- struct affs_bm_info *bm;
-
- pr_debug("AFFS: free_block(%d)\n",block);
-
- blk = block - sb->u.affs_sb.s_reserved;
- bmap = blk / (sb->s_blocksize * 8 - 32);
- bit = blk % (sb->s_blocksize * 8 - 32);
- zone_no = (bmap << (sb->s_blocksize_bits - 7)) + bit / 1024;
- bm = &sb->u.affs_sb.s_bitmap[bmap];
- if (bmap >= sb->u.affs_sb.s_bm_count) {
- affs_error(sb,"affs_free_block","Block %d outside partition",block);
- return;
+ struct affs_bm_info *bm;
+ struct buffer_head *bh;
+ u32 blk, bmap, bit, mask, tmp;
+ u32 *data;
+
+ pr_debug("AFFS: free_block(%u)\n", block);
+
+ if (block > AFFS_SB->s_partition_size)
+ goto err_range;
+
+ blk = block - AFFS_SB->s_reserved;
+ bmap = blk / AFFS_SB->s_bmap_bits;
+ bit = blk % AFFS_SB->s_bmap_bits;
+ bm = &AFFS_SB->s_bitmap[bmap];
+
+ down(&AFFS_SB->s_bmlock);
+
+ bh = AFFS_SB->s_bmap_bh;
+ if (AFFS_SB->s_last_bmap != bmap) {
+ affs_brelse(bh);
+ bh = affs_bread(sb, bm->bm_key);
+ if (!bh)
+ goto err_bh_read;
+ AFFS_SB->s_bmap_bh = bh;
+ AFFS_SB->s_last_bmap = bmap;
}
- blk = 0;
- set_bit(bit & 31,&blk);
-
- lock_super(sb);
- bm->bm_count++;
- if (!bm->bm_bh) {
- bm->bm_bh = affs_bread(sb->s_dev,bm->bm_key,sb->s_blocksize);
- if (!bm->bm_bh) {
- bm->bm_count--;
- unlock_super(sb);
- affs_error(sb,"affs_free_block","Cannot read bitmap block %d",bm->bm_key);
- return;
- }
- }
- if (test_and_set_bit(bit ^ BO_EXBITS,bm->bm_bh->b_data + 4))
- affs_warning(sb,"affs_free_block","Trying to free block %d which is already free",
- block);
- else {
- sb->u.affs_sb.s_alloc[zone_no].az_free++;
- ((u32 *)bm->bm_bh->b_data)[0] = cpu_to_be32(be32_to_cpu(((u32 *)bm->bm_bh->b_data)[0]) - blk);
- mark_buffer_dirty(bm->bm_bh);
- sb->s_dirt = 1;
- }
- if (--bm->bm_count == 0) {
- affs_brelse(bm->bm_bh);
- bm->bm_bh = NULL;
- }
- unlock_super(sb);
+
+ mask = 1 << (bit & 31);
+ data = (u32 *)bh->b_data + bit / 32 + 1;
+
+ /* mark block free */
+ tmp = be32_to_cpu(*data);
+ if (tmp & mask)
+ goto err_free;
+ *data = cpu_to_be32(tmp | mask);
+
+ /* fix checksum */
+ tmp = be32_to_cpu(*(u32 *)bh->b_data);
+ *(u32 *)bh->b_data = cpu_to_be32(tmp - mask);
+
+ mark_buffer_dirty(bh);
+ sb->s_dirt = 1;
+ bm->bm_free++;
+
+ up(&AFFS_SB->s_bmlock);
+ return;
+
+err_free:
+ affs_warning(sb,"affs_free_block","Trying to free block %u which is already free", block);
+ up(&AFFS_SB->s_bmlock);
+ return;
+
+err_bh_read:
+ affs_error(sb,"affs_free_block","Cannot read bitmap block %u", bm->bm_key);
+ AFFS_SB->s_bmap_bh = NULL;
+ AFFS_SB->s_last_bmap = ~0;
+ up(&AFFS_SB->s_bmlock);
+ return;
+
+err_range:
+ affs_error(sb, "affs_free_block","Block %u outside partition", block);
+ return;
}
/*
* header zone, though.
*/
-static s32
-affs_balloc(struct inode *inode, int zone_no)
+u32
+affs_alloc_block(struct inode *inode, u32 goal)
{
- u32 w;
- u32 *bm;
- int fb;
- int i;
- int fwb;
- s32 block;
- struct affs_zone *zone;
- struct affs_alloc_zone *az;
- struct super_block *sb;
-
- sb = inode->i_sb;
- zone = &sb->u.affs_sb.s_zones[zone_no];
-
- if (!zone->z_bm || !zone->z_bm->bm_bh)
- return 0;
-
- pr_debug("AFFS: balloc(inode=%lu,zone=%d)\n",inode->i_ino,zone_no);
-
- az = &sb->u.affs_sb.s_alloc[zone->z_az_no];
- bm = (u32 *)zone->z_bm->bm_bh->b_data;
-repeat:
- for (i = zone->z_start; i < zone->z_end; i++) {
- if (bm[i])
- goto found;
- }
- return 0;
-
-found:
- fwb = zone->z_bm->bm_firstblk + (i - 1) * 32;
- lock_super(sb);
- zone->z_start = i;
- w = ~be32_to_cpu(bm[i]);
- fb = find_first_zero_bit(&w,32);
- if (fb > 31 || !test_and_clear_bit(fb ^ BO_EXBITS,&bm[i])) {
- unlock_super(sb);
- affs_warning(sb,"balloc","Empty block disappeared somehow");
- goto repeat;
- }
- block = fwb + fb;
- az->az_free--;
-
- /* prealloc as much as possible within this word, but not in header zone */
-
- if (zone_no) {
- while (inode->u.affs_i.i_pa_cnt < AFFS_MAX_PREALLOC && ++fb < 32) {
- fb = find_next_zero_bit(&w,32,fb);
- if (fb > 31)
- break;
- if (!test_and_clear_bit(fb ^ BO_EXBITS,&bm[i])) {
- affs_warning(sb,"balloc","Empty block disappeared somehow");
- break;
- }
- inode->u.affs_i.i_data[inode->u.affs_i.i_pa_last++] = fwb + fb;
- inode->u.affs_i.i_pa_last &= AFFS_MAX_PREALLOC - 1;
- inode->u.affs_i.i_pa_cnt++;
- az->az_free--;
- }
- }
- w = ~w - be32_to_cpu(bm[i]);
- bm[0] = cpu_to_be32(be32_to_cpu(bm[0]) + w);
- unlock_super(sb);
- mark_buffer_dirty(zone->z_bm->bm_bh);
- sb->s_dirt = 1;
- zone->z_lru_time = jiffies;
+ struct super_block *sb;
+ struct affs_bm_info *bm;
+ struct buffer_head *bh;
+ u32 *data, *enddata;
+ u32 blk, bmap, bit, mask, mask2, tmp;
+ int i;
- return block;
-}
-
-/* Find a new allocation zone, starting at zone_no. */
+ sb = inode->i_sb;
-static int
-affs_find_new_zone(struct super_block *sb, int zone_no)
-{
- struct affs_bm_info *bm;
- struct affs_zone *zone;
- struct affs_alloc_zone *az;
- int bestfree;
- int bestno;
- int bestused;
- int lusers;
- int i;
- int min;
-
- pr_debug("AFFS: find_new_zone(zone_no=%d)\n",zone_no);
-
- bestfree = 0;
- bestused = -1;
- bestno = -1;
- lusers = MAX_ZONES;
- min = zone_no ? AFFS_DATA_MIN_FREE : AFFS_HDR_MIN_FREE;
- lock_super(sb);
- zone = &sb->u.affs_sb.s_zones[zone_no];
- i = zone->z_az_no;
- az = &sb->u.affs_sb.s_alloc[i];
- if (zone->z_bm && zone->z_bm->bm_count) {
- if (--zone->z_bm->bm_count == 0) {
- affs_brelse(zone->z_bm->bm_bh);
- zone->z_bm->bm_bh = NULL;
- }
- if (az->az_count)
- az->az_count--;
- else
- affs_error(sb,"find_new_zone","az_count=0, but bm used");
+ pr_debug("AFFS: balloc(inode=%lu,goal=%u): ", inode->i_ino, goal);
+ if (inode->u.affs_i.i_pa_cnt) {
+ pr_debug("%d\n", inode->u.affs_i.i_lastalloc+1);
+ inode->u.affs_i.i_pa_cnt--;
+ return ++inode->u.affs_i.i_lastalloc;
}
- while (1) {
- az = &sb->u.affs_sb.s_alloc[i];
- if (!az->az_count) {
- if (az->az_free > min) {
- break;
- }
- if (az->az_free > bestfree) {
- bestfree = az->az_free;
- bestno = i;
- }
- } else if (az->az_free && az->az_count < lusers) {
- lusers = az->az_count;
- bestused = i;
- }
- if (++i >= sb->u.affs_sb.s_num_az)
- i = 0;
- if (i == zone->z_az_no) { /* Seen all */
- if (bestno >= 0) {
- i = bestno;
- } else {
- i = bestused;
- }
- break;
- }
+
+ if (!goal || goal > AFFS_SB->s_partition_size) {
+ if (goal)
+ affs_warning(sb, "affs_balloc", "invalid goal %d", goal);
+ //if (!inode->u.affs_i.i_last_block)
+ // affs_warning(sb, "affs_balloc", "no last alloc block");
+ goal = AFFS_SB->s_reserved;
}
- if (i < 0) {
- /* Didn't find a single free block anywhere. */
- unlock_super(sb);
- return 0;
+
+ blk = goal - AFFS_SB->s_reserved;
+ bmap = blk / AFFS_SB->s_bmap_bits;
+ bm = &AFFS_SB->s_bitmap[bmap];
+
+ down(&AFFS_SB->s_bmlock);
+
+ if (bm->bm_free)
+ goto find_bmap_bit;
+
+find_bmap:
+ /* search for the next bmap buffer with free bits */
+ i = AFFS_SB->s_bmap_count;
+ do {
+ bmap++;
+ bm++;
+ if (bmap < AFFS_SB->s_bmap_count)
+ continue;
+ /* restart search at zero */
+ bmap = 0;
+ bm = AFFS_SB->s_bitmap;
+ if (--i <= 0)
+ goto err_full;
+ } while (!bm->bm_free);
+ blk = bmap * AFFS_SB->s_bmap_bits;
+
+find_bmap_bit:
+
+ bh = AFFS_SB->s_bmap_bh;
+ if (AFFS_SB->s_last_bmap != bmap) {
+ affs_brelse(bh);
+ bh = affs_bread(sb, bm->bm_key);
+ if (!bh)
+ goto err_bh_read;
+ AFFS_SB->s_bmap_bh = bh;
+ AFFS_SB->s_last_bmap = bmap;
}
- az = &sb->u.affs_sb.s_alloc[i];
- az->az_count++;
- bm = &sb->u.affs_sb.s_bitmap[i >> (sb->s_blocksize_bits - 7)];
- bm->bm_count++;
- if (!bm->bm_bh)
- bm->bm_bh = affs_bread(sb->s_dev,bm->bm_key,sb->s_blocksize);
- if (!bm->bm_bh) {
- bm->bm_count--;
- az->az_count--;
- unlock_super(sb);
- affs_error(sb,"find_new_zone","Cannot read bitmap");
- return 0;
+
+ /* find an unused block in this bitmap block */
+ bit = blk % AFFS_SB->s_bmap_bits;
+ data = (u32 *)bh->b_data + bit / 32 + 1;
+ enddata = (u32 *)((u8 *)bh->b_data + sb->s_blocksize);
+ mask = ~0UL << (bit & 31);
+ blk &= ~31UL;
+
+ tmp = be32_to_cpu(*data) & mask;
+ if (tmp)
+ goto find_bit;
+
+ /* scan the rest of the buffer */
+ do {
+ blk += 32;
+ if (++data >= enddata)
+ /* didn't find something, can only happen
+ * if scan didn't start at 0, try next bmap
+ */
+ goto find_bmap;
+ } while (!(tmp = *data));
+ tmp = be32_to_cpu(tmp);
+
+find_bit:
+ /* finally look for a free bit in the word */
+ bit = ffs(tmp) - 1;
+ blk += bit + AFFS_SB->s_reserved;
+ mask2 = mask = 1 << (bit & 31);
+ inode->u.affs_i.i_lastalloc = blk;
+
+ /* prealloc as much as possible within this word */
+ while ((mask2 <<= 1)) {
+ if (!(tmp & mask2))
+ break;
+ inode->u.affs_i.i_pa_cnt++;
+ mask |= mask2;
}
- zone->z_bm = bm;
- zone->z_start = (i & ((sb->s_blocksize / 128) - 1)) * 32 + 1;
- zone->z_end = zone->z_start + az->az_size;
- zone->z_az_no = i;
- zone->z_lru_time = jiffies;
- pr_debug("AFFS: found zone (%d) in bm %d at lw offset %d with %d free blocks\n",
- i,(i >> (sb->s_blocksize_bits - 7)),zone->z_start,az->az_free);
- unlock_super(sb);
- return az->az_free;
-}
+ bm->bm_free -= inode->u.affs_i.i_pa_cnt + 1;
-/* Allocate a new header block. */
+ *data = cpu_to_be32(tmp & ~mask);
-s32
-affs_new_header(struct inode *inode)
-{
- s32 block;
+ /* fix checksum */
+ tmp = be32_to_cpu(*(u32 *)bh->b_data);
+ *(u32 *)bh->b_data = cpu_to_be32(tmp + mask);
- pr_debug("AFFS: new_header(ino=%lu)\n",inode->i_ino);
+ mark_buffer_dirty(bh);
+ sb->s_dirt = 1;
- if (!(block = affs_balloc(inode,0))) {
- while (affs_find_new_zone(inode->i_sb,0)) {
- if ((block = affs_balloc(inode,0)))
- return block;
- schedule();
- }
- return 0;
- }
- return block;
-}
+ up(&AFFS_SB->s_bmlock);
-/* Allocate a new data block. */
+ pr_debug("%d\n", blk);
+ return blk;
-s32
-affs_new_data(struct inode *inode)
+err_bh_read:
+ affs_error(sb,"affs_read_block","Cannot read bitmap block %u", bm->bm_key);
+ AFFS_SB->s_bmap_bh = NULL;
+ AFFS_SB->s_last_bmap = ~0;
+err_full:
+ pr_debug("failed\n");
+ up(&AFFS_SB->s_bmlock);
+ return 0;
+}
+
+int
+affs_init_bitmap(struct super_block *sb)
{
- int empty, old;
- unsigned long oldest;
- struct affs_zone *zone;
- struct super_block *sb;
- int i = 0;
- s32 block;
+ struct affs_bm_info *bm;
+ struct buffer_head *bmap_bh = NULL, *bh = NULL;
+ u32 *bmap_blk;
+ u32 size, blk, end, offset, mask;
+ int i, res = 0;
- pr_debug("AFFS: new_data(ino=%lu)\n",inode->i_ino);
+ if (sb->s_flags & MS_RDONLY)
+ return 0;
- sb = inode->i_sb;
- lock_super(sb);
- if (inode->u.affs_i.i_pa_cnt) {
- inode->u.affs_i.i_pa_cnt--;
- unlock_super(sb);
- block = inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++];
- inode->u.affs_i.i_pa_next &= AFFS_MAX_PREALLOC - 1;
- return block;
- }
- unlock_super(sb);
- oldest = jiffies;
- old = 0;
- empty = 0;
- zone = &sb->u.affs_sb.s_zones[inode->u.affs_i.i_zone];
- if (zone->z_ino == inode->i_ino) {
- i = inode->u.affs_i.i_zone;
- goto found;
- }
- for (i = 1; i < MAX_ZONES; i++) {
- zone = &sb->u.affs_sb.s_zones[i];
- if (!empty && zone->z_bm && !zone->z_ino)
- empty = i;
- if (zone->z_bm && zone->z_lru_time < oldest) {
- old = i;
- oldest = zone->z_lru_time;
- }
+ if (!AFFS_ROOT_TAIL(sb, AFFS_SB->s_root_bh)->bm_flag) {
+ printk(KERN_NOTICE "AFFS: Bitmap invalid - mounting %s read only\n",
+ kdevname(sb->s_dev));
+ sb->s_flags |= MS_RDONLY;
+ return 0;
}
- if (empty)
- i = empty;
- else if (old)
- i = old;
- else {
- inode->u.affs_i.i_zone = 0;
- return affs_new_header(inode);
+
+ AFFS_SB->s_last_bmap = ~0;
+ AFFS_SB->s_bmap_bh = NULL;
+ AFFS_SB->s_bmap_bits = sb->s_blocksize * 8 - 32;
+ AFFS_SB->s_bmap_count = (AFFS_SB->s_partition_size - AFFS_SB->s_reserved +
+ AFFS_SB->s_bmap_bits - 1) / AFFS_SB->s_bmap_bits;
+ size = AFFS_SB->s_bmap_count * sizeof(struct affs_bm_info);
+ bm = AFFS_SB->s_bitmap = kmalloc(size, GFP_KERNEL);
+ if (!AFFS_SB->s_bitmap) {
+ printk(KERN_ERR "AFFS: Bitmap allocation failed\n");
+ return 1;
}
+ memset(AFFS_SB->s_bitmap, 0, size);
+
+ bmap_blk = (u32 *)AFFS_SB->s_root_bh->b_data;
+ blk = sb->s_blocksize / 4 - 49;
+ end = blk + 25;
- inode->u.affs_i.i_zone = i;
- zone->z_ino = inode->i_ino;
+ for (i = AFFS_SB->s_bmap_count; i > 0; bm++, i--) {
+ affs_brelse(bh);
-found:
- zone = &sb->u.affs_sb.s_zones[i];
- if (!(block = affs_balloc(inode,i))) { /* No data zones left */
- while (affs_find_new_zone(sb,i)) {
- if ((block = affs_balloc(inode,i)))
- return block;
- schedule();
+ bm->bm_key = be32_to_cpu(bmap_blk[blk]);
+ bh = affs_bread(sb, bm->bm_key);
+ if (!bh) {
+ printk(KERN_ERR "AFFS: Cannot read bitmap\n");
+ res = 1;
+ goto out;
}
- inode->u.affs_i.i_zone = 0;
- zone->z_ino = -1;
- return 0;
+ if (affs_checksum_block(sb, bh)) {
+ printk(KERN_WARNING "AFFS: Bitmap %u invalid - mounting %s read only.\n",
+ bm->bm_key, kdevname(sb->s_dev));
+ sb->s_flags |= MS_RDONLY;
+ goto out;
+ }
+ pr_debug("AFFS: read bitmap block %d: %d\n", blk, bm->bm_key);
+ bm->bm_free = affs_count_free_bits(sb->s_blocksize - 4, bh->b_data + 4);
+
+ /* Don't try read the extension if this is the last block,
+ * but we also need the right bm pointer below
+ */
+ if (++blk < end || i == 1)
+ continue;
+ if (bmap_bh)
+ affs_brelse(bmap_bh);
+ bmap_bh = affs_bread(sb, be32_to_cpu(bmap_blk[blk]));
+ if (!bmap_bh) {
+ printk(KERN_ERR "AFFS: Cannot read bitmap extension\n");
+ res = 1;
+ goto out;
+ }
+ bmap_blk = (u32 *)bmap_bh->b_data;
+ blk = 0;
+ end = sb->s_blocksize / 4 - 1;
}
- return block;
-}
-
-void
-affs_make_zones(struct super_block *sb)
-{
- int i, mid;
-
- pr_debug("AFFS: make_zones(): num_zones=%d\n",sb->u.affs_sb.s_num_az);
- mid = (sb->u.affs_sb.s_num_az + 1) / 2;
- for (i = 0; i < MAX_ZONES; i++) {
- sb->u.affs_sb.s_zones[i].z_az_no = mid;
- affs_find_new_zone(sb,i);
+ offset = (AFFS_SB->s_partition_size - AFFS_SB->s_reserved) % AFFS_SB->s_bmap_bits;
+ mask = ~(0xFFFFFFFFU << (offset & 31));
+ pr_debug("last word: %d %d %d\n", offset, offset / 32 + 1, mask);
+ offset = offset / 32 + 1;
+
+ if (mask) {
+ u32 old, new;
+
+ /* Mark unused bits in the last word as allocated */
+ old = be32_to_cpu(((u32 *)bh->b_data)[offset]);
+ new = old & mask;
+ //if (old != new) {
+ ((u32 *)bh->b_data)[offset] = cpu_to_be32(new);
+ /* fix checksum */
+ //new -= old;
+ //old = be32_to_cpu(*(u32 *)bh->b_data);
+ //*(u32 *)bh->b_data = cpu_to_be32(old - new);
+ //mark_buffer_dirty(bh);
+ //}
+ /* correct offset for the bitmap count below */
+ //offset++;
}
+ while (++offset < sb->s_blocksize / 4)
+ ((u32 *)bh->b_data)[offset] = 0;
+ ((u32 *)bh->b_data)[0] = 0;
+ ((u32 *)bh->b_data)[0] = cpu_to_be32(-affs_checksum_block(sb, bh));
+ mark_buffer_dirty(bh);
+
+ /* recalculate bitmap count for last block */
+ bm--;
+ bm->bm_free = affs_count_free_bits(sb->s_blocksize - 4, bh->b_data + 4);
+
+out:
+ affs_brelse(bh);
+ affs_brelse(bmap_bh);
+ return res;
}
*
*/
-#define DEBUG 0
#include <asm/uaccess.h>
#include <linux/errno.h>
#include <linux/fs.h>
static int
affs_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
- int j, namelen;
- s32 i;
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *dir_bh;
+ struct buffer_head *fh_bh;
+ unsigned char *name;
+ int namelen;
+ u32 i;
int hash_pos;
int chain_pos;
- unsigned long ino;
+ u32 f_pos;
+ u32 ino;
int stored;
- unsigned char *name;
- struct buffer_head *dir_bh;
- struct buffer_head *fh_bh;
- struct inode *dir;
- struct inode *inode = filp->f_dentry->d_inode;
+ int res;
- pr_debug("AFFS: readdir(ino=%lu,f_pos=%lu)\n",inode->i_ino,(unsigned long)filp->f_pos);
+ pr_debug("AFFS: readdir(ino=%lu,f_pos=%lx)\n",inode->i_ino,(unsigned long)filp->f_pos);
stored = 0;
+ res = -EIO;
dir_bh = NULL;
fh_bh = NULL;
- dir = NULL;
- ino = inode->i_ino;
+ f_pos = filp->f_pos;
- if (filp->f_pos == 0) {
+ if (f_pos == 0) {
filp->private_data = (void *)0;
- if (filldir(dirent,".",1,filp->f_pos,inode->i_ino,DT_DIR) < 0) {
+ if (filldir(dirent, ".", 1, f_pos, inode->i_ino, DT_DIR) < 0)
return 0;
- }
- ++filp->f_pos;
+ filp->f_pos = f_pos = 1;
stored++;
}
- if (filp->f_pos == 1) {
- if (filldir(dirent,"..",2,filp->f_pos,affs_parent_ino(inode),DT_DIR) < 0) {
+ if (f_pos == 1) {
+ if (filldir(dirent, "..", 2, f_pos, filp->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
return stored;
- }
- filp->f_pos = 2;
+ filp->f_pos = f_pos = 2;
stored++;
}
- chain_pos = (filp->f_pos - 2) & 0xffff;
- hash_pos = (filp->f_pos - 2) >> 16;
+
+ down(&AFFS_INODE->i_hash_lock);
+ chain_pos = (f_pos - 2) & 0xffff;
+ hash_pos = (f_pos - 2) >> 16;
if (chain_pos == 0xffff) {
- affs_warning(inode->i_sb,"readdir","More than 65535 entries in chain");
+ affs_warning(sb, "readdir", "More than 65535 entries in chain");
chain_pos = 0;
hash_pos++;
filp->f_pos = ((hash_pos << 16) | chain_pos) + 2;
}
- if (!(dir_bh = affs_bread(inode->i_dev,inode->i_ino,
- AFFS_I2BSIZE(inode))))
- goto readdir_done;
+ dir_bh = affs_bread(sb, inode->i_ino);
+ if (!dir_bh)
+ goto readdir_out;
- while (1) {
- while (hash_pos < AFFS_I2HSIZE(inode) &&
- !((struct dir_front *)dir_bh->b_data)->hashtable[hash_pos])
- hash_pos++;
- if (hash_pos >= AFFS_I2HSIZE(inode))
- break;
-
- i = be32_to_cpu(((struct dir_front *)dir_bh->b_data)->hashtable[hash_pos]);
- j = chain_pos;
+ /* If the directory hasn't changed since the last call to readdir(),
+ * we can jump directly to where we left off.
+ */
+ ino = (u32)filp->private_data;
+ if (ino && filp->f_version == inode->i_version) {
+ pr_debug("AFFS: readdir() left off=%d\n", ino);
+ goto inside;
+ }
- /* If the directory hasn't changed since the last call to readdir(),
- * we can jump directly to where we left off.
- */
- if (filp->private_data && filp->f_version == inode->i_version) {
- i = (s32)(unsigned long)filp->private_data;
- j = 0;
- pr_debug("AFFS: readdir() left off=%d\n",i);
+ ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[hash_pos]);
+ for (i = 0; ino && i < chain_pos; i++) {
+ fh_bh = affs_bread(sb, ino);
+ if (!fh_bh) {
+ affs_error(sb, "readdir","Cannot read block %d", i);
+ goto readdir_out;
}
- filp->f_version = inode->i_version;
- pr_debug("AFFS: hash_pos=%d chain_pos=%d\n",hash_pos,chain_pos);
- while (i) {
- if (!(fh_bh = affs_bread(inode->i_dev,i,AFFS_I2BSIZE(inode)))) {
- affs_error(inode->i_sb,"readdir","Cannot read block %d",i);
+ ino = be32_to_cpu(AFFS_TAIL(sb, fh_bh)->hash_chain);
+ affs_brelse(fh_bh);
+ fh_bh = NULL;
+ }
+ if (ino)
+ goto inside;
+ hash_pos++;
+
+ for (; hash_pos < AFFS_SB->s_hashsize; hash_pos++) {
+ ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[hash_pos]);
+ if (!ino)
+ continue;
+ f_pos = (hash_pos << 16) + 2;
+inside:
+ do {
+ fh_bh = affs_bread(sb, ino);
+ if (!fh_bh) {
+ affs_error(sb, "readdir","Cannot read block %d", ino);
goto readdir_done;
}
- ino = i;
- i = be32_to_cpu(FILE_END(fh_bh->b_data,inode)->hash_chain);
- if (j == 0)
- break;
- affs_brelse(fh_bh);
- fh_bh = NULL;
- j--;
- }
- if (fh_bh) {
- namelen = affs_get_file_name(AFFS_I2BSIZE(inode),fh_bh->b_data,&name);
- pr_debug("AFFS: readdir(): filldir(\"%.*s\",ino=%lu), i=%d\n",
- namelen,name,ino,i);
- filp->private_data = (void *)ino;
- if (filldir(dirent,name,namelen,filp->f_pos,ino,DT_UNKNOWN) < 0)
+
+ namelen = MIN(AFFS_TAIL(sb, fh_bh)->name[0], 30);
+ name = AFFS_TAIL(sb, fh_bh)->name + 1;
+ pr_debug("AFFS: readdir(): filldir(\"%.*s\", ino=%u), hash=%d, f_pos=%x\n",
+ namelen, name, ino, hash_pos, f_pos);
+ if (filldir(dirent, name, namelen, f_pos, ino, DT_UNKNOWN) < 0)
goto readdir_done;
- filp->private_data = (void *)(unsigned long)i;
+ stored++;
+ f_pos++;
+ ino = be32_to_cpu(AFFS_TAIL(sb, fh_bh)->hash_chain);
affs_brelse(fh_bh);
fh_bh = NULL;
- stored++;
- }
- if (i == 0) {
- hash_pos++;
- chain_pos = 0;
- } else
- chain_pos++;
- filp->f_pos = ((hash_pos << 16) | chain_pos) + 2;
+ } while (ino);
}
-
readdir_done:
+ filp->f_pos = f_pos;
+ filp->f_version = inode->i_version;
+ filp->private_data = (void *)ino;
+ res = stored;
+
+readdir_out:
affs_brelse(dir_bh);
affs_brelse(fh_bh);
- pr_debug("AFFS: readdir()=%d\n",stored);
- return stored;
+ up(&AFFS_INODE->i_hash_lock);
+ pr_debug("AFFS: readdir()=%d\n", stored);
+ return res;
}
* affs regular file handling primitives
*/
-#define DEBUG 0
#include <asm/div64.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/mm.h>
#include <linux/pagemap.h>
-#define MIN(a,b) (((a)<(b))?(a):(b))
-#define MAX(a,b) (((a)>(b))?(a):(b))
-
#if PAGE_SIZE < 4096
#error PAGE_SIZE must be at least 4096
#endif
-static struct buffer_head *affs_getblock(struct inode *inode, s32 block);
-static ssize_t affs_file_read_ofs(struct file *filp, char *buf, size_t count, loff_t *ppos);
+static int affs_grow_extcache(struct inode *inode, u32 lc_idx);
+static struct buffer_head *affs_alloc_extblock(struct inode *inode, struct buffer_head *bh, u32 ext);
+static inline struct buffer_head *affs_get_extblock(struct inode *inode, u32 ext);
+static struct buffer_head *affs_get_extblock_slow(struct inode *inode, u32 ext);
+static int affs_get_block(struct inode *inode, long block, struct buffer_head *bh_result, int create);
+
static ssize_t affs_file_write(struct file *filp, const char *buf, size_t count, loff_t *ppos);
-static ssize_t affs_file_write_ofs(struct file *filp, const char *buf, size_t cnt, loff_t *ppos);
-static int alloc_ext_cache(struct inode *inode);
+static int affs_file_open(struct inode *inode, struct file *filp);
+static int affs_file_release(struct inode *inode, struct file *filp);
struct file_operations affs_file_operations = {
read: generic_file_read,
write: affs_file_write,
mmap: generic_file_mmap,
+ open: affs_file_open,
+ release: affs_file_release,
fsync: file_fsync,
};
setattr: affs_notify_change,
};
-struct file_operations affs_file_operations_ofs = {
- read: affs_file_read_ofs,
- write: affs_file_write_ofs,
- fsync: file_fsync,
-};
-
-#define AFFS_ISINDEX(x) ((x < 129) || \
- (x < 512 && (x & 1) == 0) || \
- (x < 1024 && (x & 3) == 0) || \
- (x < 2048 && (x & 15) == 0) || \
- (x < 4096 && (x & 63) == 0) || \
- (x < 20480 && (x & 255) == 0) || \
- (x < 36864 && (x & 511) == 0))
-
-/* The keys of the extension blocks are stored in a 512-entry
- * deep cache. In order to save memory, not every key of later
- * extension blocks is stored - the larger the file gets, the
- * bigger the holes in between.
- */
-
static int
-seqnum_to_index(int seqnum)
+affs_file_open(struct inode *inode, struct file *filp)
{
- /* All of the first 127 keys are stored */
- if (seqnum < 128)
- return seqnum;
- seqnum -= 128;
-
- /* Of the next 384 keys, every 2nd is kept */
- if (seqnum < (192 * 2))
- return 128 + (seqnum >> 1);
- seqnum -= 192 * 2;
-
- /* Every 4th of the next 512 */
- if (seqnum < (128 * 4))
- return 128 + 192 + (seqnum >> 2);
- seqnum -= 128 * 4;
-
- /* Every 16th of the next 1024 */
- if (seqnum < (64 * 16))
- return 128 + 192 + 128 + (seqnum >> 4);
- seqnum -= 64 * 16;
-
- /* Every 64th of the next 2048 */
- if (seqnum < (32 * 64))
- return 128 + 192 + 128 + 64 + (seqnum >> 6);
- seqnum -= 32 * 64;
-
- /* Every 256th of the next 16384 */
- if (seqnum < (64 * 256))
- return 128 + 192 + 128 + 64 + 32 + (seqnum >> 8);
- seqnum -= 64 * 256;
-
- /* Every 512th upto 36479 (1.3 GB with 512 byte blocks).
- * Seeking to positions behind this will get slower
- * than dead snails nailed to the ground. But if
- * someone uses files that large with 512-byte blocks,
- * he or she deserves no better.
- */
-
- if (seqnum > (31 * 512))
- seqnum = 31 * 512;
- return 128 + 192 + 128 + 64 + 32 + 64 + (seqnum >> 9);
+ if (atomic_read(&filp->f_count) != 1)
+ return 0;
+ pr_debug("AFFS: open(%d)\n", AFFS_INODE->i_opencnt);
+ AFFS_INODE->i_opencnt++;
+ return 0;
}
-/* Now the other way round: Calculate the sequence
- * number of an extension block of a key at the
- * given index in the cache.
- */
-
static int
-index_to_seqnum(int index)
+affs_file_release(struct inode *inode, struct file *filp)
{
- if (index < 128)
- return index;
- index -= 128;
- if (index < 192)
- return 128 + (index << 1);
- index -= 192;
- if (index < 128)
- return 128 + 192 * 2 + (index << 2);
- index -= 128;
- if (index < 64)
- return 128 + 192 * 2 + 128 * 4 + (index << 4);
- index -= 64;
- if (index < 32)
- return 128 + 192 * 2 + 128 * 4 + 64 * 16 + (index << 6);
- index -= 32;
- if (index < 64)
- return 128 + 192 * 2 + 128 * 4 + 64 * 16 + 32 * 64 + (index << 8);
- index -= 64;
- return 128 + 192 * 2 + 128 * 4 + 64 * 16 + 32 * 64 + 64 * 256 + (index << 9);
-}
+ if (atomic_read(&filp->f_count) != 0)
+ return 0;
+ pr_debug("AFFS: release(%d)\n", AFFS_INODE->i_opencnt);
+ AFFS_INODE->i_opencnt--;
+ if (!AFFS_INODE->i_opencnt)
+ affs_free_prealloc(inode);
-static s32 __inline__
-calc_key(struct inode *inode, int *ext)
-{
- int index;
- struct key_cache *kc;
-
- for (index = 0; index < 4; index++) {
- kc = &inode->u.affs_i.i_ec->kc[index];
- if (kc->kc_last == -1)
- continue; /* don't look in cache if invalid. */
- if (*ext == kc->kc_this_seq) {
- return kc->kc_this_key;
- } else if (*ext == kc->kc_this_seq + 1) {
- if (kc->kc_next_key)
- return kc->kc_next_key;
- else {
- (*ext)--;
- return kc->kc_this_key;
- }
- }
- }
- index = seqnum_to_index(*ext);
- if (index > inode->u.affs_i.i_ec->max_ext)
- index = inode->u.affs_i.i_ec->max_ext;
- *ext = index_to_seqnum(index);
- return inode->u.affs_i.i_ec->ec[index];
+ return 0;
}
-int
-affs_bmap(struct inode *inode, int block)
+static int
+affs_grow_extcache(struct inode *inode, u32 lc_idx)
{
+ struct super_block *sb = inode->i_sb;
struct buffer_head *bh;
- s32 key, nkey;
- s32 ptype, stype;
- int ext;
- int index;
- int keycount;
- struct key_cache *kc;
- struct key_cache *tkc;
- struct timeval tv;
- s32 *keyp;
- int i;
-
- pr_debug("AFFS: bmap(%lu,%d)\n",inode->i_ino,block);
-
- lock_kernel();
- if (block < 0) {
- affs_error(inode->i_sb,"bmap","Block < 0");
- goto out_fail;
- }
- if (!inode->u.affs_i.i_ec) {
- if (alloc_ext_cache(inode)) {
- goto out_fail;
- }
+ u32 lc_max;
+ int i, j, key;
+
+ if (!AFFS_INODE->i_lc) {
+ char *ptr = (char *)get_zeroed_page(GFP_BUFFER);
+ if (!ptr)
+ return -ENOMEM;
+ AFFS_INODE->i_lc = (u32 *)ptr;
+ AFFS_INODE->i_ac = (struct affs_ext_key *)(ptr + AFFS_CACHE_SIZE / 2);
}
- /* Try to find the requested key in the cache.
- * In order to speed this up as much as possible,
- * the cache line lookup is done in a separate
- * step.
- */
+ lc_max = AFFS_LC_SIZE << AFFS_INODE->i_lc_shift;
- for (i = 0; i < 4; i++) {
- tkc = &inode->u.affs_i.i_ec->kc[i];
- /* Look in any cache if the key is there */
- if (block <= tkc->kc_last && block >= tkc->kc_first) {
- unlock_kernel();
- return tkc->kc_keys[block - tkc->kc_first];
- }
+ if (AFFS_INODE->i_extcnt > lc_max) {
+ u32 lc_shift, lc_mask, tmp, off;
+
+ /* need to recalculate linear cache, start from old size */
+ lc_shift = AFFS_INODE->i_lc_shift;
+ tmp = (AFFS_INODE->i_extcnt / AFFS_LC_SIZE) >> lc_shift;
+ for (; tmp; tmp >>= 1)
+ lc_shift++;
+ lc_mask = (1 << lc_shift) - 1;
+
+ /* fix idx and old size to new shift */
+ lc_idx >>= (lc_shift - AFFS_INODE->i_lc_shift);
+ AFFS_INODE->i_lc_size >>= (lc_shift - AFFS_INODE->i_lc_shift);
+
+ /* first shrink old cache to make more space */
+ off = 1 << (lc_shift - AFFS_INODE->i_lc_shift);
+ for (i = 1, j = off; j < AFFS_LC_SIZE; i++, j += off)
+ AFFS_INODE->i_ac[i] = AFFS_INODE->i_ac[j];
+
+ AFFS_INODE->i_lc_shift = lc_shift;
+ AFFS_INODE->i_lc_mask = lc_mask;
}
- kc = NULL;
- tv = xtime;
- for (i = 0; i < 4; i++) {
- tkc = &inode->u.affs_i.i_ec->kc[i];
- if (tkc->kc_lru_time.tv_sec > tv.tv_sec)
+
+ /* fill cache to the needed index */
+ i = AFFS_INODE->i_lc_size;
+ AFFS_INODE->i_lc_size = lc_idx + 1;
+ for (; i <= lc_idx; i++) {
+ if (!i) {
+ AFFS_INODE->i_lc[0] = inode->i_ino;
continue;
- if (tkc->kc_lru_time.tv_sec < tv.tv_sec ||
- tkc->kc_lru_time.tv_usec < tv.tv_usec) {
- kc = tkc;
- tv = tkc->kc_lru_time;
}
- }
- if (!kc) /* Really shouldn't happen */
- kc = tkc;
- kc->kc_lru_time = xtime;
- keyp = kc->kc_keys;
- kc->kc_first = block;
- kc->kc_last = -1;
- keycount = AFFS_KCSIZE;
-
- /* Calculate sequence number of the extension block where the
- * number of the requested block is stored. 0 means it's in
- * the file header.
- */
-
- ext = block / AFFS_I2HSIZE(inode);
- key = calc_key(inode,&ext);
- block -= ext * AFFS_I2HSIZE(inode);
-
- for (;;) {
- bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
- if (!bh)
- goto out_fail;
-
- index = seqnum_to_index(ext);
- if (index > inode->u.affs_i.i_ec->max_ext &&
- (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype) ||
- (ptype != T_SHORT && ptype != T_LIST) || stype != ST_FILE)) {
+ key = AFFS_INODE->i_lc[i - 1];
+ j = AFFS_INODE->i_lc_mask + 1;
+ // unlock cache
+ for (; j > 0; j--) {
+ bh = affs_bread(sb, key);
+ if (!bh)
+ goto err;
+ key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
affs_brelse(bh);
- goto out_fail;
- }
- nkey = be32_to_cpu(FILE_END(bh->b_data,inode)->extension);
- if (block < AFFS_I2HSIZE(inode)) {
- /* Fill cache as much as possible */
- if (keycount) {
- kc->kc_first = ext * AFFS_I2HSIZE(inode) + block;
- keycount = keycount < AFFS_I2HSIZE(inode) - block ? keycount :
- AFFS_I2HSIZE(inode) - block;
- for (i = 0; i < keycount; i++)
- kc->kc_keys[i] = be32_to_cpu(AFFS_BLOCK(bh->b_data,inode,block + i));
- kc->kc_last = kc->kc_first + i - 1;
- }
- break;
}
- block -= AFFS_I2HSIZE(inode);
- affs_brelse(bh);
- ext++;
- if (index > inode->u.affs_i.i_ec->max_ext && AFFS_ISINDEX(ext)) {
- inode->u.affs_i.i_ec->ec[index] = nkey;
- inode->u.affs_i.i_ec->max_ext = index;
- }
- key = nkey;
+ // lock cache
+ AFFS_INODE->i_lc[i] = key;
+ }
+
+ return 0;
+
+err:
+ // lock cache
+ return -EIO;
+}
+
+static struct buffer_head *
+affs_alloc_extblock(struct inode *inode, struct buffer_head *bh, u32 ext)
+{
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *new_bh;
+ u32 blocknr, tmp;
+
+ blocknr = affs_alloc_block(inode, bh->b_blocknr);
+ if (!blocknr)
+ return ERR_PTR(-ENOSPC);
+
+ new_bh = affs_getzeroblk(sb, blocknr);
+ if (!new_bh) {
+ affs_free_block(sb, blocknr);
+ return ERR_PTR(-EIO);
}
- kc->kc_this_key = key;
- kc->kc_this_seq = ext;
- kc->kc_next_key = nkey;
- key = be32_to_cpu(AFFS_BLOCK(bh->b_data,inode,block));
- affs_brelse(bh);
-out:
- unlock_kernel();
- return key;
-out_fail:
- key=0;
- goto out;
+ AFFS_HEAD(new_bh)->ptype = cpu_to_be32(T_LIST);
+ AFFS_HEAD(new_bh)->key = cpu_to_be32(blocknr);
+ AFFS_TAIL(sb, new_bh)->stype = cpu_to_be32(ST_FILE);
+ AFFS_TAIL(sb, new_bh)->parent = cpu_to_be32(inode->i_ino);
+ affs_fix_checksum(sb, new_bh);
+
+ mark_buffer_dirty(new_bh);
+
+ tmp = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
+ if (tmp)
+ affs_warning(sb, "alloc_ext", "previous extension set (%x)", tmp);
+ AFFS_TAIL(sb, bh)->extension = cpu_to_be32(blocknr);
+ affs_adjust_checksum(bh, blocknr - tmp);
+ mark_buffer_dirty(bh);
+
+ AFFS_INODE->i_extcnt++;
+ mark_inode_dirty(inode);
+
+ return new_bh;
}
+static inline struct buffer_head *
+affs_get_extblock(struct inode *inode, u32 ext)
+{
+ /* inline the simplest case: same extended block as last time */
+ struct buffer_head *bh = AFFS_INODE->i_ext_bh;
+ if (ext == AFFS_INODE->i_ext_last)
+ atomic_inc(&bh->b_count);
+ else
+ /* we have to do more (not inlined) */
+ bh = affs_get_extblock_slow(inode, ext);
-static int affs_get_block(struct inode *inode, long block, struct buffer_head *bh_result, int create)
+ return bh;
+}
+
+static struct buffer_head *
+affs_get_extblock_slow(struct inode *inode, u32 ext)
{
- int err, phys=0, new=0;
-
- if (!create) {
- phys = affs_bmap(inode, block);
- if (phys) {
- bh_result->b_dev = inode->i_dev;
- bh_result->b_blocknr = phys;
- bh_result->b_state |= (1UL << BH_Mapped);
- }
- return 0;
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *bh;
+ u32 ext_key;
+ u32 lc_idx, lc_off, ac_idx;
+ u32 tmp, idx;
+
+ if (ext == AFFS_INODE->i_ext_last + 1) {
+ /* read the next extended block from the current one */
+ bh = AFFS_INODE->i_ext_bh;
+ ext_key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
+ if (ext < AFFS_INODE->i_extcnt)
+ goto read_ext;
+ if (ext > AFFS_INODE->i_extcnt)
+ BUG();
+ bh = affs_alloc_extblock(inode, bh, ext);
+ if (IS_ERR(bh))
+ return bh;
+ goto store_ext;
}
- err = -EIO;
- lock_kernel();
- if (block < 0)
- goto abort_negative;
+ if (ext == 0) {
+ /* we seek back to the file header block */
+ ext_key = inode->i_ino;
+ goto read_ext;
+ }
- if (affs_getblock(inode, block)==NULL) {
- err = -EIO;
- goto abort;
+ if (ext >= AFFS_INODE->i_extcnt) {
+ struct buffer_head *prev_bh;
+
+ /* allocate a new extended block */
+ if (ext > AFFS_INODE->i_extcnt)
+ BUG();
+
+ /* get previous extended block */
+ prev_bh = affs_get_extblock(inode, ext - 1);
+ if (IS_ERR(prev_bh))
+ return prev_bh;
+ bh = affs_alloc_extblock(inode, prev_bh, ext);
+ affs_brelse(prev_bh);
+ if (IS_ERR(bh))
+ return bh;
+ goto store_ext;
+ }
+
+again:
+ /* check if there is an extended cache and whether it's large enough */
+ lc_idx = ext >> AFFS_INODE->i_lc_shift;
+ lc_off = ext & AFFS_INODE->i_lc_mask;
+
+ if (lc_idx >= AFFS_INODE->i_lc_size) {
+ int err;
+
+ err = affs_grow_extcache(inode, lc_idx);
+ if (err)
+ return ERR_PTR(err);
+ goto again;
+ }
+
+ /* every n'th key we find in the linear cache */
+ if (!lc_off) {
+ ext_key = AFFS_INODE->i_lc[lc_idx];
+ goto read_ext;
}
+ /* maybe it's still in the associative cache */
+ ac_idx = (ext - lc_idx - 1) & AFFS_AC_MASK;
+ if (AFFS_INODE->i_ac[ac_idx].ext == ext) {
+ ext_key = AFFS_INODE->i_ac[ac_idx].key;
+ goto read_ext;
+ }
+
+ /* try to find one of the previous extended blocks */
+ tmp = ext;
+ idx = ac_idx;
+ while (--tmp, --lc_off > 0) {
+ idx = (idx - 1) & AFFS_AC_MASK;
+ if (AFFS_INODE->i_ac[idx].ext == tmp) {
+ ext_key = AFFS_INODE->i_ac[idx].key;
+ goto find_ext;
+ }
+ }
+
+ /* fall back to the linear cache */
+ ext_key = AFFS_INODE->i_lc[lc_idx];
+find_ext:
+ /* read all extended blocks until we find the one we need */
+ //unlock cache
+ do {
+ bh = affs_bread(sb, ext_key);
+ if (!bh)
+ goto err_bread;
+ ext_key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
+ affs_brelse(bh);
+ tmp++;
+ } while (tmp < ext);
+ //lock cache
+
+ /* store it in the associative cache */
+ // recalculate ac_idx?
+ AFFS_INODE->i_ac[ac_idx].ext = ext;
+ AFFS_INODE->i_ac[ac_idx].key = ext_key;
+
+read_ext:
+ /* finally read the right extended block */
+ //unlock cache
+ bh = affs_bread(sb, ext_key);
+ if (!bh)
+ goto err_bread;
+ //lock cache
+
+store_ext:
+ /* release old cached extended block and store the new one */
+ affs_brelse(AFFS_INODE->i_ext_bh);
+ AFFS_INODE->i_ext_last = ext;
+ AFFS_INODE->i_ext_bh = bh;
+ atomic_inc(&bh->b_count);
+
+ return bh;
+
+err_bread:
+ affs_brelse(bh);
+ return ERR_PTR(-EIO);
+}
+
+static int
+affs_get_block(struct inode *inode, long block, struct buffer_head *bh_result, int create)
+{
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *ext_bh;
+ u32 ext;
+
+ pr_debug("AFFS: get_block(%u, %ld)\n", (u32)inode->i_ino, block);
+
+ if (block < 0)
+ goto err_small;
+
+ if (block >= AFFS_INODE->i_blkcnt) {
+ if (block > AFFS_INODE->i_blkcnt || !create)
+ goto err_big;
+ } else
+ create = 0;
+
+ //lock cache
+ down(&AFFS_INODE->i_ext_lock);
+
+ ext = block / AFFS_SB->s_hashsize;
+ block -= ext * AFFS_SB->s_hashsize;
+ ext_bh = affs_get_extblock(inode, ext);
+ if (IS_ERR(ext_bh))
+ goto err_ext;
+ bh_result->b_blocknr = be32_to_cpu(AFFS_BLOCK(sb, ext_bh, block));
bh_result->b_dev = inode->i_dev;
- bh_result->b_blocknr = phys;
bh_result->b_state |= (1UL << BH_Mapped);
- if (new)
- bh_result->b_state |= (1UL << BH_New);
-
-abort:
- unlock_kernel();
- return err;
-abort_negative:
- affs_error(inode->i_sb,"affs_get_block","Block < 0");
- goto abort;
+ if (create) {
+ u32 blocknr = affs_alloc_block(inode, ext_bh->b_blocknr);
+ if (!blocknr)
+ goto err_alloc;
+ bh_result->b_state |= (1UL << BH_New);
+ AFFS_INODE->mmu_private += AFFS_SB->s_data_blksize;
+ AFFS_INODE->i_blkcnt++;
+
+ /* store new block */
+ if (bh_result->b_blocknr)
+ affs_warning(sb, "get_block", "block already set (%x)", bh_result->b_blocknr);
+ AFFS_BLOCK(sb, ext_bh, block) = cpu_to_be32(blocknr);
+ AFFS_HEAD(ext_bh)->block_count = cpu_to_be32(block + 1);
+ affs_adjust_checksum(ext_bh, blocknr - bh_result->b_blocknr + 1);
+ bh_result->b_blocknr = blocknr;
+
+ if (!block) {
+ /* insert first block into header block */
+ u32 tmp = be32_to_cpu(AFFS_HEAD(ext_bh)->first_data);
+ if (tmp)
+ affs_warning(sb, "get_block", "first block already set (%d)", tmp);
+ AFFS_HEAD(ext_bh)->first_data = cpu_to_be32(blocknr);
+ affs_adjust_checksum(ext_bh, blocknr - tmp);
+ }
+ }
+ affs_brelse(ext_bh);
+ //unlock cache
+ up(&AFFS_INODE->i_ext_lock);
+ return 0;
+
+err_small:
+ affs_error(inode->i_sb,"get_block","Block < 0");
+ return -EIO;
+err_big:
+ affs_error(inode->i_sb,"get_block","strange block request %d", block);
+ return -EIO;
+err_ext:
+ // unlock cache
+ up(&AFFS_INODE->i_ext_lock);
+ return PTR_ERR(ext_bh);
+err_alloc:
+ brelse(ext_bh);
+ bh_result->b_state &= ~(1UL << BH_Mapped);
+ // unlock cache
+ up(&AFFS_INODE->i_ext_lock);
+ return -ENOSPC;
}
-
+
static int affs_writepage(struct page *page)
{
- return block_write_full_page(page,affs_get_block);
+ return block_write_full_page(page, affs_get_block);
}
static int affs_readpage(struct file *file, struct page *page)
{
- return block_read_full_page(page,affs_get_block);
+ return block_read_full_page(page, affs_get_block);
}
static int affs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
{
- return cont_prepare_write(page,from,to,affs_get_block,
+ return cont_prepare_write(page, from, to, affs_get_block,
&page->mapping->host->u.affs_i.mmu_private);
}
static int _affs_bmap(struct address_space *mapping, long block)
bmap: _affs_bmap
};
-/* With the affs, getting a random block from a file is not
- * a simple business. Since this fs does not allow holes,
- * it may be necessary to allocate all the missing blocks
- * in between, as well as some new extension blocks. The OFS
- * is even worse: All data blocks contain pointers to the
- * next ones, so you have to fix [n-1] after allocating [n].
- * What a mess.
- */
-
-static struct buffer_head * affs_getblock(struct inode *inode, s32 block)
+static inline struct buffer_head *
+affs_bread_ino(struct inode *inode, int block, int create)
{
- struct super_block *sb = inode->i_sb;
- int ofs = sb->u.affs_sb.s_flags & SF_OFS;
- int ext = block / AFFS_I2HSIZE(inode);
- struct buffer_head *bh, *ebh, *pbh = NULL;
- struct key_cache *kc;
- s32 key, nkey;
- int cf, j, pt;
- int index;
- int err;
-
- pr_debug("AFFS: getblock(%lu,%d)\n",inode->i_ino,block);
-
- key = calc_key(inode,&ext);
- block -= ext * AFFS_I2HSIZE(inode);
- pt = ext ? T_LIST : T_SHORT;
-
- /* Key refers now to the last known extension block,
- * ext is its sequence number (if 0, key refers to the
- * header block), and block is the block number relative
- * to the first block stored in that extension block.
- */
- for (;;) { /* Loop over header block and extension blocks */
- struct file_front *fdp;
-
- bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
- if (!bh)
- goto out_fail;
- fdp = (struct file_front *) bh->b_data;
- err = affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&cf,&j);
- if (err || cf != pt || j != ST_FILE) {
- affs_error(sb, "getblock",
- "Block %d is not a valid %s", key,
- pt == T_SHORT ? "file header" : "ext block");
- goto out_free_bh;
- }
- j = be32_to_cpu(((struct file_front *)bh->b_data)->block_count);
- for (cf = 0; j < AFFS_I2HSIZE(inode) && j <= block; j++) {
- if (ofs && !pbh && inode->u.affs_i.i_lastblock >= 0) {
- if (j > 0) {
- s32 k = AFFS_BLOCK(bh->b_data, inode, j - 1);
- pbh = affs_bread(inode->i_dev,
- be32_to_cpu(k),
- AFFS_I2BSIZE(inode));
- } else
- pbh = affs_getblock(inode,inode->u.affs_i.i_lastblock);
- if (!pbh) {
- affs_error(sb,"getblock", "Cannot get last block in file");
- break;
- }
- }
- nkey = affs_new_data(inode);
- if (!nkey)
- break;
- inode->u.affs_i.i_lastblock++;
- if (AFFS_BLOCK(bh->b_data,inode,j)) {
- affs_warning(sb,"getblock","Block already allocated");
- affs_free_block(sb,nkey);
- continue;
- }
- AFFS_BLOCK(bh->b_data,inode,j) = cpu_to_be32(nkey);
- if (ofs) {
- ebh = affs_bread(inode->i_dev,nkey,AFFS_I2BSIZE(inode));
- if (!ebh) {
- affs_error(sb,"getblock", "Cannot get block %d",nkey);
- affs_free_block(sb,nkey);
- AFFS_BLOCK(bh->b_data,inode,j) = 0;
- break;
- }
- DATA_FRONT(ebh)->primary_type = cpu_to_be32(T_DATA);
- DATA_FRONT(ebh)->header_key = cpu_to_be32(inode->i_ino);
- DATA_FRONT(ebh)->sequence_number = cpu_to_be32(inode->u.affs_i.i_lastblock + 1);
- affs_fix_checksum(AFFS_I2BSIZE(inode), ebh->b_data, 5);
- mark_buffer_dirty(ebh);
- if (pbh) {
- DATA_FRONT(pbh)->data_size = cpu_to_be32(AFFS_I2BSIZE(inode) - 24);
- DATA_FRONT(pbh)->next_data = cpu_to_be32(nkey);
- affs_fix_checksum(AFFS_I2BSIZE(inode),pbh->b_data,5);
- mark_buffer_dirty(pbh);
- affs_brelse(pbh);
- }
- pbh = ebh;
- }
- cf = 1;
- }
- /* N.B. May need to release pbh after here */
-
- if (cf) {
- if (pt == T_SHORT)
- fdp->first_data = AFFS_BLOCK(bh->b_data,inode,0);
- fdp->block_count = cpu_to_be32(j);
- affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
- mark_buffer_dirty(bh);
- }
+ struct buffer_head *bh, tmp_bh;
+ int err;
- if (block < j) {
- if (pbh)
- affs_brelse(pbh);
- break;
- }
- if (j < AFFS_I2HSIZE(inode)) {
- /* N.B. What about pbh here? */
- goto out_free_bh;
- }
-
- block -= AFFS_I2HSIZE(inode);
- key = be32_to_cpu(FILE_END(bh->b_data,inode)->extension);
- if (!key) {
- key = affs_new_header(inode);
- if (!key)
- goto out_free_bh;
- ebh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
- if (!ebh) {
- /* N.B. must free bh here */
- goto out_free_block;
- }
- ((struct file_front *)ebh->b_data)->primary_type = cpu_to_be32(T_LIST);
- ((struct file_front *)ebh->b_data)->own_key = cpu_to_be32(key);
- FILE_END(ebh->b_data,inode)->secondary_type = cpu_to_be32(ST_FILE);
- FILE_END(ebh->b_data,inode)->parent = cpu_to_be32(inode->i_ino);
- affs_fix_checksum(AFFS_I2BSIZE(inode),ebh->b_data,5);
- mark_buffer_dirty(ebh);
- FILE_END(bh->b_data,inode)->extension = cpu_to_be32(key);
- affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
- mark_buffer_dirty(bh);
- affs_brelse(bh);
- bh = ebh;
- }
- pt = T_LIST;
- ext++;
- index = seqnum_to_index(ext);
- if (index > inode->u.affs_i.i_ec->max_ext &&
- AFFS_ISINDEX(ext)) {
- inode->u.affs_i.i_ec->ec[index] = key;
- inode->u.affs_i.i_ec->max_ext = index;
+ tmp_bh.b_state = 0;
+ err = affs_get_block(inode, block, &tmp_bh, create);
+ if (!err) {
+ bh = affs_bread(inode->i_sb, tmp_bh.b_blocknr);
+ if (bh) {
+ bh->b_state |= tmp_bh.b_state;
+ return bh;
}
- affs_brelse(bh);
- }
-
- /* Invalidate key cache */
- for (j = 0; j < 4; j++) {
- kc = &inode->u.affs_i.i_ec->kc[j];
- kc->kc_last = -1;
+ err = -EIO;
}
- key = be32_to_cpu(AFFS_BLOCK(bh->b_data,inode,block));
- affs_brelse(bh);
- if (!key)
- goto out_fail;
-
- bh = affs_bread(inode->i_dev, key, AFFS_I2BSIZE(inode));
- return bh;
-
-out_free_block:
- affs_free_block(sb, key);
-out_free_bh:
- affs_brelse(bh);
-out_fail:
- return NULL;
+ return ERR_PTR(err);
}
-static ssize_t
-affs_file_read_ofs(struct file *filp, char *buf, size_t count, loff_t *ppos)
+static inline struct buffer_head *
+affs_getzeroblk_ino(struct inode *inode, int block)
{
- struct inode *inode = filp->f_dentry->d_inode;
- char *start;
- ssize_t left, offset, size, sector;
- ssize_t blocksize;
- struct buffer_head *bh;
- void *data;
- loff_t tmp;
-
- pr_debug("AFFS: file_read_ofs(ino=%lu,pos=%lu,%d)\n",inode->i_ino,
- (unsigned long)*ppos,count);
+ struct buffer_head *bh, tmp_bh;
+ int err;
- if (!inode) {
- affs_error(inode->i_sb,"file_read_ofs","Inode = NULL");
- return -EINVAL;
- }
- blocksize = AFFS_I2BSIZE(inode) - 24;
- if (!(S_ISREG(inode->i_mode))) {
- pr_debug("AFFS: file_read: mode = %07o",inode->i_mode);
- return -EINVAL;
+ tmp_bh.b_state = 0;
+ err = affs_get_block(inode, block, &tmp_bh, 1);
+ if (!err) {
+ bh = affs_getzeroblk(inode->i_sb, tmp_bh.b_blocknr);
+ if (bh) {
+ bh->b_state |= tmp_bh.b_state;
+ return bh;
+ }
+ err = -EIO;
}
- if (*ppos >= inode->i_size || count <= 0)
- return 0;
+ return ERR_PTR(err);
+}
- start = buf;
- for (;;) {
- left = MIN (inode->i_size - *ppos,count - (buf - start));
- if (!left)
- break;
- tmp = *ppos;
- do_div(tmp, blocksize);
- sector = affs_bmap(inode, tmp);
- if (!sector)
- break;
- tmp = *ppos;
- offset = do_div(tmp, blocksize);
- bh = affs_bread(inode->i_dev,sector,AFFS_I2BSIZE(inode));
- if (!bh)
- break;
- data = bh->b_data + 24;
- size = MIN(blocksize - offset,left);
- *ppos += size;
- copy_to_user(buf,data + offset,size);
- buf += size;
- affs_brelse(bh);
+static inline struct buffer_head *
+affs_getemptyblk_ino(struct inode *inode, int block)
+{
+ struct buffer_head *bh, tmp_bh;
+ int err;
+
+ tmp_bh.b_state = 0;
+ err = affs_get_block(inode, block, &tmp_bh, 1);
+ if (!err) {
+ bh = affs_getemptyblk(inode->i_sb, tmp_bh.b_blocknr);
+ if (bh) {
+ bh->b_state |= tmp_bh.b_state;
+ return bh;
+ }
+ err = -EIO;
}
- if (start == buf)
- return -EIO;
- return buf - start;
+ return ERR_PTR(err);
}
static ssize_t
return retval;
}
-static ssize_t
-affs_file_write_ofs(struct file *file, const char *buf, size_t count, loff_t *ppos)
+static int
+affs_do_readpage_ofs(struct file *file, struct page *page, unsigned from, unsigned to)
{
- ssize_t retval;
+ struct inode *inode = file->f_dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *bh;
+ char *data;
+ u32 bidx, boff, bsize;
+ u32 tmp;
+
+ pr_debug("AFFS: read_page(%u, %ld, %d, %d)\n", (u32)inode->i_ino, page->index, from, to);
+ data = page_address(page);
+ bsize = AFFS_SB->s_data_blksize;
+ tmp = (page->index << PAGE_CACHE_SHIFT) + from;
+ bidx = tmp / bsize;
+ boff = tmp % bsize;
+
+ while (from < to) {
+ bh = affs_bread_ino(inode, bidx, 0);
+ if (IS_ERR(bh))
+ return PTR_ERR(bh);
+ tmp = MIN(bsize - boff, from - to);
+ memcpy(data + from, AFFS_DATA(bh) + boff, tmp);
+ affs_brelse(bh);
+ bidx++;
+ from += tmp;
+ boff = 0;
+ }
+ return 0;
+}
- retval = generic_file_write (file, buf, count, ppos);
- if (retval >0) {
- struct inode *inode = file->f_dentry->d_inode;
- inode->i_ctime = inode->i_mtime = CURRENT_TIME;
- mark_inode_dirty(inode);
+static int
+affs_extent_file_ofs(struct file *file, u32 newsize)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *bh, *prev_bh;
+ u32 bidx, boff;
+ u32 size, bsize;
+ u32 tmp;
+
+ pr_debug("AFFS: extent_file(%u, %d)\n", (u32)inode->i_ino, newsize);
+ bsize = AFFS_SB->s_data_blksize;
+ bh = NULL;
+ size = inode->i_size;
+ bidx = size / bsize;
+ boff = size % bsize;
+ if (boff) {
+ bh = affs_bread_ino(inode, bidx, 0);
+ if (IS_ERR(bh))
+ return PTR_ERR(bh);
+ tmp = MIN(bsize - boff, newsize - size);
+ memset(AFFS_DATA(bh) + boff, 0, tmp);
+ AFFS_DATA_HEAD(bh)->size = cpu_to_be32(be32_to_cpu(AFFS_DATA_HEAD(bh)->size) + tmp);
+ affs_fix_checksum(sb, bh);
+ mark_buffer_dirty(bh);
+ size += tmp;
+ bidx++;
+ } else if (bidx) {
+ bh = affs_bread_ino(inode, bidx - 1, 0);
+ if (IS_ERR(bh))
+ return PTR_ERR(bh);
}
- return retval;
+
+ while (size < newsize) {
+ prev_bh = bh;
+ bh = affs_getzeroblk_ino(inode, bidx);
+ if (IS_ERR(bh))
+ goto out;
+ tmp = MIN(bsize, newsize - size);
+ AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA);
+ AFFS_DATA_HEAD(bh)->key = cpu_to_be32(inode->i_ino);
+ AFFS_DATA_HEAD(bh)->sequence = cpu_to_be32(bidx);
+ AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp);
+ affs_fix_checksum(sb, bh);
+ mark_buffer_dirty(bh);
+ if (prev_bh) {
+ u32 tmp = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next);
+ if (tmp)
+ affs_warning(sb, "prepare_write_ofs", "next block already set for %d (%d)", bidx, tmp);
+ AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr);
+ affs_adjust_checksum(prev_bh, bidx - tmp);
+ mark_buffer_dirty(prev_bh);
+ affs_brelse(prev_bh);
+ }
+ size += bsize;
+ bidx++;
+ }
+ affs_brelse(bh);
+ inode->i_size = AFFS_INODE->mmu_private = size;
+ return 0;
+
+out:
+ inode->i_size = AFFS_INODE->mmu_private = size;
+ return PTR_ERR(bh);
}
-/* Free any preallocated blocks. */
+static int
+affs_readpage_ofs(struct file *file, struct page *page)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ u32 to;
+ int err;
+
+ pr_debug("AFFS: read_page(%u, %ld)\n", (u32)inode->i_ino, page->index);
+ to = PAGE_CACHE_SIZE;
+ if (((page->index + 1) << PAGE_CACHE_SHIFT) > inode->i_size) {
+ to = inode->i_size & ~PAGE_CACHE_MASK;
+ memset(page_address(page) + to, 0, PAGE_CACHE_SIZE - to);
+ }
-void
-affs_free_prealloc(struct inode *inode)
+ err = affs_do_readpage_ofs(file, page, 0, to);
+ if (!err)
+ SetPageUptodate(page);
+ UnlockPage(page);
+ return err;
+}
+
+static int affs_prepare_write_ofs(struct file *file, struct page *page, unsigned from, unsigned to)
{
- struct super_block *sb = inode->i_sb;
- struct affs_zone *zone;
- int block;
+ struct inode *inode = file->f_dentry->d_inode;
+ u32 size, offset;
+ u32 tmp;
+ int err = 0;
- pr_debug("AFFS: free_prealloc(ino=%lu)\n", inode->i_ino);
+ pr_debug("AFFS: prepare_write(%u, %ld, %d, %d)\n", (u32)inode->i_ino, page->index, from, to);
+ if (Page_Uptodate(page))
+ return 0;
- while (inode->u.affs_i.i_pa_cnt) {
- block = inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++];
- inode->u.affs_i.i_pa_next &= AFFS_MAX_PREALLOC - 1;
- inode->u.affs_i.i_pa_cnt--;
- affs_free_block(sb, block);
+ size = inode->i_size;
+ offset = page->index << PAGE_CACHE_SHIFT;
+ if (offset + from > size) {
+ err = affs_extent_file_ofs(file, offset + from);
+ if (err)
+ return err;
}
- if (inode->u.affs_i.i_zone) {
- zone = &sb->u.affs_sb.s_zones[inode->u.affs_i.i_zone];
- if (zone->z_ino == inode->i_ino)
- zone->z_ino = 0;
+
+ if (from) {
+ err = affs_do_readpage_ofs(file, page, 0, from);
+ if (err)
+ return err;
}
+ if (to < PAGE_CACHE_SIZE) {
+ memset(page_address(page) + to, 0, PAGE_CACHE_SIZE - to);
+ if (size > offset + to) {
+ if (size < offset + PAGE_CACHE_SIZE)
+ tmp = size & PAGE_CACHE_MASK;
+ else
+ tmp = PAGE_CACHE_SIZE;
+ err = affs_do_readpage_ofs(file, page, to, tmp);
+ }
+ }
+ return err;
}
-/* Truncate (or enlarge) a file to the requested size. */
-
-void
-affs_truncate(struct inode *inode)
+static int affs_commit_write_ofs(struct file *file, struct page *page, unsigned from, unsigned to)
{
- struct buffer_head *bh = NULL;
- int first; /* First block to be thrown away */
- int block;
- s32 key;
- s32 *keyp;
- s32 ekey;
- s32 ptype, stype;
- int freethis;
- int net_blocksize;
- int blocksize = AFFS_I2BSIZE(inode);
- int rem;
- int ext;
- loff_t tmp;
-
- pr_debug("AFFS: truncate(inode=%ld,size=%lu)\n",inode->i_ino,inode->i_size);
-
- net_blocksize = blocksize - ((inode->i_sb->u.affs_sb.s_flags & SF_OFS) ? 24 : 0);
- first = inode->i_size + net_blocksize -1;
- do_div (first, net_blocksize);
- if (inode->u.affs_i.i_lastblock < first - 1) {
- /* There has to be at least one new block to be allocated */
- if (!inode->u.affs_i.i_ec && alloc_ext_cache(inode)) {
- /* XXX Fine! No way to indicate an error. */
- return /* -ENOSPC */;
- }
- bh = affs_getblock(inode,first - 1);
- if (!bh) {
- affs_warning(inode->i_sb,"truncate","Cannot extend file");
- inode->i_size = net_blocksize * (inode->u.affs_i.i_lastblock + 1);
- } else if (inode->i_sb->u.affs_sb.s_flags & SF_OFS) {
- tmp = inode->i_size;
- rem = do_div(tmp, net_blocksize);
- DATA_FRONT(bh)->data_size = cpu_to_be32(rem ? rem : net_blocksize);
- affs_fix_checksum(blocksize,bh->b_data,5);
- mark_buffer_dirty(bh);
- }
- goto out_truncate;
- }
- ekey = inode->i_ino;
- ext = 0;
-
- /* Free all blocks starting at 'first' and all then-empty
- * extension blocks. Do not free the header block, though.
- */
- while (ekey) {
- if (!(bh = affs_bread(inode->i_dev,ekey,blocksize))) {
- affs_error(inode->i_sb,"truncate","Cannot read block %d",ekey);
- goto out_truncate;
- }
- if (affs_checksum_block(blocksize,bh->b_data,&ptype,&stype)) {
- affs_error(inode->i_sb,"truncate","Checksum error in header/ext block %d",
- ekey);
- goto out_truncate;
- }
- if (stype != ST_FILE || (ptype != T_SHORT && ptype != T_LIST)) {
- affs_error(inode->i_sb,"truncate",
- "Bad block (key=%d, ptype=%d, stype=%d)",ekey,ptype,stype);
- goto out_truncate;
- }
- /* Do we have to free this extension block after
- * freeing the data blocks pointed to?
- */
- freethis = first == 0 && ekey != inode->i_ino;
-
- /* Free the data blocks. 'first' is relative to this
- * extension block and may well lie behind this block.
- */
- for (block = first; block < AFFS_I2HSIZE(inode); block++) {
- keyp = &AFFS_BLOCK(bh->b_data,inode,block);
- key = be32_to_cpu(*keyp);
- if (key) {
- *keyp = 0;
- affs_free_block(inode->i_sb,key);
- } else
- break;
+ struct inode *inode = file->f_dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *bh, *prev_bh;
+ char *data;
+ u32 bidx, boff, bsize;
+ u32 tmp;
+ int written;
+
+ pr_debug("AFFS: commit_write(%u, %ld, %d, %d)\n", (u32)inode->i_ino, page->index, from, to);
+ bsize = AFFS_SB->s_data_blksize;
+ data = page_address(page);
+
+ bh = NULL;
+ written = 0;
+ tmp = (page->index << PAGE_CACHE_SHIFT) + from;
+ bidx = tmp / bsize;
+ boff = tmp % bsize;
+ if (boff) {
+ bh = affs_bread_ino(inode, bidx, 0);
+ if (IS_ERR(bh))
+ return PTR_ERR(bh);
+ tmp = MIN(bsize - boff, to - from);
+ memcpy(AFFS_DATA(bh) + boff, data + from, tmp);
+ AFFS_DATA_HEAD(bh)->size = cpu_to_be32(be32_to_cpu(AFFS_DATA_HEAD(bh)->size) + tmp);
+ affs_fix_checksum(sb, bh);
+ mark_buffer_dirty(bh);
+ written += tmp;
+ from += tmp;
+ bidx++;
+ } else if (bidx) {
+ bh = affs_bread_ino(inode, bidx - 1, 0);
+ if (IS_ERR(bh))
+ return PTR_ERR(bh);
+ }
+ while (from + bsize <= to) {
+ prev_bh = bh;
+ bh = affs_getemptyblk_ino(inode, bidx);
+ if (IS_ERR(bh))
+ goto out;
+ memcpy(AFFS_DATA(bh), data + from, bsize);
+ if (bh->b_state & (1UL << BH_New)) {
+ AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA);
+ AFFS_DATA_HEAD(bh)->key = cpu_to_be32(inode->i_ino);
+ AFFS_DATA_HEAD(bh)->sequence = cpu_to_be32(bidx);
+ AFFS_DATA_HEAD(bh)->size = cpu_to_be32(bsize);
+ AFFS_DATA_HEAD(bh)->next = 0;
+ if (prev_bh) {
+ u32 tmp = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next);
+ if (tmp)
+ affs_warning(sb, "prepare_write_ofs", "next block already set for %d (%d)", bidx, tmp);
+ AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr);
+ affs_adjust_checksum(prev_bh, bidx - tmp);
+ mark_buffer_dirty(prev_bh);
+ }
}
- keyp = &GET_END_PTR(struct file_end,bh->b_data,blocksize)->extension;
- key = be32_to_cpu(*keyp);
-
- /* If 'first' is in this block or is the first
- * in the next one, this will be the last in
- * the list, thus we have to adjust the count
- * and zero the pointer to the next ext block.
- */
- if (first <= AFFS_I2HSIZE(inode)) {
- ((struct file_front *)bh->b_data)->block_count = cpu_to_be32(first);
- first = 0;
- *keyp = 0;
- affs_fix_checksum(blocksize,bh->b_data,5);
- mark_buffer_dirty(bh);
- } else
- first -= AFFS_I2HSIZE(inode);
- affs_brelse(bh);
- bh = NULL;
- if (freethis) /* Don't bother fixing checksum */
- affs_free_block(inode->i_sb,ekey);
- ekey = key;
- }
- block = inode->i_size + net_blocksize - 1;
- do_div (block, net_blocksize);
- block--;
- inode->u.affs_i.i_lastblock = block;
-
- /* If the file is not truncated to a block boundary,
- * the partial block after the EOF must be zeroed
- * so it cannot become accessible again.
- */
-
- tmp = inode->i_size;
- rem = do_div(tmp, net_blocksize);
- if (rem) {
- if ((inode->i_sb->u.affs_sb.s_flags & SF_OFS))
- rem += 24;
- pr_debug("AFFS: Zeroing from offset %d in block %d\n",rem,block);
- bh = affs_getblock(inode,block);
- if (bh) {
- memset(bh->b_data + rem,0,blocksize - rem);
- if ((inode->i_sb->u.affs_sb.s_flags & SF_OFS)) {
- ((struct data_front *)bh->b_data)->data_size = cpu_to_be32(rem);
- ((struct data_front *)bh->b_data)->next_data = 0;
- affs_fix_checksum(blocksize,bh->b_data,5);
+ affs_brelse(prev_bh);
+ affs_fix_checksum(sb, bh);
+ mark_buffer_dirty(bh);
+ written += bsize;
+ from += bsize;
+ bidx++;
+ }
+ if (from < to) {
+ prev_bh = bh;
+ bh = affs_bread_ino(inode, bidx, 1);
+ if (IS_ERR(bh))
+ goto out;
+ tmp = MIN(bsize, to - from);
+ memcpy(AFFS_DATA(bh), data + from, tmp);
+ if (bh->b_state & (1UL << BH_New)) {
+ AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA);
+ AFFS_DATA_HEAD(bh)->key = cpu_to_be32(inode->i_ino);
+ AFFS_DATA_HEAD(bh)->sequence = cpu_to_be32(bidx);
+ AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp);
+ AFFS_DATA_HEAD(bh)->next = 0;
+ if (prev_bh) {
+ u32 tmp = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next);
+ if (tmp)
+ affs_warning(sb, "prepare_write_ofs", "next block already set for %d (%d)", bidx, tmp);
+ AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr);
+ affs_adjust_checksum(prev_bh, bidx - tmp);
+ mark_buffer_dirty(prev_bh);
}
- mark_buffer_dirty(bh);
- } else
- affs_error(inode->i_sb,"truncate","Cannot read block %d",block);
+ } else if (be32_to_cpu(AFFS_DATA_HEAD(bh)->size) < tmp)
+ AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp);
+ affs_brelse(prev_bh);
+ affs_fix_checksum(sb, bh);
+ mark_buffer_dirty(bh);
+ written += tmp;
+ from += tmp;
+ bidx++;
}
+ SetPageUptodate(page);
-out_truncate:
+done:
affs_brelse(bh);
- /* Invalidate cache */
- if (inode->u.affs_i.i_ec) {
- inode->u.affs_i.i_ec->max_ext = 0;
- for (key = 0; key < 4; key++) {
- inode->u.affs_i.i_ec->kc[key].kc_next_key = 0;
- inode->u.affs_i.i_ec->kc[key].kc_last = -1;
- }
- }
- mark_inode_dirty(inode);
+ tmp = (page->index << PAGE_CACHE_SHIFT) + from;
+ if (tmp > inode->i_size)
+ inode->i_size = AFFS_INODE->mmu_private = tmp;
+
+ return written;
+
+out:
+ bh = prev_bh;
+ if (!written)
+ written = PTR_ERR(bh);
+ goto done;
}
-/*
- * Called only when we need to allocate the extension cache.
- */
+struct address_space_operations affs_aops_ofs = {
+ readpage: affs_readpage_ofs,
+ //writepage: affs_writepage_ofs,
+ //sync_page: affs_sync_page_ofs,
+ prepare_write: affs_prepare_write_ofs,
+ commit_write: affs_commit_write_ofs
+};
-static int
-alloc_ext_cache(struct inode *inode)
+/* Free any preallocated blocks. */
+
+void
+affs_free_prealloc(struct inode *inode)
{
- s32 key;
- int i;
- unsigned long cache_page;
- int error = 0;
-
- pr_debug("AFFS: alloc_ext_cache(ino=%lu)\n",inode->i_ino);
-
- cache_page = get_free_page(GFP_KERNEL);
- /*
- * Check whether somebody else allocated it for us ...
- */
- if (inode->u.affs_i.i_ec)
- goto out_free;
- if (!cache_page)
- goto out_error;
-
- inode->u.affs_i.i_ec = (struct ext_cache *) cache_page;
- /* We only have to initialize non-zero values.
- * get_free_page() zeroed the page already.
- */
- key = inode->i_ino;
- inode->u.affs_i.i_ec->ec[0] = key;
- for (i = 0; i < 4; i++) {
- inode->u.affs_i.i_ec->kc[i].kc_this_key = key;
- inode->u.affs_i.i_ec->kc[i].kc_last = -1;
+ struct super_block *sb = inode->i_sb;
+
+ pr_debug("AFFS: free_prealloc(ino=%lu)\n", inode->i_ino);
+
+ while (inode->u.affs_i.i_pa_cnt) {
+ inode->u.affs_i.i_pa_cnt--;
+ affs_free_block(sb, ++inode->u.affs_i.i_lastalloc);
}
-out:
- return error;
+}
-out_free:
- if (cache_page)
- free_page(cache_page);
- goto out;
+/* Truncate (or enlarge) a file to the requested size. */
-out_error:
- affs_error(inode->i_sb,"alloc_ext_cache","Cache allocation failed");
- error = -ENOMEM;
- goto out;
+void
+affs_truncate(struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+ u32 ext, ext_key;
+ u32 last_blk, blkcnt, blk;
+ u32 size;
+ struct buffer_head *ext_bh;
+ int i;
+
+ pr_debug("AFFS: truncate(inode=%d, size=%u)\n", (u32)inode->i_ino, (u32)inode->i_size);
+
+ last_blk = 0;
+ ext = 0;
+ if (inode->i_size) {
+ last_blk = ((u32)inode->i_size - 1) / AFFS_SB->s_data_blksize;
+ ext = last_blk / AFFS_SB->s_hashsize;
+ }
+
+ if (inode->i_size > AFFS_INODE->mmu_private) {
+ struct address_space *mapping = inode->i_mapping;
+ struct page *page;
+ u32 size = inode->i_size - 1;
+ int res;
+
+ page = grab_cache_page(mapping, size >> PAGE_CACHE_SHIFT);
+ if (!page)
+ return;
+ size = (size & (PAGE_CACHE_SIZE - 1)) + 1;
+ res = mapping->a_ops->prepare_write(NULL, page, size, size);
+ if (!res)
+ res = mapping->a_ops->commit_write(NULL, page, size, size);
+ UnlockPage(page);
+ page_cache_release(page);
+ mark_inode_dirty(inode);
+ return;
+ } else if (inode->i_size == AFFS_INODE->mmu_private)
+ return;
+
+ // lock cache
+ ext_bh = affs_get_extblock(inode, ext);
+ if (AFFS_INODE->i_lc) {
+ /* clear linear cache */
+ for (i = (ext + 1) >> AFFS_INODE->i_lc_shift; i < AFFS_LC_SIZE; i++)
+ AFFS_INODE->i_lc[i] = 0;
+ /* clear associative cache */
+ for (i = 0; i < AFFS_AC_SIZE; i++)
+ if (AFFS_INODE->i_ac[i].ext >= ext)
+ AFFS_INODE->i_ac[i].ext = 0;
+ }
+ ext_key = be32_to_cpu(AFFS_TAIL(sb, ext_bh)->extension);
+
+ blkcnt = AFFS_INODE->i_blkcnt;
+ i = 0;
+ blk = last_blk;
+ if (inode->i_size) {
+ i = last_blk % AFFS_SB->s_hashsize + 1;
+ blk++;
+ } else
+ AFFS_HEAD(ext_bh)->first_data = 0;
+ size = AFFS_SB->s_hashsize;
+ if (size > blkcnt - blk + i)
+ size = blkcnt - blk + i;
+ for (; i < size; i++, blk++) {
+ affs_free_block(sb, be32_to_cpu(AFFS_BLOCK(sb, ext_bh, i)));
+ AFFS_BLOCK(sb, ext_bh, i) = 0;
+ }
+ AFFS_TAIL(sb, ext_bh)->extension = 0;
+ affs_fix_checksum(sb, ext_bh);
+ mark_buffer_dirty(ext_bh);
+ affs_brelse(ext_bh);
+
+ if (inode->i_size) {
+ AFFS_INODE->i_blkcnt = last_blk + 1;
+ AFFS_INODE->i_extcnt = ext + 1;
+ } else {
+ AFFS_INODE->i_blkcnt = 0;
+ AFFS_INODE->i_extcnt = 1;
+ }
+ AFFS_INODE->mmu_private = inode->i_size;
+ // unlock cache
+
+ while (ext_key) {
+ ext_bh = affs_bread(sb, ext_key);
+ size = AFFS_SB->s_hashsize;
+ if (size > blkcnt - blk)
+ size = blkcnt - blk;
+ for (i = 0; i < size; i++, blk++)
+ affs_free_block(sb, be32_to_cpu(AFFS_BLOCK(sb, ext_bh, i)));
+ affs_free_block(sb, ext_key);
+ ext_key = be32_to_cpu(AFFS_TAIL(sb, ext_bh)->extension);
+ affs_brelse(ext_bh);
+ }
}
* (C) 1991 Linus Torvalds - minix filesystem
*/
-#define DEBUG 0
#include <asm/div64.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <asm/system.h>
#include <asm/uaccess.h>
-extern int *blk_size[];
-extern struct timezone sys_tz;
extern struct inode_operations affs_symlink_inode_operations;
-
-#define MIN(a,b) (((a)<(b))?(a):(b))
-
-unsigned long
-affs_parent_ino(struct inode *dir)
-{
- int root_ino = (dir->i_sb->u.affs_sb.s_root_block);
-
- if (!S_ISDIR(dir->i_mode)) {
- affs_error(dir->i_sb,"parent_ino","Trying to get parent of non-directory");
- return root_ino;
- }
- if (dir->i_ino == root_ino)
- return root_ino;
- return dir->u.affs_i.i_parent;
-}
+extern struct timezone sys_tz;
void
affs_read_inode(struct inode *inode)
{
+ struct super_block *sb = inode->i_sb;
struct buffer_head *bh;
- struct file_front *file_front;
- struct file_end *file_end;
- s32 block;
- unsigned long prot;
- s32 ptype, stype;
- unsigned short id;
- loff_t tmp;
+ struct affs_head *head;
+ struct affs_tail *tail;
+ u32 block;
+ u32 size;
+ u32 prot;
+ u16 id;
pr_debug("AFFS: read_inode(%lu)\n",inode->i_ino);
block = inode->i_ino;
- if (!(bh = affs_bread(inode->i_dev,block,AFFS_I2BSIZE(inode)))) {
- affs_error(inode->i_sb,"read_inode","Cannot read block %d",block);
- return;
+ bh = affs_bread(sb, block);
+ if (!bh) {
+ affs_warning(sb, "read_inode", "Cannot read block %d", block);
+ goto bad_inode;
}
- if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype) || ptype != T_SHORT) {
- affs_error(inode->i_sb,"read_inode",
- "Checksum or type (ptype=%d) error on inode %d",ptype,block);
- affs_brelse(bh);
- return;
+ if (affs_checksum_block(sb, bh) || be32_to_cpu(AFFS_HEAD(bh)->ptype) != T_SHORT) {
+ affs_warning(sb,"read_inode",
+ "Checksum or type (ptype=%d) error on inode %d",
+ AFFS_HEAD(bh)->ptype, block);
+ goto bad_inode;
}
- file_front = (struct file_front *)bh->b_data;
- file_end = GET_END_PTR(struct file_end, bh->b_data,AFFS_I2BSIZE(inode));
- prot = (be32_to_cpu(file_end->protect) & ~0x10) ^ FIBF_OWNER;
-
- inode->u.affs_i.i_protect = prot;
- inode->u.affs_i.i_parent = be32_to_cpu(file_end->parent);
- inode->u.affs_i.i_original = 0;
- inode->u.affs_i.i_zone = 0;
- inode->u.affs_i.i_hlink = 0;
- inode->u.affs_i.i_pa_cnt = 0;
- inode->u.affs_i.i_pa_next = 0;
- inode->u.affs_i.i_pa_last = 0;
- inode->u.affs_i.i_ec = NULL;
- inode->u.affs_i.i_lastblock = -1;
- inode->i_nlink = 1;
- inode->i_mode = 0;
-
- if (inode->i_sb->u.affs_sb.s_flags & SF_SETMODE)
- inode->i_mode = inode->i_sb->u.affs_sb.s_mode;
+ head = AFFS_HEAD(bh);
+ tail = AFFS_TAIL(sb, bh);
+ prot = be32_to_cpu(tail->protect);
+
+ inode->i_size = 0;
+ inode->i_nlink = 1;
+ inode->i_mode = 0;
+ memset(AFFS_INODE, 0, sizeof(struct affs_inode_info));
+ init_MUTEX(&AFFS_INODE->i_link_lock);
+ init_MUTEX(&AFFS_INODE->i_ext_lock);
+ AFFS_INODE->i_extcnt = 1;
+ AFFS_INODE->i_ext_last = ~1;
+ AFFS_INODE->i_protect = prot;
+
+ if (AFFS_SB->s_flags & SF_SETMODE)
+ inode->i_mode = AFFS_SB->s_mode;
else
inode->i_mode = prot_to_mode(prot);
- if (inode->i_sb->u.affs_sb.s_flags & SF_SETUID)
- inode->i_uid = inode->i_sb->u.affs_sb.s_uid;
- id = be16_to_cpu(file_end->owner_uid);
- if (id == 0 || inode->i_sb->u.affs_sb.s_flags & SF_SETUID)
- inode->i_uid = inode->i_sb->u.affs_sb.s_uid;
- else if (id == 0xFFFF && inode->i_sb->u.affs_sb.s_flags & SF_MUFS)
+ id = be16_to_cpu(tail->uid);
+ if (id == 0 || AFFS_SB->s_flags & SF_SETUID)
+ inode->i_uid = AFFS_SB->s_uid;
+ else if (id == 0xFFFF && AFFS_SB->s_flags & SF_MUFS)
inode->i_uid = 0;
- else
+ else
inode->i_uid = id;
- id = be16_to_cpu(file_end->owner_gid);
- if (id == 0 || inode->i_sb->u.affs_sb.s_flags & SF_SETGID)
- inode->i_gid = inode->i_sb->u.affs_sb.s_gid;
- else if (id == 0xFFFF && inode->i_sb->u.affs_sb.s_flags & SF_MUFS)
+ id = be16_to_cpu(tail->gid);
+ if (id == 0 || AFFS_SB->s_flags & SF_SETGID)
+ inode->i_gid = AFFS_SB->s_gid;
+ else if (id == 0xFFFF && AFFS_SB->s_flags & SF_MUFS)
inode->i_gid = 0;
else
inode->i_gid = id;
- switch (be32_to_cpu(file_end->secondary_type)) {
- case ST_ROOT:
- inode->i_uid = inode->i_sb->u.affs_sb.s_uid;
- inode->i_gid = inode->i_sb->u.affs_sb.s_gid;
- case ST_USERDIR:
- if (be32_to_cpu(file_end->secondary_type) == ST_USERDIR ||
- inode->i_sb->u.affs_sb.s_flags & SF_SETMODE) {
- if (inode->i_mode & S_IRUSR)
- inode->i_mode |= S_IXUSR;
- if (inode->i_mode & S_IRGRP)
- inode->i_mode |= S_IXGRP;
- if (inode->i_mode & S_IROTH)
- inode->i_mode |= S_IXOTH;
- inode->i_mode |= S_IFDIR;
- } else
- inode->i_mode = S_IRUGO | S_IXUGO | S_IWUSR | S_IFDIR;
- inode->i_size = 0;
- break;
- case ST_LINKDIR:
- affs_error(inode->i_sb,"read_inode","inode is LINKDIR");
- affs_brelse(bh);
- return;
- case ST_LINKFILE:
- affs_error(inode->i_sb,"read_inode","inode is LINKFILE");
- affs_brelse(bh);
- return;
- case ST_FILE:
- inode->i_mode |= S_IFREG;
- inode->i_size = be32_to_cpu(file_end->byte_size);
- if (inode->i_sb->u.affs_sb.s_flags & SF_OFS)
- block = AFFS_I2BSIZE(inode) - 24;
- else
- block = AFFS_I2BSIZE(inode);
- tmp = inode->i_size + block -1;
- do_div (tmp, block);
- tmp--;
- inode->u.affs_i.i_lastblock = tmp;
- break;
- case ST_SOFTLINK:
- inode->i_mode |= S_IFLNK;
- inode->i_size = 0;
- break;
+ switch (be32_to_cpu(tail->stype)) {
+ case ST_ROOT:
+ inode->i_uid = AFFS_SB->s_uid;
+ inode->i_gid = AFFS_SB->s_gid;
+ /* fall through */
+ case ST_USERDIR:
+ if (be32_to_cpu(tail->stype) == ST_USERDIR ||
+ AFFS_SB->s_flags & SF_SETMODE) {
+ if (inode->i_mode & S_IRUSR)
+ inode->i_mode |= S_IXUSR;
+ if (inode->i_mode & S_IRGRP)
+ inode->i_mode |= S_IXGRP;
+ if (inode->i_mode & S_IROTH)
+ inode->i_mode |= S_IXOTH;
+ inode->i_mode |= S_IFDIR;
+ } else
+ inode->i_mode = S_IRUGO | S_IXUGO | S_IWUSR | S_IFDIR;
+ if (tail->link_chain)
+ inode->i_nlink = 2;
+ /* Maybe it should be controlled by mount parameter? */
+ //inode->i_mode |= S_ISVTX;
+ inode->i_op = &affs_dir_inode_operations;
+ inode->i_fop = &affs_dir_operations;
+ break;
+ case ST_LINKDIR:
+ affs_warning(sb, "read_inode", "inode is LINKDIR");
+ goto bad_inode;
+ case ST_LINKFILE:
+ affs_warning(sb, "read_inode", "inode is LINKFILE");
+ goto bad_inode;
+ case ST_FILE:
+ size = be32_to_cpu(tail->size);
+ inode->i_mode |= S_IFREG;
+ AFFS_INODE->mmu_private = inode->i_size = size;
+ if (inode->i_size) {
+ AFFS_INODE->i_blkcnt = (size - 1) /
+ AFFS_SB->s_data_blksize + 1;
+ AFFS_INODE->i_extcnt = (AFFS_INODE->i_blkcnt - 1) /
+ AFFS_SB->s_hashsize + 1;
+ }
+ if (tail->link_chain)
+ inode->i_nlink = 2;
+ inode->i_mapping->a_ops = (AFFS_SB->s_flags & SF_OFS) ? &affs_aops_ofs : &affs_aops;
+ inode->i_op = &affs_file_inode_operations;
+ inode->i_fop = &affs_file_operations;
+ break;
+ case ST_SOFTLINK:
+ inode->i_mode |= S_IFLNK;
+ inode->i_op = &affs_symlink_inode_operations;
+ inode->i_data.a_ops = &affs_symlink_aops;
+ break;
}
inode->i_mtime = inode->i_atime = inode->i_ctime
- = (be32_to_cpu(file_end->created.ds_Days) * (24 * 60 * 60) +
- be32_to_cpu(file_end->created.ds_Minute) * 60 +
- be32_to_cpu(file_end->created.ds_Tick) / 50 +
+ = (be32_to_cpu(tail->change.days) * (24 * 60 * 60) +
+ be32_to_cpu(tail->change.mins) * 60 +
+ be32_to_cpu(tail->change.ticks) / 50 +
((8 * 365 + 2) * 24 * 60 * 60)) +
sys_tz.tz_minuteswest * 60;
affs_brelse(bh);
+ return;
- if (S_ISREG(inode->i_mode)) {
- if (inode->i_sb->u.affs_sb.s_flags & SF_OFS) {
- inode->i_op = &affs_file_inode_operations;
- inode->i_fop = &affs_file_operations_ofs;
- return;
- }
- inode->i_op = &affs_file_inode_operations;
- inode->i_fop = &affs_file_operations;
- inode->i_mapping->a_ops = &affs_aops;
- inode->u.affs_i.mmu_private = inode->i_size;
- } else if (S_ISDIR(inode->i_mode)) {
- /* Maybe it should be controlled by mount parameter? */
- inode->i_mode |= S_ISVTX;
- inode->i_op = &affs_dir_inode_operations;
- inode->i_fop = &affs_dir_operations;
- }
- else if (S_ISLNK(inode->i_mode)) {
- inode->i_op = &affs_symlink_inode_operations;
- inode->i_data.a_ops = &affs_symlink_aops;
- }
+bad_inode:
+ make_bad_inode(inode);
+ affs_brelse(bh);
+ return;
}
void
affs_write_inode(struct inode *inode, int unused)
{
+ struct super_block *sb = inode->i_sb;
struct buffer_head *bh;
- struct file_end *file_end;
+ struct affs_tail *tail;
uid_t uid;
gid_t gid;
pr_debug("AFFS: write_inode(%lu)\n",inode->i_ino);
if (!inode->i_nlink)
+ // possibly free block
return;
lock_kernel();
- if (!(bh = bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode)))) {
- affs_error(inode->i_sb,"write_inode","Cannot read block %lu",inode->i_ino);
+ bh = affs_bread(sb, inode->i_ino);
+ if (!bh) {
+ affs_error(sb,"write_inode","Cannot read block %lu",inode->i_ino);
unlock_kernel();
return;
}
- file_end = GET_END_PTR(struct file_end, bh->b_data,AFFS_I2BSIZE(inode));
- if (file_end->secondary_type == be32_to_cpu(ST_ROOT)) {
- secs_to_datestamp(inode->i_mtime,&ROOT_END(bh->b_data,inode)->disk_altered);
+ tail = AFFS_TAIL(sb, bh);
+ if (tail->stype == be32_to_cpu(ST_ROOT)) {
+ secs_to_datestamp(inode->i_mtime,&AFFS_ROOT_TAIL(sb, bh)->root_change);
} else {
- file_end->protect = cpu_to_be32(inode->u.affs_i.i_protect ^ FIBF_OWNER);
- file_end->byte_size = cpu_to_be32(inode->i_size);
- secs_to_datestamp(inode->i_mtime,&file_end->created);
- if (!(inode->i_ino == inode->i_sb->u.affs_sb.s_root_block)) {
+ tail->protect = cpu_to_be32(AFFS_INODE->i_protect);
+ tail->size = cpu_to_be32(inode->i_size);
+ secs_to_datestamp(inode->i_mtime,&tail->change);
+ if (!(inode->i_ino == AFFS_SB->s_root_block)) {
uid = inode->i_uid;
gid = inode->i_gid;
- if (inode->i_sb->u.affs_sb.s_flags & SF_MUFS) {
+ if (sb->u.affs_sb.s_flags & SF_MUFS) {
if (inode->i_uid == 0 || inode->i_uid == 0xFFFF)
uid = inode->i_uid ^ ~0;
if (inode->i_gid == 0 || inode->i_gid == 0xFFFF)
gid = inode->i_gid ^ ~0;
}
- if (!(inode->i_sb->u.affs_sb.s_flags & SF_SETUID))
- file_end->owner_uid = cpu_to_be16(uid);
- if (!(inode->i_sb->u.affs_sb.s_flags & SF_SETGID))
- file_end->owner_gid = cpu_to_be16(gid);
+ if (!(sb->u.affs_sb.s_flags & SF_SETUID))
+ tail->uid = cpu_to_be16(uid);
+ if (!(sb->u.affs_sb.s_flags & SF_SETGID))
+ tail->gid = cpu_to_be16(gid);
}
}
- affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+ affs_fix_checksum(sb, bh);
mark_buffer_dirty(bh);
- brelse(bh);
+ affs_brelse(bh);
unlock_kernel();
}
goto out;
}
- if (attr->ia_valid & ATTR_MODE)
- inode->u.affs_i.i_protect = mode_to_prot(attr->ia_mode);
-
- error = 0;
- inode_setattr(inode, attr);
+ error = inode_setattr(inode, attr);
+ if (!error && (attr->ia_valid & ATTR_MODE))
+ mode_to_prot(inode);
out:
return error;
}
void
affs_put_inode(struct inode *inode)
{
- pr_debug("AFFS: put_inode(ino=%lu, nlink=%u)\n",
- inode->i_ino,inode->i_nlink);
-
+ pr_debug("AFFS: put_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink);
lock_kernel();
affs_free_prealloc(inode);
if (atomic_read(&inode->i_count) == 1) {
- unsigned long cache_page = (unsigned long) inode->u.affs_i.i_ec;
- if (cache_page) {
- pr_debug("AFFS: freeing ext cache\n");
- inode->u.affs_i.i_ec = NULL;
- free_page(cache_page);
- }
+ if (inode->i_size != AFFS_INODE->mmu_private)
+ affs_truncate(inode);
+ //if (inode->i_nlink)
+ // affs_clear_inode(inode);
}
unlock_kernel();
}
void
affs_delete_inode(struct inode *inode)
{
- pr_debug("AFFS: delete_inode(ino=%lu, nlink=%u)\n",inode->i_ino,inode->i_nlink);
+ pr_debug("AFFS: delete_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink);
lock_kernel();
inode->i_size = 0;
- if (S_ISREG(inode->i_mode) && !inode->u.affs_i.i_hlink)
+ if (S_ISREG(inode->i_mode))
affs_truncate(inode);
- affs_free_block(inode->i_sb,inode->i_ino);
- unlock_kernel();
clear_inode(inode);
+ affs_free_block(inode->i_sb, inode->i_ino);
+ unlock_kernel();
+}
+
+void
+affs_clear_inode(struct inode *inode)
+{
+ unsigned long cache_page = (unsigned long) inode->u.affs_i.i_lc;
+
+ pr_debug("AFFS: clear_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink);
+ if (cache_page) {
+ pr_debug("AFFS: freeing ext cache\n");
+ inode->u.affs_i.i_lc = NULL;
+ inode->u.affs_i.i_ac = NULL;
+ free_page(cache_page);
+ }
+ affs_brelse(AFFS_INODE->i_ext_bh);
+ AFFS_INODE->i_ext_last = ~1;
+ AFFS_INODE->i_ext_bh = NULL;
}
struct inode *
-affs_new_inode(const struct inode *dir)
+affs_new_inode(struct inode *dir)
{
+ struct super_block *sb = dir->i_sb;
struct inode *inode;
- struct super_block *sb;
- s32 block;
+ u32 block;
+ struct buffer_head *bh;
- if (!dir)
- return NULL;
+ if (!dir || !(inode = get_empty_inode()))
+ goto err_inode;
- sb = dir->i_sb;
- inode = new_inode(sb);
- if (!inode)
- return NULL;
+ if (!(block = affs_alloc_block(dir, dir->i_ino)))
+ goto err_block;
- if (!(block = affs_new_header((struct inode *)dir))) {
- iput(inode);
- return NULL;
- }
+ bh = affs_getzeroblk(sb, block);
+ if (!bh)
+ goto err_bh;
+ mark_buffer_dirty(bh);
+ affs_brelse(bh);
+ inode->i_sb = sb;
+ inode->i_dev = sb->s_dev;
inode->i_uid = current->fsuid;
inode->i_gid = current->fsgid;
inode->i_ino = block;
+ inode->i_nlink = 1;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
-
- inode->u.affs_i.i_original = 0;
- inode->u.affs_i.i_parent = dir->i_ino;
- inode->u.affs_i.i_zone = 0;
- inode->u.affs_i.i_hlink = 0;
- inode->u.affs_i.i_pa_cnt = 0;
- inode->u.affs_i.i_pa_next = 0;
- inode->u.affs_i.i_pa_last = 0;
- inode->u.affs_i.i_ec = NULL;
- inode->u.affs_i.i_lastblock = -1;
+ memset(AFFS_INODE, 0, sizeof(struct affs_inode_info));
+ AFFS_INODE->i_extcnt = 1;
+ AFFS_INODE->i_ext_last = ~1;
+ init_MUTEX(&AFFS_INODE->i_link_lock);
+ init_MUTEX(&AFFS_INODE->i_ext_lock);
insert_inode_hash(inode);
- mark_inode_dirty(inode);
return inode;
+
+err_bh:
+ affs_free_block(sb, block);
+err_block:
+ iput(inode);
+err_inode:
+ return NULL;
}
/*
*/
int
-affs_add_entry(struct inode *dir, struct inode *link, struct inode *inode,
- struct dentry *dentry, int type)
+affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s32 type)
{
- struct buffer_head *dir_bh;
- struct buffer_head *inode_bh;
- struct buffer_head *link_bh;
- int retval;
- const unsigned char *name = dentry->d_name.name;
- int len = dentry->d_name.len;
-
- pr_debug("AFFS: add_entry(dir=%lu,inode=%lu,\"%*s\",type=%d)\n",dir->i_ino,inode->i_ino,
- len,name,type);
-
- if ((retval = affs_check_name(name,len)))
- return retval;
- if (len > 30)
- len = 30;
-
- dir_bh = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
- inode_bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
- link_bh = NULL;
- retval = -EIO;
- if (!dir_bh || !inode_bh)
- goto addentry_done;
- if (link) {
- link_bh = affs_bread(link->i_dev,link->i_ino,AFFS_I2BSIZE(link));
- if (!link_bh)
- goto addentry_done;
+ struct super_block *sb = dir->i_sb;
+ struct buffer_head *inode_bh = NULL;
+ struct buffer_head *bh = NULL;
+ u32 block = 0;
+ int retval;
+
+ pr_debug("AFFS: add_entry(dir=%u, inode=%u, \"%*s\", type=%d)\n", (u32)dir->i_ino,
+ (u32)inode->i_ino, (int)dentry->d_name.len, dentry->d_name.name, type);
+
+ retval = -EIO;
+ bh = affs_bread(sb, inode->i_ino);
+ if (!bh)
+ goto done;
+
+ switch (type) {
+ case ST_LINKFILE:
+ case ST_LINKDIR:
+ inode_bh = bh;
+ retval = -ENOSPC;
+ block = affs_alloc_block(dir, dir->i_ino);
+ if (!block)
+ goto err;
+ retval = -EIO;
+ bh = affs_getzeroblk(sb, block);
+ if (!bh)
+ goto err;
+ break;
+ default:
+ break;
}
- ((struct dir_front *)inode_bh->b_data)->primary_type = cpu_to_be32(T_SHORT);
- ((struct dir_front *)inode_bh->b_data)->own_key = cpu_to_be32(inode->i_ino);
- DIR_END(inode_bh->b_data,inode)->dir_name[0] = len;
- strncpy(DIR_END(inode_bh->b_data,inode)->dir_name + 1,name,len);
- DIR_END(inode_bh->b_data,inode)->secondary_type = cpu_to_be32(type);
- DIR_END(inode_bh->b_data,inode)->parent = cpu_to_be32(dir->i_ino);
-
- lock_super(inode->i_sb);
- retval = affs_insert_hash(dir->i_ino,inode_bh,dir);
-
- if (link_bh) {
- LINK_END(inode_bh->b_data,inode)->original = cpu_to_be32(link->i_ino);
- LINK_END(inode_bh->b_data,inode)->link_chain =
- FILE_END(link_bh->b_data,link)->link_chain;
- FILE_END(link_bh->b_data,link)->link_chain = cpu_to_be32(inode->i_ino);
- affs_fix_checksum(AFFS_I2BSIZE(link),link_bh->b_data,5);
- link->i_version = ++event;
- mark_inode_dirty(link);
- mark_buffer_dirty(link_bh);
+
+ AFFS_HEAD(bh)->ptype = cpu_to_be32(T_SHORT);
+ AFFS_HEAD(bh)->key = cpu_to_be32(bh->b_blocknr);
+ affs_copy_name(AFFS_TAIL(sb, bh)->name, dentry);
+ AFFS_TAIL(sb, bh)->stype = cpu_to_be32(type);
+ AFFS_TAIL(sb, bh)->parent = cpu_to_be32(dir->i_ino);
+
+ if (inode_bh) {
+ u32 chain;
+ down(&AFFS_INODE->i_link_lock);
+ chain = AFFS_TAIL(sb, inode_bh)->link_chain;
+ AFFS_TAIL(sb, bh)->original = cpu_to_be32(inode->i_ino);
+ AFFS_TAIL(sb, bh)->link_chain = chain;
+ AFFS_TAIL(sb, inode_bh)->link_chain = cpu_to_be32(block);
+ affs_adjust_checksum(inode_bh, block - be32_to_cpu(chain));
+ mark_buffer_dirty(inode_bh);
+ inode->i_nlink = 2;
+ atomic_inc(&inode->i_count);
+ up(&AFFS_INODE->i_link_lock);
}
- affs_fix_checksum(AFFS_I2BSIZE(inode),inode_bh->b_data,5);
- affs_fix_checksum(AFFS_I2BSIZE(dir),dir_bh->b_data,5);
- dir->i_version = ++event;
- dir->i_mtime = dir->i_atime = dir->i_ctime = CURRENT_TIME;
- unlock_super(inode->i_sb);
-
- mark_inode_dirty(dir);
- mark_inode_dirty(inode);
- mark_buffer_dirty(dir_bh);
- mark_buffer_dirty(inode_bh);
-
-addentry_done:
- affs_brelse(dir_bh);
- affs_brelse(inode_bh);
- affs_brelse(link_bh);
+ affs_fix_checksum(sb, bh);
+ mark_buffer_dirty(bh);
+ down(&AFFS_DIR->i_hash_lock);
+ retval = affs_insert_hash(dir, bh);
+ up(&AFFS_DIR->i_hash_lock);
+
+ dentry->d_fsdata = (void *)bh->b_blocknr;
+ d_instantiate(dentry, inode);
+done:
+ affs_brelse(inode_bh);
+ affs_brelse(bh);
return retval;
+err:
+ if (block)
+ affs_free_block(sb, block);
+ goto done;
}
* (C) 1991 Linus Torvalds - minix filesystem
*/
-#define DEBUG 0
#include <linux/sched.h>
#include <linux/affs_fs.h>
#include <linux/kernel.h>
#include <linux/errno.h>
+typedef int (*toupper_t)(int);
+
extern struct inode_operations affs_symlink_inode_operations;
+static int affs_toupper(int ch);
+static int affs_hash_dentry(struct dentry *, struct qstr *);
+static int affs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
+static int affs_intl_toupper(int ch);
+static int affs_intl_hash_dentry(struct dentry *, struct qstr *);
+static int affs_intl_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
+
+struct dentry_operations affs_dentry_operations = {
+ d_hash: affs_hash_dentry,
+ d_compare: affs_compare_dentry,
+};
+
+struct dentry_operations affs_intl_dentry_operations = {
+ d_hash: affs_intl_hash_dentry,
+ d_compare: affs_intl_compare_dentry,
+};
+
+
/* Simple toupper() for DOS\1 */
-static unsigned int
-affs_toupper(unsigned int ch)
+static int
+affs_toupper(int ch)
{
return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch;
}
/* International toupper() for DOS\3 ("international") */
-static unsigned int
-affs_intl_toupper(unsigned int ch)
+static int
+affs_intl_toupper(int ch)
{
return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0
&& ch <= 0xFE && ch != 0xF7) ?
ch - ('a' - 'A') : ch;
}
-static int affs_hash_dentry(struct dentry *, struct qstr *);
-static int affs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
-struct dentry_operations affs_dentry_operations = {
- d_hash: affs_hash_dentry,
- d_compare: affs_compare_dentry,
-};
+static inline toupper_t
+affs_get_toupper(struct super_block *sb)
+{
+ return AFFS_SB->s_flags & SF_INTL ? affs_intl_toupper : affs_toupper;
+}
/*
* Note: the dentry argument is the parent dentry.
*/
-static int
-affs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+static inline int
+__affs_hash_dentry(struct dentry *dentry, struct qstr *qstr, toupper_t toupper)
{
- unsigned int (*toupper)(unsigned int) = affs_toupper;
- unsigned long hash;
- int i;
+ const char *name = qstr->name;
+ unsigned long hash;
+ int i;
- if ((i = affs_check_name(qstr->name,qstr->len)))
+ i = affs_check_name(qstr->name,qstr->len);
+ if (i)
return i;
- /* Check whether to use the international 'toupper' routine */
- if (AFFS_I2FSTYPE(dentry->d_inode))
- toupper = affs_intl_toupper;
hash = init_name_hash();
- for (i = 0; i < qstr->len && i < 30; i++)
- hash = partial_name_hash(toupper(qstr->name[i]), hash);
+ i = MIN(qstr->len, 30);
+ for (; i > 0; name++, i--)
+ hash = partial_name_hash(toupper(*name), hash);
qstr->hash = end_name_hash(hash);
return 0;
}
static int
-affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+affs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+{
+ return __affs_hash_dentry(dentry, qstr, affs_toupper);
+}
+static int
+affs_intl_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+{
+ return __affs_hash_dentry(dentry, qstr, affs_intl_toupper);
+}
+
+static inline int
+__affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b, toupper_t toupper)
{
- unsigned int (*toupper)(unsigned int) = affs_toupper;
- int alen = a->len;
- int blen = b->len;
- int i;
+ const u8 *aname = a->name;
+ const u8 *bname = b->name;
+ int len;
/* 'a' is the qstr of an already existing dentry, so the name
* must be valid. 'b' must be validated first.
*/
-
+
if (affs_check_name(b->name,b->len))
return 1;
/* If the names are longer than the allowed 30 chars,
* the excess is ignored, so their length may differ.
*/
- if (alen > 30)
- alen = 30;
- if (blen > 30)
- blen = 30;
- if (alen != blen)
+ len = a->len;
+ if (len >= 30) {
+ if (b->len < 30)
+ return 1;
+ len = 30;
+ } else if (len != b->len)
return 1;
- /* Check whether to use the international 'toupper' routine */
- if (AFFS_I2FSTYPE(dentry->d_inode))
- toupper = affs_intl_toupper;
-
- for (i = 0; i < alen; i++)
- if (toupper(a->name[i]) != toupper(b->name[i]))
+ for (; len > 0; len--)
+ if (toupper(*aname++) != toupper(*bname++))
return 1;
-
+
return 0;
}
+static int
+affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+ return __affs_compare_dentry(dentry, a, b, affs_toupper);
+}
+static int
+affs_intl_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+ return __affs_compare_dentry(dentry, a, b, affs_intl_toupper);
+}
+
/*
* NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure.
*/
-static int
-affs_match(const unsigned char *name, int len, const unsigned char *compare, int dlen, int intl)
+static inline int
+affs_match(struct dentry *dentry, const u8 *name2, toupper_t toupper)
{
- unsigned int (*toupper)(unsigned int) = intl ? affs_intl_toupper : affs_toupper;
- int i;
-
- if (!compare)
- return 0;
+ const u8 *name = dentry->d_name.name;
+ int len = dentry->d_name.len;
- if (len > 30)
+ if (len >= 30) {
+ if (*name2 < 30)
+ return 0;
len = 30;
- if (dlen > 30)
- dlen = 30;
-
- /* "" means "." ---> so paths like "/usr/lib//libc.a" work */
- if (!len && dlen == 1 && compare[0] == '.')
- return 1;
- if (dlen != len)
+ } else if (len != *name2)
return 0;
- for (i = 0; i < len; i++)
- if (toupper(name[i]) != toupper(compare[i]))
+
+ for (name2++; len > 0; len--)
+ if (toupper(*name++) != toupper(*name2++))
return 0;
return 1;
}
int
-affs_hash_name(const unsigned char *name, int len, int intl, int hashsize)
+affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len)
{
- unsigned int i, x;
-
- if (len > 30)
- len = 30;
+ toupper_t toupper = affs_get_toupper(sb);
+ int hash;
- x = len;
- for (i = 0; i < len; i++)
- if (intl)
- x = (x * 13 + affs_intl_toupper(name[i] & 0xFF)) & 0x7ff;
- else
- x = (x * 13 + affs_toupper(name[i] & 0xFF)) & 0x7ff;
+ hash = len = MIN(len, 30);
+ for (; len > 0; len--)
+ hash = (hash * 13 + toupper(*name++)) & 0x7ff;
- return x % hashsize;
+ return hash % AFFS_SB->s_hashsize;
}
static struct buffer_head *
-affs_find_entry(struct inode *dir, struct dentry *dentry, unsigned long *ino)
+affs_find_entry(struct inode *dir, struct dentry *dentry)
{
- struct buffer_head *bh;
- int intl = AFFS_I2FSTYPE(dir);
- s32 key;
- const char *name = dentry->d_name.name;
- int namelen = dentry->d_name.len;
+ struct super_block *sb = dir->i_sb;
+ struct buffer_head *bh;
+ toupper_t toupper = affs_get_toupper(sb);
+ u32 key;
- pr_debug("AFFS: find_entry(\"%.*s\")\n",namelen,name);
+ pr_debug("AFFS: find_entry(\"%.*s\")\n", (int)dentry->d_name.len, dentry->d_name.name);
- bh = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
+ bh = affs_bread(sb, dir->i_ino);
if (!bh)
- return NULL;
+ return ERR_PTR(-EIO);
- if (namelen == 1 && name[0] == '.') {
- *ino = dir->i_ino;
- return bh;
- }
- if (namelen == 2 && name[0] == '.' && name[1] == '.') {
- *ino = affs_parent_ino(dir);
- return bh;
- }
-
- key = AFFS_GET_HASHENTRY(bh->b_data,affs_hash_name(name,namelen,intl,AFFS_I2HSIZE(dir)));
+ key = be32_to_cpu(AFFS_HEAD(bh)->table[affs_hash_name(sb, dentry->d_name.name, dentry->d_name.len)]);
for (;;) {
- unsigned char *cname;
- int cnamelen;
-
affs_brelse(bh);
- bh = NULL;
if (key == 0)
- break;
- bh = affs_bread(dir->i_dev,key,AFFS_I2BSIZE(dir));
+ return NULL;
+ bh = affs_bread(sb, key);
if (!bh)
- break;
- cnamelen = affs_get_file_name(AFFS_I2BSIZE(dir),bh->b_data,&cname);
- if (affs_match(name,namelen,cname,cnamelen,intl))
- break;
- key = be32_to_cpu(FILE_END(bh->b_data,dir)->hash_chain);
+ return ERR_PTR(-EIO);
+ if (affs_match(dentry, AFFS_TAIL(sb, bh)->name, toupper))
+ return bh;
+ key = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain);
}
- *ino = key;
- return bh;
}
struct dentry *
affs_lookup(struct inode *dir, struct dentry *dentry)
{
- unsigned long ino;
- struct buffer_head *bh;
- struct inode *inode;
+ struct super_block *sb = dir->i_sb;
+ struct buffer_head *bh;
+ struct inode *inode = NULL;
pr_debug("AFFS: lookup(\"%.*s\")\n",(int)dentry->d_name.len,dentry->d_name.name);
- inode = NULL;
- bh = affs_find_entry(dir,dentry,&ino);
+ down(&AFFS_DIR->i_hash_lock);
+ bh = affs_find_entry(dir, dentry);
+ up(&AFFS_DIR->i_hash_lock);
+ if (IS_ERR(bh))
+ return ERR_PTR(PTR_ERR(bh));
if (bh) {
- if (FILE_END(bh->b_data,dir)->original)
- ino = be32_to_cpu(FILE_END(bh->b_data,dir)->original);
+ u32 ino = bh->b_blocknr;
+
+ /* store the real header ino in d_fsdata for faster lookups */
+ dentry->d_fsdata = (void *)ino;
+ switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
+ case ST_LINKDIR:
+ case ST_LINKFILE:
+ ino = be32_to_cpu(AFFS_TAIL(sb, bh)->original);
+ }
affs_brelse(bh);
- inode = iget(dir->i_sb,ino);
+ inode = iget(sb, ino);
if (!inode)
return ERR_PTR(-EACCES);
}
- dentry->d_op = &affs_dentry_operations;
- d_add(dentry,inode);
+ dentry->d_op = AFFS_SB->s_flags & SF_INTL ? &affs_intl_dentry_operations : &affs_dentry_operations;
+ d_add(dentry, inode);
return NULL;
}
int
affs_unlink(struct inode *dir, struct dentry *dentry)
{
- int retval;
- struct buffer_head *bh;
- unsigned long ino;
- struct inode *inode;
-
- pr_debug("AFFS: unlink(dir=%ld,\"%.*s\")\n",dir->i_ino,
- (int)dentry->d_name.len,dentry->d_name.name);
-
- retval = -ENOENT;
- if (!(bh = affs_find_entry(dir,dentry,&ino)))
- goto unlink_done;
+ pr_debug("AFFS: unlink(dir=%d, \"%.*s\")\n", (u32)dir->i_ino,
+ (int)dentry->d_name.len, dentry->d_name.name);
- inode = dentry->d_inode;
+ if (!dentry->d_inode)
+ return -ENOENT;
- if ((retval = affs_remove_header(bh,inode)) < 0)
- goto unlink_done;
-
- inode->i_nlink = retval;
- inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
- dir->i_version = ++event;
- mark_inode_dirty(inode);
- mark_inode_dirty(dir);
- retval = 0;
-
-unlink_done:
- affs_brelse(bh);
- return retval;
+ return affs_remove_header(dentry);
}
int
affs_create(struct inode *dir, struct dentry *dentry, int mode)
{
+ struct super_block *sb = dir->i_sb;
struct inode *inode;
int error;
-
+
pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,(int)dentry->d_name.len,
dentry->d_name.name,mode);
- error = -ENOSPC;
inode = affs_new_inode(dir);
if (!inode)
- goto out;
-
- pr_debug("AFFS: ino=%lu\n",inode->i_ino);
- if (dir->i_sb->u.affs_sb.s_flags & SF_OFS) {
- inode->i_op = &affs_file_inode_operations;
- inode->i_fop = &affs_file_operations_ofs;
- } else {
- inode->i_op = &affs_file_inode_operations;
- inode->i_fop = &affs_file_operations;
- inode->i_mapping->a_ops = &affs_aops;
- inode->u.affs_i.mmu_private = inode->i_size;
- }
- error = affs_add_entry(dir,NULL,inode,dentry,ST_FILE);
- if (error)
- goto out_iput;
+ return -ENOSPC;
+
inode->i_mode = mode;
- inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
- d_instantiate(dentry,inode);
+ mode_to_prot(inode);
mark_inode_dirty(inode);
- dir->i_version = ++event;
- mark_inode_dirty(dir);
-out:
- return error;
-out_iput:
- inode->i_nlink = 0;
- mark_inode_dirty(inode);
- iput(inode);
- goto out;
+ inode->i_op = &affs_file_inode_operations;
+ inode->i_fop = &affs_file_operations;
+ inode->i_mapping->a_ops = (AFFS_SB->s_flags & SF_OFS) ? &affs_aops_ofs : &affs_aops;
+ error = affs_add_entry(dir, inode, dentry, ST_FILE);
+ if (error) {
+ inode->i_nlink = 0;
+ iput(inode);
+ return error;
+ }
+ return 0;
}
int
{
struct inode *inode;
int error;
-
+
pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino,
(int)dentry->d_name.len,dentry->d_name.name,mode);
- error = -ENOSPC;
inode = affs_new_inode(dir);
if (!inode)
- goto out;
+ return -ENOSPC;
+
+ inode->i_mode = S_IFDIR | mode;
+ mode_to_prot(inode);
inode->i_op = &affs_dir_inode_operations;
inode->i_fop = &affs_dir_operations;
- error = affs_add_entry(dir,NULL,inode,dentry,ST_USERDIR);
- if (error)
- goto out_iput;
- inode->i_mode = S_IFDIR | S_ISVTX | mode;
- inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
- d_instantiate(dentry,inode);
- mark_inode_dirty(inode);
- dir->i_version = ++event;
- mark_inode_dirty(dir);
-out:
- return error;
-out_iput:
- inode->i_nlink = 0;
- mark_inode_dirty(inode);
- iput(inode);
- goto out;
-}
-
-static int
-empty_dir(struct buffer_head *bh, int hashsize)
-{
- while (--hashsize >= 0) {
- if (((struct dir_front *)bh->b_data)->hashtable[hashsize])
- return 0;
+ error = affs_add_entry(dir, inode, dentry, ST_USERDIR);
+ if (error) {
+ inode->i_nlink = 0;
+ mark_inode_dirty(inode);
+ iput(inode);
+ return error;
}
- return 1;
+ return 0;
}
int
affs_rmdir(struct inode *dir, struct dentry *dentry)
{
- struct inode *inode = dentry->d_inode;
- int retval;
- unsigned long ino;
- struct buffer_head *bh;
-
- pr_debug("AFFS: rmdir(dir=%lu,\"%.*s\")\n",dir->i_ino,
- (int)dentry->d_name.len,dentry->d_name.name);
-
- retval = -ENOENT;
- if (!(bh = affs_find_entry(dir,dentry,&ino)))
- goto rmdir_done;
+ pr_debug("AFFS: rmdir(dir=%u, \"%.*s\")\n", (u32)dir->i_ino,
+ (int)dentry->d_name.len, dentry->d_name.name);
- /*
- * Make sure the directory is empty and the dentry isn't busy.
- */
- retval = -ENOTEMPTY;
- if (!empty_dir(bh,AFFS_I2HSIZE(inode)))
- goto rmdir_done;
- retval = -EBUSY;
- if (!d_unhashed(dentry))
- goto rmdir_done;
-
- if ((retval = affs_remove_header(bh,inode)) < 0)
- goto rmdir_done;
-
- inode->i_nlink = retval;
- inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
- retval = 0;
- dir->i_version = ++event;
- mark_inode_dirty(dir);
- mark_inode_dirty(inode);
+ if (!dentry->d_inode)
+ return -ENOENT;
-rmdir_done:
- affs_brelse(bh);
- return retval;
+ return affs_remove_header(dentry);
}
int
affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
{
+ struct super_block *sb = dir->i_sb;
struct buffer_head *bh;
struct inode *inode;
char *p;
- unsigned long tmp;
int i, maxlen, error;
char c, lc;
pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino,
(int)dentry->d_name.len,dentry->d_name.name,symname);
-
- maxlen = 4 * AFFS_I2HSIZE(dir) - 1;
+
+ maxlen = AFFS_SB->s_hashsize * sizeof(u32) - 1;
error = -ENOSPC;
inode = affs_new_inode(dir);
if (!inode)
- goto out;
+ return -ENOSPC;
inode->i_op = &affs_symlink_inode_operations;
inode->i_data.a_ops = &affs_symlink_aops;
inode->i_mode = S_IFLNK | 0777;
- inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
+ mode_to_prot(inode);
+
error = -EIO;
- bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
+ bh = affs_bread(sb, inode->i_ino);
if (!bh)
- goto out_iput;
+ goto err;
i = 0;
- p = ((struct slink_front *)bh->b_data)->symname;
+ p = (char *)AFFS_HEAD(bh)->table;
lc = '/';
if (*symname == '/') {
while (*symname == '/')
symname++;
- while (inode->i_sb->u.affs_sb.s_volume[i]) /* Cannot overflow */
- *p++ = inode->i_sb->u.affs_sb.s_volume[i++];
+ while (AFFS_SB->s_volume[i]) /* Cannot overflow */
+ *p++ = AFFS_SB->s_volume[i++];
}
while (i < maxlen && (c = *symname++)) {
if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') {
affs_brelse(bh);
mark_inode_dirty(inode);
- /* N.B. This test shouldn't be necessary ... dentry must be negative */
- error = -EEXIST;
- bh = affs_find_entry(dir,dentry,&tmp);
- if (bh)
- goto out_release;
- /* N.B. Shouldn't we add the entry before dirtying the buffer? */
- error = affs_add_entry(dir,NULL,inode,dentry,ST_SOFTLINK);
+ error = affs_add_entry(dir, inode, dentry, ST_SOFTLINK);
if (error)
- goto out_release;
- d_instantiate(dentry,inode);
- dir->i_version = ++event;
- mark_inode_dirty(dir);
+ goto err;
-out:
- return error;
+ return 0;
-out_release:
- affs_brelse(bh);
-out_iput:
+err:
inode->i_nlink = 0;
mark_inode_dirty(inode);
iput(inode);
- goto out;
+ return error;
}
int
affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
{
- struct inode *oldinode = old_dentry->d_inode;
- struct inode *inode;
- struct buffer_head *bh;
- unsigned long i;
- int error;
-
- pr_debug("AFFS: link(%lu,%lu,\"%.*s\")\n",oldinode->i_ino,dir->i_ino,
+ struct inode *inode = old_dentry->d_inode;
+ int error;
+
+ pr_debug("AFFS: link(%u, %u, \"%.*s\")\n", (u32)inode->i_ino, (u32)dir->i_ino,
(int)dentry->d_name.len,dentry->d_name.name);
- /* N.B. Do we need this test? The dentry must be negative ... */
- bh = affs_find_entry(dir,dentry,&i);
- if (bh) {
- affs_brelse(bh);
- return -EEXIST;
- }
- if (oldinode->u.affs_i.i_hlink) { /* Cannot happen */
- affs_warning(dir->i_sb,"link","Impossible link to link");
- return -EINVAL;
- }
- error = -ENOSPC;
- if (!(inode = affs_new_inode(dir)))
- goto out;
-
- inode->i_op = oldinode->i_op;
- inode->i_fop = oldinode->i_fop;
- inode->u.affs_i.i_protect = mode_to_prot(oldinode->i_mode);
- inode->u.affs_i.i_original = oldinode->i_ino;
- inode->u.affs_i.i_hlink = 1;
- inode->i_mtime = oldinode->i_mtime;
-
- if (S_ISDIR(oldinode->i_mode))
- error = affs_add_entry(dir,oldinode,inode,dentry,ST_LINKDIR);
- else
- error = affs_add_entry(dir,oldinode,inode,dentry,ST_LINKFILE);
- if (error)
+ error = affs_add_entry(dir, inode, dentry, S_ISDIR(inode->i_mode) ? ST_LINKDIR : ST_LINKFILE);
+ if (error) {
inode->i_nlink = 0;
- else {
- dir->i_version = ++event;
- mark_inode_dirty(dir);
- mark_inode_dirty(oldinode);
- atomic_inc(&oldinode->i_count);
- d_instantiate(dentry,oldinode);
+ mark_inode_dirty(inode);
+ iput(inode);
+ return error;
}
- mark_inode_dirty(inode);
- iput(inode);
-
-out:
- return error;
+ return 0;
}
int
affs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
- struct inode *old_inode = old_dentry->d_inode;
- struct inode *new_inode = new_dentry->d_inode;
- struct buffer_head *old_bh;
- struct buffer_head *new_bh;
- unsigned long old_ino;
- unsigned long new_ino;
- int retval;
-
- pr_debug("AFFS: rename(old=%lu,\"%*s\" (inode=%p) to new=%lu,\"%*s\" (inode=%p))\n",
- old_dir->i_ino,old_dentry->d_name.len,old_dentry->d_name.name,old_inode,
- new_dir->i_ino,new_dentry->d_name.len,new_dentry->d_name.name,new_inode);
-
+ struct super_block *sb = old_dir->i_sb;
+ struct inode *dir;
+ struct buffer_head *bh = NULL;
+ int retval;
+
+ pr_debug("AFFS: rename(old=%u,\"%*s\" to new=%u,\"%*s\")\n",
+ (u32)old_dir->i_ino, (int)old_dentry->d_name.len, old_dentry->d_name.name,
+ (u32)new_dir->i_ino, (int)new_dentry->d_name.len, new_dentry->d_name.name);
+
if ((retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len)))
- goto out;
-
- new_bh = NULL;
- retval = -ENOENT;
- old_bh = affs_find_entry(old_dir,old_dentry,&old_ino);
- if (!old_bh)
- goto end_rename;
-
- new_bh = affs_find_entry(new_dir,new_dentry,&new_ino);
- if (new_bh && !new_inode) {
- affs_error(old_inode->i_sb,"affs_rename",
- "No inode for entry found (key=%lu)\n",new_ino);
- goto end_rename;
- }
- if (S_ISDIR(old_inode->i_mode)) {
- if (new_inode) {
- retval = -EBUSY;
- if (!d_unhashed(new_dentry))
- goto end_rename;
- retval = -ENOTEMPTY;
- if (!empty_dir(new_bh,AFFS_I2HSIZE(new_inode)))
- goto end_rename;
- }
+ goto done;
- retval = -ENOENT;
- if (affs_parent_ino(old_inode) != old_dir->i_ino)
- goto end_rename;
- }
/* Unlink destination if it already exists */
- if (new_inode) {
- if ((retval = affs_remove_header(new_bh,new_dir)) < 0)
- goto end_rename;
- new_inode->i_nlink = retval;
- mark_inode_dirty(new_inode);
- if (new_inode->i_ino == new_ino)
- new_inode->i_nlink = 0;
+ if (new_dentry->d_inode) {
+ retval = affs_remove_header(new_dentry);
+ if (retval)
+ return retval;
}
+
+ retval = -EIO;
+ bh = affs_bread(sb, old_dentry->d_inode->i_ino);
+ if (!bh)
+ goto done;
+
/* Remove header from its parent directory. */
- if ((retval = affs_remove_hash(old_bh,old_dir)))
- goto end_rename;
+ dir = old_dir;
+ down(&AFFS_DIR->i_hash_lock);
+ retval = affs_remove_hash(dir, bh);
+ if (retval)
+ goto done_unlock;
+
+ up(&AFFS_DIR->i_hash_lock);
+
/* And insert it into the new directory with the new name. */
- affs_copy_name(FILE_END(old_bh->b_data,old_inode)->file_name,new_dentry->d_name.name);
- if ((retval = affs_insert_hash(new_dir->i_ino,old_bh,new_dir)))
- goto end_rename;
- affs_fix_checksum(AFFS_I2BSIZE(new_dir),old_bh->b_data,5);
-
- new_dir->i_ctime = new_dir->i_mtime = old_dir->i_ctime
- = old_dir->i_mtime = CURRENT_TIME;
- new_dir->i_version = ++event;
- old_dir->i_version = ++event;
- retval = 0;
- mark_inode_dirty(new_dir);
- mark_inode_dirty(old_dir);
- mark_buffer_dirty(old_bh);
-
-end_rename:
- affs_brelse(old_bh);
- affs_brelse(new_bh);
-out:
+ dir = new_dir;
+ affs_copy_name(AFFS_TAIL(sb, bh)->name, new_dentry);
+ affs_fix_checksum(sb, bh);
+ down(&AFFS_DIR->i_hash_lock);
+ retval = affs_insert_hash(new_dir, bh);
+ if (retval)
+ goto done_unlock;
+ up(&AFFS_DIR->i_hash_lock);
+
+done:
+ affs_brelse(bh);
return retval;
+
+done_unlock:
+ up(&AFFS_DIR->i_hash_lock);
+ goto done;
}
* (C) 1991 Linus Torvalds - minix filesystem
*/
-#define DEBUG 0
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/fs.h>
extern int *blk_size[];
extern struct timezone sys_tz;
-#define MIN(a,b) (((a)<(b))?(a):(b))
-
static int affs_statfs(struct super_block *sb, struct statfs *buf);
static int affs_remount (struct super_block *sb, int *flags, char *data);
static void
affs_put_super(struct super_block *sb)
{
- int i;
-
pr_debug("AFFS: put_super()\n");
- for (i = 0; i < sb->u.affs_sb.s_bm_count; i++)
- affs_brelse(sb->u.affs_sb.s_bitmap[i].bm_bh);
if (!(sb->s_flags & MS_RDONLY)) {
- ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->bm_flag = be32_to_cpu(1);
+ AFFS_ROOT_TAIL(sb, AFFS_SB->s_root_bh)->bm_flag = be32_to_cpu(1);
secs_to_datestamp(CURRENT_TIME,
- &ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->disk_altered);
- affs_fix_checksum(sb->s_blocksize,sb->u.affs_sb.s_root_bh->b_data,5);
- mark_buffer_dirty(sb->u.affs_sb.s_root_bh);
+ &AFFS_ROOT_TAIL(sb, AFFS_SB->s_root_bh)->disk_change);
+ affs_fix_checksum(sb, AFFS_SB->s_root_bh);
+ mark_buffer_dirty(AFFS_SB->s_root_bh);
}
- if (sb->u.affs_sb.s_prefix)
- kfree(sb->u.affs_sb.s_prefix);
- kfree(sb->u.affs_sb.s_bitmap);
- affs_brelse(sb->u.affs_sb.s_root_bh);
-
- /*
- * Restore the previous value of this device's blksize_size[][]
- */
- set_blocksize(sb->s_dev, sb->u.affs_sb.s_blksize);
+ affs_brelse(AFFS_SB->s_bmap_bh);
+ if (AFFS_SB->s_prefix)
+ kfree(AFFS_SB->s_prefix);
+ kfree(AFFS_SB->s_bitmap);
+ affs_brelse(AFFS_SB->s_root_bh);
return;
}
static void
affs_write_super(struct super_block *sb)
{
- int i, clean = 2;
+ int clean = 2;
if (!(sb->s_flags & MS_RDONLY)) {
- lock_super(sb);
- for (i = 0, clean = 1; i < sb->u.affs_sb.s_bm_count; i++) {
- if (sb->u.affs_sb.s_bitmap[i].bm_bh) {
- if (buffer_dirty(sb->u.affs_sb.s_bitmap[i].bm_bh)) {
- clean = 0;
- break;
- }
- }
- }
- unlock_super(sb);
- ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->bm_flag = be32_to_cpu(clean);
+ // if (AFFS_SB->s_bitmap[i].bm_bh) {
+ // if (buffer_dirty(AFFS_SB->s_bitmap[i].bm_bh)) {
+ // clean = 0;
+ AFFS_ROOT_TAIL(sb, AFFS_SB->s_root_bh)->bm_flag = be32_to_cpu(clean);
secs_to_datestamp(CURRENT_TIME,
- &ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->disk_altered);
- affs_fix_checksum(sb->s_blocksize,sb->u.affs_sb.s_root_bh->b_data,5);
- mark_buffer_dirty(sb->u.affs_sb.s_root_bh);
+ &AFFS_ROOT_TAIL(sb, AFFS_SB->s_root_bh)->disk_change);
+ affs_fix_checksum(sb, AFFS_SB->s_root_bh);
+ mark_buffer_dirty(AFFS_SB->s_root_bh);
sb->s_dirt = !clean; /* redo until bitmap synced */
} else
sb->s_dirt = 0;
write_inode: affs_write_inode,
put_inode: affs_put_inode,
delete_inode: affs_delete_inode,
+ clear_inode: affs_clear_inode,
put_super: affs_put_super,
write_super: affs_write_super,
statfs: affs_statfs,
*/
static struct super_block *
-affs_read_super(struct super_block *s, void *data, int silent)
+affs_read_super(struct super_block *sb, void *data, int silent)
{
- struct buffer_head *bh = NULL;
- struct buffer_head *bb;
- struct inode *root_inode;
- kdev_t dev = s->s_dev;
+ struct buffer_head *root_bh = NULL;
+ struct buffer_head *boot_bh;
+ struct inode *root_inode = NULL;
+ kdev_t dev = sb->s_dev;
s32 root_block;
int blocks, size, blocksize;
u32 chksum;
- u32 *bm;
- s32 ptype, stype;
- int mapidx;
int num_bm;
int i, j;
s32 key;
uid_t uid;
gid_t gid;
int reserved;
- int az_no;
- int bmalt = 0;
unsigned long mount_flags;
- unsigned long offset;
pr_debug("AFFS: read_super(%s)\n",data ? (const char *)data : "no options");
- s->s_magic = AFFS_SUPER_MAGIC;
- s->s_op = &affs_sops;
- s->u.affs_sb.s_bitmap = NULL;
- s->u.affs_sb.s_root_bh = NULL;
- s->u.affs_sb.s_prefix = NULL;
- s->u.affs_sb.s_hashsize= 0;
+ sb->s_magic = AFFS_SUPER_MAGIC;
+ sb->s_op = &affs_sops;
+ memset(AFFS_SB, 0, sizeof(struct affs_sb_info));
+ init_MUTEX(&AFFS_SB->s_bmlock);
if (!parse_options(data,&uid,&gid,&i,&reserved,&root_block,
- &blocksize,&s->u.affs_sb.s_prefix,
- s->u.affs_sb.s_volume, &mount_flags))
- goto out_bad_opts;
+ &blocksize,&AFFS_SB->s_prefix,
+ AFFS_SB->s_volume, &mount_flags)) {
+ printk(KERN_ERR "AFFS: Error parsing options\n");
+ return NULL;
+ }
/* N.B. after this point s_prefix must be released */
- s->u.affs_sb.s_flags = mount_flags;
- s->u.affs_sb.s_mode = i;
- s->u.affs_sb.s_uid = uid;
- s->u.affs_sb.s_gid = gid;
- s->u.affs_sb.s_reserved= reserved;
+ AFFS_SB->s_flags = mount_flags;
+ AFFS_SB->s_mode = i;
+ AFFS_SB->s_uid = uid;
+ AFFS_SB->s_gid = gid;
+ AFFS_SB->s_reserved= reserved;
/* Get the size of the device in 512-byte blocks.
* If we later see that the partition uses bigger
* blocks, we will have to change it.
*/
- blocks = blk_size[MAJOR(dev)][MINOR(dev)];
- if (blocks == 0)
- goto out_bad_size;
- s->u.affs_sb.s_blksize = blksize_size[MAJOR(dev)][MINOR(dev)];
- if (!s->u.affs_sb.s_blksize)
- s->u.affs_sb.s_blksize = BLOCK_SIZE;
- size = (s->u.affs_sb.s_blksize / 512) * blocks;
- pr_debug("AFFS: initial blksize=%d, blocks=%d\n",
- s->u.affs_sb.s_blksize, blocks);
+ blocks = blk_size[MAJOR(dev)] ? blk_size[MAJOR(dev)][MINOR(dev)] : 0;
+ if (!blocks) {
+ printk(KERN_ERR "AFFS: Could not determine device size\n");
+ goto out_error;
+ }
+ size = (BLOCK_SIZE / 512) * blocks;
+ pr_debug("AFFS: initial blksize=%d, blocks=%d\n", 512, blocks);
+#warning
+ affs_set_blocksize(sb, PAGE_SIZE);
/* Try to find root block. Its location depends on the block size. */
i = 512;
size = size / (blocksize / 512);
}
for (blocksize = i, key = 0; blocksize <= j; blocksize <<= 1, size >>= 1) {
- s->u.affs_sb.s_root_block = root_block;
+ AFFS_SB->s_root_block = root_block;
if (root_block < 0)
- s->u.affs_sb.s_root_block = (reserved + size - 1) / 2;
+ AFFS_SB->s_root_block = (reserved + size - 1) / 2;
pr_debug("AFFS: setting blocksize to %d\n", blocksize);
- set_blocksize(dev, blocksize);
+ affs_set_blocksize(sb, blocksize);
+ AFFS_SB->s_partition_size = size;
/* The root block location that was calculated above is not
* correct if the partition size is an odd number of 512-
pr_debug("AFFS: Dev %s, trying root=%u, bs=%d, "
"size=%d, reserved=%d\n",
kdevname(dev),
- s->u.affs_sb.s_root_block + num_bm,
+ AFFS_SB->s_root_block + num_bm,
blocksize, size, reserved);
- bh = affs_bread(dev, s->u.affs_sb.s_root_block + num_bm,
- blocksize);
- if (!bh)
+ root_bh = affs_bread(sb, AFFS_SB->s_root_block + num_bm);
+ if (!root_bh)
continue;
- if (!affs_checksum_block(blocksize,bh->b_data,&ptype,&stype) &&
- ptype == T_SHORT && stype == ST_ROOT) {
- s->s_blocksize = blocksize;
- s->u.affs_sb.s_hashsize = blocksize / 4 - 56;
- s->u.affs_sb.s_root_block += num_bm;
+ if (!affs_checksum_block(sb, root_bh) &&
+ be32_to_cpu(AFFS_ROOT_HEAD(root_bh)->ptype) == T_SHORT &&
+ be32_to_cpu(AFFS_ROOT_TAIL(sb, root_bh)->stype) == ST_ROOT) {
+ AFFS_SB->s_hashsize = blocksize / 4 - 56;
+ AFFS_SB->s_root_block += num_bm;
key = 1;
goto got_root;
}
- affs_brelse(bh);
- bh = NULL;
+ affs_brelse(root_bh);
+ root_bh = NULL;
}
}
- goto out_no_valid_block;
+ if (!silent)
+ printk(KERN_ERR "AFFS: No valid root block on device %s\n",
+ kdevname(dev));
+ goto out_error;
/* N.B. after this point bh must be released */
got_root:
- root_block = s->u.affs_sb.s_root_block;
+ root_block = AFFS_SB->s_root_block;
- s->u.affs_sb.s_partition_size = size;
- s->s_blocksize_bits = blocksize == 512 ? 9 :
- blocksize == 1024 ? 10 :
- blocksize == 2048 ? 11 : 12;
+ sb->s_blocksize_bits = blocksize == 512 ? 9 :
+ blocksize == 1024 ? 10 :
+ blocksize == 2048 ? 11 : 12;
/* Find out which kind of FS we have */
- bb = affs_bread(dev,0,s->s_blocksize);
- if (!bb)
- goto out_no_root_block;
- chksum = be32_to_cpu(*(u32 *)bb->b_data);
- affs_brelse(bb);
+ boot_bh = bread(sb->s_dev, 0, sb->s_blocksize);
+ if (!boot_bh) {
+ printk(KERN_ERR "AFFS: Cannot read boot block\n");
+ goto out_error;
+ }
+ chksum = be32_to_cpu(*(u32 *)boot_bh->b_data);
+ brelse(boot_bh);
/* Dircache filesystems are compatible with non-dircache ones
* when reading. As long as they aren't supported, writing is
* not recommended.
*/
if ((chksum == FS_DCFFS || chksum == MUFS_DCFFS || chksum == FS_DCOFS
- || chksum == MUFS_DCOFS) && !(s->s_flags & MS_RDONLY)) {
+ || chksum == MUFS_DCOFS) && !(sb->s_flags & MS_RDONLY)) {
printk(KERN_NOTICE "AFFS: Dircache FS - mounting %s read only\n",
kdevname(dev));
- s->s_flags |= MS_RDONLY;
- s->u.affs_sb.s_flags |= SF_READONLY;
+ sb->s_flags |= MS_RDONLY;
+ AFFS_SB->s_flags |= SF_READONLY;
}
switch (chksum) {
case MUFS_FS:
case MUFS_INTLFFS:
- s->u.affs_sb.s_flags |= SF_MUFS;
+ AFFS_SB->s_flags |= SF_MUFS;
/* fall thru */
case FS_INTLFFS:
- s->u.affs_sb.s_flags |= SF_INTL;
+ AFFS_SB->s_flags |= SF_INTL;
break;
case MUFS_DCFFS:
case MUFS_FFS:
- s->u.affs_sb.s_flags |= SF_MUFS;
+ AFFS_SB->s_flags |= SF_MUFS;
break;
case FS_DCFFS:
case FS_FFS:
break;
case MUFS_OFS:
- s->u.affs_sb.s_flags |= SF_MUFS;
+ AFFS_SB->s_flags |= SF_MUFS;
/* fall thru */
case FS_OFS:
- s->u.affs_sb.s_flags |= SF_OFS;
- s->s_flags |= MS_NOEXEC;
+ AFFS_SB->s_flags |= SF_OFS;
+ sb->s_flags |= MS_NOEXEC;
break;
case MUFS_DCOFS:
case MUFS_INTLOFS:
- s->u.affs_sb.s_flags |= SF_MUFS;
+ AFFS_SB->s_flags |= SF_MUFS;
case FS_DCOFS:
case FS_INTLOFS:
- s->u.affs_sb.s_flags |= SF_INTL | SF_OFS;
- s->s_flags |= MS_NOEXEC;
+ AFFS_SB->s_flags |= SF_INTL | SF_OFS;
+ sb->s_flags |= MS_NOEXEC;
break;
default:
- goto out_unknown_fs;
+ printk(KERN_ERR "AFFS: Unknown filesystem on device %s: %08X\n",
+ kdevname(dev), chksum);
+ goto out_error;
}
if (mount_flags & SF_VERBOSE) {
chksum = cpu_to_be32(chksum);
printk(KERN_NOTICE "AFFS: Mounting volume \"%*s\": Type=%.3s\\%c, Blocksize=%d\n",
- GET_END_PTR(struct root_end,bh->b_data,blocksize)->disk_name[0],
- &GET_END_PTR(struct root_end,bh->b_data,blocksize)->disk_name[1],
+ AFFS_ROOT_TAIL(sb, root_bh)->disk_name[0],
+ AFFS_ROOT_TAIL(sb, root_bh)->disk_name + 1,
(char *)&chksum,((char *)&chksum)[3] + '0',blocksize);
}
- s->s_flags |= MS_NODEV | MS_NOSUID;
+ sb->s_flags |= MS_NODEV | MS_NOSUID;
+
+ AFFS_SB->s_data_blksize = sb->s_blocksize;
+ if (AFFS_SB->s_flags & SF_OFS)
+ AFFS_SB->s_data_blksize -= 24;
/* Keep super block in cache */
- bb = affs_bread(dev,root_block,s->s_blocksize);
- if (!bb)
- goto out_no_root_block;
- s->u.affs_sb.s_root_bh = bb;
+ AFFS_SB->s_root_bh = root_bh;
/* N.B. after this point s_root_bh must be released */
- /* Allocate space for bitmaps, zones and others */
-
- size = s->u.affs_sb.s_partition_size - reserved;
- num_bm = (size + s->s_blocksize * 8 - 32 - 1) / (s->s_blocksize * 8 - 32);
- az_no = (size + AFFS_ZONE_SIZE - 1) / (AFFS_ZONE_SIZE - 32);
- ptype = num_bm * sizeof(struct affs_bm_info) +
- az_no * sizeof(struct affs_alloc_zone) +
- MAX_ZONES * sizeof(struct affs_zone);
- pr_debug("AFFS: num_bm=%d, az_no=%d, sum=%d\n",num_bm,az_no,ptype);
- if (!(s->u.affs_sb.s_bitmap = kmalloc(ptype, GFP_KERNEL)))
- goto out_no_bitmap;
- memset(s->u.affs_sb.s_bitmap,0,ptype);
- /* N.B. after the point s_bitmap must be released */
-
- s->u.affs_sb.s_zones = (struct affs_zone *)&s->u.affs_sb.s_bitmap[num_bm];
- s->u.affs_sb.s_alloc = (struct affs_alloc_zone *)&s->u.affs_sb.s_zones[MAX_ZONES];
- s->u.affs_sb.s_num_az = az_no;
-
- mapidx = 0;
-
- if (ROOT_END_S(bh->b_data,s)->bm_flag == 0) {
- if (!(s->s_flags & MS_RDONLY)) {
- printk(KERN_NOTICE "AFFS: Bitmap invalid - mounting %s read only\n",
- kdevname(dev));
- s->s_flags |= MS_RDONLY;
- }
- affs_brelse(bh);
- bh = NULL;
- goto nobitmap;
- }
-
- /* The following section is ugly, I know. Especially because of the
- * reuse of some variables that are not named properly.
- */
-
- key = root_block;
- ptype = s->s_blocksize / 4 - 49;
- stype = ptype + 25;
- offset = s->u.affs_sb.s_reserved;
- az_no = 0;
- while (bh) {
- bm = (u32 *)bh->b_data;
- for (i = ptype; i < stype && bm[i]; i++, mapidx++) {
- if (mapidx >= num_bm) {
- printk(KERN_ERR "AFFS: Extraneous bitmap pointer - "
- "mounting %s read only.\n",kdevname(dev));
- s->s_flags |= MS_RDONLY;
- s->u.affs_sb.s_flags |= SF_READONLY;
- continue;
- }
- bb = affs_bread(dev,be32_to_cpu(bm[i]),s->s_blocksize);
- if (!bb)
- goto out_no_read_bm;
- if (affs_checksum_block(s->s_blocksize,bb->b_data,NULL,NULL) &&
- !(s->s_flags & MS_RDONLY)) {
- printk(KERN_WARNING "AFFS: Bitmap (%d,key=%u) invalid - "
- "mounting %s read only.\n",mapidx,be32_to_cpu(bm[i]),
- kdevname(dev));
- s->s_flags |= MS_RDONLY;
- s->u.affs_sb.s_flags |= SF_READONLY;
- }
- /* Mark unused bits in the last word as allocated */
- if (size <= s->s_blocksize * 8 - 32) { /* last bitmap */
- ptype = size / 32 + 1; /* word number */
- key = size & 0x1F; /* used bits */
- if (key && !(s->s_flags & MS_RDONLY)) {
- chksum = cpu_to_be32(0x7FFFFFFF >> (31 - key));
- ((u32 *)bb->b_data)[ptype] &= chksum;
- affs_fix_checksum(s->s_blocksize,bb->b_data,0);
- mark_buffer_dirty(bb);
- bmalt = 1;
- }
- ptype = (size + 31) & ~0x1F;
- size = 0;
- s->u.affs_sb.s_flags |= SF_BM_VALID;
- } else {
- ptype = s->s_blocksize * 8 - 32;
- size -= ptype;
- }
- s->u.affs_sb.s_bitmap[mapidx].bm_firstblk = offset;
- s->u.affs_sb.s_bitmap[mapidx].bm_bh = NULL;
- s->u.affs_sb.s_bitmap[mapidx].bm_key = be32_to_cpu(bm[i]);
- s->u.affs_sb.s_bitmap[mapidx].bm_count = 0;
- offset += ptype;
-
- for (j = 0; ptype > 0; j++, az_no++, ptype -= key) {
- key = MIN(ptype,AFFS_ZONE_SIZE); /* size in bits */
- s->u.affs_sb.s_alloc[az_no].az_size = key / 32;
- s->u.affs_sb.s_alloc[az_no].az_free =
- affs_count_free_bits(key / 8,bb->b_data +
- j * (AFFS_ZONE_SIZE / 8) + 4);
- }
- affs_brelse(bb);
- }
- key = be32_to_cpu(bm[stype]); /* Next block of bitmap pointers */
- ptype = 0;
- stype = s->s_blocksize / 4 - 1;
- affs_brelse(bh);
- bh = NULL;
- if (key) {
- bh = affs_bread(dev,key,s->s_blocksize);
- if (!bh)
- goto out_no_bm_ext;
- }
- }
- if (mapidx < num_bm)
- goto out_bad_num;
-
-nobitmap:
- s->u.affs_sb.s_bm_count = num_bm;
+ if (affs_init_bitmap(sb))
+ goto out_error;
/* set up enough so that it can read an inode */
- s->s_dirt = 1;
- root_inode = iget(s,root_block);
- if (!root_inode)
- goto out_no_root;
- s->s_root = d_alloc_root(root_inode);
- if (!s->s_root)
- goto out_no_root;
- s->s_root->d_op = &affs_dentry_operations;
-
- /* Record date of last change if the bitmap was truncated and
- * create data zones if the volume is writable.
- */
-
- if (!(s->s_flags & MS_RDONLY)) {
- if (bmalt) {
- secs_to_datestamp(CURRENT_TIME,&ROOT_END(
- s->u.affs_sb.s_root_bh->b_data,root_inode)->disk_altered);
- affs_fix_checksum(s->s_blocksize,s->u.affs_sb.s_root_bh->b_data,5);
- mark_buffer_dirty(s->u.affs_sb.s_root_bh);
- }
- affs_make_zones(s);
+ root_inode = iget(sb, root_block);
+ sb->s_root = d_alloc_root(root_inode);
+ if (!sb->s_root) {
+ printk(KERN_ERR "AFFS: Get root inode failed\n");
+ goto out_error;
}
+ sb->s_root->d_op = &affs_dentry_operations;
- pr_debug("AFFS: s_flags=%lX\n",s->s_flags);
- return s;
-
-out_bad_opts:
- printk(KERN_ERR "AFFS: Error parsing options\n");
- goto out_fail;
-out_bad_size:
- printk(KERN_ERR "AFFS: Could not determine device size\n");
- goto out_free_prefix;
-out_no_valid_block:
- if (!silent)
- printk(KERN_ERR "AFFS: No valid root block on device %s\n",
- kdevname(dev));
- goto out_restore;
-out_unknown_fs:
- printk(KERN_ERR "AFFS: Unknown filesystem on device %s: %08X\n",
- kdevname(dev), chksum);
- goto out_free_bh;
-out_no_root_block:
- printk(KERN_ERR "AFFS: Cannot read root block\n");
- goto out_free_bh;
-out_no_bitmap:
- printk(KERN_ERR "AFFS: Bitmap allocation failed\n");
- goto out_free_root_block;
-out_no_read_bm:
- printk(KERN_ERR "AFFS: Cannot read bitmap\n");
- goto out_free_bitmap;
-out_no_bm_ext:
- printk(KERN_ERR "AFFS: Cannot read bitmap extension\n");
- goto out_free_bitmap;
-out_bad_num:
- printk(KERN_ERR "AFFS: Got only %d bitmap blocks, expected %d\n",
- mapidx, num_bm);
- goto out_free_bitmap;
-out_no_root:
- printk(KERN_ERR "AFFS: Get root inode failed\n");
+ pr_debug("AFFS: s_flags=%lX\n",sb->s_flags);
+ return sb;
/*
* Begin the cascaded cleanup ...
*/
- iput(root_inode);
-out_free_bitmap:
- kfree(s->u.affs_sb.s_bitmap);
-out_free_root_block:
- affs_brelse(s->u.affs_sb.s_root_bh);
-out_free_bh:
- affs_brelse(bh);
-out_restore:
- set_blocksize(dev, s->u.affs_sb.s_blksize);
-out_free_prefix:
- if (s->u.affs_sb.s_prefix)
- kfree(s->u.affs_sb.s_prefix);
-out_fail:
+out_error:
+ if (root_inode)
+ iput(root_inode);
+ if (AFFS_SB->s_bitmap)
+ kfree(AFFS_SB->s_bitmap);
+ affs_brelse(root_bh);
+ if (AFFS_SB->s_prefix)
+ kfree(AFFS_SB->s_prefix);
return NULL;
}
int reserved;
int root_block;
unsigned long mount_flags;
- unsigned long read_only = sb->u.affs_sb.s_flags & SF_READONLY;
+ unsigned long read_only = AFFS_SB->s_flags & SF_READONLY;
pr_debug("AFFS: remount(flags=0x%x,opts=\"%s\")\n",*flags,data);
if (!parse_options(data,&uid,&gid,&mode,&reserved,&root_block,
- &blocksize,&sb->u.affs_sb.s_prefix,sb->u.affs_sb.s_volume,&mount_flags))
+ &blocksize,&AFFS_SB->s_prefix,AFFS_SB->s_volume,&mount_flags))
return -EINVAL;
- sb->u.affs_sb.s_flags = mount_flags | read_only;
- sb->u.affs_sb.s_mode = mode;
- sb->u.affs_sb.s_uid = uid;
- sb->u.affs_sb.s_gid = gid;
+ AFFS_SB->s_flags = mount_flags | read_only;
+ AFFS_SB->s_mode = mode;
+ AFFS_SB->s_uid = uid;
+ AFFS_SB->s_gid = gid;
if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
return 0;
while (sb->s_dirt)
affs_write_super(sb);
sb->s_flags |= MS_RDONLY;
- } else if (!(sb->u.affs_sb.s_flags & SF_READONLY)) {
+ } else if (!(AFFS_SB->s_flags & SF_READONLY)) {
sb->s_flags &= ~MS_RDONLY;
- affs_make_zones(sb);
} else {
affs_warning(sb,"remount","Cannot remount fs read/write because of errors");
return -EINVAL;
{
int free;
- pr_debug("AFFS: statfs() partsize=%d, reserved=%d\n",sb->u.affs_sb.s_partition_size,
- sb->u.affs_sb.s_reserved);
+ pr_debug("AFFS: statfs() partsize=%d, reserved=%d\n",AFFS_SB->s_partition_size,
+ AFFS_SB->s_reserved);
free = affs_count_free_blocks(sb);
buf->f_type = AFFS_SUPER_MAGIC;
buf->f_bsize = sb->s_blocksize;
- buf->f_blocks = sb->u.affs_sb.s_partition_size - sb->u.affs_sb.s_reserved;
+ buf->f_blocks = AFFS_SB->s_partition_size - AFFS_SB->s_reserved;
buf->f_bfree = free;
buf->f_bavail = free;
return 0;
err = -EIO;
lock_kernel();
- bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
+ bh = affs_bread(inode->i_sb, inode->i_ino);
unlock_kernel();
if (!bh)
goto fail;
top, count));
this_parent = top;
- count--; /* top is passed in after being dgot */
-
if (is_autofs4_dentry(top)) {
count--;
DPRINTK(("is_tree_busy: autofs; count=%d\n", count));
unsigned long timeout;
struct dentry *root = sb->s_root;
struct list_head *tmp;
- struct dentry *d;
- struct vfsmount *p;
if (!sbi->exp_timeout || !root)
return NULL;
attempts if expire fails the first time */
ino->last_used = now;
}
- p = mntget(mnt);
- d = dget_locked(dentry);
-
- if (!is_tree_busy(p, d)) {
+ if (!is_tree_busy(mnt, dentry)) {
DPRINTK(("autofs_expire: returning %p %.*s\n",
dentry, (int)dentry->d_name.len, dentry->d_name.name));
/* Start from here next time */
list_del(&root->d_subdirs);
list_add(&root->d_subdirs, &dentry->d_child);
+ dget(dentry);
spin_unlock(&dcache_lock);
- dput(d);
- mntput(p);
return dentry;
}
- dput(d);
- mntput(p);
}
spin_unlock(&dcache_lock);
pkt.len = dentry->d_name.len;
memcpy(pkt.name, dentry->d_name.name, pkt.len);
pkt.name[pkt.len] = '\0';
+ dput(dentry);
if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) )
return -EFAULT;
de_info->flags |= AUTOFS_INF_EXPIRING;
ret = autofs4_wait(sbi, &dentry->d_name, NFY_EXPIRE);
de_info->flags &= ~AUTOFS_INF_EXPIRING;
+ dput(dentry);
}
return ret;
if ((unsigned long) filename >= TASK_SIZE) {
if (!segment_eq(get_fs(), KERNEL_DS))
return -EFAULT;
- } else if (TASK_SIZE - (unsigned long) filename < PAGE_SIZE)
+ } else if (TASK_SIZE - (unsigned long) filename < PATH_MAX + 1)
len = TASK_SIZE - (unsigned long) filename;
retval = strncpy_from_user((char *)page, filename, len);
** then run the per filesystem commit task queue when we wakeup.
*/
static int reiserfs_journal_commit_thread(void *nullp) {
- exit_files(current);
- exit_mm(current);
+
+ daemonize() ;
spin_lock_irq(¤t->sigmask_lock);
sigfillset(¤t->blocked);
recalc_sigpending(current);
spin_unlock_irq(¤t->sigmask_lock);
- current->session = 1;
- current->pgrp = 1;
sprintf(current->comm, "kreiserfsd") ;
lock_kernel() ;
while(1) {
+++ /dev/null
-/* rwsem.h: R/W semaphores based on spinlocks
- *
- * Written by David Howells (dhowells@redhat.com).
- *
- * Derived from asm-i386/semaphore.h and asm-i386/spinlock.h
- */
-
-#ifndef _I386_RWSEM_SPIN_H
-#define _I386_RWSEM_SPIN_H
-
-#include <linux/config.h>
-
-#ifndef _LINUX_RWSEM_H
-#error please dont include asm/rwsem-spin.h directly, use linux/rwsem.h instead
-#endif
-
-#include <linux/spinlock.h>
-
-#ifdef __KERNEL__
-
-/*
- * the semaphore definition
- */
-struct rw_semaphore {
- signed long count;
-#define RWSEM_UNLOCKED_VALUE 0x00000000
-#define RWSEM_ACTIVE_BIAS 0x00000001
-#define RWSEM_ACTIVE_MASK 0x0000ffff
-#define RWSEM_WAITING_BIAS (-0x00010000)
-#define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS
-#define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS)
- spinlock_t lock;
-#define RWSEM_SPINLOCK_OFFSET_STR "4" /* byte offset of spinlock */
- wait_queue_head_t wait;
-#define RWSEM_WAITING_FOR_READ WQ_FLAG_CONTEXT_0 /* bits to use in wait_queue_t.flags */
-#define RWSEM_WAITING_FOR_WRITE WQ_FLAG_CONTEXT_1
-#if RWSEM_DEBUG
- int debug;
-#endif
-#if RWSEM_DEBUG_MAGIC
- long __magic;
- atomic_t readers;
- atomic_t writers;
-#endif
-};
-
-/*
- * initialisation
- */
-#if RWSEM_DEBUG
-#define __RWSEM_DEBUG_INIT , 0
-#else
-#define __RWSEM_DEBUG_INIT /* */
-#endif
-#if RWSEM_DEBUG_MAGIC
-#define __RWSEM_DEBUG_MINIT(name) , (int)&(name).__magic, ATOMIC_INIT(0), ATOMIC_INIT(0)
-#else
-#define __RWSEM_DEBUG_MINIT(name) /* */
-#endif
-
-#define __RWSEM_INITIALIZER(name) \
-{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, \
- __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \
- __RWSEM_DEBUG_INIT __RWSEM_DEBUG_MINIT(name) }
-
-#define DECLARE_RWSEM(name) \
- struct rw_semaphore name = __RWSEM_INITIALIZER(name)
-
-static inline void init_rwsem(struct rw_semaphore *sem)
-{
- sem->count = RWSEM_UNLOCKED_VALUE;
- spin_lock_init(&sem->lock);
- init_waitqueue_head(&sem->wait);
-#if RWSEM_DEBUG
- sem->debug = 0;
-#endif
-#if RWSEM_DEBUG_MAGIC
- sem->__magic = (long)&sem->__magic;
- atomic_set(&sem->readers, 0);
- atomic_set(&sem->writers, 0);
-#endif
-}
-
-/*
- * lock for reading
- */
-static inline void __down_read(struct rw_semaphore *sem)
-{
- __asm__ __volatile__(
- "# beginning down_read\n\t"
-#ifdef CONFIG_SMP
-LOCK_PREFIX " decb "RWSEM_SPINLOCK_OFFSET_STR"(%%eax)\n\t" /* try to grab the spinlock */
- " js 3f\n" /* jump if failed */
- "1:\n\t"
-#endif
- " incl (%%eax)\n\t" /* adds 0x00000001, returns the old value */
-#ifdef CONFIG_SMP
- " movb $1,"RWSEM_SPINLOCK_OFFSET_STR"(%%eax)\n\t" /* release the spinlock */
-#endif
- " js 4f\n\t" /* jump if we weren't granted the lock */
- "2:\n"
- ".section .text.lock,\"ax\"\n"
-#ifdef CONFIG_SMP
- "3:\n\t" /* spin on the spinlock till we get it */
- " cmpb $0,"RWSEM_SPINLOCK_OFFSET_STR"(%%eax)\n\t"
- " rep;nop \n\t"
- " jle 3b\n\t"
- " jmp 1b\n"
-#endif
- "4:\n\t"
- " call __rwsem_down_read_failed\n\t"
- " jmp 2b\n"
- ".previous"
- "# ending __down_read\n\t"
- : "=m"(sem->count), "=m"(sem->lock)
- : "a"(sem), "m"(sem->count), "m"(sem->lock)
- : "memory");
-}
-
-/*
- * lock for writing
- */
-static inline void __down_write(struct rw_semaphore *sem)
-{
- int tmp;
-
- tmp = RWSEM_ACTIVE_WRITE_BIAS;
- __asm__ __volatile__(
- "# beginning down_write\n\t"
-#ifdef CONFIG_SMP
-LOCK_PREFIX " decb "RWSEM_SPINLOCK_OFFSET_STR"(%%eax)\n\t" /* try to grab the spinlock */
- " js 3f\n" /* jump if failed */
- "1:\n\t"
-#endif
- " xchg %0,(%%eax)\n\t" /* retrieve the old value */
- " add %0,(%%eax)\n\t" /* add 0xffff0001, result in memory */
-#ifdef CONFIG_SMP
- " movb $1,"RWSEM_SPINLOCK_OFFSET_STR"(%%eax)\n\t" /* release the spinlock */
-#endif
- " testl %0,%0\n\t" /* was the count 0 before? */
- " jnz 4f\n\t" /* jump if we weren't granted the lock */
- "2:\n\t"
- ".section .text.lock,\"ax\"\n"
-#ifdef CONFIG_SMP
- "3:\n\t" /* spin on the spinlock till we get it */
- " cmpb $0,"RWSEM_SPINLOCK_OFFSET_STR"(%%eax)\n\t"
- " rep;nop \n\t"
- " jle 3b\n\t"
- " jmp 1b\n"
-#endif
- "4:\n\t"
- " call __rwsem_down_write_failed\n\t"
- " jmp 2b\n"
- ".previous\n"
- "# ending down_write"
- : "+r"(tmp), "=m"(sem->count), "=m"(sem->lock)
- : "a"(sem), "m"(sem->count), "m"(sem->lock)
- : "memory");
-}
-
-/*
- * unlock after reading
- */
-static inline void __up_read(struct rw_semaphore *sem)
-{
- int tmp;
-
- tmp = -RWSEM_ACTIVE_READ_BIAS;
- __asm__ __volatile__(
- "# beginning __up_read\n\t"
-#ifdef CONFIG_SMP
-LOCK_PREFIX " decb "RWSEM_SPINLOCK_OFFSET_STR"(%%eax)\n\t" /* try to grab the spinlock */
- " js 3f\n" /* jump if failed */
- "1:\n\t"
-#endif
- " xchg %0,(%%eax)\n\t" /* retrieve the old value */
- " addl %0,(%%eax)\n\t" /* subtract 1, result in memory */
-#ifdef CONFIG_SMP
- " movb $1,"RWSEM_SPINLOCK_OFFSET_STR"(%%eax)\n\t" /* release the spinlock */
-#endif
- " js 4f\n\t" /* jump if the lock is being waited upon */
- "2:\n\t"
- ".section .text.lock,\"ax\"\n"
-#ifdef CONFIG_SMP
- "3:\n\t" /* spin on the spinlock till we get it */
- " cmpb $0,"RWSEM_SPINLOCK_OFFSET_STR"(%%eax)\n\t"
- " rep;nop \n\t"
- " jle 3b\n\t"
- " jmp 1b\n"
-#endif
- "4:\n\t"
- " decl %0\n\t" /* xchg gave us the old count */
- " testl %4,%0\n\t" /* do nothing if still outstanding active readers */
- " jnz 2b\n\t"
- " call __rwsem_wake\n\t"
- " jmp 2b\n"
- ".previous\n"
- "# ending __up_read\n"
- : "+r"(tmp), "=m"(sem->count), "=m"(sem->lock)
- : "a"(sem), "i"(RWSEM_ACTIVE_MASK), "m"(sem->count), "m"(sem->lock)
- : "memory");
-}
-
-/*
- * unlock after writing
- */
-static inline void __up_write(struct rw_semaphore *sem)
-{
- __asm__ __volatile__(
- "# beginning __up_write\n\t"
-#ifdef CONFIG_SMP
-LOCK_PREFIX " decb "RWSEM_SPINLOCK_OFFSET_STR"(%%eax)\n\t" /* try to grab the spinlock */
- " js 3f\n" /* jump if failed */
- "1:\n\t"
-#endif
- " addl %3,(%%eax)\n\t" /* adds 0x00010001 */
-#ifdef CONFIG_SMP
- " movb $1,"RWSEM_SPINLOCK_OFFSET_STR"(%%eax)\n\t" /* release the spinlock */
-#endif
- " js 4f\n\t" /* jump if the lock is being waited upon */
- "2:\n\t"
- ".section .text.lock,\"ax\"\n"
-#ifdef CONFIG_SMP
- "3:\n\t" /* spin on the spinlock till we get it */
- " cmpb $0,"RWSEM_SPINLOCK_OFFSET_STR"(%%eax)\n\t"
- " rep;nop \n\t"
- " jle 3b\n\t"
- " jmp 1b\n"
-#endif
- "4:\n\t"
- " call __rwsem_wake\n\t"
- " jmp 2b\n"
- ".previous\n"
- "# ending __up_write\n"
- : "=m"(sem->count), "=m"(sem->lock)
- : "a"(sem), "i"(-RWSEM_ACTIVE_WRITE_BIAS), "m"(sem->count), "m"(sem->lock)
- : "memory");
-}
-
-/*
- * implement exchange and add functionality
- */
-static inline int rwsem_atomic_update(int delta, struct rw_semaphore *sem)
-{
- int tmp = delta;
-
- __asm__ __volatile__(
- "# beginning rwsem_atomic_update\n\t"
-#ifdef CONFIG_SMP
-LOCK_PREFIX " decb "RWSEM_SPINLOCK_OFFSET_STR"(%1)\n\t" /* try to grab the spinlock */
- " js 3f\n" /* jump if failed */
- "1:\n\t"
-#endif
- " xchgl %0,(%1)\n\t" /* retrieve the old value */
- " addl %0,(%1)\n\t" /* add 0xffff0001, result in memory */
-#ifdef CONFIG_SMP
- " movb $1,"RWSEM_SPINLOCK_OFFSET_STR"(%1)\n\t" /* release the spinlock */
-#endif
- ".section .text.lock,\"ax\"\n"
-#ifdef CONFIG_SMP
- "3:\n\t" /* spin on the spinlock till we get it */
- " cmpb $0,"RWSEM_SPINLOCK_OFFSET_STR"(%1)\n\t"
- " rep;nop \n\t"
- " jle 3b\n\t"
- " jmp 1b\n"
-#endif
- ".previous\n"
- "# ending rwsem_atomic_update\n\t"
- : "+r"(tmp)
- : "r"(sem)
- : "memory");
-
- return tmp+delta;
-}
-
-/*
- * implement compare and exchange functionality on the rw-semaphore count LSW
- */
-static inline __u16 rwsem_cmpxchgw(struct rw_semaphore *sem, __u16 old, __u16 new)
-{
- __u16 prev;
-
- __asm__ __volatile__(
- "# beginning rwsem_cmpxchgw\n\t"
-#ifdef CONFIG_SMP
-LOCK_PREFIX " decb "RWSEM_SPINLOCK_OFFSET_STR"(%3)\n\t" /* try to grab the spinlock */
- " js 3f\n" /* jump if failed */
- "1:\n\t"
-#endif
- " cmpw %w1,(%3)\n\t"
- " jne 4f\n\t" /* jump if old doesn't match sem->count LSW */
- " movw %w2,(%3)\n\t" /* replace sem->count LSW with the new value */
- "2:\n\t"
-#ifdef CONFIG_SMP
- " movb $1,"RWSEM_SPINLOCK_OFFSET_STR"(%3)\n\t" /* release the spinlock */
-#endif
- ".section .text.lock,\"ax\"\n"
-#ifdef CONFIG_SMP
- "3:\n\t" /* spin on the spinlock till we get it */
- " cmpb $0,"RWSEM_SPINLOCK_OFFSET_STR"(%3)\n\t"
- " rep;nop \n\t"
- " jle 3b\n\t"
- " jmp 1b\n"
-#endif
- "4:\n\t"
- " movw (%3),%w0\n" /* we'll want to return the current value */
- " jmp 2b\n"
- ".previous\n"
- "# ending rwsem_cmpxchgw\n\t"
- : "=r"(prev)
- : "r0"(old), "r"(new), "r"(sem)
- : "memory");
-
- return prev;
-}
-
-#endif /* __KERNEL__ */
-#endif /* _I386_RWSEM_SPIN_H */
+++ /dev/null
-/* rwsem-xadd.h: R/W semaphores implemented using XADD/CMPXCHG
- *
- * Written by David Howells (dhowells@redhat.com), 2001.
- * Derived from asm-i386/semaphore.h
- */
-
-#ifndef _I386_RWSEM_XADD_H
-#define _I386_RWSEM_XADD_H
-
-#ifndef _LINUX_RWSEM_H
-#error please dont include asm/rwsem-xadd.h directly, use linux/rwsem.h instead
-#endif
-
-#ifdef __KERNEL__
-
-/*
- * the semaphore definition
- */
-struct rw_semaphore {
- signed long count;
-#define RWSEM_UNLOCKED_VALUE 0x00000000
-#define RWSEM_ACTIVE_BIAS 0x00000001
-#define RWSEM_ACTIVE_MASK 0x0000ffff
-#define RWSEM_WAITING_BIAS (-0x00010000)
-#define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS
-#define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS)
- wait_queue_head_t wait;
-#define RWSEM_WAITING_FOR_READ WQ_FLAG_CONTEXT_0 /* bits to use in wait_queue_t.flags */
-#define RWSEM_WAITING_FOR_WRITE WQ_FLAG_CONTEXT_1
-#if RWSEM_DEBUG
- int debug;
-#endif
-#if RWSEM_DEBUG_MAGIC
- long __magic;
- atomic_t readers;
- atomic_t writers;
-#endif
-};
-
-/*
- * initialisation
- */
-#if RWSEM_DEBUG
-#define __RWSEM_DEBUG_INIT , 0
-#else
-#define __RWSEM_DEBUG_INIT /* */
-#endif
-#if RWSEM_DEBUG_MAGIC
-#define __RWSEM_DEBUG_MINIT(name) , (int)&(name).__magic, ATOMIC_INIT(0), ATOMIC_INIT(0)
-#else
-#define __RWSEM_DEBUG_MINIT(name) /* */
-#endif
-
-#define __RWSEM_INITIALIZER(name) \
-{ RWSEM_UNLOCKED_VALUE, __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \
- __RWSEM_DEBUG_INIT __RWSEM_DEBUG_MINIT(name) }
-
-#define DECLARE_RWSEM(name) \
- struct rw_semaphore name = __RWSEM_INITIALIZER(name)
-
-static inline void init_rwsem(struct rw_semaphore *sem)
-{
- sem->count = RWSEM_UNLOCKED_VALUE;
- init_waitqueue_head(&sem->wait);
-#if RWSEM_DEBUG
- sem->debug = 0;
-#endif
-#if RWSEM_DEBUG_MAGIC
- sem->__magic = (long)&sem->__magic;
- atomic_set(&sem->readers, 0);
- atomic_set(&sem->writers, 0);
-#endif
-}
-
-/*
- * lock for reading
- */
-static inline void __down_read(struct rw_semaphore *sem)
-{
- __asm__ __volatile__(
- "# beginning down_read\n\t"
-LOCK_PREFIX " incl (%%eax)\n\t" /* adds 0x00000001, returns the old value */
- " js 2f\n\t" /* jump if we weren't granted the lock */
- "1:\n\t"
- ".section .text.lock,\"ax\"\n"
- "2:\n\t"
- " call __rwsem_down_read_failed\n\t"
- " jmp 1b\n"
- ".previous"
- "# ending down_read\n\t"
- : "=m"(sem->count)
- : "a"(sem), "m"(sem->count)
- : "memory");
-}
-
-/*
- * lock for writing
- */
-static inline void __down_write(struct rw_semaphore *sem)
-{
- int tmp;
-
- tmp = RWSEM_ACTIVE_WRITE_BIAS;
- __asm__ __volatile__(
- "# beginning down_write\n\t"
-LOCK_PREFIX " xadd %0,(%%eax)\n\t" /* subtract 0x00010001, returns the old value */
- " testl %0,%0\n\t" /* was the count 0 before? */
- " jnz 2f\n\t" /* jump if we weren't granted the lock */
- "1:\n\t"
- ".section .text.lock,\"ax\"\n"
- "2:\n\t"
- " call __rwsem_down_write_failed\n\t"
- " jmp 1b\n"
- ".previous\n"
- "# ending down_write"
- : "+r"(tmp), "=m"(sem->count)
- : "a"(sem), "m"(sem->count)
- : "memory");
-}
-
-/*
- * unlock after reading
- */
-static inline void __up_read(struct rw_semaphore *sem)
-{
- int tmp;
-
- tmp = -RWSEM_ACTIVE_READ_BIAS;
- __asm__ __volatile__(
- "# beginning __up_read\n\t"
-LOCK_PREFIX " xadd %0,(%%eax)\n\t" /* subtracts 1, returns the old value */
- " js 2f\n\t" /* jump if the lock is being waited upon */
- "1:\n\t"
- ".section .text.lock,\"ax\"\n"
- "2:\n\t"
- " decl %0\n\t" /* xadd gave us the old count */
- " testl %3,%0\n\t" /* do nothing if still outstanding active readers */
- " jnz 1b\n\t"
- " call __rwsem_wake\n\t"
- " jmp 1b\n"
- ".previous\n"
- "# ending __up_read\n"
- : "+r"(tmp), "=m"(sem->count)
- : "a"(sem), "i"(RWSEM_ACTIVE_MASK), "m"(sem->count)
- : "memory");
-}
-
-/*
- * unlock after writing
- */
-static inline void __up_write(struct rw_semaphore *sem)
-{
- __asm__ __volatile__(
- "# beginning __up_write\n\t"
-LOCK_PREFIX " addl %2,(%%eax)\n\t" /* adds 0x0000ffff */
- " js 2f\n\t" /* jump if the lock is being waited upon */
- "1:\n\t"
- ".section .text.lock,\"ax\"\n"
- "2:\n\t"
- " call __rwsem_wake\n\t"
- " jmp 1b\n"
- ".previous\n"
- "# ending __up_write\n"
- : "=m"(sem->count)
- : "a"(sem), "i"(-RWSEM_ACTIVE_WRITE_BIAS), "m"(sem->count)
- : "memory");
-}
-
-/*
- * implement exchange and add functionality
- */
-static inline int rwsem_atomic_update(int delta, struct rw_semaphore *sem)
-{
- int tmp = delta;
-
- __asm__ __volatile__(
- LOCK_PREFIX "xadd %0,(%1)"
- : "+r"(tmp)
- : "r"(sem)
- : "memory");
-
- return tmp+delta;
-}
-
-/*
- * implement compare and exchange functionality on the rw-semaphore count LSW
- */
-static inline __u16 rwsem_cmpxchgw(struct rw_semaphore *sem, __u16 old, __u16 new)
-{
- return cmpxchg((__u16*)&sem->count,0,RWSEM_ACTIVE_BIAS);
-}
-
-#endif /* __KERNEL__ */
-#endif /* _I386_RWSEM_XADD_H */
-/* rwsem.h: R/W semaphores optimised using i386 assembly
+/* rwsem.h: R/W semaphores implemented using XADD/CMPXCHG for i486+
*
* Written by David Howells (dhowells@redhat.com).
*
#ifdef __KERNEL__
-#ifdef CONFIG_X86_XADD
-#include <asm/rwsem-xadd.h> /* use XADD based semaphores if possible */
-#else
-#include <asm/rwsem-spin.h> /* use optimised spinlock based semaphores otherwise */
-#endif
+#include <linux/list.h>
+#include <linux/spinlock.h>
/* we use FASTCALL convention for the helpers */
extern struct rw_semaphore *FASTCALL(__rwsem_down_read_failed(struct rw_semaphore *sem));
extern struct rw_semaphore *FASTCALL(__rwsem_down_write_failed(struct rw_semaphore *sem));
extern struct rw_semaphore *FASTCALL(__rwsem_wake(struct rw_semaphore *sem));
+struct rwsem_waiter;
+
+/*
+ * the semaphore definition
+ */
+struct rw_semaphore {
+ signed long count;
+#define RWSEM_UNLOCKED_VALUE 0x00000000
+#define RWSEM_ACTIVE_BIAS 0x00000001
+#define RWSEM_ACTIVE_MASK 0x0000ffff
+#define RWSEM_WAITING_BIAS (-0x00010000)
+#define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS
+#define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS)
+ spinlock_t wait_lock;
+ struct rwsem_waiter *wait_front;
+ struct rwsem_waiter **wait_back;
+#if RWSEM_DEBUG
+ int debug;
+#endif
+#if RWSEM_DEBUG_MAGIC
+ long __magic;
+ atomic_t readers;
+ atomic_t writers;
+#endif
+};
+
+/*
+ * initialisation
+ */
+#if RWSEM_DEBUG
+#define __RWSEM_DEBUG_INIT , 0
+#else
+#define __RWSEM_DEBUG_INIT /* */
+#endif
+#if RWSEM_DEBUG_MAGIC
+#define __RWSEM_DEBUG_MINIT(name) , (int)&(name).__magic, ATOMIC_INIT(0), ATOMIC_INIT(0)
+#else
+#define __RWSEM_DEBUG_MINIT(name) /* */
+#endif
+
+#define __RWSEM_INITIALIZER(name) \
+{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, NULL, &(name).wait_front \
+ __RWSEM_DEBUG_INIT __RWSEM_DEBUG_MINIT(name) }
+
+#define DECLARE_RWSEM(name) \
+ struct rw_semaphore name = __RWSEM_INITIALIZER(name)
+
+static inline void init_rwsem(struct rw_semaphore *sem)
+{
+ sem->count = RWSEM_UNLOCKED_VALUE;
+ spin_lock_init(&sem->wait_lock);
+ sem->wait_front = NULL;
+ sem->wait_back = &sem->wait_front;
+#if RWSEM_DEBUG
+ sem->debug = 0;
+#endif
+#if RWSEM_DEBUG_MAGIC
+ sem->__magic = (long)&sem->__magic;
+ atomic_set(&sem->readers, 0);
+ atomic_set(&sem->writers, 0);
+#endif
+}
+
+/*
+ * lock for reading
+ */
+static inline void __down_read(struct rw_semaphore *sem)
+{
+ __asm__ __volatile__(
+ "# beginning down_read\n\t"
+LOCK_PREFIX " incl (%%eax)\n\t" /* adds 0x00000001, returns the old value */
+ " js 2f\n\t" /* jump if we weren't granted the lock */
+ "1:\n\t"
+ ".section .text.lock,\"ax\"\n"
+ "2:\n\t"
+ " call __rwsem_down_read_failed\n\t"
+ " jmp 1b\n"
+ ".previous"
+ "# ending down_read\n\t"
+ : "=m"(sem->count)
+ : "a"(sem), "m"(sem->count)
+ : "memory");
+}
+
+/*
+ * lock for writing
+ */
+static inline void __down_write(struct rw_semaphore *sem)
+{
+ int tmp;
+
+ tmp = RWSEM_ACTIVE_WRITE_BIAS;
+ __asm__ __volatile__(
+ "# beginning down_write\n\t"
+LOCK_PREFIX " xadd %0,(%%eax)\n\t" /* subtract 0x00010001, returns the old value */
+ " testl %0,%0\n\t" /* was the count 0 before? */
+ " jnz 2f\n\t" /* jump if we weren't granted the lock */
+ "1:\n\t"
+ ".section .text.lock,\"ax\"\n"
+ "2:\n\t"
+ " call __rwsem_down_write_failed\n\t"
+ " jmp 1b\n"
+ ".previous\n"
+ "# ending down_write"
+ : "+r"(tmp), "=m"(sem->count)
+ : "a"(sem), "m"(sem->count)
+ : "memory");
+}
+
+/*
+ * unlock after reading
+ */
+static inline void __up_read(struct rw_semaphore *sem)
+{
+ int tmp;
+
+ tmp = -RWSEM_ACTIVE_READ_BIAS;
+ __asm__ __volatile__(
+ "# beginning __up_read\n\t"
+LOCK_PREFIX " xadd %0,(%%eax)\n\t" /* subtracts 1, returns the old value */
+ " js 2f\n\t" /* jump if the lock is being waited upon */
+ "1:\n\t"
+ ".section .text.lock,\"ax\"\n"
+ "2:\n\t"
+ " decl %0\n\t" /* xadd gave us the old count */
+ " testl %3,%0\n\t" /* do nothing if still outstanding active readers */
+ " jnz 1b\n\t"
+ " call __rwsem_wake\n\t"
+ " jmp 1b\n"
+ ".previous\n"
+ "# ending __up_read\n"
+ : "+r"(tmp), "=m"(sem->count)
+ : "a"(sem), "i"(RWSEM_ACTIVE_MASK), "m"(sem->count)
+ : "memory");
+}
+
+/*
+ * unlock after writing
+ */
+static inline void __up_write(struct rw_semaphore *sem)
+{
+ __asm__ __volatile__(
+ "# beginning __up_write\n\t"
+LOCK_PREFIX " addl %2,(%%eax)\n\t" /* adds 0x0000ffff */
+ " js 2f\n\t" /* jump if the lock is being waited upon */
+ "1:\n\t"
+ ".section .text.lock,\"ax\"\n"
+ "2:\n\t"
+ " call __rwsem_wake\n\t"
+ " jmp 1b\n"
+ ".previous\n"
+ "# ending __up_write\n"
+ : "=m"(sem->count)
+ : "a"(sem), "i"(-RWSEM_ACTIVE_WRITE_BIAS), "m"(sem->count)
+ : "memory");
+}
+
+/*
+ * implement exchange and add functionality
+ */
+static inline int rwsem_atomic_update(int delta, struct rw_semaphore *sem)
+{
+ int tmp = delta;
+
+ __asm__ __volatile__(
+ LOCK_PREFIX "xadd %0,(%1)"
+ : "+r"(tmp)
+ : "r"(sem)
+ : "memory");
+
+ return tmp+delta;
+}
+
+/*
+ * implement compare and exchange functionality on the rw-semaphore count LSW
+ */
+static inline __u16 rwsem_cmpxchgw(struct rw_semaphore *sem, __u16 old, __u16 new)
+{
+ return cmpxchg((__u16*)&sem->count,0,RWSEM_ACTIVE_BIAS);
+}
+
#endif /* __KERNEL__ */
#endif /* _I386_RWSEM_H */
#ifdef __KERNEL__
+struct rwsem_waiter;
+
struct rw_semaphore {
signed int count;
#define RWSEM_UNLOCKED_VALUE 0x00000000
#define RWSEM_WAITING_BIAS 0xffff0000
#define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS
#define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS)
- wait_queue_head_t wait;
-#define RWSEM_WAITING_FOR_READ WQ_FLAG_CONTEXT_0 /* bits to use in wait_queue_t.flags */
-#define RWSEM_WAITING_FOR_WRITE WQ_FLAG_CONTEXT_1
+ spinlock_t wait_lock;
+ struct rwsem_waiter *wait_front;
+ struct rwsem_waiter **wait_back;
};
#define __RWSEM_INITIALIZER(name) \
-{ RWSEM_UNLOCKED_VALUE, __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) }
+{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, NULL, &(name).wait_front }
#define DECLARE_RWSEM(name) \
struct rw_semaphore name = __RWSEM_INITIALIZER(name)
#define AFFS_SUPER_MAGIC 0xadff
-/* Get the filesystem block size given an inode. */
-#define AFFS_I2BSIZE(inode) ((inode)->i_sb->s_blocksize)
-
-/* Get the filesystem hash table size given an inode. */
-#define AFFS_I2HSIZE(inode) ((inode)->i_sb->u.affs_sb.s_hashsize)
-
-/* Get the block number bits given an inode */
-#define AFFS_I2BITS(inode) ((inode)->i_sb->s_blocksize_bits)
-
-/* Get the fs type given an inode */
-#define AFFS_I2FSTYPE(inode) ((inode)->i_sb->u.affs_sb.s_flags & SF_INTL)
-
-struct DateStamp
-{
- u32 ds_Days;
- u32 ds_Minute;
- u32 ds_Tick;
-};
+struct affs_date;
/* --- Prototypes ----------------------------------------------------------------------------- */
/* amigaffs.c */
-extern int affs_get_key_entry(int bsize, void *data, int entry_pos);
-extern int affs_get_file_name(int bsize, void *fh_data, unsigned char **name);
-extern u32 affs_checksum_block(int bsize, void *data, s32 *ptype, s32 *stype);
-extern void affs_fix_checksum(int bsize, void *data, int cspos);
-extern void secs_to_datestamp(time_t secs, struct DateStamp *ds);
-extern int prot_to_mode(unsigned int prot);
-extern u32 mode_to_prot(int mode);
-extern int affs_insert_hash(unsigned long dir_ino, struct buffer_head *header,
- struct inode *inode);
-extern int affs_remove_hash(struct buffer_head *bh, struct inode *inode);
-extern int affs_remove_link(struct buffer_head *bh, struct inode *inode);
-extern int affs_remove_header(struct buffer_head *bh, struct inode *inode);
+extern int affs_insert_hash(struct inode *inode, struct buffer_head *bh);
+extern int affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh);
+extern int affs_remove_header(struct dentry *dentry);
+extern u32 affs_checksum_block(struct super_block *sb, struct buffer_head *bh);
+extern void affs_fix_checksum(struct super_block *sb, struct buffer_head *bh);
+extern void secs_to_datestamp(time_t secs, struct affs_date *ds);
+extern mode_t prot_to_mode(u32 prot);
+extern void mode_to_prot(struct inode *inode);
extern void affs_error(struct super_block *sb, const char *function, const char *fmt, ...);
extern void affs_warning(struct super_block *sb, const char *function, const char *fmt, ...);
extern int affs_check_name(const unsigned char *name, int len);
-extern int affs_copy_name(unsigned char *bstr, const unsigned char *name);
+extern int affs_copy_name(unsigned char *bstr, struct dentry *dentry);
/* bitmap. c */
-extern int affs_count_free_blocks(struct super_block *s);
-extern int affs_count_free_bits(int blocksize, const char *data);
-extern void affs_free_block(struct super_block *sb, s32 block);
-extern s32 affs_new_header(struct inode *inode);
-extern s32 affs_new_data(struct inode *inode);
-extern void affs_make_zones(struct super_block *sb);
+extern u32 affs_count_free_bits(u32 blocksize, const void *data);
+extern u32 affs_count_free_blocks(struct super_block *s);
+extern void affs_free_block(struct super_block *sb, u32 block);
+extern u32 affs_alloc_block(struct inode *inode, u32 goal);
+extern int affs_init_bitmap(struct super_block *sb);
/* namei.c */
-extern int affs_hash_name(const unsigned char *name, int len, int intl, int hashsize);
+extern int affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len);
extern struct dentry *affs_lookup(struct inode *dir, struct dentry *dentry);
extern int affs_unlink(struct inode *dir, struct dentry *dentry);
extern int affs_create(struct inode *dir, struct dentry *dentry, int mode);
/* inode.c */
-extern struct buffer_head *affs_bread(kdev_t dev, int block, int size);
-extern void affs_brelse(struct buffer_head *buf);
extern unsigned long affs_parent_ino(struct inode *dir);
-extern struct inode *affs_new_inode(const struct inode *dir);
+extern struct inode *affs_new_inode(struct inode *dir);
extern int affs_notify_change(struct dentry *dentry, struct iattr *attr);
-extern int affs_add_entry(struct inode *dir, struct inode *link,
- struct inode *inode, struct dentry *dentry, s32 type);
extern void affs_put_inode(struct inode *inode);
extern void affs_delete_inode(struct inode *inode);
+extern void affs_clear_inode(struct inode *inode);
extern void affs_read_inode(struct inode *inode);
extern void affs_write_inode(struct inode *inode, int);
+extern int affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s32 type);
/* super.c */
extern struct file_operations affs_dir_operations;
extern struct address_space_operations affs_symlink_aops;
extern struct address_space_operations affs_aops;
+extern struct address_space_operations affs_aops_ofs;
extern struct dentry_operations affs_dentry_operations;
extern struct dentry_operations affs_dentry_operations_intl;
-extern int affs_bmap(struct inode *, int);
#endif
#define _AFFS_FS_I
#include <linux/a.out.h>
+
+// move this to linux/coda.h!!!
#include <linux/time.h>
-#define AFFS_MAX_PREALLOC 16 /* MUST be a power of 2 */
-#define AFFS_KCSIZE 73 /* Allows for 1 extension block at 512 byte-blocks */
-
-struct key_cache {
- struct timeval kc_lru_time; /* Last time this cache was used */
- s32 kc_first; /* First cached key */
- s32 kc_last; /* Last cached key */
- s32 kc_this_key; /* Key of extension block this data block keys are from */
- int kc_this_seq; /* Sequence number of this extension block */
- s32 kc_next_key; /* Key of next extension block */
- s32 kc_keys[AFFS_KCSIZE]; /* Key cache */
-};
+#define AFFS_CACHE_SIZE PAGE_SIZE
+//#define AFFS_CACHE_SIZE (4*4)
-#define EC_SIZE (PAGE_SIZE - 4 * sizeof(struct key_cache) - 4) / 4
+#define AFFS_MAX_PREALLOC 32
+#define AFFS_LC_SIZE (AFFS_CACHE_SIZE/sizeof(u32)/2)
+#define AFFS_AC_SIZE (AFFS_CACHE_SIZE/sizeof(struct affs_ext_key)/2)
+#define AFFS_AC_MASK (AFFS_AC_SIZE-1)
-struct ext_cache {
- struct key_cache kc[4]; /* The 4 key caches */
- s32 ec[EC_SIZE]; /* Keys of assorted extension blocks */
- int max_ext; /* Index of last known extension block */
+struct affs_ext_key {
+ u32 ext; /* idx of the extended block */
+ u32 key; /* block number */
};
/*
* affs fs inode data in memory
*/
struct affs_inode_info {
+ u32 i_opencnt;
+ struct semaphore i_link_lock; /* Protects internal inode access. */
+ struct semaphore i_ext_lock; /* Protects internal inode access. */
+#define i_hash_lock i_ext_lock
+ u32 i_blkcnt; /* block count */
+ u32 i_extcnt; /* extended block count */
+ u32 *i_lc; /* linear cache of extended blocks */
+ u32 i_lc_size;
+ u32 i_lc_shift;
+ u32 i_lc_mask;
+ struct affs_ext_key *i_ac; /* associative cache of extended blocks */
+ u32 i_ext_last; /* last accessed extended block */
+ struct buffer_head *i_ext_bh; /* bh of last extended block */
unsigned long mmu_private;
u32 i_protect; /* unused attribute bits */
- s32 i_parent; /* parent ino */
+ u32 i_lastalloc; /* last allocated block */
+ int i_pa_cnt; /* number of preallocated blocks */
+#if 0
s32 i_original; /* if != 0, this is the key of the original */
- s32 i_data[AFFS_MAX_PREALLOC]; /* preallocated blocks */
- struct ext_cache *i_ec; /* Cache gets allocated dynamically */
+ u32 i_data[AFFS_MAX_PREALLOC]; /* preallocated blocks */
int i_cache_users; /* Cache cannot be freed while > 0 */
- int i_lastblock; /* last allocated block */
- short i_pa_cnt; /* number of preallocated blocks */
- short i_pa_next; /* Index of next block in i_data[] */
- short i_pa_last; /* Index of next free slot in i_data[] */
- short i_zone; /* write zone */
unsigned char i_hlink; /* This is a fake */
unsigned char i_pad;
+ s32 i_parent; /* parent ino */
+#endif
};
+/* short cut to get to the affs specific inode data */
+#define AFFS_INODE (&inode->u.affs_i)
+#define AFFS_DIR (&dir->u.affs_i)
+
#endif
*
*/
-#define MAX_ZONES 8
-#define AFFS_DATA_MIN_FREE 512 /* Number of free blocks in zone for data blocks */
-#define AFFS_HDR_MIN_FREE 128 /* Same for header blocks */
-#define AFFS_ZONE_SIZE 1024 /* Blocks per alloc zone, must be multiple of 32 */
-
struct affs_bm_info {
- struct buffer_head *bm_bh; /* Buffer head if loaded (bm_count > 0) */
- s32 bm_firstblk; /* Block number of first bit in this map */
- s32 bm_key; /* Disk block number */
- int bm_count; /* Usage counter */
-};
-
-struct affs_alloc_zone {
- short az_size; /* Size of this allocation zone in double words */
- short az_count; /* Number of users */
- int az_free; /* Free blocks in here (no. of bits) */
-};
-
-struct affs_zone {
- unsigned long z_ino; /* Associated inode number */
- struct affs_bm_info *z_bm; /* Zone lies in this bitmap */
- int z_start; /* Index of first word in bitmap */
- int z_end; /* Index of last word in zone + 1 */
- int z_az_no; /* Zone number */
- unsigned long z_lru_time; /* Time of last usage */
+ u32 bm_key; /* Disk block number */
+ u32 bm_free; /* Free blocks in here */
};
struct affs_sb_info {
int s_partition_size; /* Partition size in blocks. */
- int s_blksize; /* Initial device blksize */
- s32 s_root_block; /* FFS root block number. */
+ int s_reserved; /* Number of reserved blocks. */
+ //u32 s_blksize; /* Initial device blksize */
+ u32 s_data_blksize; /* size of the data block w/o header */
+ u32 s_root_block; /* FFS root block number. */
int s_hashsize; /* Size of hash table. */
unsigned long s_flags; /* See below. */
- s16 s_uid; /* uid to override */
- s16 s_gid; /* gid to override */
+ uid_t s_uid; /* uid to override */
+ gid_t s_gid; /* gid to override */
umode_t s_mode; /* mode to override */
- int s_reserved; /* Number of reserved blocks. */
struct buffer_head *s_root_bh; /* Cached root block. */
+ struct semaphore s_bmlock; /* Protects bitmap access. */
struct affs_bm_info *s_bitmap; /* Bitmap infos. */
- int s_bm_count; /* Number of bitmap blocks. */
- int s_nextzone; /* Next zone to look for free blocks. */
- int s_num_az; /* Total number of alloc zones. */
- struct affs_zone *s_zones; /* The zones themselves. */
- struct affs_alloc_zone *s_alloc;/* The allocation zones. */
- char *s_zonemap; /* Bitmap for allocation zones. */
+ u32 s_bmap_count; /* # of bitmap blocks. */
+ u32 s_bmap_bits; /* # of bits in one bitmap blocks */
+ u32 s_last_bmap;
+ struct buffer_head *s_bmap_bh;
char *s_prefix; /* Prefix for volumes and assigns. */
int s_prefix_len; /* Length of prefix. */
char s_volume[32]; /* Volume prefix for absolute symlinks. */
#define SF_VERBOSE 0x0800 /* Talk about fs when mounting */
#define SF_READONLY 0x1000 /* Don't allow to remount rw */
+/* short cut to get to the affs specific sb data */
+#define AFFS_SB (&sb->u.affs_sb)
+
#endif
#ifndef AMIGAFFS_H
#define AMIGAFFS_H
-#include <asm/byteorder.h>
#include <linux/types.h>
+#include <linux/locks.h>
+
+#include <asm/byteorder.h>
/* AmigaOS allows file names with up to 30 characters length.
* Names longer than that will be silently truncated. If you
#define GET_END_PTR(st,p,sz) ((st *)((char *)(p)+((sz)-sizeof(st))))
#define AFFS_GET_HASHENTRY(data,hashkey) be32_to_cpu(((struct dir_front *)data)->hashtable[hashkey])
-#define AFFS_BLOCK(data,ino,blk) ((struct file_front *)data)->blocks[AFFS_I2HSIZE(ino)-1-(blk)]
+#define AFFS_BLOCK(sb, bh, blk) (AFFS_HEAD(bh)->table[(sb)->u.affs_sb.s_hashsize-1-(blk)])
+
+static inline void
+affs_set_blocksize(struct super_block *sb, int size)
+{
+ set_blocksize(sb->s_dev, size);
+ sb->s_blocksize = size;
+}
+static inline struct buffer_head *
+affs_bread(struct super_block *sb, int block)
+{
+ pr_debug(KERN_DEBUG "affs_bread: %d\n", block);
+ if (block >= AFFS_SB->s_reserved && block < AFFS_SB->s_partition_size)
+ return bread(sb->s_dev, block, sb->s_blocksize);
+ return NULL;
+}
+static inline struct buffer_head *
+affs_getblk(struct super_block *sb, int block)
+{
+ pr_debug(KERN_DEBUG "affs_getblk: %d\n", block);
+ if (block >= AFFS_SB->s_reserved && block < AFFS_SB->s_partition_size)
+ return getblk(sb->s_dev, block, sb->s_blocksize);
+ return NULL;
+}
+static inline struct buffer_head *
+affs_getzeroblk(struct super_block *sb, int block)
+{
+ struct buffer_head *bh;
+ pr_debug(KERN_DEBUG "affs_getzeroblk: %d\n", block);
+ if (block >= AFFS_SB->s_reserved && block < AFFS_SB->s_partition_size) {
+ bh = getblk(sb->s_dev, block, sb->s_blocksize);
+ wait_on_buffer(bh);
+ memset(bh->b_data, 0 , sb->s_blocksize);
+ mark_buffer_uptodate(bh, 1);
+ return bh;
+ }
+ return NULL;
+}
+static inline struct buffer_head *
+affs_getemptyblk(struct super_block *sb, int block)
+{
+ struct buffer_head *bh;
+ pr_debug(KERN_DEBUG "affs_getemptyblk: %d\n", block);
+ if (block >= AFFS_SB->s_reserved && block < AFFS_SB->s_partition_size) {
+ bh = getblk(sb->s_dev, block, sb->s_blocksize);
+ wait_on_buffer(bh);
+ mark_buffer_uptodate(bh, 1);
+ return bh;
+ }
+ return NULL;
+}
+static inline void
+affs_brelse(struct buffer_head *bh)
+{
+ if (bh)
+ pr_debug(KERN_DEBUG "affs_brelse: %ld\n", bh->b_blocknr);
+ brelse(bh);
+}
-#define FILE_END(p,i) GET_END_PTR(struct file_end,p,AFFS_I2BSIZE(i))
-#define ROOT_END(p,i) GET_END_PTR(struct root_end,p,AFFS_I2BSIZE(i))
-#define DIR_END(p,i) GET_END_PTR(struct dir_end,p,AFFS_I2BSIZE(i))
-#define LINK_END(p,i) GET_END_PTR(struct hlink_end,p,AFFS_I2BSIZE(i))
-#define ROOT_END_S(p,s) GET_END_PTR(struct root_end,p,(s)->s_blocksize)
-#define DATA_FRONT(bh) ((struct data_front *)(bh)->b_data)
-#define DIR_FRONT(bh) ((struct dir_front *)(bh)->b_data)
+static inline void
+affs_adjust_checksum(struct buffer_head *bh, u32 val)
+{
+ u32 tmp = be32_to_cpu(((u32 *)bh->b_data)[5]);
+ ((u32 *)bh->b_data)[5] = cpu_to_be32(tmp - val);
+}
+static inline void
+affs_adjust_bitmapchecksum(struct buffer_head *bh, u32 val)
+{
+ u32 tmp = be32_to_cpu(((u32 *)bh->b_data)[0]);
+ ((u32 *)bh->b_data)[0] = cpu_to_be32(tmp - val);
+}
-/* Only for easier debugging if need be */
-#define affs_bread bread
-#define affs_brelse brelse
+#define MIN(a, b) ({ \
+ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ _a < _b ? _a : _b; \
+})
#ifdef __LITTLE_ENDIAN
#define BO_EXBITS 0x18UL
#define ST_SOFTLINK 3
#define ST_LINKDIR 4
-struct root_front
-{
- s32 primary_type;
- s32 spare1[2];
- s32 hash_size;
- s32 spare2;
- u32 checksum;
- s32 hashtable[0];
-};
+#define AFFS_ROOT_BMAPS 25
-struct root_end
-{
- s32 bm_flag;
- s32 bm_keys[25];
- s32 bm_extend;
- struct DateStamp dir_altered;
- u8 disk_name[40];
- struct DateStamp disk_altered;
- struct DateStamp disk_made;
- s32 spare1[3];
- s32 secondary_type;
-};
+#define AFFS_HEAD(bh) ((struct affs_head *)(bh)->b_data)
+#define AFFS_TAIL(sb, bh) ((struct affs_tail *)((bh)->b_data+(sb)->s_blocksize-sizeof(struct affs_tail)))
+#define AFFS_ROOT_HEAD(bh) ((struct affs_root_head *)(bh)->b_data)
+#define AFFS_ROOT_TAIL(sb, bh) ((struct affs_root_tail *)((bh)->b_data+(sb)->s_blocksize-sizeof(struct affs_root_tail)))
+#define AFFS_DATA_HEAD(bh) ((struct affs_data_head *)(bh)->b_data)
+#define AFFS_DATA(bh) (((struct affs_data_head *)(bh)->b_data)->data)
-struct dir_front
-{
- s32 primary_type;
- s32 own_key;
- s32 spare1[3];
- u32 checksum;
- s32 hashtable[0];
+struct affs_date {
+ u32 days;
+ u32 mins;
+ u32 ticks;
};
-struct dir_end
-{
- s32 spare1;
- s16 owner_uid;
- s16 owner_gid;
- u32 protect;
- s32 spare2;
- u8 comment[92];
- struct DateStamp created;
- u8 dir_name[32];
- s32 spare3[2];
- s32 link_chain;
- s32 spare4[5];
- s32 hash_chain;
- s32 parent;
- s32 spare5;
- s32 secondary_type;
+struct affs_short_date {
+ u16 days;
+ u16 mins;
+ u16 ticks;
};
-struct file_front
-{
- s32 primary_type;
- s32 own_key;
- s32 block_count;
- s32 unknown1;
- s32 first_data;
- u32 checksum;
- s32 blocks[0];
+struct affs_root_head {
+ u32 ptype;
+ u32 spare1;
+ u32 spare2;
+ u32 hash_size;
+ u32 spare3;
+ u32 checksum;
+ u32 hashtable[1];
};
-struct file_end
-{
- s32 spare1;
- s16 owner_uid;
- s16 owner_gid;
- u32 protect;
- s32 byte_size;
- u8 comment[92];
- struct DateStamp created;
- u8 file_name[32];
- s32 spare2;
- s32 original; /* not really in file_end */
- s32 link_chain;
- s32 spare3[5];
- s32 hash_chain;
- s32 parent;
- s32 extension;
- s32 secondary_type;
+struct affs_root_tail {
+ u32 bm_flag;
+ u32 bm_blk[AFFS_ROOT_BMAPS];
+ u32 bm_ext;
+ struct affs_date root_change;
+ u8 disk_name[32];
+ u32 spare1;
+ u32 spare2;
+ struct affs_date disk_change;
+ struct affs_date disk_create;
+ u32 spare3;
+ u32 spare4;
+ u32 dcache;
+ u32 stype;
};
-struct hlink_front
-{
- s32 primary_type;
- s32 own_key;
- s32 spare1[3];
- u32 checksum;
+struct affs_head {
+ u32 ptype;
+ u32 key;
+ u32 block_count;
+ u32 spare1;
+ u32 first_data;
+ u32 checksum;
+ u32 table[1];
};
-struct hlink_end
-{
- s32 spare1;
- s16 owner_uid;
- s16 owner_gid;
- u32 protect;
- u8 comment[92];
- struct DateStamp created;
- u8 link_name[32];
- s32 spare2;
- s32 original;
- s32 link_chain;
- s32 spare3[5];
- s32 hash_chain;
- s32 parent;
- s32 spare4;
- s32 secondary_type;
+struct affs_tail {
+ u32 spare1;
+ u16 uid;
+ u16 gid;
+ u32 protect;
+ u32 size;
+ u8 comment[92];
+ struct affs_date change;
+ u8 name[32];
+ u32 spare2;
+ u32 original;
+ u32 link_chain;
+ u32 spare[5];
+ u32 hash_chain;
+ u32 parent;
+ u32 extension;
+ u32 stype;
};
struct slink_front
{
- s32 primary_type;
- s32 own_key;
- s32 spare1[3];
- s32 checksum;
- u8 symname[288]; /* depends on block size */
+ u32 ptype;
+ u32 key;
+ u32 spare1[3];
+ u32 checksum;
+ u8 symname[1]; /* depends on block size */
};
-struct data_front
+struct affs_data_head
{
- s32 primary_type;
- s32 header_key;
- s32 sequence_number;
- s32 data_size;
- s32 next_data;
- s32 checksum;
- u8 data[488]; /* depends on block size */
+ u32 ptype;
+ u32 key;
+ u32 sequence;
+ u32 size;
+ u32 next;
+ u32 checksum;
+ u8 data[1]; /* depends on block size */
};
/* Permission bits */
#define FIBF_GRP_EXECUTE 0x0200
#define FIBF_GRP_DELETE 0x0100
+#define FIBF_HIDDEN 0x0080
#define FIBF_SCRIPT 0x0040
#define FIBF_PURE 0x0020 /* no use under linux */
-#define FIBF_ARCHIVE 0x0010 /* never set, always cleared on write */
-#define FIBF_READ 0x0008 /* 0 means allowed */
-#define FIBF_WRITE 0x0004 /* 0 means allowed */
-#define FIBF_EXECUTE 0x0002 /* 0 means allowed, ignored under linux */
-#define FIBF_DELETE 0x0001 /* 0 means allowed */
+#define FIBF_ARCHIVED 0x0010 /* never set, always cleared on write */
+#define FIBF_NOREAD 0x0008 /* 0 means allowed */
+#define FIBF_NOWRITE 0x0004 /* 0 means allowed */
+#define FIBF_NOEXECUTE 0x0002 /* 0 means allowed, ignored under linux */
+#define FIBF_NODELETE 0x0001 /* 0 means allowed */
#define FIBF_OWNER 0x000F /* Bits pertaining to owner */
-
-#define AFFS_UMAYWRITE(prot) (((prot) & (FIBF_WRITE|FIBF_DELETE)) == (FIBF_WRITE|FIBF_DELETE))
-#define AFFS_UMAYREAD(prot) ((prot) & FIBF_READ)
-#define AFFS_UMAYEXECUTE(prot) ((prot) & FIBF_EXECUTE)
-#define AFFS_GMAYWRITE(prot) (((prot)&(FIBF_GRP_WRITE|FIBF_GRP_DELETE))==\
- (FIBF_GRP_WRITE|FIBF_GRP_DELETE))
-#define AFFS_GMAYREAD(prot) ((prot) & FIBF_GRP_READ)
-#define AFFS_GMAYEXECUTE(prot) ((prot) & FIBF_EXECUTE)
-#define AFFS_OMAYWRITE(prot) (((prot)&(FIBF_OTR_WRITE|FIBF_OTR_DELETE))==\
- (FIBF_OTR_WRITE|FIBF_OTR_DELETE))
-#define AFFS_OMAYREAD(prot) ((prot) & FIBF_OTR_READ)
-#define AFFS_OMAYEXECUTE(prot) ((prot) & FIBF_EXECUTE)
+#define FIBF_MASK 0xEE0E /* Bits modified by Linux */
#endif
#ifdef __KERNEL__
extern int sk_run_filter(struct sk_buff *skb, struct sock_filter *filter, int flen);
extern int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk);
+extern int sk_chk_filter(struct sock_filter *filter, int flen);
#endif /* __KERNEL__ */
#endif /* __LINUX_FILTER_H__ */
-/* $Id: if_ppp.h,v 1.19 1999/03/31 06:07:57 paulus Exp $ */
+/* $Id: if_ppp.h,v 1.21 2000/03/27 06:03:36 paulus Exp $ */
/*
* if_ppp.h - Point-to-Point Protocol definitions.
*/
/*
- * ==FILEVERSION 20000324==
+ * ==FILEVERSION 20000724==
*
* NOTE TO MAINTAINERS:
* If you modify this file at all, please set the above date.
#define PPPIOCSCOMPRESS _IOW('t', 77, struct ppp_option_data)
#define PPPIOCGNPMODE _IOWR('t', 76, struct npioctl) /* get NP mode */
#define PPPIOCSNPMODE _IOW('t', 75, struct npioctl) /* set NP mode */
+#define PPPIOCSPASS _IOW('t', 71, struct sock_fprog) /* set pass filter */
+#define PPPIOCSACTIVE _IOW('t', 70, struct sock_fprog) /* set active filt */
#define PPPIOCGDEBUG _IOR('t', 65, int) /* Read debug level */
#define PPPIOCSDEBUG _IOW('t', 64, int) /* Set debug level */
#define PPPIOCGIDLE _IOR('t', 63, struct ppp_idle) /* get idle time */
-/* $Id: isdn.h,v 1.111.6.1 2001/02/07 11:31:31 kai Exp $
+/* $Id: isdn.h,v 1.111.6.5 2001/04/20 02:40:48 keil Exp $
* Main header for the Linux ISDN subsystem (linklevel).
*
#ifndef isdn_h
#define isdn_h
+#ifdef __KERNEL__
#include <linux/config.h>
+#endif
#include <linux/ioctl.h>
#define ISDN_TTY_MAJOR 43
* the correspondent code in isdn.c
*/
+#ifdef USE_MINIMUM_MEM
+/* Save memory */
+#define ISDN_MAX_DRIVERS 2
+#define ISDN_MAX_CHANNELS 8
+#else
#define ISDN_MAX_DRIVERS 32
#define ISDN_MAX_CHANNELS 64
+#endif
#define ISDN_MINOR_B 0
#define ISDN_MINOR_BMAX (ISDN_MAX_CHANNELS-1)
#define ISDN_MINOR_CTRL 64
#define IIOCDRVCTL _IO('I',128)
+/* cisco hdlck device private ioctls */
+#define SIOCGKEEPPERIOD (SIOCDEVPRIVATE + 0)
+#define SIOCSKEEPPERIOD (SIOCDEVPRIVATE + 1)
+#define SIOCGDEBSERINT (SIOCDEVPRIVATE + 2)
+#define SIOCSDEBSERINT (SIOCDEVPRIVATE + 3)
+
/* Packet encapsulations for net-interfaces */
#define ISDN_NET_ENCAP_ETHER 0
#define ISDN_NET_ENCAP_RAWIP 1
((x & ISDN_USAGE_MASK)==ISDN_USAGE_VOICE) )
/* Timer-delays and scheduling-flags */
-#define ISDN_TIMER_RES 3 /* Main Timer-Resolution */
-#define ISDN_TIMER_02SEC (HZ/(ISDN_TIMER_RES+1)/5) /* Slow-Timer1 .2 sec */
-#define ISDN_TIMER_1SEC (HZ/(ISDN_TIMER_RES+1)) /* Slow-Timer2 1 sec */
+#define ISDN_TIMER_RES 4 /* Main Timer-Resolution */
+#define ISDN_TIMER_02SEC (HZ/ISDN_TIMER_RES/5) /* Slow-Timer1 .2 sec */
+#define ISDN_TIMER_1SEC (HZ/ISDN_TIMER_RES) /* Slow-Timer2 1 sec */
#define ISDN_TIMER_RINGING 5 /* tty RINGs = ISDN_TIMER_1SEC * this factor */
#define ISDN_TIMER_KEEPINT 10 /* Cisco-Keepalive = ISDN_TIMER_1SEC * this factor */
#define ISDN_TIMER_MODEMREAD 1
#define ISDN_TIMER_MODEMXMIT 8
#define ISDN_TIMER_NETDIAL 16
#define ISDN_TIMER_NETHANGUP 32
-#define ISDN_TIMER_KEEPALIVE 128 /* Cisco-Keepalive */
#define ISDN_TIMER_CARRIER 256 /* Wait for Carrier */
#define ISDN_TIMER_FAST (ISDN_TIMER_MODEMREAD | ISDN_TIMER_MODEMPLUS | \
ISDN_TIMER_MODEMXMIT)
#define ISDN_TIMER_SLOW (ISDN_TIMER_MODEMRING | ISDN_TIMER_NETHANGUP | \
- ISDN_TIMER_NETDIAL | ISDN_TIMER_KEEPALIVE | \
- ISDN_TIMER_CARRIER)
+ ISDN_TIMER_NETDIAL | ISDN_TIMER_CARRIER)
/* Timeout-Values for isdn_net_dial() */
#define ISDN_TIMER_DTIMEOUT10 (10*HZ/(ISDN_TIMER_02SEC*(ISDN_TIMER_RES+1)))
#ifdef CONFIG_ISDN_X25
struct concap_device_ops *dops; /* callbacks used by encapsulator */
#endif
- int cisco_loop; /* Loop counter for Cisco-SLARP */
+ /* use an own struct for that in later versions */
ulong cisco_myseq; /* Local keepalive seq. for Cisco */
+ ulong cisco_mineseen; /* returned keepalive seq. from remote */
ulong cisco_yourseq; /* Remote keepalive seq. for Cisco */
+ int cisco_keepalive_period; /* keepalive period */
+ ulong cisco_last_slarp_in; /* jiffie of last keepalive packet we received */
+ char cisco_line_state; /* state of line according to keepalive packets */
+ char cisco_debserint; /* debugging flag of cisco hdlc with slarp */
+ struct timer_list cisco_timer;
struct tq_struct tqueue;
} isdn_net_local;
#ifndef _LINUX_ISDN_PPP_H
#define _LINUX_ISDN_PPP_H
+#ifdef __KERNEL__
#include <linux/config.h>
+#endif
#define CALLTYPE_INCOMING 0x1
#define CALLTYPE_OUTGOING 0x2
#ifndef isdnif_h
#define isdnif_h
+#ifdef __KERNEL__
#include <linux/config.h>
+#endif
/*
* Values for general protocol-selection
#include <linux/list.h>
#include <linux/skbuff.h>
#include <linux/poll.h>
-#include <asm/atomic.h>
struct ppp_channel;
int (*start_xmit)(struct ppp_channel *, struct sk_buff *);
/* Handle an ioctl call that has come in via /dev/ppp. */
int (*ioctl)(struct ppp_channel *, unsigned int, unsigned long);
-
};
struct ppp_channel {
* that ppp_unregister_channel returns.
*/
-/* The following are temporary compatibility stuff */
-ssize_t ppp_channel_read(struct ppp_channel *chan, struct file *file,
- char *buf, size_t count);
-ssize_t ppp_channel_write(struct ppp_channel *chan, const char *buf,
- size_t count);
-unsigned int ppp_channel_poll(struct ppp_channel *chan, struct file *file,
- poll_table *wait);
-int ppp_channel_ioctl(struct ppp_channel *chan, unsigned int cmd,
- unsigned long arg);
-
#endif /* __KERNEL__ */
#endif
/*
* proc_tty.c
*/
+struct tty_driver;
extern void proc_tty_init(void);
extern void proc_tty_register_driver(struct tty_driver *driver);
extern void proc_tty_unregister_driver(struct tty_driver *driver);
#ifdef __KERNEL__
+#include <linux/types.h>
+
+struct rwsem_waiter;
+
/*
* the semaphore definition
*/
struct rw_semaphore {
- signed long count;
+ signed long count;
#define RWSEM_UNLOCKED_VALUE 0x00000000
#define RWSEM_ACTIVE_BIAS 0x00000001
#define RWSEM_ACTIVE_MASK 0x0000ffff
#define RWSEM_WAITING_BIAS (-0x00010000)
#define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS
#define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS)
- spinlock_t lock;
-#define RWSEM_SPINLOCK_OFFSET_STR "4" /* byte offset of spinlock */
- wait_queue_head_t wait;
-#define RWSEM_WAITING_FOR_READ WQ_FLAG_CONTEXT_0 /* bits to use in wait_queue_t.flags */
-#define RWSEM_WAITING_FOR_WRITE WQ_FLAG_CONTEXT_1
+ spinlock_t wait_lock;
+ struct rwsem_waiter *wait_front;
+ struct rwsem_waiter **wait_back;
#if RWSEM_DEBUG
int debug;
#endif
#endif
#define __RWSEM_INITIALIZER(name) \
-{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, \
- __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \
+{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, NULL, &(name).wait_front \
__RWSEM_DEBUG_INIT __RWSEM_DEBUG_MINIT(name) }
#define DECLARE_RWSEM(name) \
static inline void init_rwsem(struct rw_semaphore *sem)
{
sem->count = RWSEM_UNLOCKED_VALUE;
- spin_lock_init(&sem->lock);
- init_waitqueue_head(&sem->wait);
+ spin_lock_init(&sem->wait_lock);
+ sem->wait_front = NULL;
+ sem->wait_back = &sem->wait_front;
#if RWSEM_DEBUG
sem->debug = 0;
#endif
static inline void __down_read(struct rw_semaphore *sem)
{
int count;
- spin_lock(&sem->lock);
+ spin_lock(&sem->wait_lock);
sem->count += RWSEM_ACTIVE_READ_BIAS;
count = sem->count;
- spin_unlock(&sem->lock);
+ spin_unlock(&sem->wait_lock);
if (count<0)
rwsem_down_read_failed(sem);
}
static inline void __down_write(struct rw_semaphore *sem)
{
int count;
- spin_lock(&sem->lock);
+ spin_lock(&sem->wait_lock);
count = sem->count;
sem->count += RWSEM_ACTIVE_WRITE_BIAS;
- spin_unlock(&sem->lock);
+ spin_unlock(&sem->wait_lock);
if (count)
rwsem_down_write_failed(sem);
}
static inline void __up_read(struct rw_semaphore *sem)
{
int count;
- spin_lock(&sem->lock);
+ spin_lock(&sem->wait_lock);
count = sem->count;
sem->count -= RWSEM_ACTIVE_READ_BIAS;
- spin_unlock(&sem->lock);
+ spin_unlock(&sem->wait_lock);
if (count<0 && !((count-RWSEM_ACTIVE_READ_BIAS)&RWSEM_ACTIVE_MASK))
rwsem_wake(sem);
}
static inline void __up_write(struct rw_semaphore *sem)
{
int count;
- spin_lock(&sem->lock);
+ spin_lock(&sem->wait_lock);
sem->count -= RWSEM_ACTIVE_WRITE_BIAS;
count = sem->count;
- spin_unlock(&sem->lock);
+ spin_unlock(&sem->wait_lock);
if (count<0)
rwsem_wake(sem);
}
/*
* implement exchange and add functionality
+ * - only called when spinlock is already held
*/
static inline int rwsem_atomic_update(int delta, struct rw_semaphore *sem)
{
int count;
- spin_lock(&sem->lock);
sem->count += delta;
count = sem->count;
- spin_unlock(&sem->lock);
return count;
}
/*
* implement compare and exchange functionality on the rw-semaphore count LSW
+ * - only called by __rwsem_do_wake(), so spinlock is already held when called
*/
static inline __u16 rwsem_cmpxchgw(struct rw_semaphore *sem, __u16 old, __u16 new)
{
__u16 prev;
- spin_lock(&sem->lock);
prev = sem->count & RWSEM_ACTIVE_MASK;
if (prev==old)
sem->count = (sem->count & ~RWSEM_ACTIVE_MASK) | new;
- spin_unlock(&sem->lock);
return prev;
}
#ifdef __KERNEL__
+#include <linux/types.h>
#include <asm/system.h>
#include <asm/atomic.h>
-#include <linux/wait.h>
+
+struct rw_semaphore;
/* defined contention handler functions for the generic case
* - these are also used for the exchange-and-add based algorithm
#ifndef rwsemtrace
#if RWSEM_DEBUG
-#include <asm/current.h>
-#define rwsemtrace(SEM,FMT) do { if ((SEM)->debug) printk("[%d] "FMT"(count=%08lx)\n",current->pid,(SEM)->count); } while(0)
+extern void FASTCALL(rwsemtrace(struct rw_semaphore *sem, const char *str));
#else
#define rwsemtrace(SEM,FMT)
#endif
extern void FASTCALL(__wake_up(wait_queue_head_t *q, unsigned int mode, int nr));
extern void FASTCALL(__wake_up_sync(wait_queue_head_t *q, unsigned int mode, int nr));
-extern int FASTCALL(__wake_up_ctx(wait_queue_head_t *q, unsigned int mode, int count, int bit));
-extern int FASTCALL(__wake_up_sync_ctx(wait_queue_head_t *q, unsigned int mode, int count, int bit));
extern void FASTCALL(sleep_on(wait_queue_head_t *q));
extern long FASTCALL(sleep_on_timeout(wait_queue_head_t *q,
signed long timeout));
#define wake_up_interruptible_all(x) __wake_up((x),TASK_INTERRUPTIBLE, 0)
#define wake_up_interruptible_sync(x) __wake_up_sync((x),TASK_INTERRUPTIBLE, 1)
#define wake_up_interruptible_sync_nr(x) __wake_up_sync((x),TASK_INTERRUPTIBLE, nr)
-#define wake_up_ctx(x,count,bit) __wake_up_ctx((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE,count,bit)
-#define wake_up_sync_ctx(x,count,bit) __wake_up_ctx((x),TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE,count,bit)
asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struct rusage * ru);
extern int in_group_p(gid_t);
struct __wait_queue {
unsigned int flags;
#define WQ_FLAG_EXCLUSIVE 0x01
-#define WQ_FLAG_CONTEXT_0 8 /* context specific flag bit numbers */
-#define WQ_FLAG_CONTEXT_1 9
-#define WQ_FLAG_CONTEXT_2 10
-#define WQ_FLAG_CONTEXT_3 11
-#define WQ_FLAG_CONTEXT_4 12
-#define WQ_FLAG_CONTEXT_5 13
-#define WQ_FLAG_CONTEXT_6 14
-#define WQ_FLAG_CONTEXT_7 15
struct task_struct * task;
struct list_head task_list;
#if WAITQUEUE_DEBUG
si_swapinfo(&val);
{
- /* If the sum of all the available memory (i.e. ram + swap +
- * highmem) is less then can be stored in a 32 bit unsigned long
- * then we can be binary compatible with 2.2.x kernels. If not,
- * well, who cares since in that case 2.2.x was broken anyways...
+ unsigned long mem_total, sav_total;
+ unsigned int mem_unit, bitcount;
+
+ /* If the sum of all the available memory (i.e. ram + swap)
+ * is less than can be stored in a 32 bit unsigned long then
+ * we can be binary compatible with 2.2.x kernels. If not,
+ * well, in that case 2.2.x was broken anyways...
*
* -Erik Andersen <andersee@debian.org> */
- unsigned long mem_total = val.totalram + val.totalswap;
- if ( !(mem_total < val.totalram || mem_total < val.totalswap)) {
- unsigned long mem_total2 = mem_total + val.totalhigh;
- if (!(mem_total2 < mem_total || mem_total2 < val.totalhigh))
- {
- /* If mem_total did not overflow. Divide all memory values by
- * mem_unit and set mem_unit=1. This leaves things compatible with
- * 2.2.x, and also retains compatibility with earlier 2.4.x
- * kernels... */
-
- int bitcount = 0;
- while (val.mem_unit > 1)
- {
- bitcount++;
- val.mem_unit >>= 1;
- }
- val.totalram <<= bitcount;
- val.freeram <<= bitcount;
- val.sharedram <<= bitcount;
- val.bufferram <<= bitcount;
- val.totalswap <<= bitcount;
- val.freeswap <<= bitcount;
- val.totalhigh <<= bitcount;
- val.freehigh <<= bitcount;
- }
+ mem_total = val.totalram + val.totalswap;
+ if (mem_total < val.totalram || mem_total < val.totalswap)
+ goto out;
+ bitcount = 0;
+ mem_unit = val.mem_unit;
+ while (mem_unit > 1) {
+ bitcount++;
+ mem_unit >>= 1;
+ sav_total = mem_total;
+ mem_total <<= 1;
+ if (mem_total < sav_total)
+ goto out;
}
- }
+ /* If mem_total did not overflow, multiply all memory values by
+ * val.mem_unit and set it to 1. This leaves things compatible
+ * with 2.2.x, and also retains compatibility with earlier 2.4.x
+ * kernels... */
+
+ val.mem_unit = 1;
+ val.totalram <<= bitcount;
+ val.freeram <<= bitcount;
+ val.sharedram <<= bitcount;
+ val.bufferram <<= bitcount;
+ val.totalswap <<= bitcount;
+ val.freeswap <<= bitcount;
+ val.totalhigh <<= bitcount;
+ val.freehigh <<= bitcount;
+ }
+out:
if (copy_to_user(info, &val, sizeof(struct sysinfo)))
return -EFAULT;
return 0;
}
}
-/*
- * wake up processes in the wait queue depending on the state of a context bit in the flags
- * - wakes up a process if the specified bit is set in the flags member
- * - the context bit is cleared if the process is woken up
- * - if the bit number is negative, then the loop stops at the first unset context bit encountered
- * - returns the number of processes woken
- */
-static inline int __wake_up_ctx_common (wait_queue_head_t *q,
- int count, int bit, const int sync)
-{
- struct list_head *tmp, *head;
- struct task_struct *p;
- int stop, woken;
-
- woken = 0;
- stop = bit<0;
- if (bit<0) bit = -bit;
-
- CHECK_MAGIC_WQHEAD(q);
- head = &q->task_list;
- WQ_CHECK_LIST_HEAD(head);
- tmp = head->next;
- while (tmp != head) {
- wait_queue_t *curr = list_entry(tmp, wait_queue_t, task_list);
-
- tmp = tmp->next;
- CHECK_MAGIC(curr->__magic);
- p = curr->task;
- if (!test_and_clear_bit(bit,&curr->flags)) {
- if (stop)
- break;
- continue;
- }
-
- WQ_NOTE_WAKER(curr);
- try_to_wake_up(p,sync);
-
- woken++;
- if (woken>=count)
- break;
- }
-
- return woken;
-}
-
-int __wake_up_ctx(wait_queue_head_t *q, unsigned int mode, int count, int bit)
-{
- int woken = 0;
- if (q && count) {
- unsigned long flags;
- wq_read_lock_irqsave(&q->lock, flags);
- woken = __wake_up_ctx_common(q, count, bit, 0);
- wq_read_unlock_irqrestore(&q->lock, flags);
- }
- return woken;
-}
-
-int __wake_up_ctx_sync(wait_queue_head_t *q, unsigned int mode, int count, int bit)
-{
- int woken = 0;
- if (q && count) {
- unsigned long flags;
- wq_read_lock_irqsave(&q->lock, flags);
- woken = __wake_up_ctx_common(q, count, bit, 1);
- wq_read_unlock_irqrestore(&q->lock, flags);
- }
- return woken;
-}
-
#define SLEEP_ON_VAR \
unsigned long flags; \
wait_queue_t wait; \
#include <linux/sched.h>
#include <linux/module.h>
+struct rwsem_waiter {
+ struct rwsem_waiter *next;
+ struct task_struct *task;
+ unsigned int flags;
+#define RWSEM_WAITING_FOR_READ 0x00000001
+#define RWSEM_WAITING_FOR_WRITE 0x00000002
+};
+#define RWSEM_WAITER_MAGIC 0x52575345
+
+static struct rw_semaphore *FASTCALL(__rwsem_do_wake(struct rw_semaphore *sem));
+
+#if RWSEM_DEBUG
+void rwsemtrace(struct rw_semaphore *sem, const char *str)
+{
+ if (sem->debug)
+ printk("[%d] %s(count=%08lx)\n",current->pid,str,sem->count);
+}
+#endif
+
+/*
+ * handle the lock being released whilst there are processes blocked on it that can now run
+ * - if we come here, then:
+ * - the 'active part' of the count (&0x0000ffff) reached zero (but may no longer be zero)
+ * - the 'waiting part' of the count (&0xffff0000) is negative (and will still be so)
+ * - the spinlock must be held before entry
+ * - woken process blocks are discarded from the list after having flags zeroised
+ */
+static struct rw_semaphore *__rwsem_do_wake(struct rw_semaphore *sem)
+{
+ struct rwsem_waiter *waiter, *next;
+ int woken, loop;
+
+ rwsemtrace(sem,"Entering __rwsem_do_wake");
+
+ /* try to grab an 'activity' marker
+ * - need to make sure two copies of rwsem_wake() don't do this for two separate processes
+ * simultaneously
+ * - be horribly naughty, and only deal with the LSW of the atomic counter
+ */
+ if (rwsem_cmpxchgw(sem,0,RWSEM_ACTIVE_BIAS)!=0) {
+ rwsemtrace(sem,"__rwsem_do_wake: abort wakeup due to renewed activity");
+ goto out;
+ }
+
+ /* check the wait queue is populated */
+ waiter = sem->wait_front;
+
+ if (__builtin_expect(!waiter,0)) {
+ printk("__rwsem_do_wake(): wait_list unexpectedly empty\n");
+ BUG();
+ goto out;
+ }
+
+ if (__builtin_expect(!waiter->flags,0)) {
+ printk("__rwsem_do_wake(): wait_list front apparently not waiting\n");
+ BUG();
+ goto out;
+ }
+
+ next = NULL;
+
+ /* try to grant a single write lock if there's a writer at the front of the queue
+ * - note we leave the 'active part' of the count incremented by 1 and the waiting part
+ * incremented by 0x00010000
+ */
+ if (waiter->flags & RWSEM_WAITING_FOR_WRITE) {
+ next = waiter->next;
+ waiter->flags = 0;
+ wake_up_process(waiter->task);
+ goto discard_woken_processes;
+ }
+
+ /* grant an infinite number of read locks to the readers at the front of the queue
+ * - note we increment the 'active part' of the count by the number of readers (less one
+ * for the activity decrement we've already done) before waking any processes up
+ */
+ woken = 0;
+ do {
+ woken++;
+ waiter = waiter->next;
+ } while (waiter && waiter->flags&RWSEM_WAITING_FOR_READ);
+
+ loop = woken;
+ woken *= RWSEM_ACTIVE_BIAS-RWSEM_WAITING_BIAS;
+ woken -= RWSEM_ACTIVE_BIAS;
+ rwsem_atomic_update(woken,sem);
+
+ waiter = sem->wait_front;
+ for (; loop>0; loop--) {
+ next = waiter->next;
+ waiter->flags = 0;
+ wake_up_process(waiter->task);
+ waiter = next;
+ }
+
+ discard_woken_processes:
+ sem->wait_front = next;
+ if (!next) sem->wait_back = &sem->wait_front;
+
+ out:
+ rwsemtrace(sem,"Leaving __rwsem_do_wake");
+ return sem;
+}
+
/*
* wait for the read lock to be granted
* - need to repeal the increment made inline by the caller
*/
struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem)
{
+ struct rwsem_waiter waiter;
struct task_struct *tsk = current;
- DECLARE_WAITQUEUE(wait,tsk);
signed long count;
rwsemtrace(sem,"Entering rwsem_down_read_failed");
+
+ set_task_state(tsk,TASK_UNINTERRUPTIBLE);
- /* this waitqueue context flag will be cleared when we are granted the lock */
- __set_bit(RWSEM_WAITING_FOR_READ,&wait.flags);
- set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+ /* set up my own style of waitqueue */
+ waiter.next = NULL;
+ waiter.task = tsk;
+ waiter.flags = RWSEM_WAITING_FOR_READ;
+
+ spin_lock(&sem->wait_lock);
- add_wait_queue_exclusive(&sem->wait, &wait); /* FIFO */
+ *sem->wait_back = &waiter; /* add to back of queue */
+ sem->wait_back = &waiter.next;
/* note that we're now waiting on the lock, but no longer actively read-locking */
count = rwsem_atomic_update(RWSEM_WAITING_BIAS-RWSEM_ACTIVE_BIAS,sem);
* - it might even be this process, since the waker takes a more active part
*/
if (!(count & RWSEM_ACTIVE_MASK))
- rwsem_wake(sem);
+ __rwsem_do_wake(sem);
+
+ spin_unlock(&sem->wait_lock);
/* wait to be given the lock */
for (;;) {
- if (!test_bit(RWSEM_WAITING_FOR_READ,&wait.flags))
+ if (!waiter.flags)
break;
schedule();
set_task_state(tsk, TASK_UNINTERRUPTIBLE);
}
- remove_wait_queue(&sem->wait,&wait);
tsk->state = TASK_RUNNING;
rwsemtrace(sem,"Leaving rwsem_down_read_failed");
*/
struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem)
{
+ struct rwsem_waiter waiter;
struct task_struct *tsk = current;
- DECLARE_WAITQUEUE(wait,tsk);
signed long count;
rwsemtrace(sem,"Entering rwsem_down_write_failed");
- /* this waitqueue context flag will be cleared when we are granted the lock */
- __set_bit(RWSEM_WAITING_FOR_WRITE,&wait.flags);
set_task_state(tsk, TASK_UNINTERRUPTIBLE);
- add_wait_queue_exclusive(&sem->wait, &wait); /* FIFO */
+ /* set up my own style of waitqueue */
+ waiter.next = NULL;
+ waiter.task = tsk;
+ waiter.flags = RWSEM_WAITING_FOR_WRITE;
+
+ spin_lock(&sem->wait_lock);
+
+ *sem->wait_back = &waiter; /* add to back of queue */
+ sem->wait_back = &waiter.next;
/* note that we're waiting on the lock, but no longer actively locking */
count = rwsem_atomic_update(-RWSEM_ACTIVE_BIAS,sem);
* - it might even be this process, since the waker takes a more active part
*/
if (!(count & RWSEM_ACTIVE_MASK))
- rwsem_wake(sem);
+ __rwsem_do_wake(sem);
+
+ spin_unlock(&sem->wait_lock);
/* wait to be given the lock */
for (;;) {
- if (!test_bit(RWSEM_WAITING_FOR_WRITE,&wait.flags))
+ if (!waiter.flags)
break;
schedule();
set_task_state(tsk, TASK_UNINTERRUPTIBLE);
}
- remove_wait_queue(&sem->wait,&wait);
tsk->state = TASK_RUNNING;
rwsemtrace(sem,"Leaving rwsem_down_write_failed");
}
/*
- * handle the lock being released whilst there are processes blocked on it that can now run
- * - if we come here, then:
- * - the 'active part' of the count (&0x0000ffff) reached zero (but may no longer be zero)
- * - the 'waiting part' of the count (&0xffff0000) is negative (and will still be so)
+ * spinlock grabbing wrapper for __rwsem_do_wake()
*/
struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem)
{
- signed long count;
- int woken;
-
rwsemtrace(sem,"Entering rwsem_wake");
- try_again:
- /* try to grab an 'activity' marker
- * - need to make sure two copies of rwsem_wake() don't do this for two separate processes
- * simultaneously
- * - be horribly naughty, and only deal with the LSW of the atomic counter
- */
- if (rwsem_cmpxchgw(sem,0,RWSEM_ACTIVE_BIAS)!=0) {
- rwsemtrace(sem,"rwsem_wake: abort wakeup due to renewed activity");
- goto out;
- }
-
- /* try to grant a single write lock if there's a writer at the front of the queue
- * - note we leave the 'active part' of the count incremented by 1 and the waiting part
- * incremented by 0x00010000
- */
- if (wake_up_ctx(&sem->wait,1,-RWSEM_WAITING_FOR_WRITE)==1)
- goto out;
+ spin_lock(&sem->wait_lock);
- /* grant an infinite number of read locks to the readers at the front of the queue
- * - note we increment the 'active part' of the count by the number of readers just woken,
- * less one for the activity decrement we've already done
- */
- woken = wake_up_ctx(&sem->wait,65535,-RWSEM_WAITING_FOR_READ);
- if (woken<=0)
- goto counter_correction;
+ sem = __rwsem_do_wake(sem);
- woken *= RWSEM_ACTIVE_BIAS-RWSEM_WAITING_BIAS;
- woken -= RWSEM_ACTIVE_BIAS;
- rwsem_atomic_update(woken,sem);
+ spin_unlock(&sem->wait_lock);
- out:
rwsemtrace(sem,"Leaving rwsem_wake");
return sem;
-
- /* come here if we need to correct the counter for odd SMP-isms */
- counter_correction:
- count = rwsem_atomic_update(-RWSEM_ACTIVE_BIAS,sem);
- rwsemtrace(sem,"corrected count");
- if (!(count & RWSEM_ACTIVE_MASK))
- goto try_again;
- goto out;
}
EXPORT_SYMBOL(rwsem_down_read_failed);
EXPORT_SYMBOL(rwsem_down_write_failed);
EXPORT_SYMBOL(rwsem_wake);
+#if RWSEM_DEBUG
+EXPORT_SYMBOL(rwsemtrace);
+#endif
/* If we are connected to ppp_generic, let it handle the job */
if(ap->ppp_open)
- return ppp_channel_write(&ap->chan, buf, count);
+ return -EAGAIN;
else
return irnet_ctrl_write(ap, buf, count);
}
/* If we are connected to ppp_generic, let it handle the job */
if(ap->ppp_open)
- return ppp_channel_read(&ap->chan, file, buf, count);
+ return -EAGAIN;
else
return irnet_ctrl_read(ap, file, buf, count);
}
DABORT(ap == NULL, mask, FS_ERROR, "ap is NULL !!!\n");
/* If we are connected to ppp_generic, let it handle the job */
- if(ap->ppp_open)
- mask |= ppp_channel_poll(&ap->chan, file, wait);
- else
+ if(!ap->ppp_open)
mask |= irnet_ctrl_poll(ap, file, wait);
DEXIT(FS_TRACE, " - mask=0x%X\n", mask);
}
break;
- /* Attach this PPP instance to the PPP driver (set it active) */
- case PPPIOCATTACH:
- case PPPIOCDETACH:
- if(ap->ppp_open)
- err = ppp_channel_ioctl(&ap->chan, cmd, arg);
- else
- DERROR(FS_ERROR, "Channel not registered !\n");
- break;
-
/* Query PPP channel and unit number */
case PPPIOCGCHAN:
if(!ap->ppp_open)
-#!/usr/bin/perl
+#!/usr/bin/perl -w
+
+use strict;
## Copyright (c) 1998 Michael Zucchi, All Rights Reserved ##
-## Copyright (C) 2000 Tim Waugh <twaugh@redhat.com> ##
+## Copyright (C) 2000, 1 Tim Waugh <twaugh@redhat.com> ##
+## Copyright (C) 2001 Simon Huggins ##
+## ##
+## #define enhancements by Armin Kuster <akuster@mvista.com> ##
+## Copyright (c) 2000 MontaVista Software, Inc. ##
## ##
-## #define enhancements by Armin Kuster <akuster@mvista.com> ##
-## Copyright (c) 2000 MontaVista Software, Inc. ##
-## ##
## This software falls under the GNU General Public License. ##
## Please read the COPYING file for more information ##
# w.o. 03-11-2000: added the '-filelist' option.
+# 18/01/2001 - Cleanups
+# Functions prototyped as foo(void) same as foo()
+# Stop eval'ing where we don't need to.
+# -- huggie@earth.li
+# Still to do:
+# - add perldoc documentation
+# - Look more closely at some of the scarier bits :)
+
#
# This will read a 'c' file and scan for embedded comments in the
# style of gnome comments (+minor extensions - see below).
# '%CONST' - name of a constant.
# match expressions used to find embedded type information
-$type_constant = "\\\%([-_\\w]+)";
-$type_func = "(\\w+)\\(\\)";
-$type_param = "\\\@(\\w+)";
-$type_struct = "\\\&((struct\\s*)?[_\\w]+)";
-$type_env = "(\\\$\\w+)";
+my $type_constant = '\%([-_\w]+)';
+my $type_func = '(\w+)\(\)';
+my $type_param = '\@(\w+)';
+my $type_struct = '\&((struct\s*)?[_\w]+)';
+my $type_env = '(\$\w+)';
# Output conversion substitutions.
# One for each output format
# these work fairly well
-%highlights_html = ( $type_constant, "<i>\$1</i>",
- $type_func, "<b>\$1</b>",
- $type_struct, "<i>\$1</i>",
- $type_param, "<tt><b>\$1</b></tt>" );
-$blankline_html = "<p>";
+my %highlights_html = ( $type_constant, "<i>\$1</i>",
+ $type_func, "<b>\$1</b>",
+ $type_struct, "<i>\$1</i>",
+ $type_param, "<tt><b>\$1</b></tt>" );
+my $blankline_html = "<p>";
# sgml, docbook format
-%highlights_sgml = ( "([^=])\\\"([^\\\"<]+)\\\"", "\$1<quote>\$2</quote>",
- $type_constant, "<constant>\$1</constant>",
- $type_func, "<function>\$1</function>",
- $type_struct, "<structname>\$1</structname>",
- $type_env, "<envar>\$1</envar>",
- $type_param, "<parameter>\$1</parameter>" );
-$blankline_sgml = "</para><para>\n";
+my %highlights_sgml = ( "([^=])\\\"([^\\\"<]+)\\\"", "\$1<quote>\$2</quote>",
+ $type_constant, "<constant>\$1</constant>",
+ $type_func, "<function>\$1</function>",
+ $type_struct, "<structname>\$1</structname>",
+ $type_env, "<envar>\$1</envar>",
+ $type_param, "<parameter>\$1</parameter>" );
+my $blankline_sgml = "</para><para>\n";
# gnome, docbook format
-%highlights_gnome = ( $type_constant, "<replaceable class=\"option\">\$1</replaceable>",
- $type_func, "<function>\$1</function>",
- $type_struct, "<structname>\$1</structname>",
- $type_env, "<envar>\$1</envar>",
- $type_param, "<parameter>\$1</parameter>" );
-$blankline_gnome = "</para><para>\n";
+my %highlights_gnome = ( $type_constant, "<replaceable class=\"option\">\$1</replaceable>",
+ $type_func, "<function>\$1</function>",
+ $type_struct, "<structname>\$1</structname>",
+ $type_env, "<envar>\$1</envar>",
+ $type_param, "<parameter>\$1</parameter>" );
+my $blankline_gnome = "</para><para>\n";
# these are pretty rough
-%highlights_man = ( $type_constant, "\$1",
- $type_func, "\\\\fB\$1\\\\fP",
- $type_struct, "\\\\fI\$1\\\\fP",
- $type_param, "\\\\fI\$1\\\\fP" );
-$blankline_man = "";
+my %highlights_man = ( $type_constant, "\$1",
+ $type_func, "\\\\fB\$1\\\\fP",
+ $type_struct, "\\\\fI\$1\\\\fP",
+ $type_param, "\\\\fI\$1\\\\fP" );
+my $blankline_man = "";
# text-mode
-%highlights_text = ( $type_constant, "\$1",
- $type_func, "\$1",
- $type_struct, "\$1",
- $type_param, "\$1" );
-$blankline_text = "";
+my %highlights_text = ( $type_constant, "\$1",
+ $type_func, "\$1",
+ $type_struct, "\$1",
+ $type_param, "\$1" );
+my $blankline_text = "";
sub usage {
usage();
}
-$verbose = 0;
-$output_mode = "man";
-%highlights = %highlights_man;
-$blankline = $blankline_man;
-$modulename = "API Documentation";
-$function_only = 0;
-$filelist = '';
+my $verbose = 0;
+my $output_mode = "man";
+my %highlights = %highlights_man;
+my $blankline = $blankline_man;
+my $modulename = "API Documentation";
+my $function_only = 0;
+
+# Essentially these are globals
+# They probably want to be tidied up made more localised or summat.
+# CAVEAT EMPTOR! Some of the others I localised may not want to be which
+# could cause "use of undefined value" or other bugs.
+my ($function, %function_table,%parametertypes,$function_purpose);
+my ($type,$file,$function_name,$return_type);
+my ($newsection,$newcontents,$prototype,$filelist);
+
+my $lineprefix="";
+
+# states
+# 0 - normal code
+# 1 - looking for function name
+# 2 - scanning field start.
+# 3 - scanning prototype.
+my $state = 0;
+my $doc_special = "\@\%\$\&";
+
+my $doc_start = "^/\\*\\*\$";
+my $doc_end = "\\*/";
+my $doc_com = "\\s*\\*\\s*";
+my $doc_func = $doc_com."(\\w+):?";
+my $doc_sect = $doc_com."([".$doc_special."]?[\\w ]+):(.*)";
+my $doc_content = $doc_com."(.*)";
+my $doc_block = $doc_com."DOC:\\s*(.*)?";
+
+my %constants = ();
+my %parameters = ();
+my @parameterlist = ();
+my %sections = ();
+my @sectionlist = ();
+
+my $contents = "";
+my $section_default = "Description"; # default section
+my $section_intro = "Introduction";
+my $section = $section_default;
while ($ARGV[0] =~ m/^-(.*)/) {
- $cmd = shift @ARGV;
+ my $cmd = shift @ARGV;
if ($cmd eq "-html") {
$output_mode = "html";
%highlights = %highlights_html;
# generate a sequence of code that will splice in highlighting information
# using the s// operator.
-$dohighlight = "";
-foreach $pattern (keys %highlights) {
+my $dohighlight = "";
+foreach my $pattern (keys %highlights) {
# print "scanning pattern $pattern ($highlights{$pattern})\n";
$dohighlight .= "\$contents =~ s:$pattern:$highlights{$pattern}:gs;\n";
}
# dumps section contents to arrays/hashes intended for that purpose.
#
sub dump_section {
- my $name = shift @_;
+ my $name = shift;
my $contents = join "\n", @_;
if ($name =~ m/$type_constant/) {
# sections => %descriont descriptions
#
-sub output_highlight {
- my $contents = join "\n", @_;
+sub output_highlight(@) {
+ my $contents = join "\n",@_;
my $line;
+# DEBUG
+# if (!defined $contents) {
+# use Carp;
+# confess "output_highlight got called with no args?\n";
+# }
+
eval $dohighlight;
+ die $@ if $@;
foreach $line (split "\n", $contents) {
if ($line eq ""){
print $lineprefix, $blankline;
# output in html
-sub output_html {
+sub output_html(%) {
my %args = %{$_[0]};
my ($parameter, $section);
my $count;
# output in html
-sub output_intro_html {
+sub output_intro_html(%) {
my %args = %{$_[0]};
my ($parameter, $section);
my $count;
print "<hr>\n";
}
-
-
# output in sgml DocBook
-sub output_sgml {
+sub output_sgml(%) {
my %args = %{$_[0]};
my ($parameter, $section);
my $count;
print "<function>".$args{'function'}." ";
print "</function></funcdef>\n";
-# print "<refsect1>\n";
-# print " <title>Synopsis</title>\n";
-# print " <funcsynopsis>\n";
-# print " <funcdef>".$args{'functiontype'}." ";
-# print "<function>".$args{'function'}." ";
-# print "</function></funcdef>\n";
-
$count = 0;
if ($#{$args{'parameterlist'}} >= 0) {
foreach $parameter (@{$args{'parameterlist'}}) {
}
print " </funcsynopsis>\n";
print "</refsynopsisdiv>\n";
-# print "</refsect1>\n";
# print parameters
print "<refsect1>\n <title>Arguments</title>\n";
-# print "<para>\nArguments\n";
if ($#{$args{'parameterlist'}} >= 0) {
print " <variablelist>\n";
foreach $parameter (@{$args{'parameterlist'}}) {
$lineprefix=" ";
foreach $section (@{$args{'sectionlist'}}) {
print "<refsect1>\n <title>$section</title>\n <para>\n";
-# print "<para>\n$section\n";
if ($section =~ m/EXAMPLE/i) {
print "<example><para>\n";
}
output_highlight($args{'sections'}{$section});
-# print "</para>";
if ($section =~ m/EXAMPLE/i) {
print "</para></example>\n";
}
}
# output in sgml DocBook
-sub output_intro_sgml {
+sub output_intro_sgml(%) {
my %args = %{$_[0]};
my ($parameter, $section);
my $count;
- my $id;
- $id = $args{'module'};
+ my $id = $args{'module'};
$id =~ s/[^A-Za-z0-9]/-/g;
# print out each section
$lineprefix=" ";
foreach $section (@{$args{'sectionlist'}}) {
print "<refsect1>\n <title>$section</title>\n <para>\n";
-# print "<para>\n$section\n";
if ($section =~ m/EXAMPLE/i) {
print "<example><para>\n";
}
output_highlight($args{'sections'}{$section});
-# print "</para>";
if ($section =~ m/EXAMPLE/i) {
print "</para></example>\n";
}
print "<sect2>\n";
print " <title id=\"$id\">".$args{'function'}."</title>\n";
-# print "<simplesect>\n";
-# print " <title>Synopsis</title>\n";
print " <funcsynopsis>\n";
print " <funcdef>".$args{'functiontype'}." ";
print "<function>".$args{'function'}." ";
print " <void>\n";
}
print " </funcsynopsis>\n";
-# print "</simplesect>\n";
-# print "</refsect1>\n";
-
- # print parameters
-# print "<simplesect>\n <title>Arguments</title>\n";
-# if ($#{$args{'parameterlist'}} >= 0) {
-# print " <variablelist>\n";
-# foreach $parameter (@{$args{'parameterlist'}}) {
-# print " <varlistentry>\n <term><parameter>$parameter</parameter></term>\n";
-# print " <listitem>\n <para>\n";
-# $lineprefix=" ";
-# output_highlight($args{'parameters'}{$parameter});
-# print " </para>\n </listitem>\n </varlistentry>\n";
-# }
-# print " </variablelist>\n";
-# } else {
-# print " <para>\n None\n </para>\n";
-# }
-# print "</simplesect>\n";
-
-# print "<simplesect>\n <title>Arguments</title>\n";
if ($#{$args{'parameterlist'}} >= 0) {
print " <informaltable pgwide=\"1\" frame=\"none\" role=\"params\">\n";
print "<tgroup cols=\"2\">\n";
} else {
print " <para>\n None\n </para>\n";
}
-# print "</simplesect>\n";
# print out each section
$lineprefix=" ";
foreach $section (@{$args{'sectionlist'}}) {
print "<simplesect>\n <title>$section</title>\n";
-# print "<para>\n$section\n";
if ($section =~ m/EXAMPLE/i) {
print "<example><programlisting>\n";
} else {
}
print "<para>\n";
output_highlight($args{'sections'}{$section});
-# print "</para>";
print "</para>\n";
if ($section =~ m/EXAMPLE/i) {
print "</programlisting></example>\n";
##
# output in man
-sub output_man {
+sub output_man(%) {
my %args = %{$_[0]};
my ($parameter, $section);
my $count;
print ".SH SYNOPSIS\n";
print ".B \"".$args{'functiontype'}."\" ".$args{'function'}."\n";
$count = 0;
- $parenth = "(";
- $post = ",";
- foreach $parameter (@{$args{'parameterlist'}}) {
+ my $parenth = "(";
+ my $post = ",";
+ foreach my $parameter (@{$args{'parameterlist'}}) {
if ($count == $#{$args{'parameterlist'}}) {
$post = ");";
}
# pointer-to-function
print ".BI \"".$parenth.$1."\" ".$parameter." \") (".$2.")".$post."\"\n";
} else {
- $type =~ s/([^\*])$/\1 /;
+ $type =~ s/([^\*])$/$1 /;
print ".BI \"".$parenth.$type."\" ".$parameter." \"".$post."\"\n";
}
$count++;
}
}
-sub output_intro_man {
+sub output_intro_man(%) {
my %args = %{$_[0]};
my ($parameter, $section);
my $count;
##
# output in text
-sub output_text {
+sub output_text(%) {
my %args = %{$_[0]};
my ($parameter, $section);
print "Function:\n\n";
- $start=$args{'functiontype'}." ".$args{'function'}." (";
+ my $start=$args{'functiontype'}." ".$args{'function'}." (";
print $start;
- $count = 0;
- foreach $parameter (@{$args{'parameterlist'}}) {
+ my $count = 0;
+ foreach my $parameter (@{$args{'parameterlist'}}) {
if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
# pointer-to-function
print $1.$parameter.") (".$2;
print "\n\n";
}
-sub output_intro_text {
+sub output_intro_text(%) {
my %args = %{$_[0]};
my ($parameter, $section);
# generic output function - calls the right one based
# on current output mode.
sub output_function {
-# output_html(@_);
- eval "output_".$output_mode."(\@_);";
+ no strict 'refs';
+ my $func = "output_".$output_mode;
+ &$func(@_);
}
##
# generic output function - calls the right one based
# on current output mode.
sub output_intro {
-# output_html(@_);
- eval "output_intro_".$output_mode."(\@_);";
+ no strict 'refs';
+ my $func = "output_intro_".$output_mode;
+ &$func(@_);
}
##
-# takes a function prototype and spits out all the details
-# stored in the global arrays/hashes.
-sub dump_function {
- my $prototype = shift @_;
+# takes a function prototype and the name of the current file being
+# processed and spits out all the details stored in the global
+# arrays/hashes.
+sub dump_function($$) {
+ my $prototype = shift;
+ my $file = shift;
$prototype =~ s/^static +//;
$prototype =~ s/^extern +//;
$prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/) {
$return_type = $1;
$function_name = $2;
- $args = $3;
-
- # allow for up to fours args to function pointers
- $args =~ s/(\([^\),]+),/\1#/g;
- $args =~ s/(\([^\),]+),/\1#/g;
- $args =~ s/(\([^\),]+),/\1#/g;
+ my $args = $3;
+ my ($param);
+
+ # allow for up to six args to function pointers
+ $args =~ s/(\([^\),]+),/$1#/g;
+ $args =~ s/(\([^\),]+),/$1#/g;
+ $args =~ s/(\([^\),]+),/$1#/g;
+ $args =~ s/(\([^\),]+),/$1#/g;
+ $args =~ s/(\([^\),]+),/$1#/g;
# print STDERR "ARGS = '$args'\n";
- foreach $arg (split ',', $args) {
+ foreach my $arg (split ',', $args) {
# strip leading/trailing spaces
$arg =~ s/^\s*//;
$arg =~ s/\s*$//;
$arg =~ m/[^\(]+\(\*([^\)]+)\)/;
$param = $1;
$type = $arg;
- $type =~ s/([^\(]+\(\*)$param/\1/;
+ $type =~ s/([^\(]+\(\*)$param/$1/;
} else {
# evil magic to get fixed array parameters to work
- $arg =~ s/(.+\s+)(.+)\[.*/\1* \2/;
+ $arg =~ s/(.+\s+)(.+)\[.*/$1* $2/;
# print STDERR "SCAN ARG: '$arg'\n";
- @args = split('\s', $arg);
+ my @args = split('\s', $arg);
# print STDERR " -> @args\n";
$param = pop @args;
$param="...";
$parameters{"..."} = "variable arguments";
}
- elsif ($type eq "" && $param eq "")
+ elsif ($type eq "" && ($param eq "" or $param eq "void"))
{
$type="";
$param="void";
$parameters{void} = "no arguments";
}
- if ($type ne "" && $parameters{$param} eq "") {
+ if (defined $type && $type && !defined $parameters{$param}) {
$parameters{$param} = "-- undescribed --";
- print STDERR "Warning($file:$lineno): Function parameter '$param' not described in '$function_name'\n";
+ print STDERR "Warning($file:$.): Function parameter '$param' not described in '$function_name'\n";
}
push @parameterlist, $param;
# print STDERR "param = '$param', type = '$type'\n";
}
} else {
- print STDERR "Error($lineno): cannot understand prototype: '$prototype'\n";
+ print STDERR "Error($.): cannot understand prototype: '$prototype'\n";
return;
}
$section_intro = "Introduction";
$section = $section_default;
-if( $filelist ne '' ) {
+sub process_file($);
+
+if ($filelist) {
open(FLIST,"<$filelist") or die "Can't open file list $filelist";
while(<FLIST>) {
chop;
}
}
-foreach $file (@ARGV) {
- chomp($file);
- process_file($file);
+foreach (@ARGV) {
+ chomp;
+ process_file($_);
}
sub process_file($) {
return;
}
- $lineno = 0;
while (<IN>) {
- $lineno++;
-
if ($state == 0) {
if (/$doc_start/o) {
$state = 1; # next line is always the function name
$function_purpose = "";
}
if ($verbose) {
- print STDERR "Info($lineno): Scanning doc for $function\n";
+ print STDERR "Info($.): Scanning doc for $function\n";
}
} else {
- print STDERR "WARN($lineno): Cannot understand $_ on line $lineno",
+ print STDERR "WARN($.): Cannot understand $_ on line $.",
" - I thought it was a doc line\n";
$state = 0;
}
}
} else {
# i dont know - bad line? ignore.
- print STDERR "WARNING($lineno): bad line: $_";
+ print STDERR "WARNING($.): bad line: $_";
}
} elsif ($state == 3) { # scanning for function { (end of prototype)
if (m#\s*/\*\s+MACDOC\s*#io) {
$prototype =~ s@/\*.*?\*/@@gos; # strip comments.
$prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's.
$prototype =~ s@^ +@@gos; # strip leading spaces
- dump_function($prototype);
+ dump_function($prototype,$file);
$function = "";
%constants = ();