diff options
author | Linus Torvalds <torvalds@cc.helsinki.fi> | 1993-04-09 14:45:07 +0000 |
---|---|---|
committer | Nicolas Pitre <nico@cam.org> | 2007-08-19 14:19:14 -0400 |
commit | d2c3ec4b1398e55d918df0d1fa718c9940d373f6 (patch) | |
tree | d10540f9ee2a6143e28f9706933b284b7d859ce6 | |
parent | 3ccb23223d9cbee8e7946aa23f44a84ceff88d3c (diff) | |
download | archive-d2c3ec4b1398e55d918df0d1fa718c9940d373f6.tar.gz |
ANNOUNCE: linux 0.99 patchlevel 8 availablev0.99-pl8
Yet another kernel release is now available on nic.funet.fi in the usual
place (pub/OS/Linux/PEOPLE/Linus for those of you that have already
forgotten), and will probably show up on the other ftp-sites within a
day or two. There are two new files:
linux-0.99.8.tar.z - the full gzipped and tarred source-tree of the
linux kernel.
linux-0.99.patch8.z - unified diffs against the last official release
(0.99pl7).
There is no SLIP or new networking routines in this kernel despite the
rumors that have been flying around - the main changes to 0.99.7 are
(some of them were in 0.99pl7A as well):
- the signal handling code has been extensively reworked, and should be
POSIX as well as clean.
- dosfs is upgraded to version 12 (Werner Almesberger)
- xiafs is upgraded to the latest version (Qi Xia)
- ext2fs is upgraded to the latest version (Remy Card/Stephen Tweedie)
- FPU-emulation patches for v86 mode and precision rounding (Bill
Metzenthen)
- SCSI patches by various people (Eric Youngdale & co)
- XT harddisk support (Pat Mackinlay)
- new trial code to try to handle 387 lockups on some systems more
gracefully.
- keyboard, lp and serial driver fixes
- various minor changes (mounting root read-only, bootup messages
cleaned up etc)
As always, comments/bugs etc are encouraged,
Linus
87 files changed, 1810 insertions, 1095 deletions
@@ -4,6 +4,7 @@ # It's a fast hack - feel free to do something better. CONFIG=.config~ CONFIG_H=include/linux/autoconf.h +> config.new echo "#" > $CONFIG echo "# Automatically generated make config: don't edit" >> $CONFIG echo "#" >> $CONFIG @@ -17,6 +18,7 @@ old="y" while read i do + echo $i >> config.new echo >> $CONFIG echo >> $CONFIG_H echo @@ -29,11 +31,13 @@ do echo " * "$i >> $CONFIG_H echo "**" $i read i || break + echo $i >> config.new done echo "#" >> $CONFIG echo " */" >> $CONFIG_H echo "**" read i || break + echo $i >> config.new while [ "$i" != "." -a "$i" != ":" ] do read j ques def || break @@ -49,6 +53,7 @@ do ans=$def fi fi + echo $j $ques $ans >> config.new if [ "$ans" = "y" ] then echo $j = $j >> $CONFIG @@ -56,6 +61,7 @@ do next="y"; fi read i || break + echo $i >> config.new done old=$next next="y" @@ -65,6 +71,8 @@ do fi done +mv config.new config.in + echo echo "The linux kernel is now hopefully configured for your setup." echo "Check the top-level Makefile for additional configuration," @@ -139,7 +139,7 @@ tools/./version.h: tools/version.h tools/version.h: $(CONFIGURE) Makefile @./makever.sh - @echo \#define UTS_RELEASE \"0.99.pl7A-`cat .version`\" > tools/version.h + @echo \#define UTS_RELEASE \"0.99.pl8-`cat .version`\" > tools/version.h @echo \#define UTS_VERSION \"`date +%D`\" >> tools/version.h @echo \#define LINUX_COMPILE_TIME \"`date +%T`\" >> tools/version.h @echo \#define LINUX_COMPILE_BY \"`whoami`\" >> tools/version.h diff --git a/boot/bootsect.S b/boot/bootsect.S index 7655cff..610acac 100644 --- a/boot/bootsect.S +++ b/boot/bootsect.S @@ -45,6 +45,9 @@ SWAP_DEV = 0 #ifndef RAMDISK #define RAMDISK 0 #endif +#ifndef CONFIG_ROOT_RDONLY +#define CONFIG_ROOT_RDONLY 0 +#endif ! ld86 requires an entry symbol. This may as well be the usual one. .globl _main @@ -429,7 +432,9 @@ msg1: .byte 13,10 .ascii "Loading" -.org 500 +.org 498 +root_flags: + .word CONFIG_ROOT_RDONLY syssize: .word SYSSIZE swap_dev: @@ -11,7 +11,7 @@ CONFIG_TCPIP y/n y Kernel profiling support CONFIG_PROFILE y/n n Limit memory to low 16MB -CONFIG_MAX_16M y/n y +CONFIG_MAX_16M y/n n Use -m486 flag for 486-specific optimizations CONFIG_M486 y/n y : @@ -23,29 +23,31 @@ CONFIG_SCSI y/n n SCSI support type (disk, tape, CDrom) . Scsi disk support -CONFIG_BLK_DEV_SD y/n y +CONFIG_BLK_DEV_SD y/n n Scsi tape support -CONFIG_BLK_DEV_ST y/n y +CONFIG_BLK_DEV_ST y/n n Scsi CDROM support -CONFIG_BLK_DEV_SR y/n y +CONFIG_BLK_DEV_SR y/n n . SCSI low-level drivers . Adaptec AHA1542 support -CONFIG_SCSI_AHA1542 y/n y +CONFIG_SCSI_AHA1542 y/n n Adaptec AHA1740 support -CONFIG_SCSI_AHA1740 y/n y +CONFIG_SCSI_AHA1740 y/n n Future Domain SCSI support -CONFIG_SCSI_FUTURE_DOMAIN y/n y +CONFIG_SCSI_FUTURE_DOMAIN y/n n Seagate ST-02 and Future Domain TMC-8xx SCSI support -CONFIG_SCSI_SEAGATE y/n y +CONFIG_SCSI_SEAGATE y/n n UltraStor SCSI support -CONFIG_SCSI_ULTRASTOR y/n y +CONFIG_SCSI_ULTRASTOR y/n n 7000FASST SCSI support -CONFIG_SCSI_7000FASST y/n y +CONFIG_SCSI_7000FASST y/n n . Filesystems . +Mount root initially readonly +CONFIG_ROOT_RDONLY y/n n Standard (minix) fs support CONFIG_MINIX_FS y/n y Extended fs support @@ -550,7 +550,7 @@ restart_interp: p = copy_strings(1, &i_name, page, p, 2); argc++; if (!p) { - retval = -ENOMEM; + retval = -E2BIG; goto exec_error1; } /* @@ -578,7 +578,7 @@ restart_interp: p = copy_strings(envc,envp,page,p,0); p = copy_strings(argc,argv,page,p,0); if (!p) { - retval = -ENOMEM; + retval = -E2BIG; goto exec_error2; } } diff --git a/fs/ext/inode.c b/fs/ext/inode.c index 33942a1..ff8efc1 100644 --- a/fs/ext/inode.c +++ b/fs/ext/inode.c @@ -53,7 +53,8 @@ static struct super_operations ext_sops = { ext_statfs }; -struct super_block *ext_read_super(struct super_block *s,void *data) +struct super_block *ext_read_super(struct super_block *s,void *data, + int silent) { struct buffer_head *bh; struct ext_super_block *es; @@ -82,7 +83,9 @@ struct super_block *ext_read_super(struct super_block *s,void *data) if (s->s_magic != EXT_SUPER_MAGIC) { s->s_dev = 0; unlock_super(s); - printk("EXT-fs: magic match failed\n"); + if (!silent) + printk("VFS: Can't find an extfs filesystem on dev 0x%04x.\n", + dev); return NULL; } if (!s->u.ext_sb.s_firstfreeblocknumber) @@ -151,6 +154,7 @@ void ext_statfs (struct super_block *sb, struct statfs *buf) put_fs_long(tmp, &buf->f_bavail); put_fs_long(sb->u.ext_sb.s_ninodes, &buf->f_files); put_fs_long(ext_count_free_inodes(sb), &buf->f_ffree); + put_fs_long(EXT_NAME_LEN, &buf->f_namelen); /* Don't know what value to put in buf->f_fsid */ } @@ -375,14 +379,8 @@ void ext_read_inode(struct inode * inode) inode->i_op = &chrdev_inode_operations; else if (S_ISBLK(inode->i_mode)) inode->i_op = &blkdev_inode_operations; - else if (S_ISFIFO(inode->i_mode)) { - inode->i_op = &fifo_inode_operations; - inode->i_pipe = 1; - PIPE_BASE(*inode) = NULL; - PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0; - PIPE_READ_WAIT(*inode) = PIPE_WRITE_WAIT(*inode) = NULL; - PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; - } + else if (S_ISFIFO(inode->i_mode)) + init_fifo(inode); } void ext_write_inode(struct inode * inode) diff --git a/fs/ext/namei.c b/fs/ext/namei.c index 62045f1..49e22d3 100644 --- a/fs/ext/namei.c +++ b/fs/ext/namei.c @@ -372,14 +372,8 @@ int ext_mknod(struct inode * dir, const char * name, int len, int mode, int rdev inode->i_op = &chrdev_inode_operations; else if (S_ISBLK(inode->i_mode)) inode->i_op = &blkdev_inode_operations; - else if (S_ISFIFO(inode->i_mode)) { - inode->i_op = &fifo_inode_operations; - inode->i_pipe = 1; - PIPE_BASE(*inode) = NULL; - PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0; - PIPE_READ_WAIT(*inode) = PIPE_WRITE_WAIT(*inode) = NULL; - PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; - } + else if (S_ISFIFO(inode->i_mode)) + init_fifo(inode); if (S_ISBLK(mode) || S_ISCHR(mode)) inode->i_rdev = rdev; inode->i_mtime = inode->i_atime = CURRENT_TIME; diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 6ea0c62..a080f44 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -3,6 +3,7 @@ * * Copyright (C) 1992, 1993 Remy Card (card@masi.ibp.fr) * + * Enhanced block allocation by Stephen Tweedie (sct@dcs.ed.ac.uk), 1993 */ /* balloc.c contains the blocks allocation and deallocation routines */ @@ -27,6 +28,8 @@ #include <linux/string.h> #include <linux/locks.h> +#include <asm/bitops.h> + #define clear_block(addr,size) \ __asm__("cld\n\t" \ "rep\n\t" \ @@ -35,44 +38,66 @@ :"a" (0), "c" (size/4), "D" ((long) (addr)) \ :"cx", "di") -#define set_bit(nr,addr) ( \ -{ \ - char res; \ - __asm__ __volatile__("btsl %1,%2\n\tsetb %0" \ - :"=q" (res) \ - :"r" (nr),"m" (*(addr))); \ - res; \ -} \ -) +static inline int find_first_zero_bit(unsigned *addr, unsigned size) +{ + int res; + if (!size) + return 0; + __asm__(" + cld + movl $-1,%%eax + repe; scasl + je 1f + subl $4,%%edi + movl (%%edi),%%eax + notl %%eax + bsfl %%eax,%%edx + jmp 2f +1: xorl %%edx,%%edx +2: subl %%ebx,%%edi + shll $3,%%edi + addl %%edi,%%edx" + :"=d" (res):"c" ((size+31)>>5), "D" (addr), "b" (addr) + :"ax", "bx", "cx", "di"); + return res; +} -#define clear_bit(nr,addr) ( \ -{ \ - char res; \ - __asm__ __volatile__("btrl %1,%2\n\tsetnb %0" \ - :"=q" (res) \ - :"r" (nr),"m" (*(addr))); \ - res; \ -} \ -) +static inline int find_next_zero_bit(unsigned *addr, int size, int offset) +{ + unsigned *p = ((unsigned *) addr) + (offset >> 5); + int set = 0, bit = offset & 31, res; + + if (bit) { + /* Look for zero in first byte */ + __asm__(" + bsfl %1,%0 + jne 1f + movl $32, %0 +1: " : "=r" (set) : "r" (~(*p >> bit))); + if (set < (32-bit)) + return set + offset; + set = 32-bit; + p++; + } + /* No zero yet, search remaining full bytes for a zero */ + res = find_first_zero_bit(p, size-32*(p-addr)); + return (offset + set + res); +} -#define find_first_zero(addr,size) ( \ -{ \ - int __res; \ - __asm__("cld\n" \ - "1:\tlodsl\n\t" \ - "notl %%eax\n\t" \ - "bsfl %%eax,%%edx\n\t" \ - "jne 2f\n\t" \ - "addl $32,%%ecx\n\t" \ - "cmpl %%ebx,%%ecx\n\t" \ - "jl 1b\n\t" \ - "xorl %%edx,%%edx\n" \ - "2:\taddl %%edx,%%ecx" \ - :"=c" (__res):"0" (0), "S" (addr), "b" (size) \ - :"ax", "bx", "dx", "si"); \ - __res; \ -} \ -) +static inline char * find_first_zero_byte(char *addr,int size) +{ + char *res; + if (!size) + return 0; + __asm__(" + cld + mov $0,%%eax + repnz; scasb + jnz 1f + dec %%edi +1: " : "=D" (res) : "0" (addr), "c" (size) : "ax"); + return res; +} static void read_block_bitmap (struct super_block * sb, unsigned int block_group, @@ -90,11 +115,14 @@ static void read_block_bitmap (struct super_block * sb, block_group, group_desc, desc); panic ("read_block_bitmap: Group descriptor not loaded"); } - gdp = (struct ext2_group_desc *) sb->u.ext2_sb.s_group_desc[group_desc]->b_data; + gdp = (struct ext2_group_desc *) + sb->u.ext2_sb.s_group_desc[group_desc]->b_data; bh = bread (sb->s_dev, gdp[desc].bg_block_bitmap, sb->s_blocksize); if (!bh) { - printk ("block_group = %d,group_desc = %d,desc = %d,block_bitmap = %d\n", - block_group, group_desc, desc, gdp[desc].bg_block_bitmap); + printk ("block_group = %d,group_desc = %d," + "desc = %d,block_bitmap = %d\n", + block_group, group_desc, desc, + gdp[desc].bg_block_bitmap); panic ("read_block_bitmap: Cannot read block bitmap"); } sb->u.ext2_sb.s_block_bitmap_number[bitmap_nr] = block_group; @@ -112,8 +140,8 @@ static void read_block_bitmap (struct super_block * sb, * 2/ If the file system contains less than EXT2_MAX_GROUP_LOADED groups, * this function reads the bitmap without maintaining a LRU cache. */ -static int load_block_bitmap (struct super_block * sb, - unsigned int block_group) +static int load__block_bitmap (struct super_block * sb, + unsigned int block_group) { int i, j; unsigned long block_bitmap_number; @@ -124,14 +152,13 @@ static int load_block_bitmap (struct super_block * sb, block_group, sb->u.ext2_sb.s_groups_count); panic ("load_block_bitmap: block_group >= groups_count"); } - if (sb->u.ext2_sb.s_loaded_block_bitmaps > 0 && - sb->u.ext2_sb.s_block_bitmap_number[0] == block_group) - return 0; if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED) { if (sb->u.ext2_sb.s_block_bitmap[block_group]) { - if (sb->u.ext2_sb.s_block_bitmap_number[block_group] != block_group) - panic ("load_block_bitmap: block_group != block_bitmap_number"); + if (sb->u.ext2_sb.s_block_bitmap_number[block_group] + != block_group) + panic ("load_block_bitmap: " + "block_group != block_bitmap_number"); else return block_group; } else { @@ -156,11 +183,13 @@ static int load_block_bitmap (struct super_block * sb, sb->u.ext2_sb.s_block_bitmap_number[0] = block_bitmap_number; sb->u.ext2_sb.s_block_bitmap[0] = block_bitmap; } else { - if (sb->u.ext2_sb.s_loaded_block_bitmaps < EXT2_MAX_GROUP_LOADED) + if (sb->u.ext2_sb.s_loaded_block_bitmaps < + EXT2_MAX_GROUP_LOADED) sb->u.ext2_sb.s_loaded_block_bitmaps++; else - brelse (sb->u.ext2_sb.s_block_bitmap[EXT2_MAX_GROUP_LOADED - 1]); - for (j = sb->u.ext2_sb.s_loaded_block_bitmaps - 1; j > 0; j--) { + brelse (sb->u.ext2_sb.s_block_bitmap + [EXT2_MAX_GROUP_LOADED - 1]); + for (j = sb->u.ext2_sb.s_loaded_block_bitmaps-1; j>0; j--) { sb->u.ext2_sb.s_block_bitmap_number[j] = sb->u.ext2_sb.s_block_bitmap_number[j - 1]; sb->u.ext2_sb.s_block_bitmap[j] = @@ -171,6 +200,21 @@ static int load_block_bitmap (struct super_block * sb, return 0; } +static inline int load_block_bitmap (struct super_block * sb, + unsigned int block_group) +{ + if (sb->u.ext2_sb.s_loaded_block_bitmaps > 0 && + sb->u.ext2_sb.s_block_bitmap_number[0] == block_group) + return 0; + + if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED && + sb->u.ext2_sb.s_block_bitmap_number[block_group] == block_group && + sb->u.ext2_sb.s_block_bitmap[block_group]) + return block_group; + + return load__block_bitmap (sb, block_group); +} + void ext2_free_block (struct super_block * sb, unsigned long block) { struct buffer_head * bh; @@ -236,20 +280,27 @@ void ext2_free_block (struct super_block * sb, unsigned long block) } /* - * ext2_new_block does not use a very clever allocation algorithm yet - * - * Currently, the group descriptors are scanned until a free block is found + * ext2_new_block uses a goal block to assist allocation. If the goal is + * free, or there is a free block within 32 blocks of the goal, that block + * is allocated. Otherwise a forward search is made for a free block; within + * each block group the search first looks for an entire free byte in the block + * bitmap, and then for any free bit if that fails. */ -int ext2_new_block (struct super_block * sb, unsigned long block_group) +int ext2_new_block (struct super_block * sb, unsigned long goal) { struct buffer_head * bh; - int i, j; + char *p, *r; + int i, j, k; + unsigned long lmap; unsigned long group_desc; unsigned long desc; int bitmap_nr; struct ext2_group_desc * gdp; struct ext2_super_block * es; +#ifdef EXT2FS_DEBUG + static int goal_hits = 0, goal_attempts = 0; +#endif if (!sb) { printk ("ext2_new_block: nonexistant device"); return 0; @@ -260,49 +311,158 @@ int ext2_new_block (struct super_block * sb, unsigned long block_group) unlock_super (sb); return 0; } + +#ifdef EXT2FS_DEBUG + printk ("ext2_new_block: goal=%d.\n", goal); +#endif repeat: - group_desc = 0; - desc = 0; - gdp = NULL; - for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) { + /* First, test whether the goal block is free. */ + i = ((goal - sb->u.ext2_sb.s_first_data_block) / + EXT2_BLOCKS_PER_GROUP(sb)); + group_desc = i / EXT2_DESC_PER_BLOCK(sb); + desc = i % EXT2_DESC_PER_BLOCK(sb); + gdp = (struct ext2_group_desc *) + sb->u.ext2_sb.s_group_desc[group_desc]->b_data; + if (!gdp) { + panic ("ext2_new_block: Descriptor not loaded"); + } + if (gdp[desc].bg_free_blocks_count > 0) { + j = ((goal - sb->u.ext2_sb.s_first_data_block) % + EXT2_BLOCKS_PER_GROUP(sb)); +#ifdef EXT2FS_DEBUG + if (j) + goal_attempts++; +#endif + bitmap_nr = load_block_bitmap (sb, i); + bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr]; + if (!bh) { + printk ("Cannot load bitmap_nr %d.\n", + bitmap_nr); + unlock_super (sb); + return 0; + } +#ifdef EXT2FS_DEBUG + printk ("goal is at %d[%d,%d]:%d.\n", + i, group_desc, desc, j); +#endif + if (!test_bit(j, bh->b_data)) { +#ifdef EXT2FS_DEBUG + goal_hits++; + printk ("ext2_new_block: goal bit allocated.\n"); +#endif + goto got_block; + } + if (j) { + /* The goal was occupied; search forward for a free + block within the next 32 blocks */ + lmap = (((((unsigned long *) bh->b_data)[j >> 5]) + >> ((j&31)+1)) | + ((((unsigned long *) bh->b_data)[(j>>5)+1]) + <<(31-(j&31)))); + if (lmap != 0xffffffffl) { + __asm__ ("bsfl %1,%0" : + "=r" (k) : + "r" (~lmap)); k++; + if ((j + k) < EXT2_BLOCKS_PER_GROUP(sb)) { + j += k; + goto got_block; + } + } + } + +#ifdef EXT2FS_DEBUG + printk ("Bit not found near goal\n"); +#endif + /* There has been no free block found in the near vicinity + of the goal: do a search forward through the block groups, + searching in each group first for an entire free byte in + the bitmap and then for any free bit. + + Search first in the remainder of the current group; then, + cyclicly search throught the rest of the groups. */ + p = ((char *) bh->b_data) + (j>>3); + r = find_first_zero_byte (p, + (EXT2_BLOCKS_PER_GROUP(sb)-j+7)>>3); + k = (r - ((char *) bh->b_data)) << 3; + if (k < EXT2_BLOCKS_PER_GROUP(sb)) { + j = k; + goto got_block; + } + k = find_next_zero_bit ((unsigned long *) bh->b_data, + EXT2_BLOCKS_PER_GROUP(sb), + j); + if (k < EXT2_BLOCKS_PER_GROUP(sb)) { + j = k; + goto got_block; + } + } + +#ifdef EXT2FS_DEBUG + printk ("Bit not found in block group %d.\n", i); +#endif + /* Now search the rest of the groups. We assume that group_desc, desc, + i and gdp correctly point to the last group visited. */ + for (k = 0; k < sb->u.ext2_sb.s_groups_count; k++) { + i++; + if (i >= sb->u.ext2_sb.s_groups_count) { + i = 0; + group_desc = 0; + desc = 0; + gdp = (struct ext2_group_desc *) + sb->u.ext2_sb.s_group_desc[group_desc]->b_data; + } + else { + desc++; + if (desc >= EXT2_DESC_PER_BLOCK(sb)) { + group_desc++; + desc = 0; + gdp = (struct ext2_group_desc *) + sb->u.ext2_sb.s_group_desc[group_desc] + ->b_data; + } + } if (!gdp) { - if (!sb->u.ext2_sb.s_group_desc[group_desc]) - panic ("ext2_new_block: Descriptor not loaded"); - gdp = (struct ext2_group_desc *) sb->u.ext2_sb.s_group_desc[group_desc]->b_data; + panic ("ext2_new_block: Descriptor not loaded"); } if (gdp[desc].bg_free_blocks_count > 0) break; - desc ++; - if (desc == EXT2_DESC_PER_BLOCK(sb)) { - group_desc ++; - desc = 0; - gdp = NULL; - } } - if (i >= sb->u.ext2_sb.s_groups_count) { + if (k >= sb->u.ext2_sb.s_groups_count) { unlock_super (sb); return 0; } -#ifdef EXT2FS_DEBUG - printk ("ext2_new_block: using block group %d(%d,%d,%d)\n", - i, group_desc, desc, gdp[desc].bg_free_blocks_count); -#endif bitmap_nr = load_block_bitmap (sb, i); bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr]; if (!bh) { printk ("block_group = %d\n", i); panic ("ext2_new_block: Unable to load group bitmap"); } - if ((j = find_first_zero (bh->b_data, EXT2_BLOCKS_PER_GROUP(sb))) < - EXT2_BLOCKS_PER_GROUP(sb)) { - if (set_bit (j, bh->b_data)) { - printk ("ext2_new_block: bit already set\n"); - goto repeat; - } - bh->b_dirt = 1; - } else + r = find_first_zero_byte (bh->b_data, + EXT2_BLOCKS_PER_GROUP(sb) >> 3); + j = (r-bh->b_data) << 3; + if (j >= EXT2_BLOCKS_PER_GROUP(sb)) + j = find_first_zero_bit ((unsigned long *) bh->b_data, + EXT2_BLOCKS_PER_GROUP(sb)); + if (j >= EXT2_BLOCKS_PER_GROUP(sb)) { + printk ("ext2_new_block: " + "Unable to locate free bit in block group %d.\n",i); + unlock_super (sb); + return 0; + } + +got_block: +#ifdef EXT2FS_DEBUG + printk ("ext2_new_block: using block group %d(%d,%d,%d)\n", + i, group_desc, desc, gdp[desc].bg_free_blocks_count); +#endif + + if (set_bit (j, bh->b_data)) { + printk ("ext2_new_block: bit already set\n"); goto repeat; + } + bh->b_dirt = 1; + #ifdef EXT2FS_DEBUG printk ("ext2_new_block: found bit %d\n", j); #endif @@ -311,6 +471,7 @@ repeat: if (j >= sb->u.ext2_sb.s_blocks_count) { printk ("block_group = %d,block=%d\n", i, j); printk ("ext2_new_block: block >= blocks count"); + unlock_super (sb); return 0; } if (!(bh = getblk (sb->s_dev, j, sb->s_blocksize))) { @@ -323,7 +484,8 @@ repeat: bh->b_dirt = 1; brelse (bh); #ifdef EXT2FS_DEBUG - printk("ext2_new_block: allocating block %d\n", j); + printk("ext2_new_block: allocating block %d. " + "Goal hits %d of %d.\n", j, goal_hits, goal_attempts); #endif gdp[desc].bg_free_blocks_count --; sb->u.ext2_sb.s_group_desc[group_desc]->b_dirt = 1; @@ -344,7 +506,7 @@ unsigned long ext2_count_free_blocks (struct super_block *sb) int bitmap_nr; struct ext2_group_desc * gdp; int i; - + lock_super (sb); es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data; desc_count = 0; @@ -355,16 +517,19 @@ unsigned long ext2_count_free_blocks (struct super_block *sb) for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) { if (!gdp) { if (!sb->u.ext2_sb.s_group_desc[group_desc]) { - printk ("ext2_count_free_block: Descriptor not loaded\n"); + printk ("ext2_count_free_block: " + "Descriptor not loaded\n"); break; } - gdp = (struct ext2_group_desc *) sb->u.ext2_sb.s_group_desc[group_desc]->b_data; + gdp = (struct ext2_group_desc *) + sb->u.ext2_sb.s_group_desc[group_desc]->b_data; } desc_count += gdp[desc].bg_free_blocks_count; bitmap_nr = load_block_bitmap (sb, i); if (sb->u.ext2_sb.s_block_bitmap[bitmap_nr]) - x = ext2_count_free (sb->u.ext2_sb.s_block_bitmap[bitmap_nr], - sb->s_blocksize); + x = ext2_count_free + (sb->u.ext2_sb.s_block_bitmap[bitmap_nr], + sb->s_blocksize); else { x = 0; printk ("Cannot load bitmap for group %d\n", i); @@ -380,7 +545,7 @@ unsigned long ext2_count_free_blocks (struct super_block *sb) } } printk("ext2_count_free_blocks: stored = %d, computed = %d, %d\n", - es->s_free_blocks_count, desc_count, bitmap_count); + es->s_free_blocks_count, desc_count, bitmap_count); unlock_super (sb); return bitmap_count; #else diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index 0c7f9c1..937d1b2 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -65,6 +65,31 @@ struct inode_operations ext2_dir_inode_operations = { NULL /* permission */ }; +int ext2_check_dir_entry (char * function, struct inode * dir, + struct ext2_dir_entry * de, struct buffer_head * bh, + unsigned int offset) +{ + char * error_msg = NULL; + + if (de->rec_len < EXT2_DIR_REC_LEN(1)) + error_msg = "rec_len is smaller than minimal"; + else if (de->rec_len % 4 != 0) + error_msg = "rec_len % 4 != 0"; + else if (de->rec_len < EXT2_DIR_REC_LEN(de->name_len)) + error_msg = "rec_len is too small for name_len"; + else if (((char *) de - bh->b_data) + de->rec_len > + dir->i_sb->s_blocksize) + error_msg = "directory entry accross blocks"; + + if (error_msg != NULL) { + printk ("%s: bad directory entry (dev %04x, dir %d): %s\n", + function, dir->i_dev, dir->i_ino, error_msg); + printk ("offset=%d, inode=%d, rec_len=%d, name_len=%d\n", + offset, de->inode, de->rec_len, de->name_len); + } + return error_msg == NULL ? 1 : 0; +} + static int ext2_readdir (struct inode * inode, struct file * filp, struct dirent * dirent, int count) { @@ -85,13 +110,8 @@ static int ext2_readdir (struct inode * inode, struct file * filp, } de = (struct ext2_dir_entry *) (offset + bh->b_data); while (offset < sb->s_blocksize && filp->f_pos < inode->i_size) { - if (de->rec_len < EXT2_DIR_REC_LEN(1) || - de->rec_len % 4 != 0 || - de->rec_len < EXT2_DIR_REC_LEN(de->name_len)) { - printk ("ext2_readdir: bad directory entry (dev %04x, dir %d)\n", - inode->i_dev, inode->i_ino); - printk ("offset=%d, inode=%d, rec_len=%d, name_len=%d\n", - offset, de->inode, de->rec_len, de->name_len); + if (! ext2_check_dir_entry ("ext2_readdir", inode, de, + bh, offset)) { brelse (bh); return 0; } diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index 017e5ee..e0df46f 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -1,8 +1,11 @@ + /* * linux/fs/ext2/ialloc.c * * Copyright (C) 1992, 1993 Remy Card (card@masi.ibp.fr) * + * BSD ufs-inspired inode and directory allocation by + * Stephen Tweedie (sct@dcs.ed.ac.uk), 1993 */ /* ialloc.c contains the inodes allocation and deallocation routines */ @@ -27,45 +30,31 @@ #include <linux/string.h> #include <linux/locks.h> -#define set_bit(nr,addr) ( \ -{ \ - char res; \ - __asm__ __volatile__("btsl %1,%2\n\tsetb %0" \ - :"=q" (res) \ - :"r" (nr),"m" (*(addr))); \ - res; \ -} \ -) - -#define clear_bit(nr,addr) ( \ -{ \ - char res; \ - __asm__ __volatile__("btrl %1,%2\n\tsetnb %0" \ - :"=q" (res) \ - :"r" (nr),"m" (*(addr))); \ - res; \ -} \ -) - +#include <asm/bitops.h> -#define find_first_zero(addr,size) ( \ -{ \ - int __res; \ - __asm__("cld\n" \ - "1:\tlodsl\n\t" \ - "notl %%eax\n\t" \ - "bsfl %%eax,%%edx\n\t" \ - "jne 2f\n\t" \ - "addl $32,%%ecx\n\t" \ - "cmpl %%ebx,%%ecx\n\t" \ - "jl 1b\n\t" \ - "xorl %%edx,%%edx\n" \ - "2:\taddl %%edx,%%ecx" \ - :"=c" (__res):"0" (0), "S" (addr), "b" (size) \ - :"ax", "bx", "dx", "si"); \ - __res; \ -} \ -) +static inline int find_first_zero_bit(unsigned *addr, unsigned size) +{ + int res; + if (!size) + return 0; + __asm__(" + cld + movl $-1,%%eax + repe; scasl + je 1f + subl $4,%%edi + movl (%%edi),%%eax + notl %%eax + bsfl %%eax,%%edx + jmp 2f +1: xorl %%edx,%%edx +2: subl %%ebx,%%edi + shll $3,%%edi + addl %%edi,%%edx" + :"=d" (res):"c" ((size+31)>>5), "D" (addr), "b" (addr) + :"ax", "bx", "cx", "di"); + return res; +} static void read_inode_bitmap (struct super_block * sb, unsigned long block_group, @@ -278,13 +267,13 @@ void ext2_free_inode (struct inode * inode) */ static void inc_inode_version (struct inode * inode, struct ext2_group_desc *gdp, - unsigned long desc) + int mode) { unsigned long inode_block; struct buffer_head * bh; struct ext2_inode * raw_inode; - inode_block = gdp[desc].bg_inode_table + (((inode->i_ino - 1) % + inode_block = gdp->bg_inode_table + (((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb)) / EXT2_INODES_PER_BLOCK(inode->i_sb)); bh = bread (inode->i_sb->s_dev, inode_block, inode->i_sb->s_blocksize); @@ -300,26 +289,44 @@ static void inc_inode_version (struct inode * inode, EXT2_INODES_PER_GROUP(inode->i_sb)) % EXT2_INODES_PER_BLOCK(inode->i_sb)); raw_inode->i_version++; - inode->u.ext2_i.i_version = raw_inode->i_version; + if (!S_ISFIFO(mode)) + inode->u.ext2_i.i_version = raw_inode->i_version; bh->b_dirt = 1; brelse (bh); } +static struct ext2_group_desc * +get_group_desc(struct super_block *sb, int group) +{ + struct ext2_group_desc * gdp; + if (group >= sb->u.ext2_sb.s_groups_count || group < 0 ) + panic ("ext2: get_group_desc: Invalid group\n"); + if (!sb->u.ext2_sb.s_group_desc[group / EXT2_DESC_PER_BLOCK(sb)]) + panic ("ext2: get_group_desc: Descriptor not loaded"); + gdp = (struct ext2_group_desc *) + sb->u.ext2_sb.s_group_desc[group / EXT2_DESC_PER_BLOCK(sb)] + ->b_data; + return gdp + (group % EXT2_DESC_PER_BLOCK(sb)); +} + /* - * ext2_new_inode does not use a very clever algorithm yet + * There are two policies for allocating an inode. If the new inode is + * a directory, then a forward search is made for a block group with both + * free space and a low directory-to-inode ratio; if that fails, then of + * the groups with above-average free space, that group with the fewest + * directories already is chosen. * - * Currently, the group descriptors are scanned until a free block is found + * For other inodes, search forward from the parent directory\'s block + * group to find a free inode. */ struct inode * ext2_new_inode (const struct inode * dir, int mode) { struct super_block * sb; struct buffer_head * bh; - int i, j; + int i, j, avefreei; struct inode * inode; - unsigned long group_desc; - unsigned long desc; int bitmap_nr; - struct ext2_group_desc * gdp; + struct ext2_group_desc * gdp, * tmp; struct ext2_super_block * es; if (!dir || !(inode = get_empty_inode ())) @@ -330,25 +337,73 @@ struct inode * ext2_new_inode (const struct inode * dir, int mode) lock_super (sb); es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data; repeat: - group_desc = 0; - desc = 0; - gdp = NULL; - for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) { + gdp = NULL; i=0; + + if (S_ISDIR(mode)) { + avefreei = es->s_free_inodes_count / + sb->u.ext2_sb.s_groups_count; +/* I am not yet convinced that this next bit is necessary. + i = dir->u.ext2_i.i_block_group; + for (j = 0; j < sb->u.ext2_sb.s_groups_count; j++) { + tmp = get_group_desc(sb, i); + if ((tmp->bg_used_dirs_count << 8) < + tmp->bg_free_inodes_count) { + gdp = tmp; + break; + } + else + i = ++i % sb->u.ext2_sb.s_groups_count; + } +*/ if (!gdp) { - if (!sb->u.ext2_sb.s_group_desc[group_desc]) - panic ("ext2_new_inode: Descriptor not loaded"); - gdp = (struct ext2_group_desc *) sb->u.ext2_sb.s_group_desc[group_desc]->b_data; + for (j = 0; j < sb->u.ext2_sb.s_groups_count; j++) { + tmp = get_group_desc(sb, j); + if (tmp->bg_free_inodes_count >= avefreei) { + if (!gdp || + (tmp->bg_free_inodes_count > + gdp->bg_free_inodes_count)) { + i = j; + gdp = tmp; + } + } + } } - if (gdp[desc].bg_free_inodes_count > 0) - break; - desc ++; - if (desc == EXT2_DESC_PER_BLOCK(sb)) { - group_desc ++; - desc = 0; - gdp = NULL; + } + else + { /* Try to place the inode in it\'s parent directory */ + i = dir->u.ext2_i.i_block_group; + tmp = get_group_desc(sb, i); + if (tmp->bg_free_inodes_count) + gdp = tmp; + else + { /* Use a quadratic hash to find a group with a free inode */ + for (j=1; j<sb->u.ext2_sb.s_groups_count; j<<=1) { + i+=j; + if (i>=sb->u.ext2_sb.s_groups_count) + i-=sb->u.ext2_sb.s_groups_count; + tmp = get_group_desc(sb,i); + if (tmp->bg_free_inodes_count) { + gdp = tmp; + break; + } + } + } + if (!gdp) { + /* That failed: try linear search for a free inode */ + i = dir->u.ext2_i.i_block_group + 2; + for (j=2; j<sb->u.ext2_sb.s_groups_count; j++) { + if (++i > sb->u.ext2_sb.s_groups_count) + i=0; + tmp = get_group_desc(sb,i); + if (tmp->bg_free_inodes_count) { + gdp = tmp; + break; + } + } } } - if (i >= sb->u.ext2_sb.s_groups_count) { + + if (!gdp) { unlock_super (sb); return NULL; } @@ -358,7 +413,8 @@ repeat: printk ("block_group = %d\n", i); panic ("ext2_new_inode: Unable to load group inode bitmap"); } - if ((j = find_first_zero (bh->b_data, EXT2_INODES_PER_GROUP(sb))) < + if ((j = find_first_zero_bit ((unsigned long *) bh->b_data, + EXT2_INODES_PER_GROUP(sb))) < EXT2_INODES_PER_GROUP(sb)) { if (set_bit (j, bh->b_data)) { printk ("ext2_new_inode: bit already set\n"); @@ -373,10 +429,10 @@ repeat: printk ("ext2_new_inode: inode > inodes count"); return NULL; } - gdp[desc].bg_free_inodes_count --; + gdp->bg_free_inodes_count --; if (S_ISDIR(mode)) - gdp[desc].bg_used_dirs_count ++; - sb->u.ext2_sb.s_group_desc[group_desc]->b_dirt = 1; + gdp->bg_used_dirs_count ++; + sb->u.ext2_sb.s_group_desc[i / EXT2_DESC_PER_BLOCK(sb)]->b_dirt = 1; es->s_free_inodes_count --; sb->u.ext2_sb.s_sbh->b_dirt = 1; sb->s_dirt = 1; @@ -398,9 +454,10 @@ repeat: inode->u.ext2_i.i_file_acl = 0; inode->u.ext2_i.i_dir_acl = 0; inode->u.ext2_i.i_dtime = 0; - inode->u.ext2_i.i_block_group = i; + if (!S_ISFIFO(mode)) + inode->u.ext2_i.i_block_group = i; inode->i_op = NULL; - inc_inode_version (inode, gdp, desc); + inc_inode_version (inode, gdp, mode); #ifdef EXT2FS_DEBUG printk ("ext2_new_inode : allocating inode %d\n", inode->i_ino); #endif diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 7086c1d..0451fb1 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -8,6 +8,8 @@ * linux/fs/minix/inode.c * * Copyright (C) 1991, 1992 Linus Torvalds + * + * Goal-directed block allocation by Stephen Tweedie (sct@dcs.ed.ac.uk), 1993 */ #include <linux/sched.h> @@ -38,7 +40,7 @@ void ext2_put_super (struct super_block * sb) lock_super (sb); es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data; - es->s_valid = 1; + es->s_valid = sb->u.ext2_sb.s_was_mounted_valid; sb->u.ext2_sb.s_sbh->b_dirt = 1; #ifndef DONT_USE_DCACHE ext2_dcache_invalidate (sb->s_dev); @@ -105,7 +107,8 @@ static int convert_pre_02b_fs (struct super_block * sb, return 1; } -struct super_block * ext2_read_super (struct super_block * s, void * data) +struct super_block * ext2_read_super (struct super_block * s, void * data, + int silent) { struct buffer_head * bh; struct ext2_super_block * es; @@ -122,6 +125,7 @@ struct super_block * ext2_read_super (struct super_block * s, void * data) return NULL; } es = (struct ext2_super_block *) bh->b_data; + s->s_magic = es->s_magic; s->s_blocksize = EXT2_MIN_BLOCK_SIZE << es->s_log_block_size; s->u.ext2_sb.s_inodes_count = es->s_inodes_count; s->u.ext2_sb.s_blocks_count = es->s_blocks_count; @@ -131,8 +135,11 @@ struct super_block * ext2_read_super (struct super_block * s, void * data) s->u.ext2_sb.s_log_frag_size = es->s_log_frag_size; s->u.ext2_sb.s_frag_size = EXT2_MIN_FRAG_SIZE << es->s_log_frag_size; - s->u.ext2_sb.s_frags_per_block = s->s_blocksize / + if (s->u.ext2_sb.s_frag_size) + s->u.ext2_sb.s_frags_per_block = s->s_blocksize / s->u.ext2_sb.s_frag_size; + else + s->s_magic = 0; s->u.ext2_sb.s_blocks_per_group = es->s_blocks_per_group; s->u.ext2_sb.s_frags_per_group = es->s_frags_per_group; s->u.ext2_sb.s_inodes_per_group = es->s_inodes_per_group; @@ -140,8 +147,8 @@ struct super_block * ext2_read_super (struct super_block * s, void * data) sizeof (struct ext2_inode); s->u.ext2_sb.s_desc_per_block = s->s_blocksize / sizeof (struct ext2_group_desc); - s->s_magic = es->s_magic; s->u.ext2_sb.s_sbh = bh; + s->u.ext2_sb.s_was_mounted_valid = es->s_valid; s->u.ext2_sb.s_rename_lock = 0; s->u.ext2_sb.s_rename_wait = NULL; if (s->s_magic == EXT2_OLD_SUPER_MAGIC) { @@ -177,7 +184,9 @@ struct super_block * ext2_read_super (struct super_block * s, void * data) s->s_dev = 0; unlock_super (s); brelse (bh); - printk ("EXT2-fs: magic match failed\n"); + if (!silent) + printk("VFS: Can't find an ext2fs filesystem on dev 0x%04x.\n", + dev); return NULL; } if (s->s_blocksize != s->u.ext2_sb.s_frag_size) { @@ -188,7 +197,8 @@ struct super_block * ext2_read_super (struct super_block * s, void * data) return NULL; } if (!es->s_valid) - printk ("EXT2-fs warning: mounting non valid file system\n"); + printk ("EXT2-fs warning: mounting unchecked file system, " + "running e2fsck is recommended\n"); s->u.ext2_sb.s_groups_count = (s->u.ext2_sb.s_blocks_count - s->u.ext2_sb.s_first_data_block + (EXT2_BLOCK_SIZE(s) * 8) - 1) / @@ -322,7 +332,11 @@ void ext2_statfs (struct super_block * sb, struct statfs * buf) &buf->f_blocks); tmp = ext2_count_free_blocks (sb); put_fs_long (tmp, &buf->f_bfree); - put_fs_long (tmp - sb->u.ext2_sb.s_r_blocks_count, &buf->f_bavail); + if (tmp >= sb->u.ext2_sb.s_r_blocks_count) + put_fs_long (tmp - sb->u.ext2_sb.s_r_blocks_count, + &buf->f_bavail); + else + put_fs_long (0, &buf->f_bavail); put_fs_long (sb->u.ext2_sb.s_inodes_count, &buf->f_files); put_fs_long (ext2_count_free_inodes(sb), &buf->f_ffree); put_fs_long (EXT2_NAME_LEN, &buf->f_namelen); @@ -398,9 +412,9 @@ int ext2_bmap (struct inode * inode, int block) } static struct buffer_head * inode_getblk (struct inode * inode, int nr, - int create) + int create, int new_block) { - int tmp; + int tmp, goal = 0; unsigned long * p; struct buffer_head * result; int blocks = inode->i_sb->s_blocksize / 512; @@ -415,9 +429,32 @@ repeat: brelse (result); goto repeat; } - if (!create) + if (!create || new_block >= + (current->rlim[RLIMIT_FSIZE].rlim_cur >> + EXT2_BLOCK_SIZE_BITS(inode->i_sb))) return NULL; - tmp = ext2_new_block (inode->i_sb, inode->u.ext2_i.i_block_group); + if (inode->u.ext2_i.i_next_alloc_block == new_block) + goal = inode->u.ext2_i.i_next_alloc_goal; +#ifdef EXT2FS_DEBUG + printk ("ext2 inode_getblk: hint = %d,", goal); +#endif + if (!goal) { + for (tmp = nr-1; tmp>=0; tmp--) { + if (inode->u.ext2_i.i_data[tmp]) { + goal = inode->u.ext2_i.i_data[tmp]; + break; + } + } + if (!goal) + goal = (inode->u.ext2_i.i_block_group * + EXT2_BLOCKS_PER_GROUP(inode->i_sb)) + + inode->i_sb->u.ext2_sb.s_first_data_block; + } + +#ifdef EXT2FS_DEBUG + printk (" goal = %d.\n", goal); +#endif + tmp = ext2_new_block (inode->i_sb, goal); if (!tmp) return NULL; result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize); @@ -427,6 +464,8 @@ repeat: goto repeat; } *p = tmp; + inode->u.ext2_i.i_next_alloc_block = new_block; + inode->u.ext2_i.i_next_alloc_goal = tmp; inode->i_ctime = CURRENT_TIME; inode->i_blocks += blocks; inode->i_dirt = 1; @@ -435,9 +474,9 @@ repeat: static struct buffer_head * block_getblk (struct inode * inode, struct buffer_head * bh, int nr, - int create, int blocksize) + int create, int blocksize, int new_block) { - int tmp; + int tmp, goal; unsigned long * p; struct buffer_head * result; int blocks = inode->i_sb->s_blocksize / 512; @@ -464,11 +503,25 @@ repeat: brelse (result); goto repeat; } - if (!create) { + if (!create || new_block >= + (current->rlim[RLIMIT_FSIZE].rlim_cur >> + EXT2_BLOCK_SIZE_BITS(inode->i_sb))) { brelse (bh); return NULL; } - tmp = ext2_new_block (inode->i_sb, inode->u.ext2_i.i_block_group); + if (inode->u.ext2_i.i_next_alloc_block == new_block) + goal = inode->u.ext2_i.i_next_alloc_goal; + if (!goal) { + for (tmp = nr-1; tmp>=0; tmp--) { + if (bh->b_data[tmp]) { + goal = bh->b_data[tmp]; + break; + } + } + if (!goal) + goal = bh->b_blocknr+1; + } + tmp = ext2_new_block (inode->i_sb, goal); if (!tmp) { #ifdef EXT2FS_DEBUG printk ("inode_getblk: ext2_new_block returned 0\n"); @@ -484,8 +537,11 @@ repeat: } *p = tmp; bh->b_dirt = 1; + inode->i_ctime = CURRENT_TIME; inode->i_blocks += blocks; inode->i_dirt = 1; + inode->u.ext2_i.i_next_alloc_block = new_block; + inode->u.ext2_i.i_next_alloc_goal = tmp; brelse (bh); return result; } @@ -494,6 +550,7 @@ struct buffer_head * ext2_getblk (struct inode * inode, int block, int create) { struct buffer_head * bh; + int b; unsigned long addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); if (block < 0) { @@ -506,30 +563,44 @@ struct buffer_head * ext2_getblk (struct inode * inode, int block, printk ("ext2_getblk: block > big\n"); return NULL; } + /* If this is a sequential block allocation, set the next_alloc_block + to this block now so that all the indblock and data block allocations + use the same goal zone */ +#ifdef EXT2FS_DEBUG + printk ("ext2_getblk: block %d, next %d, goal %d.\n", block, + inode->u.ext2_i.i_next_alloc_block, + inode->u.ext2_i.i_next_alloc_goal); +#endif + if (block == inode->u.ext2_i.i_next_alloc_block + 1) { + inode->u.ext2_i.i_next_alloc_block++; + inode->u.ext2_i.i_next_alloc_goal++; + } + + b = block; if (block < EXT2_NDIR_BLOCKS) - return inode_getblk (inode, block, create); + return inode_getblk (inode, block, create, b); block -= EXT2_NDIR_BLOCKS; if (block < addr_per_block) { - bh = inode_getblk (inode, EXT2_IND_BLOCK, create); + bh = inode_getblk (inode, EXT2_IND_BLOCK, create, b); return block_getblk (inode, bh, block, create, - inode->i_sb->s_blocksize); + inode->i_sb->s_blocksize, b); } block -= addr_per_block; if (block < addr_per_block * addr_per_block) { - bh = inode_getblk (inode, EXT2_DIND_BLOCK, create); + bh = inode_getblk (inode, EXT2_DIND_BLOCK, create,b ); bh = block_getblk (inode, bh, block / addr_per_block, create, - inode->i_sb->s_blocksize); + inode->i_sb->s_blocksize, b); return block_getblk (inode, bh, block & (addr_per_block - 1), - create, inode->i_sb->s_blocksize); + create, inode->i_sb->s_blocksize, b); } block -= addr_per_block * addr_per_block; - bh = inode_getblk (inode, EXT2_IND_BLOCK, create); + bh = inode_getblk (inode, EXT2_TIND_BLOCK, create, b); bh = block_getblk (inode, bh, block / (addr_per_block * addr_per_block), - create, inode->i_sb->s_blocksize); + create, inode->i_sb->s_blocksize, b); bh = block_getblk (inode, bh, (block / addr_per_block) & (addr_per_block - 1), - create, inode->i_sb->s_blocksize); + create, inode->i_sb->s_blocksize, b); return block_getblk (inode, bh, block & (addr_per_block - 1), create, - inode->i_sb->s_blocksize); + inode->i_sb->s_blocksize, b); } struct buffer_head * ext2_bread (struct inode * inode, int block, int create) @@ -587,21 +658,26 @@ void ext2_read_inode (struct inode * inode) inode->i_atime = raw_inode->i_atime; inode->i_ctime = raw_inode->i_ctime; inode->i_mtime = raw_inode->i_mtime; - inode->u.ext2_i.i_dtime = raw_inode->i_dtime; + if (!S_ISFIFO(inode->i_mode)) + inode->u.ext2_i.i_dtime = raw_inode->i_dtime; inode->i_blksize = inode->i_sb->s_blocksize; inode->i_blocks = raw_inode->i_blocks; - inode->u.ext2_i.i_flags = raw_inode->i_flags; - inode->u.ext2_i.i_faddr = raw_inode->i_faddr; - inode->u.ext2_i.i_frag = raw_inode->i_frag; - inode->u.ext2_i.i_fsize = raw_inode->i_fsize; - inode->u.ext2_i.i_file_acl = raw_inode->i_file_acl; - inode->u.ext2_i.i_dir_acl = raw_inode->i_dir_acl; - inode->u.ext2_i.i_version = raw_inode->i_version; - inode->u.ext2_i.i_block_group = block_group; - if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) - inode->i_rdev = raw_inode->i_block[0]; - else for (block = 0; block < EXT2_N_BLOCKS; block++) - inode->u.ext2_i.i_data[block] = raw_inode->i_block[block]; + if (!S_ISFIFO(inode->i_mode)) { + inode->u.ext2_i.i_flags = raw_inode->i_flags; + inode->u.ext2_i.i_faddr = raw_inode->i_faddr; + inode->u.ext2_i.i_frag = raw_inode->i_frag; + inode->u.ext2_i.i_fsize = raw_inode->i_fsize; + inode->u.ext2_i.i_file_acl = raw_inode->i_file_acl; + inode->u.ext2_i.i_dir_acl = raw_inode->i_dir_acl; + inode->u.ext2_i.i_version = raw_inode->i_version; + inode->u.ext2_i.i_block_group = block_group; + inode->u.ext2_i.i_next_alloc_block = 0; + inode->u.ext2_i.i_next_alloc_goal = 0; + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) + inode->i_rdev = raw_inode->i_block[0]; + else for (block = 0; block < EXT2_N_BLOCKS; block++) + inode->u.ext2_i.i_data[block] = raw_inode->i_block[block]; + } brelse (bh); inode->i_op = NULL; if (S_ISREG(inode->i_mode)) @@ -614,14 +690,8 @@ void ext2_read_inode (struct inode * inode) inode->i_op = &chrdev_inode_operations; else if (S_ISBLK(inode->i_mode)) inode->i_op = &blkdev_inode_operations; - else if (S_ISFIFO(inode->i_mode)) { - inode->i_op = &fifo_inode_operations; - inode->i_pipe = 1; - PIPE_BASE(*inode) = NULL; - PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0; - PIPE_READ_WAIT(*inode) = PIPE_WRITE_WAIT(*inode) = NULL; - PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; - } + else if (S_ISFIFO(inode->i_mode)) + init_fifo(inode); } void ext2_write_inode (struct inode * inode) @@ -664,19 +734,21 @@ void ext2_write_inode (struct inode * inode) raw_inode->i_atime = inode->i_atime; raw_inode->i_ctime = inode->i_ctime; raw_inode->i_mtime = inode->i_mtime; - raw_inode->i_dtime = inode->u.ext2_i.i_dtime; raw_inode->i_blocks = inode->i_blocks; - raw_inode->i_flags = inode->u.ext2_i.i_flags; - raw_inode->i_faddr = inode->u.ext2_i.i_faddr; - raw_inode->i_frag = inode->u.ext2_i.i_frag; - raw_inode->i_fsize = inode->u.ext2_i.i_fsize; - raw_inode->i_file_acl = inode->u.ext2_i.i_file_acl; - raw_inode->i_dir_acl = inode->u.ext2_i.i_dir_acl; - raw_inode->i_version = inode->u.ext2_i.i_version; - if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) - raw_inode->i_block[0] = inode->i_rdev; - else for (block = 0; block < EXT2_N_BLOCKS; block++) - raw_inode->i_block[block] = inode->u.ext2_i.i_data[block]; + if (!S_ISFIFO(inode->i_mode)) { + raw_inode->i_dtime = inode->u.ext2_i.i_dtime; + raw_inode->i_flags = inode->u.ext2_i.i_flags; + raw_inode->i_faddr = inode->u.ext2_i.i_faddr; + raw_inode->i_frag = inode->u.ext2_i.i_frag; + raw_inode->i_fsize = inode->u.ext2_i.i_fsize; + raw_inode->i_file_acl = inode->u.ext2_i.i_file_acl; + raw_inode->i_dir_acl = inode->u.ext2_i.i_dir_acl; + raw_inode->i_version = inode->u.ext2_i.i_version; + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) + raw_inode->i_block[0] = inode->i_rdev; + else for (block = 0; block < EXT2_N_BLOCKS; block++) + raw_inode->i_block[block] = inode->u.ext2_i.i_data[block]; + } bh->b_dirt = 1; inode->i_dirt = 0; brelse (bh); diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 2088888..c133e6a 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -105,13 +105,8 @@ static struct buffer_head * ext2_find_entry (struct inode * dir, if (prev_dir) *prev_dir = NULL; } - if (de->rec_len < EXT2_DIR_REC_LEN(1) || - de->rec_len % 4 != 0 || - de->rec_len < EXT2_DIR_REC_LEN(de->name_len)) { - printk ("ext2_find_entry: bad directory entry (dev %04x, dir %d)\n", - dir->i_dev, dir->i_ino); - printk ("offset=%d, inode=%d, rec_len=%d, name_len=%d\n", - offset, de->inode, de->rec_len, de->name_len); + if (! ext2_check_dir_entry ("ext2_find_entry", dir, de, bh, + offset)) { brelse (bh); return NULL; } @@ -230,13 +225,8 @@ static struct buffer_head * ext2_add_entry (struct inode * dir, de = (struct ext2_dir_entry *) bh->b_data; } } - if (de->rec_len < EXT2_DIR_REC_LEN(1) || - de->rec_len % 4 != 0 || - de->rec_len < EXT2_DIR_REC_LEN(de->name_len)) { - printk ("ext2_add_entry: bad directory entry (dev %04x, dir %d)\n", - dir->i_dev, dir->i_ino); - printk ("offset=%d, inode=%d, rec_len=%d, name_len=%d\n", - offset, de->inode, de->rec_len, de->name_len); + if (! ext2_check_dir_entry ("ext2_add_entry", dir, de, bh, + offset)) { brelse (bh); return NULL; } @@ -267,6 +257,19 @@ static struct buffer_head * ext2_add_entry (struct inode * dir, return NULL; } +/* + * ext2_delete_entry deletes a directory entry by merging it with the + * previous entry + */ +static void ext2_delete_entry (struct ext2_dir_entry * dir, + struct ext2_dir_entry * prev_dir) +{ + if (prev_dir != NULL) + prev_dir->rec_len += dir->rec_len; + else + dir->inode = 0; +} + int ext2_create (struct inode * dir,const char * name, int len, int mode, struct inode ** result) { @@ -341,14 +344,8 @@ int ext2_mknod (struct inode * dir, const char * name, int len, int mode, inode->i_op = &chrdev_inode_operations; else if (S_ISBLK(inode->i_mode)) inode->i_op = &blkdev_inode_operations; - else if (S_ISFIFO(inode->i_mode)) { - inode->i_op = &fifo_inode_operations; - inode->i_pipe = 1; - PIPE_BASE(*inode) = NULL; - PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0; - PIPE_READ_WAIT(*inode) = PIPE_WRITE_WAIT(*inode) = NULL; - PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; - } + else if (S_ISFIFO(inode->i_mode)) + init_fifo(inode); if (S_ISBLK(mode) || S_ISCHR(mode)) inode->i_rdev = rdev; inode->i_mtime = inode->i_atime = CURRENT_TIME; @@ -477,13 +474,8 @@ static int empty_dir (struct inode * inode) } de = (struct ext2_dir_entry *) bh->b_data; } - if (de->rec_len < EXT2_DIR_REC_LEN(1) || - de->rec_len % 4 != 0 || - de->rec_len < EXT2_DIR_REC_LEN(de->name_len)) { - printk ("empty_dir: bad directory entry (dev %04x, dir %d)\n", - inode->i_dev, inode->i_ino); - printk ("offset=%d, inode=%d, rec_len=%d, name_len=%d\n", - offset, de->inode, de->rec_len, de->name_len); + if (! ext2_check_dir_entry ("empty_dir", inode, de, bh, + offset)) { brelse (bh); return 1; } @@ -537,10 +529,13 @@ int ext2_rmdir (struct inode * dir, const char * name, int len) #ifndef DONT_USE_DCACHE ext2_dcache_remove (dir->i_dev, dir->i_ino, de->name, de->name_len); #endif + ext2_delete_entry (de, pde); +#if 0 if (pde) pde->rec_len += de->rec_len; else de->inode = 0; +#endif bh->b_dirt = 1; inode->i_nlink = 0; inode->i_dirt = 1; @@ -584,10 +579,13 @@ int ext2_unlink (struct inode * dir, const char * name, int len) #ifndef DONT_USE_DCACHE ext2_dcache_remove (dir->i_dev, dir->i_ino, de->name, de->name_len); #endif + ext2_delete_entry (de, pde); +#if 0 if (pde) pde->rec_len += de->rec_len; else de->inode = 0; +#endif bh->b_dirt = 1; dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_dirt = 1; @@ -857,12 +855,19 @@ start_up: ext2_dcache_add (new_dir->i_dev, new_dir->i_ino, new_de->name, new_de->name_len, new_de->inode); #endif + if (old_bh->b_blocknr == new_bh->b_blocknr && + ((char *) new_de) + new_de->rec_len == (char *) old_de) + new_de->rec_len += old_de->rec_len; + else + ext2_delete_entry (old_de, pde); +#if 0 if (((char *) new_de) + new_de->rec_len == (char *) old_de) new_de->rec_len += old_de->rec_len; else if (pde) pde->rec_len += old_de->rec_len; else old_de->inode = 0; +#endif if (new_inode) { new_inode->i_nlink --; new_inode->i_dirt = 1; diff --git a/fs/ext2/truncate.c b/fs/ext2/truncate.c index bc613bc..0028845 100644 --- a/fs/ext2/truncate.c +++ b/fs/ext2/truncate.c @@ -47,7 +47,7 @@ repeat: tmp = *p; if (!tmp) continue; - bh = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize); + bh = get_hash_table (inode->i_dev, tmp, inode->i_sb->s_blocksize); if (i < direct_block) { brelse (bh); goto repeat; @@ -100,7 +100,8 @@ repeat: tmp = *ind; if (!tmp) continue; - bh = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize); + bh = get_hash_table (inode->i_dev, tmp, + inode->i_sb->s_blocksize); if (i < indirect_block) { brelse (bh); goto repeat; @@ -122,7 +122,7 @@ static int fifo_open(struct inode * inode,struct file * filp) * is contain the open that then fills in the correct operations * depending on the access mode of the file... */ -struct file_operations def_fifo_fops = { +static struct file_operations def_fifo_fops = { NULL, NULL, NULL, @@ -135,7 +135,7 @@ struct file_operations def_fifo_fops = { NULL }; -struct inode_operations fifo_inode_operations = { +static struct inode_operations fifo_inode_operations = { &def_fifo_fops, /* default file operations */ NULL, /* create */ NULL, /* lookup */ @@ -152,3 +152,14 @@ struct inode_operations fifo_inode_operations = { NULL, /* truncate */ NULL /* permission */ }; + +void init_fifo(struct inode * inode) +{ + inode->i_op = &fifo_inode_operations; + inode->i_pipe = 1; + PIPE_BASE(*inode) = NULL; + PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0; + PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0; + PIPE_READ_WAIT(*inode) = PIPE_WRITE_WAIT(*inode) = NULL; + PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; +} diff --git a/fs/filesystems.c b/fs/filesystems.c index 81f9de3..7c9f71c 100644 --- a/fs/filesystems.c +++ b/fs/filesystems.c @@ -37,15 +37,15 @@ struct file_system_type file_systems[] = { #ifdef CONFIG_MINIX_FS {minix_read_super, "minix", 1}, #endif -#ifdef CONFIG_XIA_FS - {xiafs_read_super, "xiafs", 1}, -#endif #ifdef CONFIG_EXT_FS {ext_read_super, "ext", 1}, #endif #ifdef CONFIG_EXT2_FS {ext2_read_super, "ext2", 1}, #endif +#ifdef CONFIG_XIA_FS + {xiafs_read_super, "xiafs", 1}, +#endif #ifdef CONFIG_MSDOS_FS {msdos_read_super, "msdos", 1}, #endif diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index ff86ea2..4a087f9 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -91,7 +91,8 @@ static int parse_options(char *options,char *map,char *conversion, char * rock, return 1; } -struct super_block *isofs_read_super(struct super_block *s,void *data) +struct super_block *isofs_read_super(struct super_block *s,void *data, + int silent) { struct buffer_head *bh; int iso_blknum; @@ -155,7 +156,8 @@ struct super_block *isofs_read_super(struct super_block *s,void *data) brelse(bh); } if(iso_blknum == 100) { - printk("Unable to identify CD-ROM format.\n"); + if (!silent) + printk("Unable to identify CD-ROM format.\n"); s->s_dev = 0; unlock_super(s); return NULL; @@ -247,6 +249,7 @@ void isofs_statfs (struct super_block *sb, struct statfs *buf) put_fs_long(0, &buf->f_bavail); put_fs_long(sb->u.isofs_sb.s_ninodes, &buf->f_files); put_fs_long(0, &buf->f_ffree); + put_fs_long(NAME_MAX, &buf->f_namelen); /* Don't know what value to put in buf->f_fsid */ } @@ -415,14 +418,8 @@ void isofs_read_inode(struct inode * inode) inode->i_op = &chrdev_inode_operations; else if (S_ISBLK(inode->i_mode)) inode->i_op = &blkdev_inode_operations; - else if (S_ISFIFO(inode->i_mode)) { - inode->i_op = &fifo_inode_operations; - inode->i_pipe = 1; - PIPE_BASE(*inode) = NULL; - PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0; - PIPE_READ_WAIT(*inode) = PIPE_WRITE_WAIT(*inode) = NULL; - PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; - } + else if (S_ISFIFO(inode->i_mode)) + init_fifo(inode); } /* There are times when we need to know the inode number of a parent of diff --git a/fs/minix/inode.c b/fs/minix/inode.c index a019314..1f17383 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -48,7 +48,8 @@ static struct super_operations minix_sops = { minix_statfs }; -struct super_block *minix_read_super(struct super_block *s,void *data) +struct super_block *minix_read_super(struct super_block *s,void *data, + int silent) { struct buffer_head *bh; struct minix_super_block *ms; @@ -83,7 +84,9 @@ struct super_block *minix_read_super(struct super_block *s,void *data) } else { s->s_dev = 0; unlock_super(s); - printk("MINIX-fs magic match failed\n"); + if (!silent) + printk("VFS: Can't find a minix filesystem on dev 0x%04x.\n", + dev); return NULL; } for (i=0;i < MINIX_I_MAP_SLOTS;i++) @@ -138,6 +141,7 @@ void minix_statfs(struct super_block *sb, struct statfs *buf) put_fs_long(tmp, &buf->f_bavail); put_fs_long(sb->u.minix_sb.s_ninodes, &buf->f_files); put_fs_long(minix_count_free_inodes(sb), &buf->f_ffree); + put_fs_long(sb->u.minix_sb.s_namelen, &buf->f_namelen); /* Don't know what value to put in buf->f_fsid */ } @@ -354,14 +358,8 @@ void minix_read_inode(struct inode * inode) inode->i_op = &chrdev_inode_operations; else if (S_ISBLK(inode->i_mode)) inode->i_op = &blkdev_inode_operations; - else if (S_ISFIFO(inode->i_mode)) { - inode->i_op = &fifo_inode_operations; - inode->i_pipe = 1; - PIPE_BASE(*inode) = NULL; - PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0; - PIPE_READ_WAIT(*inode) = PIPE_WRITE_WAIT(*inode) = NULL; - PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; - } + else if (S_ISFIFO(inode->i_mode)) + init_fifo(inode); } void minix_write_inode(struct inode * inode) diff --git a/fs/minix/namei.c b/fs/minix/namei.c index e7e9702..c5e8285 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -15,11 +15,25 @@ #include <asm/segment.h> /* - * comment out this line if you want names > MINIX_NAME_LEN chars to be - * truncated. Else they will be disallowed. + * comment out this line if you want names > info->s_namelen chars to be + * truncated. Else they will be disallowed (ENAMETOOLONG). */ /* #define NO_TRUNCATE */ +static inline int namecompare(int len, int maxlen, + const char * name, const char * buffer) +{ + if (len >= maxlen || !buffer[len]) { + unsigned char same; + __asm__("repe ; cmpsb ; setz %0" + :"=q" (same) + :"S" ((long) name),"D" ((long) buffer),"c" (len) + :"cx","di","si"); + return same; + } + return 0; +} + /* * ok, we cannot use strncmp, as the name is not in our data space. * Thus we'll have to use minix_match. No big problem. Match also makes @@ -32,7 +46,6 @@ static int minix_match(int len, const char * name, struct minix_sb_info * info) { struct minix_dir_entry * de; - register int same __asm__("ax"); de = (struct minix_dir_entry *) (bh->b_data + *offset); *offset += info->s_dirsize; @@ -41,15 +54,7 @@ static int minix_match(int len, const char * name, /* "" means "." ---> so paths like "/usr/lib//libc.a" work */ if (!len && (de->name[0]=='.') && (de->name[1]=='\0')) return 1; - if (len < info->s_namelen && de->name[len]) - return 0; - __asm__("cld\n\t" - "repe ; cmpsb\n\t" - "setz %%al" - :"=a" (same) - :"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len) - :"cx","di","si"); - return same; + return namecompare(len,info->s_namelen,name,de->name); } /* @@ -134,15 +139,17 @@ int minix_lookup(struct inode * dir,const char * name, int len, /* * minix_add_entry() * - * adds a file entry to the specified directory, using the same - * semantics as minix_find_entry(). It returns NULL if it failed. + * adds a file entry to the specified directory, returning a possible + * error value if it fails. * * NOTE!! The inode part of 'de' is left at 0 - which means you * may not sleep between calling this and putting something into * the entry, as someone else might have used it while you slept. */ -static struct buffer_head * minix_add_entry(struct inode * dir, - const char * name, int namelen, struct minix_dir_entry ** res_dir) +static int minix_add_entry(struct inode * dir, + const char * name, int namelen, + struct buffer_head ** res_buf, + struct minix_dir_entry ** res_dir) { int i; unsigned long block, offset; @@ -150,26 +157,27 @@ static struct buffer_head * minix_add_entry(struct inode * dir, struct minix_dir_entry * de; struct minix_sb_info * info; + *res_buf = NULL; *res_dir = NULL; if (!dir || !dir->i_sb) - return NULL; + return -ENOENT; info = &dir->i_sb->u.minix_sb; if (namelen > info->s_namelen) { #ifdef NO_TRUNCATE - return NULL; + return -ENAMETOOLONG; #else namelen = info->s_namelen; #endif } if (!namelen) - return NULL; + return -ENOENT; bh = NULL; block = offset = 0; while (1) { if (!bh) { bh = minix_bread(dir,block,1); if (!bh) - return NULL; + return -ENOSPC; } de = (struct minix_dir_entry *) (bh->b_data + offset); offset += info->s_dirsize; @@ -179,7 +187,12 @@ static struct buffer_head * minix_add_entry(struct inode * dir, dir->i_dirt = 1; dir->i_ctime = CURRENT_TIME; } - if (!de->inode) { + if (de->inode) { + if (namecompare(namelen, info->s_namelen, name, de->name)) { + brelse(bh); + return -EEXIST; + } + } else { dir->i_mtime = dir->i_ctime = CURRENT_TIME; for (i = 0; i < info->s_namelen ; i++) de->name[i] = (i < namelen) ? name[i] : 0; @@ -194,12 +207,14 @@ static struct buffer_head * minix_add_entry(struct inode * dir, offset = 0; block++; } - return bh; + *res_buf = bh; + return 0; } int minix_create(struct inode * dir,const char * name, int len, int mode, struct inode ** result) { + int error; struct inode * inode; struct buffer_head * bh; struct minix_dir_entry * de; @@ -215,13 +230,13 @@ int minix_create(struct inode * dir,const char * name, int len, int mode, inode->i_op = &minix_file_inode_operations; inode->i_mode = mode; inode->i_dirt = 1; - bh = minix_add_entry(dir,name,len,&de); - if (!bh) { + error = minix_add_entry(dir,name,len, &bh ,&de); + if (error) { inode->i_nlink--; inode->i_dirt = 1; iput(inode); iput(dir); - return -ENOSPC; + return error; } de->inode = inode->i_ino; bh->b_dirt = 1; @@ -233,6 +248,7 @@ int minix_create(struct inode * dir,const char * name, int len, int mode, int minix_mknod(struct inode * dir, const char * name, int len, int mode, int rdev) { + int error; struct inode * inode; struct buffer_head * bh; struct minix_dir_entry * de; @@ -266,25 +282,19 @@ int minix_mknod(struct inode * dir, const char * name, int len, int mode, int rd inode->i_op = &chrdev_inode_operations; else if (S_ISBLK(inode->i_mode)) inode->i_op = &blkdev_inode_operations; - else if (S_ISFIFO(inode->i_mode)) { - inode->i_op = &fifo_inode_operations; - inode->i_pipe = 1; - PIPE_BASE(*inode) = NULL; - PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0; - PIPE_READ_WAIT(*inode) = PIPE_WRITE_WAIT(*inode) = NULL; - PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; - } + else if (S_ISFIFO(inode->i_mode)) + init_fifo(inode); if (S_ISBLK(mode) || S_ISCHR(mode)) inode->i_rdev = rdev; inode->i_mtime = inode->i_atime = CURRENT_TIME; inode->i_dirt = 1; - bh = minix_add_entry(dir,name,len,&de); - if (!bh) { + error = minix_add_entry(dir, name, len, &bh, &de); + if (error) { inode->i_nlink--; inode->i_dirt = 1; iput(inode); iput(dir); - return -ENOSPC; + return error; } de->inode = inode->i_ino; bh->b_dirt = 1; @@ -296,6 +306,7 @@ int minix_mknod(struct inode * dir, const char * name, int len, int mode, int rd int minix_mkdir(struct inode * dir, const char * name, int len, int mode) { + int error; struct inode * inode; struct buffer_head * bh, *dir_block; struct minix_dir_entry * de; @@ -345,12 +356,12 @@ int minix_mkdir(struct inode * dir, const char * name, int len, int mode) if (dir->i_mode & S_ISGID) inode->i_mode |= S_ISGID; inode->i_dirt = 1; - bh = minix_add_entry(dir,name,len,&de); - if (!bh) { + error = minix_add_entry(dir, name, len, &bh, &de); + if (error) { iput(dir); inode->i_nlink=0; iput(inode); - return -ENOSPC; + return error; } de->inode = inode->i_ino; bh->b_dirt = 1; @@ -559,13 +570,13 @@ int minix_symlink(struct inode * dir, const char * name, int len, const char * s iput(dir); return -EEXIST; } - bh = minix_add_entry(dir,name,len,&de); - if (!bh) { + i = minix_add_entry(dir, name, len, &bh, &de); + if (i) { inode->i_nlink--; inode->i_dirt = 1; iput(inode); iput(dir); - return -ENOSPC; + return i; } de->inode = inode->i_ino; bh->b_dirt = 1; @@ -577,6 +588,7 @@ int minix_symlink(struct inode * dir, const char * name, int len, const char * s int minix_link(struct inode * oldinode, struct inode * dir, const char * name, int len) { + int error; struct minix_dir_entry * de; struct buffer_head * bh; @@ -597,11 +609,11 @@ int minix_link(struct inode * oldinode, struct inode * dir, const char * name, i iput(oldinode); return -EEXIST; } - bh = minix_add_entry(dir,name,len,&de); - if (!bh) { + error = minix_add_entry(dir, name, len, &bh, &de); + if (error) { iput(dir); iput(oldinode); - return -ENOSPC; + return error; } de->inode = oldinode->i_ino; bh->b_dirt = 1; @@ -717,8 +729,8 @@ start_up: current->euid != new_dir->i_uid && !suser()) goto end_rename; if (S_ISDIR(old_inode->i_mode)) { - retval = -EACCES; - if (!permission(old_inode, MAY_WRITE)) + retval = -ENOTDIR; + if (new_inode && !S_ISDIR(new_inode->i_mode)) goto end_rename; retval = -EINVAL; if (subdir(new_dir, old_inode)) @@ -730,14 +742,14 @@ start_up: if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino) goto end_rename; retval = -EMLINK; - if (new_dir->i_nlink >= MINIX_LINK_MAX) + if (!new_inode && new_dir->i_nlink >= MINIX_LINK_MAX) + goto end_rename; + } + if (!new_bh) { + retval = minix_add_entry(new_dir,new_name,new_len,&new_bh,&new_de); + if (retval) goto end_rename; } - if (!new_bh) - new_bh = minix_add_entry(new_dir,new_name,new_len,&new_de); - retval = -ENOSPC; - if (!new_bh) - goto end_rename; /* sanity checking before doing the rename - avoid races */ if (new_inode && (new_de->inode != new_inode->i_ino)) goto try_again; diff --git a/fs/msdos/inode.c b/fs/msdos/inode.c index c19df60..1d690a7 100644 --- a/fs/msdos/inode.c +++ b/fs/msdos/inode.c @@ -66,7 +66,7 @@ static struct super_operations msdos_sops = { static int parse_options(char *options,char *check,char *conversion,uid_t *uid, - gid_t *gid,int *umask,int *debug,int *fat) + gid_t *gid,int *umask,int *debug,int *fat,int *quiet) { char *this,*value; @@ -75,7 +75,7 @@ static int parse_options(char *options,char *check,char *conversion,uid_t *uid, *uid = current->uid; *gid = current->gid; *umask = current->umask; - *debug = *fat = 0; + *debug = *fat = *quiet = 0; if (!options) return 1; for (this = strtok(options,","); this; this = strtok(NULL,",")) { if ((value = strchr(this,'=')) != NULL) @@ -128,6 +128,10 @@ static int parse_options(char *options,char *check,char *conversion,uid_t *uid, if (*value || (*fat != 12 && *fat != 16)) return 0; } + else if (!strcmp(this,"quiet")) { + if (value) return 0; + *quiet = 1; + } else return 0; } return 1; @@ -136,19 +140,20 @@ static int parse_options(char *options,char *check,char *conversion,uid_t *uid, /* Read the super block of an MS-DOS FS. */ -struct super_block *msdos_read_super(struct super_block *s,void *data) +struct super_block *msdos_read_super(struct super_block *s,void *data, + int silent) { struct buffer_head *bh; struct msdos_boot_sector *b; int data_sectors,logical_sector_size,sector_mult; - int debug,error,fat; + int debug,error,fat,quiet; char check,conversion; uid_t uid; gid_t gid; int umask; if (!parse_options((char *) data,&check,&conversion,&uid,&gid,&umask, - &debug,&fat)) { + &debug,&fat,&quiet)) { s->s_dev = 0; return NULL; } @@ -198,21 +203,24 @@ struct super_block *msdos_read_super(struct super_block *s,void *data) data_sectors = (CF_LE_W(*((unsigned short *) &b->sectors)) ? CF_LE_W(*((unsigned short *) &b->sectors)) : CF_LE_L(b->total_sect))*sector_mult-MSDOS_SB(s)->data_start; - MSDOS_SB(s)->clusters = b->cluster_size ? data_sectors/b->cluster_size/ - sector_mult : 0; - MSDOS_SB(s)->fat_bits = fat ? fat : MSDOS_SB(s)->clusters > MSDOS_FAT12 - ? 16 : 12; - error = !MSDOS_SB(s)->fats || (MSDOS_SB(s)->dir_entries & (MSDOS_DPS-1)) - || !b->cluster_size || MSDOS_SB(s)->clusters+2 > MSDOS_SB(s)-> - fat_length*SECTOR_SIZE*8/MSDOS_SB(s)->fat_bits || !sector_mult || - (logical_sector_size & (SECTOR_SIZE-1)) || !b->secs_track || - !b->heads; + error = !b->cluster_size || !sector_mult; + if (!error) { + MSDOS_SB(s)->clusters = b->cluster_size ? data_sectors/ + b->cluster_size/sector_mult : 0; + MSDOS_SB(s)->fat_bits = fat ? fat : MSDOS_SB(s)->clusters > + MSDOS_FAT12 ? 16 : 12; + error = !MSDOS_SB(s)->fats || (MSDOS_SB(s)->dir_entries & + (MSDOS_DPS-1)) || MSDOS_SB(s)->clusters+2 > MSDOS_SB(s)-> + fat_length*SECTOR_SIZE*8/MSDOS_SB(s)->fat_bits || + (logical_sector_size & (SECTOR_SIZE-1)) || !b->secs_track || + !b->heads; + } brelse(bh); if (error || debug) { - printk("[MS-DOS FS Rel. alpha.10,FAT %d,check=%c,conv=%c," + printk("[MS-DOS FS Rel. 12,FAT %d,check=%c,conv=%c," "uid=%d,gid=%d,umask=%03o%s]\n",MSDOS_SB(s)->fat_bits,check, - conversion,uid,gid,umask,MSDOS_CAN_BMAP(MSDOS_SB(s)) ? ", - bmap" : ""); + conversion,uid,gid,umask,MSDOS_CAN_BMAP(MSDOS_SB(s)) ? + ",bmap" : ""); printk("[me=0x%x,cs=%d,#f=%d,fs=%d,fl=%d,ds=%d,de=%d,data=%d," "se=%d,ts=%d,ls=%d]\n",b->media,MSDOS_SB(s)->cluster_size, MSDOS_SB(s)->fats,MSDOS_SB(s)->fat_start,MSDOS_SB(s)-> @@ -221,8 +229,10 @@ struct super_block *msdos_read_super(struct super_block *s,void *data) sectors),b->total_sect,logical_sector_size); } if (error) { + if (!silent) + printk("VFS: Can't find a valid MSDOS filesystem on dev 0x%04x.\n", + s->s_dev); s->s_dev = 0; - printk("Unsupported FS parameters\n"); return NULL; } s->s_magic = MSDOS_SUPER_MAGIC; @@ -233,6 +243,7 @@ struct super_block *msdos_read_super(struct super_block *s,void *data) MSDOS_SB(s)->fs_uid = uid; MSDOS_SB(s)->fs_gid = gid; MSDOS_SB(s)->fs_umask = umask; + MSDOS_SB(s)->quiet = quiet; MSDOS_SB(s)->free_clusters = -1; /* don't know yet */ MSDOS_SB(s)->fat_wait = NULL; MSDOS_SB(s)->fat_lock = 0; @@ -267,6 +278,7 @@ void msdos_statfs(struct super_block *sb,struct statfs *buf) put_fs_long(free,&buf->f_bavail); put_fs_long(0,&buf->f_files); put_fs_long(0,&buf->f_ffree); + put_fs_long(12,&buf->f_namelen); } @@ -416,7 +428,7 @@ int msdos_notify_change(int flags,struct inode *inode) error = -EPERM; } if (!(flags & NOTIFY_MODE)) - return error; + return MSDOS_SB(inode->i_sb)->quiet ? 0 : error; if (inode->i_mode & ~MSDOS_VALID_MODE) { inode->i_mode &= MSDOS_VALID_MODE; error = -EPERM; @@ -427,5 +439,5 @@ int msdos_notify_change(int flags,struct inode *inode) inode->i_mode = ((inode->i_mode & S_IFMT) | ((((inode->i_mode & S_IRWXU & ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IRUSR) >> 6)*0111)) & ~MSDOS_SB(inode->i_sb)->fs_umask; - return error; + return MSDOS_SB(inode->i_sb)->quiet ? 0 : error; } diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index 66b9ec7..082c558 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c @@ -293,11 +293,36 @@ mkdir_error: } +static int msdos_empty(struct inode *dir) +{ + int pos; + struct buffer_head *bh; + struct msdos_dir_entry *de; + + if (dir->i_count > 1) + return -EBUSY; + if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */ + pos = 0; + bh = NULL; + while (msdos_get_entry(dir,&pos,&bh,&de) > -1) + if (!IS_FREE(de->name) && strncmp(de->name,MSDOS_DOT, + MSDOS_NAME) && strncmp(de->name,MSDOS_DOTDOT, + MSDOS_NAME)) { + brelse(bh); + return -ENOTEMPTY; + } + if (bh) + brelse(bh); + } + return 0; +} + + int msdos_rmdir(struct inode *dir,const char *name,int len) { - int res,ino,pos; - struct buffer_head *bh,*dbh; - struct msdos_dir_entry *de,*dde; + int res,ino; + struct buffer_head *bh; + struct msdos_dir_entry *de; struct inode *inode; bh = NULL; @@ -312,17 +337,9 @@ int msdos_rmdir(struct inode *dir,const char *name,int len) if (!S_ISDIR(inode->i_mode)) goto rmdir_done; res = -EBUSY; if (dir->i_dev != inode->i_dev || dir == inode) goto rmdir_done; - if (inode->i_count > 1) goto rmdir_done; - if (MSDOS_I(inode)->i_start) { /* may be zero in mkdir */ - res = -ENOTEMPTY; - pos = 0; - dbh = NULL; - while (msdos_get_entry(inode,&pos,&dbh,&dde) > -1) - if (!IS_FREE(dde->name) && strncmp(dde->name,MSDOS_DOT, - MSDOS_NAME) && strncmp(dde->name,MSDOS_DOTDOT, - MSDOS_NAME)) goto rmdir_done; - if (dbh) brelse(dbh); - } + res = msdos_empty(inode); + if (res) + goto rmdir_done; inode->i_nlink = 0; dir->i_mtime = CURRENT_TIME; dir->i_nlink--; @@ -377,8 +394,7 @@ static int rename_same_dir(struct inode *old_dir,char *old_name, struct buffer_head *new_bh; struct msdos_dir_entry *new_de; struct inode *new_inode,*old_inode; - int new_ino; - int exists; + int new_ino,exists,error; if (!strncmp(old_name,new_name,MSDOS_NAME)) return 0; exists = msdos_scan(new_dir,new_name,&new_bh,&new_de,&new_ino) >= 0; @@ -391,10 +407,17 @@ static int rename_same_dir(struct inode *old_dir,char *old_name, brelse(new_bh); return -EIO; } - if (S_ISDIR(new_inode->i_mode)) { + error = S_ISDIR(new_inode->i_mode) ? (old_de->attr & ATTR_DIR) ? + msdos_empty(new_inode) : -EPERM : (old_de->attr & ATTR_DIR) + ? -EPERM : 0; + if (error) { iput(new_inode); brelse(new_bh); - return -EPERM; + return error; + } + if (S_ISDIR(new_inode->i_mode)) { + new_dir->i_nlink--; + new_dir->i_dirt = 1; } new_inode->i_nlink = 0; MSDOS_I(new_inode)->i_busy = 1; @@ -461,11 +484,14 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name, brelse(new_bh); return -EIO; } - if (S_ISDIR(new_inode->i_mode)) { + error = S_ISDIR(new_inode->i_mode) ? (old_de->attr & ATTR_DIR) ? + msdos_empty(new_inode) : -EPERM : (old_de->attr & ATTR_DIR) + ? -EPERM : 0; + if (error) { iput(new_inode); iput(old_inode); brelse(new_bh); - return -EPERM; + return error; } new_inode->i_nlink = 0; MSDOS_I(new_inode)->i_busy = 1; @@ -485,6 +511,10 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name, } return -EIO; } + if (exists && S_ISDIR(new_inode->i_mode)) { + new_dir->i_nlink--; + new_dir->i_dirt = 1; + } msdos_read_inode(free_inode); MSDOS_I(old_inode)->i_busy = 1; cache_inval_inode(old_inode); @@ -20,12 +20,6 @@ #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE]) /* - * comment out this line if you want names > MINIX_NAME_LEN chars to be - * truncated. Else they will be disallowed. - */ -/* #define NO_TRUNCATE */ - -/* * In order to reduce some races, while at the same time doing additional * checking and hopefully speeding things up, we copy filenames to the * kernel data space before using them.. @@ -105,8 +99,13 @@ int lookup(struct inode * dir,const char * name, int len, struct inode ** result) { struct super_block * sb; + int perm; *result = NULL; + if (!dir) + return -ENOENT; +/* check permissions before traversing mount-points */ + perm = permission(dir,MAY_EXEC); if (len==2 && name[0] == '.' && name[1] == '.') { if (dir == current->root) { *result = dir; @@ -115,17 +114,16 @@ int lookup(struct inode * dir,const char * name, int len, sb = dir->i_sb; iput(dir); dir = sb->s_covered; - if (dir) - dir->i_count++; + if (!dir) + return -ENOENT; + dir->i_count++; } } - if (!dir) - return -ENOENT; if (!dir->i_op || !dir->i_op->lookup) { iput(dir); return -ENOTDIR; } - if (!permission(dir,MAY_EXEC)) { + if (!perm) { iput(dir); return -EACCES; } @@ -305,9 +303,11 @@ int open_namei(const char * pathname, int flag, int mode, *res_inode=dir; return 0; } - dir->i_count++; /* lookup eats the dir */ - error = lookup(dir,basename,namelen,&inode); - if (error) { + for (i = 0; i < 5; i++) { /* races... */ + dir->i_count++; /* lookup eats the dir */ + error = lookup(dir,basename,namelen,&inode); + if (!error) + break; if (!(flag & O_CREAT)) { iput(dir); return error; @@ -324,7 +324,16 @@ int open_namei(const char * pathname, int flag, int mode, iput(dir); return -EROFS; } - return dir->i_op->create(dir,basename,namelen,mode,res_inode); + dir->i_count++; /* create eats the dir */ + error = dir->i_op->create(dir,basename,namelen,mode,res_inode); + if (error != -EEXIST) { + iput(dir); + return error; + } + } + if (error) { + iput(dir); + return error; } if ((flag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) { iput(dir); diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 81f3fd8..c5526f9 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -592,13 +592,8 @@ void nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) inode->i_op = &chrdev_inode_operations; else if (S_ISBLK(inode->i_mode)) inode->i_op = &blkdev_inode_operations; - else if (S_ISFIFO(inode->i_mode)) { - inode->i_op = &fifo_inode_operations; - inode->i_pipe = 1; - PIPE_BASE(*inode) = NULL; - PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0; - PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; - } + else if (S_ISFIFO(inode->i_mode)) + init_fifo(inode); else inode->i_op = NULL; } diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 7fa86d0..cf72f40 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -21,6 +21,7 @@ extern int close_fp(struct file *filp); static int nfs_notify_change(int, struct inode *); +static void nfs_put_inode(struct inode *); static void nfs_put_super(struct super_block *); static void nfs_statfs(struct super_block *, struct statfs *); @@ -28,12 +29,17 @@ static struct super_operations nfs_sops = { NULL, /* read inode */ nfs_notify_change, /* notify change */ NULL, /* write inode */ - NULL, /* put inode */ + nfs_put_inode, /* put inode */ nfs_put_super, /* put superblock */ NULL, /* write superblock */ nfs_statfs /* stat filesystem */ }; +static void nfs_put_inode(struct inode * inode) +{ + clear_inode(inode); +} + void nfs_put_super(struct super_block *sb) { close_fp(sb->u.nfs_sb.s_server.file); @@ -50,7 +56,8 @@ void nfs_put_super(struct super_block *sb) * Later we can add other mount parameters like caching values. */ -struct super_block *nfs_read_super(struct super_block *sb, void *raw_data) +struct super_block *nfs_read_super(struct super_block *sb, void *raw_data, + int silent) { struct nfs_mount_data *data = (struct nfs_mount_data *) raw_data; struct nfs_server *server; @@ -134,6 +141,9 @@ void nfs_statfs(struct super_block *sb, struct statfs *buf) put_fs_long(res.bavail, &buf->f_bavail); put_fs_long(0, &buf->f_files); put_fs_long(0, &buf->f_ffree); + /* We should really try to interrogate the remote server to find + it's maximum name length here */ + put_fs_long(NAME_MAX, &buf->f_namelen); } /* diff --git a/fs/proc/inode.c b/fs/proc/inode.c index e788d41..22ef31b 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -40,7 +40,8 @@ static struct super_operations proc_sops = { proc_statfs }; -struct super_block *proc_read_super(struct super_block *s,void *data) +struct super_block *proc_read_super(struct super_block *s,void *data, + int silent) { lock_super(s); s->s_blocksize = 1024; @@ -64,6 +65,7 @@ void proc_statfs(struct super_block *sb, struct statfs *buf) put_fs_long(0, &buf->f_bavail); put_fs_long(0, &buf->f_files); put_fs_long(0, &buf->f_ffree); + put_fs_long(NAME_MAX, &buf->f_namelen); /* Don't know what value to put in buf->f_fsid */ } @@ -101,8 +103,10 @@ void proc_read_inode(struct inode * inode) if (!pid) { inode->i_mode = S_IFREG | 0444; inode->i_op = &proc_array_inode_operations; - if (ino == 5) + if (ino == 5) { + inode->i_mode = S_IFREG | 0400; inode->i_op = &proc_kmsg_inode_operations; + } return; } ino &= 0x0000ffff; @@ -17,6 +17,7 @@ #include <asm/system.h> #include <asm/segment.h> + /* * The definition of file_systems that used to be here is now in @@ -29,6 +30,8 @@ extern struct file_operations * blkdev_fops[]; extern void wait_for_keypress(void); extern void fcntl_init_locks(void); +extern int root_mountflags; + struct super_block super_block[NR_SUPER]; /* this is initialized in init/main.c */ @@ -118,7 +121,8 @@ void put_super(dev_t dev) sb->s_op->put_super(sb); } -static struct super_block * read_super(dev_t dev,char *name,int flags,void *data) +static struct super_block * read_super(dev_t dev,char *name,int flags, + void *data, int silent) { struct super_block * s; struct file_system_type *type; @@ -142,7 +146,7 @@ static struct super_block * read_super(dev_t dev,char *name,int flags,void *data } s->s_dev = dev; s->s_flags = flags; - if (!type->read_super(s,data)) { + if (!type->read_super(s,data, silent)) { s->s_dev = 0; return NULL; } @@ -308,7 +312,7 @@ static int do_mount(dev_t dev, const char * dir, char * type, int flags, void * iput(dir_i); return -EBUSY; } - sb = read_super(dev,type,flags,data); + sb = read_super(dev,type,flags,data,0); if (!sb || sb->s_covered) { iput(dir_i); return -EBUSY; @@ -452,14 +456,17 @@ void mount_root(void) for (fs_type = file_systems; fs_type->read_super; fs_type++) { if (!fs_type->requires_dev) continue; - sb = read_super(ROOT_DEV,fs_type->name,0,NULL); + sb = read_super(ROOT_DEV,fs_type->name,root_mountflags,NULL,1); if (sb) { inode = sb->s_mounted; inode->i_count += 3 ; /* NOTE! it is logically used 4 times, not 1 */ sb->s_covered = inode; - sb->s_flags = 0; + sb->s_flags = root_mountflags; current->pwd = inode; current->root = inode; + printk ("VFS: Mounted root (%s filesystem)%s.\n", + fs_type->name, + (sb->s_flags & MS_RDONLY) ? " readonly" : ""); return; } } diff --git a/fs/xiafs/inode.c b/fs/xiafs/inode.c index 4d6ac37..d93e971 100644 --- a/fs/xiafs/inode.c +++ b/fs/xiafs/inode.c @@ -55,7 +55,8 @@ static struct super_operations xiafs_sops = { xiafs_statfs }; -struct super_block *xiafs_read_super(struct super_block *s, void *data) +struct super_block *xiafs_read_super(struct super_block *s, void *data, + int silent) { struct buffer_head *bh; struct xiafs_super_block *sp; @@ -75,7 +76,9 @@ struct super_block *xiafs_read_super(struct super_block *s, void *data) s->s_dev = 0; unlock_super(s); brelse(bh); - printk("XIA-FS: super magic mismatch\n"); + if (!silent) + printk("VFS: Can't find a xiafs filesystem on dev 0x%04x.\n", + dev); return NULL; } s->s_blocksize = sp->s_zone_size; @@ -150,6 +153,7 @@ void xiafs_statfs(struct super_block *sb, struct statfs *buf) put_fs_long(tmp, &buf->f_bavail); put_fs_long(sb->u.xiafs_sb.s_ninodes, &buf->f_files); put_fs_long(xiafs_count_free_inodes(sb), &buf->f_ffree); + put_fs_long(_XIAFS_NAME_LEN, &buf->f_namelen); /* don't know what should be put in buf->f_fsid */ } @@ -394,14 +398,8 @@ void xiafs_read_inode(struct inode * inode) inode->i_op = &chrdev_inode_operations; else if (S_ISBLK(inode->i_mode)) inode->i_op = &blkdev_inode_operations; - else if (S_ISFIFO(inode->i_mode)) { - inode->i_op = &fifo_inode_operations; - inode->i_pipe = 1; - PIPE_BASE(*inode) = NULL; - PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0; - PIPE_READ_WAIT(*inode) = PIPE_WRITE_WAIT(*inode) = NULL; - PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; - } + else if (S_ISFIFO(inode->i_mode)) + init_fifo(inode); } void xiafs_write_inode(struct inode * inode) diff --git a/fs/xiafs/namei.c b/fs/xiafs/namei.c index faaf466..9a5dfd4 100644 --- a/fs/xiafs/namei.c +++ b/fs/xiafs/namei.c @@ -37,12 +37,12 @@ static int xiafs_match(int len, const char * name, struct xiafs_direct * dep) /* "" means "." ---> so paths like "/usr/lib//libc.a" work */ if (!len && (dep->d_name[0]=='.') && (dep->d_name[1]=='\0')) return 1; - if (len < _XIAFS_NAME_LEN && dep->d_name[len]) + if (len != dep->d_name_len) return 0; for (i=0; i < len; i++) if (*name++ != dep->d_name[i]) - break; - return (i==len) ? 1 : 0; + return 0; + return 1; } /* @@ -306,14 +306,8 @@ int xiafs_mknod(struct inode *dir, const char *name, int len, int mode, int rdev inode->i_op = &chrdev_inode_operations; else if (S_ISBLK(inode->i_mode)) inode->i_op = &blkdev_inode_operations; - else if (S_ISFIFO(inode->i_mode)) { - inode->i_op = &fifo_inode_operations; - inode->i_pipe = 1; - PIPE_BASE(*inode) = NULL; - PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0; - PIPE_READ_WAIT(*inode) = PIPE_WRITE_WAIT(*inode) = NULL; - PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; - } + else if (S_ISFIFO(inode->i_mode)) + init_fifo(inode); if (S_ISBLK(mode) || S_ISCHR(mode)) inode->i_rdev = rdev; inode->i_atime = inode->i_ctime = inode->i_atime = CURRENT_TIME; diff --git a/include/linux/errno.h b/include/linux/errno.h index 1a40c1a..792213f 100644 --- a/include/linux/errno.h +++ b/include/linux/errno.h @@ -126,5 +126,6 @@ /* Should never be seen by user programs */ #define ERESTARTSYS 512 #define ERESTARTNOINTR 513 +#define ERESTARTNOHAND 514 /* restart if no handler.. */ #endif diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h index 145e62a..f77a678 100644 --- a/include/linux/ext2_fs.h +++ b/include/linux/ext2_fs.h @@ -23,7 +23,7 @@ /* * The second extended file system version */ -#define EXT2FS_VERSION "0.2c, 93/03/06" +#define EXT2FS_VERSION "0.2d, 93/03/30" /* * Special inodes numbers @@ -138,10 +138,11 @@ struct ext2_inode { unsigned long i_version; /* File version (for NFS) */ unsigned long i_file_acl; /* File ACL */ unsigned long i_dir_acl; /* Directory ACL */ - unsigned short i_faddr; /* Fragment address */ + unsigned long i_faddr; /* Fragment address */ unsigned char i_frag; /* Fragment number */ unsigned char i_fsize; /* Fragment size */ - unsigned long i_reserved2[3]; + unsigned short i_pad1; + unsigned long i_reserved2[2]; }; /* @@ -155,7 +156,7 @@ struct ext2_super_block { unsigned long s_free_inodes_count;/* Free inodes count */ unsigned long s_first_data_block;/* First Data Block */ unsigned long s_log_block_size; /* Block size */ - unsigned long s_log_frag_size; /* Fragment size */ + long s_log_frag_size; /* Fragment size */ unsigned long s_blocks_per_group;/* # Blocks per group */ unsigned long s_frags_per_group;/* # Fragments per group */ unsigned long s_inodes_per_group;/* # Inodes per group */ @@ -212,6 +213,11 @@ extern void ext2_dcache_add (unsigned short, unsigned long, const char *, extern void ext2_dcache_remove (unsigned short, unsigned long, const char *, int); +/* dir.c */ +extern int ext2_check_dir_entry (char *, struct inode *, + struct ext2_dir_entry *, struct buffer_head *, + unsigned int); + /* file.c */ extern int ext2_read (struct inode *, struct file *, char *, int); extern int ext2_write (struct inode *, struct file *, char *, int); @@ -230,7 +236,7 @@ extern struct buffer_head * ext2_bread (struct inode *, int, int); extern void ext2_truncate (struct inode *); extern void ext2_put_super (struct super_block *); extern void ext2_write_super (struct super_block *); -extern struct super_block * ext2_read_super (struct super_block *,void *); +extern struct super_block * ext2_read_super (struct super_block *,void *,int); extern void ext2_read_inode (struct inode *); extern void ext2_write_inode (struct inode *); extern void ext2_put_inode (struct inode *); @@ -255,19 +261,10 @@ extern int ext2_rename (struct inode *, const char *, int, * Inodes and files operations */ -/* blkdev.c */ -extern struct inode_operations ext2_blkdev_inode_operations; - -/* chrdev.c */ -extern struct inode_operations ext2_chrdev_inode_operations; - /* dir.c */ extern struct inode_operations ext2_dir_inode_operations; extern struct file_operations ext2_dir_operations; -/* fifo.c */ -extern struct inode_operations ext2_fifo_inode_operations; - /* file.c */ extern struct inode_operations ext2_file_inode_operations; extern struct file_operations ext2_file_operations; diff --git a/include/linux/ext2_fs_i.h b/include/linux/ext2_fs_i.h index 09d1371..ce26502 100644 --- a/include/linux/ext2_fs_i.h +++ b/include/linux/ext2_fs_i.h @@ -6,14 +6,17 @@ */ struct ext2_inode_info { unsigned long i_flags; - unsigned short i_faddr; + unsigned long i_faddr; unsigned char i_frag; unsigned char i_fsize; + unsigned short i_pad1; unsigned long i_file_acl; unsigned long i_dir_acl; unsigned long i_dtime; unsigned long i_version; unsigned long i_block_group; + unsigned long i_next_alloc_block; + unsigned long i_next_alloc_goal; unsigned long i_data[15]; }; diff --git a/include/linux/ext2_fs_sb.h b/include/linux/ext2_fs_sb.h index 69d2bc1..81c5249 100644 --- a/include/linux/ext2_fs_sb.h +++ b/include/linux/ext2_fs_sb.h @@ -13,7 +13,7 @@ struct ext2_sb_info { unsigned long s_r_blocks_count; /* Reserved blocks count */ unsigned long s_first_data_block;/* First data block */ unsigned long s_log_block_size; /* Log of block size */ - unsigned long s_log_frag_size; /* Log of fragment size */ + long s_log_frag_size; /* Log of fragment size */ unsigned long s_frag_size; /* Size of a fragment in bytes */ unsigned long s_frags_per_block;/* Number of fragments per block */ unsigned long s_inodes_per_block;/* Number of inodes per block */ @@ -31,6 +31,7 @@ struct ext2_sb_info { unsigned long s_block_bitmap_number[EXT2_MAX_GROUP_LOADED]; struct buffer_head * s_block_bitmap[EXT2_MAX_GROUP_LOADED]; int s_rename_lock; + int s_was_mounted_valid; struct wait_queue * s_rename_wait; }; diff --git a/include/linux/ext_fs.h b/include/linux/ext_fs.h index a8b455d..4f5c6ad 100644 --- a/include/linux/ext_fs.h +++ b/include/linux/ext_fs.h @@ -89,7 +89,7 @@ extern struct buffer_head * ext_bread(struct inode *, int, int); extern void ext_truncate(struct inode *); extern void ext_put_super(struct super_block *); extern void ext_write_super(struct super_block *); -extern struct super_block *ext_read_super(struct super_block *,void *); +extern struct super_block *ext_read_super(struct super_block *,void *,int); extern void ext_read_inode(struct inode *); extern void ext_write_inode(struct inode *); extern void ext_put_inode(struct inode *); diff --git a/include/linux/fs.h b/include/linux/fs.h index c1259b0..d149e6a 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -300,7 +300,7 @@ struct super_operations { }; struct file_system_type { - struct super_block *(*read_super) (struct super_block *, void *); + struct super_block *(*read_super) (struct super_block *, void *, int); char *name; int requires_dev; }; @@ -318,7 +318,7 @@ extern int chrdev_open(struct inode * inode, struct file * filp); extern struct file_operations def_chr_fops; extern struct inode_operations chrdev_inode_operations; -extern struct inode_operations fifo_inode_operations; +extern void init_fifo(struct inode * inode); extern struct file_system_type *get_fs_type(char *name); diff --git a/include/linux/iso_fs.h b/include/linux/iso_fs.h index a144561..20daac3 100644 --- a/include/linux/iso_fs.h +++ b/include/linux/iso_fs.h @@ -163,7 +163,7 @@ extern int isofs_free_block(int dev, int block); extern int isofs_bmap(struct inode *,int); extern void isofs_put_super(struct super_block *); -extern struct super_block *isofs_read_super(struct super_block *,void *); +extern struct super_block *isofs_read_super(struct super_block *,void *,int); extern void isofs_read_inode(struct inode *); extern void isofs_put_inode(struct inode *); extern void isofs_statfs(struct super_block *, struct statfs *); diff --git a/include/linux/minix_fs.h b/include/linux/minix_fs.h index 90d2252..a48badb 100644 --- a/include/linux/minix_fs.h +++ b/include/linux/minix_fs.h @@ -99,7 +99,7 @@ extern struct buffer_head * minix_bread(struct inode *, int, int); extern void minix_truncate(struct inode *); extern void minix_put_super(struct super_block *); -extern struct super_block *minix_read_super(struct super_block *,void *); +extern struct super_block *minix_read_super(struct super_block *,void *,int); extern void minix_read_inode(struct inode *); extern void minix_write_inode(struct inode *); extern void minix_put_inode(struct inode *); diff --git a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h index cb87458..d32841c 100644 --- a/include/linux/msdos_fs.h +++ b/include/linux/msdos_fs.h @@ -168,7 +168,8 @@ extern int msdos_rename(struct inode *old_dir,const char *old_name,int old_len, extern void msdos_put_inode(struct inode *inode); extern void msdos_put_super(struct super_block *sb); -extern struct super_block *msdos_read_super(struct super_block *s,void *data); +extern struct super_block *msdos_read_super(struct super_block *s, + void *data,int); extern void msdos_statfs(struct super_block *sb,struct statfs *buf); extern int msdos_bmap(struct inode *inode,int block); extern void msdos_read_inode(struct inode *inode); diff --git a/include/linux/msdos_fs_sb.h b/include/linux/msdos_fs_sb.h index 025d250..8e5f304 100644 --- a/include/linux/msdos_fs_sb.h +++ b/include/linux/msdos_fs_sb.h @@ -14,6 +14,7 @@ struct msdos_sb_info { unsigned long clusters; /* number of clusters */ uid_t fs_uid; gid_t fs_gid; + int quiet; /* fake successful chmods and chowns */ unsigned short fs_umask; unsigned char name_check; /* r = relaxed, n = normal, s = strict */ unsigned char conversion; /* b = binary, t = text, a = auto */ diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index ba8dff7..23f8c67 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -98,7 +98,8 @@ extern int nfs_rpc_call(struct nfs_server *server, int *start, int *end); /* linux/fs/nfs/inode.c */ -extern struct super_block *nfs_read_super(struct super_block *sb, void *data); +extern struct super_block *nfs_read_super(struct super_block *sb, + void *data,int); extern struct inode *nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle, struct nfs_fattr *fattr); extern void nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr); diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index 8ff2eba..cf40473 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -15,7 +15,7 @@ struct proc_dir_entry { char * name; }; -extern struct super_block *proc_read_super(struct super_block *,void *); +extern struct super_block *proc_read_super(struct super_block *,void *,int); extern void proc_put_inode(struct inode *); extern void proc_put_super(struct super_block *); extern void proc_statfs(struct super_block *, struct statfs *); diff --git a/include/linux/signal.h b/include/linux/signal.h index 2886cd5..0f3a16a 100644 --- a/include/linux/signal.h +++ b/include/linux/signal.h @@ -52,7 +52,17 @@ typedef unsigned int sigset_t; /* 32 bits */ #define SIGLOST 29 */ +/* + * sa_flags values: SA_STACK is not currently supported, but will allow the + * usage of signal stacks by using the (now obsolete) sa_restorer field in + * the sigaction structure as a stack pointer. This is now possible due to + * the changes in signal handling. LBT 010493. + * SA_RESTART is a no-op, as restarting is the default anyway. Use the + * SA_INTERRUPT flag to get interrupting signals.. + */ #define SA_NOCLDSTOP 1 +#define SA_STACK 0x08000000 +#define SA_RESTART 0x10000000 #define SA_INTERRUPT 0x20000000 #define SA_NOMASK 0x40000000 #define SA_ONESHOT 0x80000000 diff --git a/include/linux/sys.h b/include/linux/sys.h index d6c15ea..da5b1a9 100644 --- a/include/linux/sys.h +++ b/include/linux/sys.h @@ -121,6 +121,7 @@ extern int sys_swapoff(); extern int sys_sysinfo(); extern int sys_ipc(); extern int sys_fsync(); +extern int sys_sigreturn(); fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read, sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link, @@ -143,7 +144,7 @@ sys_ftruncate, sys_fchmod, sys_fchown, sys_getpriority, sys_setpriority, sys_profil, sys_statfs, sys_fstatfs, sys_ioperm, sys_socketcall, sys_syslog, sys_setitimer, sys_getitimer, sys_newstat, sys_newlstat, sys_newfstat, sys_newuname, sys_iopl, sys_vhangup, sys_idle, sys_vm86, -sys_wait4, sys_swapoff, sys_sysinfo, sys_ipc, sys_fsync }; +sys_wait4, sys_swapoff, sys_sysinfo, sys_ipc, sys_fsync, sys_sigreturn }; /* So we don't have to do any more manual updating.... */ int NR_syscalls = sizeof(sys_call_table)/sizeof(fn_ptr); diff --git a/include/linux/timer.h b/include/linux/timer.h index 868b2a3..eb6d6cc 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -23,7 +23,7 @@ * * NET_TIMER tcp/ip timeout timer * - * MISC_TIMER reserved for special uses like the 387 timeouts etc + * COPRO_TIMER 387 timeout for buggy hardware.. */ #define BLANK_TIMER 0 @@ -35,7 +35,7 @@ #define SCSI_TIMER 18 #define NET_TIMER 19 #define SOUND_TIMER 20 -#define MISC_TIMER 21 +#define COPRO_TIMER 21 struct timer_struct { unsigned long expires; diff --git a/include/linux/unistd.h b/include/linux/unistd.h index c422f64..d7581dd 100644 --- a/include/linux/unistd.h +++ b/include/linux/unistd.h @@ -125,6 +125,7 @@ #define __NR_sysinfo 116 #define __NR_ipc 117 /* not implemented yet */ #define __NR_fsync 118 /* not implemented yet */ +#define __NR_sigreturn 119 extern int errno; diff --git a/include/linux/xia_fs.h b/include/linux/xia_fs.h index 9d9b5f3..2d4abff 100644 --- a/include/linux/xia_fs.h +++ b/include/linux/xia_fs.h @@ -92,7 +92,7 @@ extern struct buffer_head * xiafs_bread(struct inode *, int, int); extern void xiafs_truncate(struct inode *); extern void xiafs_put_super(struct super_block *); -extern struct super_block *xiafs_read_super(struct super_block *,void *); +extern struct super_block *xiafs_read_super(struct super_block *,void *,int); extern void xiafs_read_inode(struct inode *); extern void xiafs_write_inode(struct inode *); extern void xiafs_put_inode(struct inode *); diff --git a/init/main.c b/init/main.c index 09d7d44..272cae2 100644 --- a/init/main.c +++ b/init/main.c @@ -19,6 +19,7 @@ #include <linux/unistd.h> #include <linux/string.h> #include <linux/timer.h> +#include <linux/fs.h> extern unsigned long * prof_buffer; extern unsigned long prof_len; @@ -79,6 +80,7 @@ extern unsigned long scsi_dev_init(unsigned long, unsigned long); #define EXT_MEM_K (*(unsigned short *)0x90002) #define DRIVE_INFO (*(struct drive_info *)0x90080) #define SCREEN_INFO (*(struct screen_info *)0x90000) +#define MOUNT_ROOT_RDONLY (*(unsigned short *)0x901F2) #define RAMDISK_SIZE (*(unsigned short *)0x901F8) #define ORIG_ROOT_DEV (*(unsigned short *)0x901FC) #define AUX_DEVICE_INFO (*(unsigned char *)0x901FF) @@ -152,6 +154,9 @@ struct screen_info screen_info; unsigned char aux_device_present; int ramdisk_size; +int root_mountflags = 0; + +static char fpu_error = 0; static char command_line[80] = { 0, }; @@ -186,6 +191,14 @@ static void parse_options(char *line) ROOT_DEV = simple_strtoul(line+5,NULL,16); continue; } + if (!strcmp(line,"ro")) { + root_mountflags |= MS_RDONLY; + continue; + } + if (!strcmp(line,"rw")) { + root_mountflags &= ~MS_RDONLY; + continue; + } /* * Then check if it's an environment variable or * an option. @@ -206,13 +219,13 @@ static void parse_options(char *line) static void copro_timeout(void) { -#ifdef CONFIG_MATH_EMULATION - printk(" Trying to use software floating point\n"); - hard_math = 0; - __asm__("movl %%cr0,%%eax ; xorl $6,%%eax ; movl %%eax,%%cr0":::"ax"); -#else - printk(" No software floating point - tough cookies\n"); -#endif + fpu_error = 1; + timer_table[COPRO_TIMER].expires = jiffies+100; + timer_active |= 1<<COPRO_TIMER; + printk("387 failed: trying to reset\n"); + send_sig(SIGFPE, last_task_used_math, 1); + outb_p(0,0xf1); + outb_p(0,0xf0); } void start_kernel(void) @@ -232,6 +245,8 @@ void start_kernel(void) if (memory_end > 16*1024*1024) memory_end = 16*1024*1024; #endif + if (MOUNT_ROOT_RDONLY) + root_mountflags |= MS_RDONLY; if ((unsigned long)&end >= (1024*1024)) { memory_start = (unsigned long) &end; low_memory_start = 4096; @@ -278,18 +293,18 @@ void start_kernel(void) if (hard_math) { unsigned short control_word; - timer_table[MISC_TIMER].expires = jiffies+100; - timer_table[MISC_TIMER].fn = copro_timeout; - timer_active |= 1<<MISC_TIMER; - printk("You have a bad 386/387 coupling."); - __asm__("fninit ; fnstcw %0 ; fwait":"=m" (*&control_word)); + timer_table[COPRO_TIMER].expires = jiffies+50; + timer_table[COPRO_TIMER].fn = copro_timeout; + timer_active |= 1<<COPRO_TIMER; + printk("You have a bad 386/387 coupling.\r"); + __asm__("clts ; fninit ; fnstcw %0 ; fwait":"=m" (*&control_word)); control_word &= 0xffc0; __asm__("fldcw %0 ; fwait"::"m" (*&control_word)); outb_p(inb_p(0x21) | (1 << 2), 0x21); __asm__("fldz ; fld1 ; fdiv %st,%st(1) ; fwait"); - timer_active &= ~(1<<MISC_TIMER); - if (hard_math) - printk("\rMath coprocessor using %s error reporting.\n", + timer_active &= ~(1<<COPRO_TIMER); + if (!fpu_error) + printk("Math coprocessor using %s error reporting.\n", ignore_irq13?"exception 16":"irq13"); } move_to_user_mode(); diff --git a/kernel/FPU-emu/Makefile b/kernel/FPU-emu/Makefile index 2a1e34c..96ac5e1 100644 --- a/kernel/FPU-emu/Makefile +++ b/kernel/FPU-emu/Makefile @@ -24,6 +24,7 @@ OBJS := $(OBJS) div_small.o errors.o\ load_store.o get_address.o\ poly_atan.o poly_l2.o poly_2xm1.o poly_sin.o poly_tan.o\ poly_div.o poly_mul64.o polynomial.o\ + precision.o\ reg_add_sub.o reg_compare.o reg_constant.o reg_ld_str.o\ reg_div.o reg_mul.o reg_norm.o \ reg_u_add.o reg_u_div.o reg_u_mul.o reg_u_sub.o\ diff --git a/kernel/FPU-emu/README b/kernel/FPU-emu/README index 062bb3b..1de54ff 100644 --- a/kernel/FPU-emu/README +++ b/kernel/FPU-emu/README @@ -1,7 +1,8 @@ +---------------------------------------------------------------------------+ | wm-FPU-emu an FPU emulator for 80386 and 80486SX microprocessors. | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | This program is free software; you can redistribute it and/or modify | @@ -43,14 +44,7 @@ Please report bugs, etc to me at: --Bill Metzenthen - Oct 1992 - - -[ note: I have advanced the version number from alpha numbers - to beta numbers. This is not meant to indicate any major - changes but rather the fact that the emulator has been a - standard part of the Linux distribution for some months - and is currently reasonably stable. WM Jan 1993 ] + March 1993 ----------------------- Internals of wm-FPU-emu ----------------------- @@ -89,7 +83,7 @@ is confined to five files: ----------------------- Limitations of wm-FPU-emu ----------------------- There are a number of differences between the current wm-FPU-emu -(version beta 1.0) and the 80486 FPU (apart from bugs). Some of the +(version beta 1.2) and the 80486 FPU (apart from bugs). Some of the more important differences are listed below: Internal computations do not use de-normal numbers (but External @@ -97,9 +91,12 @@ de-normals ARE recognised and generated). The design of wm-FPU-emu allows a larger exponent range than the 80486 FPU for internal computations. -All computations are performed at full 64 bit precision (the PC bits -of the FPU control word are ignored). Under Linux, the FPU normally -runs at 64 bits precision. +All computations are performed at full 64 bit precision with `round to +nearest or even' performed for the basic functions. The results of the +basic arithmetic functions and sqrt are then rounded to lower +precision if required by the PC bits of the FPU control word. Under +the crt0 version for Linux current at March 1993, the FPU PC bits +specify 53 bits precision. The precision flag (PE of the FPU status word) is not implemented. Does anyone write code which uses this feature? diff --git a/kernel/FPU-emu/Reg_constant.c b/kernel/FPU-emu/Reg_constant.c deleted file mode 100644 index aadf589..0000000 --- a/kernel/FPU-emu/Reg_constant.c +++ /dev/null @@ -1,111 +0,0 @@ -/*---------------------------------------------------------------------------+ - | reg_constant.c | - | | - | All of the constant FPU_REGs | - | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | - | | - | | - +---------------------------------------------------------------------------*/ - -#include "fpu_system.h" -#include "fpu_emu.h" -#include "status_w.h" -#include "reg_constant.h" - - -FPU_REG CONST_1 = { SIGN_POS, TW_Valid, EXP_BIAS, - 0x00000000, 0x80000000 }; -FPU_REG CONST_2 = { SIGN_POS, TW_Valid, EXP_BIAS+1, - 0x00000000, 0x80000000 }; -FPU_REG CONST_HALF = { SIGN_POS, TW_Valid, EXP_BIAS-1, - 0x00000000, 0x80000000 }; -FPU_REG CONST_L2T = { SIGN_POS, TW_Valid, EXP_BIAS+1, - 0xcd1b8afe, 0xd49a784b }; -FPU_REG CONST_L2E = { SIGN_POS, TW_Valid, EXP_BIAS, - 0x5c17f0bc, 0xb8aa3b29 }; -FPU_REG CONST_PI = { SIGN_POS, TW_Valid, EXP_BIAS+1, - 0x2168c235, 0xc90fdaa2 }; -FPU_REG CONST_PI2 = { SIGN_POS, TW_Valid, EXP_BIAS, - 0x2168c235, 0xc90fdaa2 }; -FPU_REG CONST_PI4 = { SIGN_POS, TW_Valid, EXP_BIAS-1, - 0x2168c235, 0xc90fdaa2 }; -FPU_REG CONST_LG2 = { SIGN_POS, TW_Valid, EXP_BIAS-2, - 0xfbcff799, 0x9a209a84 }; -FPU_REG CONST_LN2 = { SIGN_POS, TW_Valid, EXP_BIAS-1, - 0xd1cf79ac, 0xb17217f7 }; - -/* Only the sign (and tag) is used in internal zeroes */ -FPU_REG CONST_Z = { SIGN_POS, TW_Zero, 0, 0x0, 0x0 }; - -/* Only the sign and significand (and tag) are used in internal NaNs */ -/* The 80486 never generates one of these -FPU_REG CONST_SNAN = { SIGN_POS, TW_NaN, EXP_OVER, 0x00000001, 0x80000000 }; - */ -/* This is the real indefinite QNaN */ -FPU_REG CONST_QNaN = { SIGN_NEG, TW_NaN, EXP_OVER, 0x00000000, 0xC0000000 }; - -/* Only the sign (and tag) is used in internal infinities */ -FPU_REG CONST_INF = { SIGN_POS, TW_Infinity, EXP_OVER, 0x00000000, 0x80000000 }; - - - -static void fld_const(FPU_REG *c) -{ - FPU_REG *st_new_ptr; - - if ( STACK_OVERFLOW ) - { - stack_overflow(); - return; - } - push(); - reg_move(c, FPU_st0_ptr); - status_word &= ~SW_C1; -} - - -static void fld1() -{ - fld_const(&CONST_1); -} - -static void fldl2t() -{ - fld_const(&CONST_L2T); -} - -static void fldl2e() -{ - fld_const(&CONST_L2E); -} - -static void fldpi() -{ - fld_const(&CONST_PI); -} - -static void fldlg2() -{ - fld_const(&CONST_LG2); -} - -static void fldln2() -{ - fld_const(&CONST_LN2); -} - -static void fldz() -{ - fld_const(&CONST_Z); -} - -static FUNC constants_table[] = { - fld1, fldl2t, fldl2e, fldpi, fldlg2, fldln2, fldz, Un_impl -}; - -void fconst() -{ - (constants_table[FPU_rm])(); -} diff --git a/kernel/FPU-emu/Reg_constant.h b/kernel/FPU-emu/Reg_constant.h deleted file mode 100644 index b6ee963..0000000 --- a/kernel/FPU-emu/Reg_constant.h +++ /dev/null @@ -1,31 +0,0 @@ -/*---------------------------------------------------------------------------+ - | reg_constant.h | - | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | - | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | - | | - +---------------------------------------------------------------------------*/ - -#ifndef _REG_CONSTANT_H_ -#define _REG_CONSTANT_H_ - -#include "fpu_emu.h" - -extern FPU_REG CONST_1; -extern FPU_REG CONST_2; -extern FPU_REG CONST_HALF; -extern FPU_REG CONST_L2T; -extern FPU_REG CONST_L2E; -extern FPU_REG CONST_PI; -extern FPU_REG CONST_PI2; -extern FPU_REG CONST_PI4; -extern FPU_REG CONST_LG2; -extern FPU_REG CONST_LN2; -extern FPU_REG CONST_Z; -extern FPU_REG CONST_PINF; -extern FPU_REG CONST_INF; -extern FPU_REG CONST_MINF; -extern FPU_REG CONST_QNaN; - -#endif _REG_CONSTANT_H_ - diff --git a/kernel/FPU-emu/control_w.h b/kernel/FPU-emu/control_w.h index 237a70b..fedcfd4 100644 --- a/kernel/FPU-emu/control_w.h +++ b/kernel/FPU-emu/control_w.h @@ -1,7 +1,8 @@ /*---------------------------------------------------------------------------+ | control_w.h | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | +---------------------------------------------------------------------------*/ @@ -30,4 +31,22 @@ #define RC_UP _Const_(0x0800) #define RC_CHOP _Const_(0x0C00) +/* p 15-5: Precision control bits affect only the following: + ADD, SUB(R), MUL, DIV(R), and SQRT */ +#define PRECISION_ADJUST_CONTROL (control_word & 0x300) +#define PR_24_BITS 0x000 +#define PR_53_BITS 0x200 +/* By doing this as a macro, we allow easy modification */ +#define PRECISION_ADJUST(x) \ + switch (PRECISION_ADJUST_CONTROL) \ + { \ + case PR_24_BITS: \ + round_to_24_bits(x); \ + break; \ + case PR_53_BITS: \ + round_to_53_bits(x); \ + break; \ + } + + #endif _CONTROLW_H_ diff --git a/kernel/FPU-emu/errors.c b/kernel/FPU-emu/errors.c index 07d5d12..1a003b8 100644 --- a/kernel/FPU-emu/errors.c +++ b/kernel/FPU-emu/errors.c @@ -3,7 +3,8 @@ | | | The error handling functions for wm-FPU-emu | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | | diff --git a/kernel/FPU-emu/fpu_arith.c b/kernel/FPU-emu/fpu_arith.c index fb86989..b27a747 100644 --- a/kernel/FPU-emu/fpu_arith.c +++ b/kernel/FPU-emu/fpu_arith.c @@ -1,9 +1,10 @@ /*---------------------------------------------------------------------------+ | fpu_arith.c | | | - | Code to implement the FPU register/register arithmetis instructions | + | Code to implement the FPU register/register arithmetic instructions | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | | @@ -11,12 +12,14 @@ #include "fpu_system.h" #include "fpu_emu.h" +#include "control_w.h" void fadd__() { /* fadd st,st(i) */ reg_add(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr); + PRECISION_ADJUST(FPU_st0_ptr); } @@ -24,6 +27,7 @@ void fmul__() { /* fmul st,st(i) */ reg_mul(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr); + PRECISION_ADJUST(FPU_st0_ptr); } @@ -32,6 +36,7 @@ void fsub__() { /* fsub st,st(i) */ reg_sub(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr); + PRECISION_ADJUST(FPU_st0_ptr); } @@ -39,6 +44,7 @@ void fsubr_() { /* fsubr st,st(i) */ reg_sub(&st(FPU_rm), FPU_st0_ptr, FPU_st0_ptr); + PRECISION_ADJUST(FPU_st0_ptr); } @@ -46,6 +52,7 @@ void fdiv__() { /* fdiv st,st(i) */ reg_div(FPU_st0_ptr, &st(FPU_rm), FPU_st0_ptr); + PRECISION_ADJUST(FPU_st0_ptr); } @@ -53,6 +60,7 @@ void fdivr_() { /* fdivr st,st(i) */ reg_div(&st(FPU_rm), FPU_st0_ptr, FPU_st0_ptr); + PRECISION_ADJUST(FPU_st0_ptr); } @@ -61,6 +69,7 @@ void fadd_i() { /* fadd st(i),st */ reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm)); + PRECISION_ADJUST(&st(FPU_rm)); } @@ -68,6 +77,7 @@ void fmul_i() { /* fmul st(i),st */ reg_mul(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm)); + PRECISION_ADJUST(&st(FPU_rm)); } @@ -77,6 +87,7 @@ void fsubri() /* This is the sense of the 80486 manual reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm)); */ reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm)); + PRECISION_ADJUST(&st(FPU_rm)); } @@ -86,6 +97,7 @@ void fsub_i() /* This is the sense of the 80486 manual reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm)); */ reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm)); + PRECISION_ADJUST(&st(FPU_rm)); } @@ -93,6 +105,7 @@ void fdivri() { /* fdivr st(i),st */ reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm)); + PRECISION_ADJUST(&st(FPU_rm)); } @@ -100,6 +113,7 @@ void fdiv_i() { /* fdiv st(i),st */ reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm)); + PRECISION_ADJUST(&st(FPU_rm)); } @@ -108,6 +122,7 @@ void faddp_() { /* faddp st(i),st */ reg_add(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm)); + PRECISION_ADJUST(&st(FPU_rm)); pop(); } @@ -116,6 +131,7 @@ void fmulp_() { /* fmulp st(i),st */ reg_mul(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm)); + PRECISION_ADJUST(&st(FPU_rm)); pop(); } @@ -127,6 +143,7 @@ void fsubrp() /* This is the sense of the 80486 manual reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm)); */ reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm)); + PRECISION_ADJUST(&st(FPU_rm)); pop(); } @@ -137,6 +154,7 @@ void fsubp_() /* This is the sense of the 80486 manual reg_sub(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm)); */ reg_sub(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm)); + PRECISION_ADJUST(&st(FPU_rm)); pop(); } @@ -145,6 +163,7 @@ void fdivrp() { /* fdivrp st(i),st */ reg_div(FPU_st0_ptr, &st(FPU_rm), &st(FPU_rm)); + PRECISION_ADJUST(&st(FPU_rm)); pop(); } @@ -153,6 +172,7 @@ void fdivp_() { /* fdivp st(i),st */ reg_div(&st(FPU_rm), FPU_st0_ptr, &st(FPU_rm)); + PRECISION_ADJUST(&st(FPU_rm)); pop(); } diff --git a/kernel/FPU-emu/fpu_aux.c b/kernel/FPU-emu/fpu_aux.c index 8d6043f..107a37c 100644 --- a/kernel/FPU-emu/fpu_aux.c +++ b/kernel/FPU-emu/fpu_aux.c @@ -3,7 +3,8 @@ | | | Code to implement some of the FPU auxiliary instructions. | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | | diff --git a/kernel/FPU-emu/fpu_emu.h b/kernel/FPU-emu/fpu_emu.h index 43354e4..770437b 100644 --- a/kernel/FPU-emu/fpu_emu.h +++ b/kernel/FPU-emu/fpu_emu.h @@ -1,7 +1,8 @@ /*---------------------------------------------------------------------------+ | fpu_emu.h | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | +---------------------------------------------------------------------------*/ diff --git a/kernel/FPU-emu/fpu_entry.c b/kernel/FPU-emu/fpu_entry.c index b425849..870a515 100644 --- a/kernel/FPU-emu/fpu_entry.c +++ b/kernel/FPU-emu/fpu_entry.c @@ -3,7 +3,8 @@ | | | The entry function for wm-FPU-emu | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | See the files "README" and "COPYING" for further copyright and warranty | @@ -31,6 +32,7 @@ #include "fpu_system.h" #include "fpu_emu.h" #include "exception.h" +#include "control_w.h" #include <asm/segment.h> @@ -157,7 +159,10 @@ void math_emulate(long arg) /* We cannot handle emulation in v86-mode */ if (FPU_EFLAGS & 0x00020000) - math_abort(FPU_info,SIGILL); + { + FPU_ORIG_EIP = FPU_EIP; + math_abort(FPU_info,SIGILL); + } /* 0x000f means user code space */ if (FPU_CS != 0x000f) @@ -227,10 +232,12 @@ do_another: break; case 2: /* fcom */ compare_st_data(); + goto no_precision_adjust; break; case 3: /* fcomp */ compare_st_data(); pop(); + goto no_precision_adjust; break; case 4: /* fsub */ reg_sub(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr); @@ -245,6 +252,9 @@ do_another: reg_div(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr); break; } + PRECISION_ADJUST(FPU_st0_ptr); +no_precision_adjust: + ; } else stack_underflow(); @@ -328,9 +338,14 @@ test_for_fp: void __math_abort(struct info * info, unsigned int signal) { + RE_ENTRANT_CHECK_OFF FPU_EIP = FPU_ORIG_EIP; send_sig(signal,current,1); + RE_ENTRANT_CHECK_OFF __asm__("movl %0,%%esp ; ret"::"g" (((long) info)-4)); +#ifdef PARANOID + printk("ERROR: wm-FPU-emu math_abort failed!\n"); +#endif PARANOID } #else /* no math emulation */ diff --git a/kernel/FPU-emu/fpu_etc.c b/kernel/FPU-emu/fpu_etc.c index f209694..b7dc7e8 100644 --- a/kernel/FPU-emu/fpu_etc.c +++ b/kernel/FPU-emu/fpu_etc.c @@ -3,7 +3,8 @@ | | | Implement a few FPU instructions. | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | | diff --git a/kernel/FPU-emu/fpu_proto.h b/kernel/FPU-emu/fpu_proto.h index dddee75..525b119 100644 --- a/kernel/FPU-emu/fpu_proto.h +++ b/kernel/FPU-emu/fpu_proto.h @@ -2,11 +2,11 @@ extern void Un_impl(void); extern void emu_printall(void); extern void exception(int n); -extern void real_2op_NaN(struct fpu_reg *a, struct fpu_reg *b, struct fpu_reg *dest); -extern void arith_invalid(struct fpu_reg *dest); -extern void divide_by_zero(int sign, struct fpu_reg *dest); -extern void arith_overflow(struct fpu_reg *dest); -extern void arith_underflow(struct fpu_reg *dest); +extern void real_2op_NaN(FPU_REG *a, FPU_REG *b, FPU_REG *dest); +extern void arith_invalid(FPU_REG *dest); +extern void divide_by_zero(int sign, FPU_REG *dest); +extern void arith_overflow(FPU_REG *dest); +extern void arith_underflow(FPU_REG *dest); extern void stack_overflow(void); extern void stack_underflow(void); /* fpu_arith.c */ @@ -41,10 +41,11 @@ extern void fst_i_(void); extern void fstp_i(void); /* fpu_entry.c */ extern void math_emulate(long arg); +extern void __math_abort(struct info *info, unsigned int signal); /* fpu_etc.c */ extern void fp_etc(void); /* fpu_trig.c */ -extern void convert_l2reg(long *arg, struct fpu_reg *dest); +extern void convert_l2reg(long *arg, FPU_REG *dest); extern void trig_a(void); extern void trig_b(void); /* get_address.c */ @@ -52,22 +53,25 @@ extern void get_address(unsigned char FPU_modrm); /* load_store.c */ extern void load_store_instr(char type); /* poly_2xm1.c */ -extern int poly_2xm1(struct fpu_reg *arg, struct fpu_reg *result); +extern int poly_2xm1(FPU_REG *arg, FPU_REG *result); /* poly_atan.c */ -extern void poly_atan(struct fpu_reg *arg); -extern void poly_add_1(struct fpu_reg *src); +extern void poly_atan(FPU_REG *arg); +extern void poly_add_1(FPU_REG *src); /* poly_l2.c */ -extern void poly_l2(struct fpu_reg *arg, struct fpu_reg *result); -extern int poly_l2p1(struct fpu_reg *arg, struct fpu_reg *result); +extern void poly_l2(FPU_REG *arg, FPU_REG *result); +extern int poly_l2p1(FPU_REG *arg, FPU_REG *result); /* poly_sin.c */ -extern void poly_sine(struct fpu_reg *arg, struct fpu_reg *result); +extern void poly_sine(FPU_REG *arg, FPU_REG *result); /* poly_tan.c */ -extern void poly_tan(struct fpu_reg *arg, struct fpu_reg *y_reg); +extern void poly_tan(FPU_REG *arg, FPU_REG *y_reg); +/* precision.c */ +extern int round_to_53_bits(FPU_REG *reg); +extern int round_to_24_bits(FPU_REG *reg); /* reg_add_sub.c */ -extern void reg_add(struct fpu_reg *a, struct fpu_reg *b, struct fpu_reg *dest); -extern void reg_sub(struct fpu_reg *a, struct fpu_reg *b, struct fpu_reg *dest); +extern void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest); +extern void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest); /* reg_compare.c */ -extern int compare(struct fpu_reg *b); +extern int compare(FPU_REG *b); extern void compare_st_data(void); extern void fcom_st(void); extern void fcompst(void); @@ -92,10 +96,10 @@ extern int reg_store_int64(void); extern int reg_store_int32(void); extern int reg_store_int16(void); extern int reg_store_bcd(void); -extern int round_to_int(struct fpu_reg *r); +extern int round_to_int(FPU_REG *r); extern char *fldenv(void); extern void frstor(void); extern char *fstenv(void); extern void fsave(void); /* reg_mul.c */ -extern void reg_mul(struct fpu_reg *a, struct fpu_reg *b, struct fpu_reg *dest); +extern void reg_mul(FPU_REG *a, FPU_REG *b, FPU_REG *dest); diff --git a/kernel/FPU-emu/fpu_trig.c b/kernel/FPU-emu/fpu_trig.c index fa74c1b..10d57df 100644 --- a/kernel/FPU-emu/fpu_trig.c +++ b/kernel/FPU-emu/fpu_trig.c @@ -3,7 +3,8 @@ | | | Implementation of the FPU "transcendental" functions. | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | | @@ -105,9 +106,8 @@ static void f2xm1(void) { /* poly_2xm1(x) requires 0 < x < 1. */ if ( poly_2xm1(FPU_st0_ptr, &rv) ) - return; + return; /* error */ reg_mul(&rv, FPU_st0_ptr, FPU_st0_ptr); - return; } else { @@ -119,9 +119,9 @@ static void f2xm1(void) reg_mul(&rv, &tmp, &tmp); reg_sub(&tmp, &CONST_1, FPU_st0_ptr); FPU_st0_ptr->exp--; + if ( FPU_st0_ptr->exp <= EXP_UNDER ) + arith_underflow(FPU_st0_ptr); } - if ( FPU_st0_ptr->exp <= EXP_UNDER ) - arith_underflow(FPU_st0_ptr); return; } case TW_Zero: @@ -301,7 +301,9 @@ static void fsqrt_(void) return; } else - single_arg_error(); + { single_arg_error(); return; } + + PRECISION_ADJUST(FPU_st0_ptr); } @@ -557,30 +559,27 @@ static void fyl2x(void) reg_mul(FPU_st0_ptr, st1_ptr, st1_ptr); pop(); FPU_st0_ptr = &st(0); if ( FPU_st0_ptr->exp <= EXP_UNDER ) - arith_underflow(FPU_st0_ptr); + { arith_underflow(FPU_st0_ptr); return; } else if ( FPU_st0_ptr->exp >= EXP_OVER ) - arith_overflow(FPU_st0_ptr); + { arith_overflow(FPU_st0_ptr); return; } } else { /* negative */ pop(); FPU_st0_ptr = &st(0); arith_invalid(FPU_st0_ptr); + return; } - return; } - - if ( (FPU_st0_tag == TW_Empty) || (st1_tag == TW_Empty) ) + else if ( (FPU_st0_tag == TW_Empty) || (st1_tag == TW_Empty) ) { stack_underflow(); return; } - - if ( (FPU_st0_tag == TW_NaN) || (st1_tag == TW_NaN) ) + else if ( (FPU_st0_tag == TW_NaN) || (st1_tag == TW_NaN) ) { real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr); pop(); return; } - - if ( (FPU_st0_tag <= TW_Zero) && (st1_tag <= TW_Zero) ) + else if ( (FPU_st0_tag <= TW_Zero) && (st1_tag <= TW_Zero) ) { /* one of the args is zero, the other valid, or both zero */ if ( FPU_st0_tag == TW_Zero ) @@ -592,7 +591,7 @@ static void fyl2x(void) divide_by_zero(st1_ptr->sign ^ SIGN_NEG, FPU_st0_ptr); return; } - if ( st1_ptr->sign == SIGN_POS ) + else if ( st1_ptr->sign == SIGN_POS ) { /* Zero is the valid answer */ char sign = FPU_st0_ptr->sign; @@ -602,13 +601,15 @@ static void fyl2x(void) FPU_st0_ptr->sign = sign; return; } - pop(); FPU_st0_ptr = &st(0); - arith_invalid(FPU_st0_ptr); - return; + else + { + pop(); FPU_st0_ptr = &st(0); + arith_invalid(FPU_st0_ptr); + return; + } } - /* One or both arg must be an infinity */ - if ( FPU_st0_tag == TW_Infinity ) + else if ( FPU_st0_tag == TW_Infinity ) { if ( (FPU_st0_ptr->sign == SIGN_NEG) || (st1_tag == TW_Zero) ) { pop(); FPU_st0_ptr = &st(0); arith_invalid(FPU_st0_ptr); return; } @@ -621,9 +622,8 @@ static void fyl2x(void) return; } } - /* st(1) must be infinity here */ - if ( (FPU_st0_tag == TW_Valid) && (FPU_st0_ptr->sign == SIGN_POS) ) + else if ( (FPU_st0_tag == TW_Valid) && (FPU_st0_ptr->sign == SIGN_POS) ) { if ( FPU_st0_ptr->exp >= EXP_BIAS ) { @@ -636,19 +636,21 @@ static void fyl2x(void) return; } pop(); - return; } else { pop(); FPU_st0_ptr = &st(0); FPU_st0_ptr->sign ^= SIGN_NEG; - return; } + return; + } + else + { + /* st(0) must be zero or negative */ + pop(); FPU_st0_ptr = &st(0); + arith_invalid(FPU_st0_ptr); + return; } - /* st(0) must be zero or negative */ - pop(); FPU_st0_ptr = &st(0); - arith_invalid(FPU_st0_ptr); - return; } @@ -686,21 +688,17 @@ static void fpatan(void) reg_move(&sum, st1_ptr); pop(); FPU_st0_ptr = &st(0); if ( FPU_st0_ptr->exp <= EXP_UNDER ) - arith_underflow(FPU_st0_ptr); - return; + { arith_underflow(FPU_st0_ptr); return; } } - - if ( (FPU_st0_tag == TW_Empty) || (st1_tag == TW_Empty) ) + else if ( (FPU_st0_tag == TW_Empty) || (st1_tag == TW_Empty) ) { stack_underflow(); return; } - - if ( (FPU_st0_tag == TW_NaN) || (st1_tag == TW_NaN) ) + else if ( (FPU_st0_tag == TW_NaN) || (st1_tag == TW_NaN) ) { real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr); pop(); return; } - - if ( (FPU_st0_tag == TW_Infinity) || (st1_tag == TW_Infinity) ) + else if ( (FPU_st0_tag == TW_Infinity) || (st1_tag == TW_Infinity) ) { char sign = st1_ptr->sign; if ( FPU_st0_tag == TW_Infinity ) @@ -726,10 +724,8 @@ static void fpatan(void) } st1_ptr->sign = sign; pop(); - return; } - - if ( st1_tag == TW_Zero ) + else if ( st1_tag == TW_Zero ) { char sign = st1_ptr->sign; /* st(0) must be valid or zero */ @@ -737,21 +733,20 @@ static void fpatan(void) { reg_move(&CONST_Z, st1_ptr); } else reg_move(&CONST_PI, st1_ptr); - st1_tag = sign; + st1_ptr->sign = sign; pop(); - return; } else if ( FPU_st0_tag == TW_Zero ) { char sign = st1_ptr->sign; /* st(1) must be TW_Valid here */ reg_move(&CONST_PI2, st1_ptr); - st1_tag = sign; + st1_ptr->sign = sign; pop(); - return; } #ifdef PARANOID - EXCEPTION(EX_INTERNAL | 0x220); + else + EXCEPTION(EX_INTERNAL | 0x220); #endif PARANOID } @@ -782,10 +777,9 @@ static void fyl2xp1(void) reg_mul(FPU_st0_ptr, st1_ptr, st1_ptr); pop(); - return; } else if ( (FPU_st0_tag == TW_Empty) | (st1_tag == TW_Empty) ) - stack_underflow(); + { stack_underflow(); return; } else if ( FPU_st0_tag == TW_Zero ) { if ( st1_tag <= TW_Zero ) @@ -796,21 +790,23 @@ static void fyl2xp1(void) else if ( st1_tag == TW_Infinity ) { arith_invalid(st1_ptr); + return; } else if ( st1_tag == TW_NaN ) { if ( !(st1_ptr->sigh & 0x40000000) ) EXCEPTION(EX_Invalid); /* signaling NaN */ st1_ptr->sigh |= 0x40000000; /* QNaN */ + return; } #ifdef PARANOID else { EXCEPTION(EX_INTERNAL | 0x116); + return; } #endif PARANOID pop(); - return; } else if ( FPU_st0_tag == TW_NaN ) { @@ -868,9 +864,9 @@ static void fscale(void) FPU_st0_ptr->exp = scale; if ( scale <= EXP_UNDER ) - arith_underflow(FPU_st0_ptr); + { arith_underflow(FPU_st0_ptr); return; } else if ( scale >= EXP_OVER ) - arith_overflow(FPU_st0_ptr); + { arith_overflow(FPU_st0_ptr); return; } return; } diff --git a/kernel/FPU-emu/get_address.c b/kernel/FPU-emu/get_address.c index 5f0ee62..b3d77b6 100644 --- a/kernel/FPU-emu/get_address.c +++ b/kernel/FPU-emu/get_address.c @@ -3,7 +3,8 @@ | | | Get the effective address from an FPU instruction. | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | | diff --git a/kernel/FPU-emu/load_store.c b/kernel/FPU-emu/load_store.c index 4a6357d..a038d17 100644 --- a/kernel/FPU-emu/load_store.c +++ b/kernel/FPU-emu/load_store.c @@ -4,7 +4,8 @@ | This file contains most of the code to interpret the FPU instructions | | which load and store from user memory. | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | | diff --git a/kernel/FPU-emu/precision.c b/kernel/FPU-emu/precision.c new file mode 100644 index 0000000..7e30782 --- /dev/null +++ b/kernel/FPU-emu/precision.c @@ -0,0 +1,134 @@ +/*---------------------------------------------------------------------------+ + | precision.c | + | | + | The functions which adjust the precision of a result. | + | | + | Copyright (C) 1993 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | + | | + | | + +---------------------------------------------------------------------------*/ + + +#include <asm/segment.h> + +#include "fpu_system.h" +#include "exception.h" +#include "reg_constant.h" +#include "fpu_emu.h" +#include "control_w.h" + + +/* Round the result to 53 bits */ +int round_to_53_bits(FPU_REG *reg) +{ + + if (reg->tag == TW_Valid) + { + unsigned long increment = 0; /* avoid gcc warnings */ + + switch (control_word & CW_RC) + { + case RC_RND: + /* Rounding can get a little messy.. */ + increment = ((reg->sigl & 0x7ff) > 0x400) | /* nearest */ + ((reg->sigl & 0xc00) == 0xc00); /* odd -> even */ + break; + case RC_DOWN: /* towards -infinity */ + increment = (reg->sign == SIGN_POS) ? 0 : reg->sigl & 0x7ff; + break; + case RC_UP: /* towards +infinity */ + increment = (reg->sign == SIGN_POS) ? reg->sigl & 0x7ff : 0; + break; + case RC_CHOP: + increment = 0; + break; + } + + /* Truncate the mantissa */ + reg->sigl &= 0xfffff800; + + if ( increment ) + { + if ( reg->sigl >= 0xfffff800 ) + { + /* the sigl part overflows */ + if ( reg->sigh == 0xffffffff ) + { + /* The sigh part overflows */ + reg->sigh = 0x80000000; + reg->exp++; + if (reg->exp >= EXP_OVER) + { arith_overflow(reg); return 1; } + } + else + { + reg->sigh ++; + } + reg->sigl = 0x00000000; + } + else + { + /* We only need to increment sigl */ + reg->sigl += 0x00000800; + } + } + } + + return 0; + +} + + +/* Round the result to 24 bits */ +int round_to_24_bits(FPU_REG *reg) +{ + + if (reg->tag == TW_Valid) + { + unsigned long increment = 0; /* avoid gcc warnings */ + unsigned long sigh = reg->sigh; + unsigned long sigl = reg->sigl; + + switch (control_word & CW_RC) + { + case RC_RND: + increment = ((sigh & 0xff) > 0x80) /* more than half */ + || (((sigh & 0xff) == 0x80) && sigl) /* more than half */ + || ((sigh & 0x180) == 0x180); /* round to even */ + break; + case RC_DOWN: /* towards -infinity */ + increment = (reg->sign == SIGN_POS) ? 0 : (sigl | (sigh & 0xff)); + break; + case RC_UP: /* towards +infinity */ + increment = (reg->sign == SIGN_POS) ? (sigl | (sigh & 0xff)) : 0; + break; + case RC_CHOP: + increment = 0; + break; + } + + /* Truncate the mantissa */ + reg->sigl = 0; + + if (increment) + { + if ( sigh >= 0xffffff00 ) + { + /* The sigh part overflows */ + reg->sigh = 0x80000000; + reg->exp++; + if (reg->exp >= EXP_OVER) + { arith_overflow(reg); return 1; } + } + else + { + reg->sigh &= 0xffffff00; + reg->sigh += 0x100; + } + } + } + + return 0; + +} diff --git a/kernel/FPU-emu/reg_add_sub.c b/kernel/FPU-emu/reg_add_sub.c index e1e2283..07e8e01 100644 --- a/kernel/FPU-emu/reg_add_sub.c +++ b/kernel/FPU-emu/reg_add_sub.c @@ -3,7 +3,8 @@ | | | Functions to add or subtract two registers and put the result in a third. | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | | diff --git a/kernel/FPU-emu/reg_compare.c b/kernel/FPU-emu/reg_compare.c index 5a5172a..7d4c6ac 100644 --- a/kernel/FPU-emu/reg_compare.c +++ b/kernel/FPU-emu/reg_compare.c @@ -3,7 +3,8 @@ | | | Compare two floating point registers | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | | @@ -91,9 +92,14 @@ int compare(FPU_REG *b) diff = FPU_st0_ptr->exp - b->exp; if ( diff == 0 ) { - diff = FPU_st0_ptr->sigh - b->sigh; + diff = FPU_st0_ptr->sigh - b->sigh; /* Works only if ms bits are + identical */ if ( diff == 0 ) - diff = FPU_st0_ptr->sigl - b->sigl; + { + diff = FPU_st0_ptr->sigl > b->sigl; + if ( diff == 0 ) + diff = -(FPU_st0_ptr->sigl < b->sigl); + } } if ( diff > 0 ) diff --git a/kernel/FPU-emu/reg_ld_str.c b/kernel/FPU-emu/reg_ld_str.c index 1972d3d..23b7968 100644 --- a/kernel/FPU-emu/reg_ld_str.c +++ b/kernel/FPU-emu/reg_ld_str.c @@ -3,7 +3,8 @@ | | | All of the functions which transfer data between user memory and FPU_REGs.| | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | | @@ -505,45 +506,19 @@ int reg_store_double(void) if (FPU_st0_tag == TW_Valid) { - /* Rounding can get a little messy.. */ - int exp = FPU_st0_ptr->exp - EXP_BIAS; - int increment = ((FPU_st0_ptr->sigl & 0x7ff) > 0x400) | /* nearest */ - ((FPU_st0_ptr->sigl & 0xc00) == 0xc00); /* odd -> even */ - if ( increment ) - { - if ( FPU_st0_ptr->sigl >= 0xfffff800 ) - { - /* the sigl part overflows */ - if ( FPU_st0_ptr->sigh == 0xffffffff ) - { - /* The sigh part overflows */ - l[0] = l[1] = 0; - exp++; /* no need to check here for overflow */ - } - else - { - /* No overflow of sigh will happen, can safely increment */ - l[0] = (FPU_st0_ptr->sigh+1) << 21; - l[1] = (((FPU_st0_ptr->sigh+1) >> 11) & 0xfffff); - } - } - else - { - /* We only need to increment sigl */ - l[0] = ((FPU_st0_ptr->sigl+0x800) >> 11) | (FPU_st0_ptr->sigh << 21); - l[1] = ((FPU_st0_ptr->sigh >> 11) & 0xfffff); - } - } - else - { - /* No increment required */ - l[0] = (FPU_st0_ptr->sigl >> 11) | (FPU_st0_ptr->sigh << 21); - l[1] = ((FPU_st0_ptr->sigh >> 11) & 0xfffff); - } + int exp; + FPU_REG tmp; + + reg_move(FPU_st0_ptr, &tmp); + if (round_to_53_bits(&tmp)) goto overflow; + l[0] = (tmp.sigl >> 11) | (tmp.sigh << 21); + l[1] = ((tmp.sigh >> 11) & 0xfffff); + exp = tmp.exp - EXP_BIAS; if ( exp > DOUBLE_Emax ) { EXCEPTION(EX_Overflow); + overflow: /* This is a special case: see sec 16.2.5.1 of the 80486 book */ if ( control_word & EX_Overflow ) { @@ -645,7 +620,6 @@ put_indefinite: RE_ENTRANT_CHECK_ON return 1; - } @@ -654,31 +628,21 @@ int reg_store_single(void) { float *single = (float *)FPU_data_address; long templ; - int exp = FPU_st0_ptr->exp - EXP_BIAS; - unsigned long sigh = FPU_st0_ptr->sigh; - if (FPU_st0_tag == TW_Valid) { - if ( ((sigh & 0xff) > 0x80) /* more than half */ - || ((sigh & 0x180) == 0x180) ) /* round to even */ - { - /* Round up */ - if ( sigh >= 0xffffff00 ) - { - /* sigh would overflow */ - exp++; - sigh = 0x80000000; - } - else - { - sigh += 0x100; - } - } - templ = (sigh >> 8) & 0x007fffff; + int exp; + FPU_REG tmp; + + reg_move(FPU_st0_ptr, &tmp); + if (round_to_24_bits(&tmp)) goto overflow; + templ = (tmp.sigh >> 8) & 0x007fffff; + exp = tmp.exp - EXP_BIAS; + if ( exp > SINGLE_Emax ) { EXCEPTION(EX_Overflow); + overflow: /* This is a special case: see sec 16.2.5.1 of the 80486 book */ if ( control_word & EX_Overflow ) { diff --git a/kernel/FPU-emu/version.h b/kernel/FPU-emu/version.h index 26c52df..75edb8c 100644 --- a/kernel/FPU-emu/version.h +++ b/kernel/FPU-emu/version.h @@ -2,11 +2,12 @@ | version.h | | | | | - | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | + | Copyright (C) 1992,1993 | + | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail apm233m@vaxc.cc.monash.edu.au | | | | | +---------------------------------------------------------------------------*/ -#define FPU_VERSION "wm-FPU-emu version BETA 1.1" +#define FPU_VERSION "wm-FPU-emu version BETA 1.2" diff --git a/kernel/blk_drv/scsi/aha1542.c b/kernel/blk_drv/scsi/aha1542.c index 2a1a448..c9aba1a 100644 --- a/kernel/blk_drv/scsi/aha1542.c +++ b/kernel/blk_drv/scsi/aha1542.c @@ -66,6 +66,7 @@ static int aha1542_last_mbo_used = (AHA1542_MAILBOXES - 1); static long WAITnexttimeout = 3000000; static int aha1542_host = 0; +static void setup_mailboxes(); extern void aha1542_interrupt(); #define aha1542_intr_reset() outb(IRST, CONTROL) @@ -185,6 +186,7 @@ int aha1542_test_port(int bse) debug = 2; /* Shouldn't have generated any interrupts during reset */ if (inb(INTRFLAGS)&INTRMASK) goto fail; + setup_mailboxes(); debug = 3; /* Test the basic ECHO command */ @@ -229,7 +231,7 @@ int aha1542_test_port(int bse) /* What's this little function for? */ const char *aha1542_info(void) { - static char buffer[] = ""; /* looks nicer without anything here */ + static char buffer[] = "Adaptec 1542"; return buffer; } @@ -396,7 +398,7 @@ int aha1542_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *)) printk("aha1542_command: dev %d cmd %02x pos %d len %d ", target, *cmd, i, bufflen); aha1542_stat(); printk("aha1542_queuecommand: dumping scsi cmd:"); - for (i = 0; i < (*cmd<=0x1f?6:10); i++) printk("%02x ", cmd[i]); + for (i = 0; i < (COMMAND_SIZE(*cmd)); i++) printk("%02x ", cmd[i]); printk("\n"); if (*cmd == WRITE_10 || *cmd == WRITE_6) return 0; /* we are still testing, so *don't* write */ @@ -430,7 +432,7 @@ int aha1542_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *)) memset(&ccb[mbo], 0, sizeof(struct ccb)); - ccb[mbo].cdblen = (*cmd<=0x1f)?6:10; /* SCSI Command Descriptor Block Length */ + ccb[mbo].cdblen = COMMAND_SIZE(*cmd); /* SCSI Command Descriptor Block Length */ direction = 0; if (*cmd == READ_10 || *cmd == READ_6) @@ -548,7 +550,6 @@ static void setup_mailboxes() aha1542_intr_reset(); } -/* Query the board to find out if it is a 1542 or a 1740, or whatever. */ static int aha1542_getconfig(int hostnum) { static unchar inquiry_cmd[] = {CMD_RETCONF }; @@ -776,6 +777,6 @@ int aha1542_biosparam(int size, int dev, int* info){ info[0] = 64; info[1] = 32; info[2] = size >> 11; - if (info[2] >= 1024) info[2] = 1024; +/* if (info[2] >= 1024) info[2] = 1024; */ return 0; } diff --git a/kernel/blk_drv/scsi/aha1740.c b/kernel/blk_drv/scsi/aha1740.c index ddc2eac..6c1e184 100644 --- a/kernel/blk_drv/scsi/aha1740.c +++ b/kernel/blk_drv/scsi/aha1740.c @@ -1,15 +1,20 @@ -/* $Id: aha1740.c,v 1.1 1992/07/24 06:27:38 root Exp root $ +/* $Id$ + * 1993/03/31 * linux/kernel/aha1740.c * * Based loosely on aha1542.c which is - * Copyright (C) 1992 Tommy Thorn - * and + * Copyright (C) 1992 Tommy Thorn and * Modified by Eric Youngdale * * This file is aha1740.c, written and - * Copyright (C) 1992 Brad McLean + * Copyright (C) 1992,1993 Brad McLean * - * aha1740_makecode needs more work + * Modifications to makecode and queuecommand + * for proper handling of multiple devices courteously + * provided by Michael Weller, March, 1993 + * + * aha1740_makecode may still need even more work + * if it doesn't work for your devices, take a look. */ #include <linux/kernel.h> @@ -27,12 +32,17 @@ #include "hosts.h" #include "aha1740.h" -/* #define DEBUG */ + +/* IF YOU ARE HAVING PROBLEMS WITH THIS DRIVER, AND WANT TO WATCH + IT WORK, THEN: +#define DEBUG +*/ #ifdef DEBUG #define DEB(x) x #else #define DEB(x) #endif + /* static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/aha1740.c,v 1.1 1992/07/24 06:27:38 root Exp root $"; */ @@ -46,7 +56,8 @@ static int aha1740_last_ecb_used = 0; /* optimization */ int aha1740_makecode(unchar *sense, unchar *status) { - struct statusword { + struct statusword + { ushort don:1, /* Command Done - No Error */ du:1, /* Data underrun */ :1, qf:1, /* Queue full */ @@ -58,8 +69,9 @@ int aha1740_makecode(unchar *sense, unchar *status) sns:1, /* Sense information Stored */ :1, ini:1, /* Initialization Required */ me:1, /* Major error or exception */ - :1, eca:1, :1; /* Extended Contingent alliance */ - } status_word; + :1, eca:1, /* Extended Contingent alliance */ + :1; + } status_word; int retval = DID_OK; status_word = * (struct statusword *) status; @@ -67,20 +79,57 @@ int aha1740_makecode(unchar *sense, unchar *status) printk("makecode from %x,%x,%x,%x %x,%x,%x,%x",status[0],status[1],status[2],status[3], sense[0],sense[1],sense[2],sense[3]); #endif - if ( status_word.don ) - return 0; -/* - if ( status_word.du && status[2] != 0x11 ) - return 0; -*/ - if ( status[2] == 0x11 ) - retval = DID_TIME_OUT; - else - retval = DID_ERROR; - return status[3] | retval << 16; /* OKAY, SO I'M LAZY! I'll fix it later... */ + if (!status_word.don) /* Anything abnormal was detected */ + { + if ( (status[1]&0x18) || status_word.sc ) /*Additional info available*/ + { + /* Use the supplied info for futher diagnostics */ + switch ( status[2] ) + { + case 0x12: + if ( status_word.dor ) + retval=DID_ERROR; /* It's an Overrun */ + /* If not overrun, assume underrun and ignore it! */ + case 0x00: /* No info, assume no error, should not occur */ + break; + case 0x11: + case 0x21: + retval=DID_TIME_OUT; + break; + case 0x0a: + retval=DID_BAD_TARGET; + break; + case 0x04: + case 0x05: + retval=DID_ABORT; /* Either by this driver or the AHA1740 + itself */ + break; + default: + retval=DID_ERROR; /* No further diagnostics possible */ + } + } + else + { /* Michael suggests, and Brad concurs: */ + if ( status_word.qf ) + { + retval = DID_TIME_OUT; /* forces a redo */ + /* I think this specific one should not happen -Brad */ + printk("aha1740.c: WARNING: AHA1740 queue overflow!\n"); + } + else if ( status[0]&0x60 ) + { + retval = DID_ERROR; /* Didn't found a better error */ + } + /* In any other case return DID_OK so for example + CONDITION_CHECKS make it through to the appropriate + device driver */ + } + } + /* Under all circumstances supply the target status -Michael */ + return status[3] | retval << 16; } -int aha1740_test_port() +int aha1740_test_port(void) { char name[4],tmp; @@ -94,10 +143,9 @@ int aha1740_test_port() if ( strcmp ( name, HID_MFG ) || inb(HID2) != HID_PRD ) return 0; /* Not an Adaptec 174x */ -/* if ( inb(HID3) < HID_REV ) */ - if ( inb(HID3) != HID_REV ) +/* if ( inb(HID3) != HID_REV ) printk("aha1740: Warning; board revision of %d; expected %d\n", - inb(HID3),HID_REV); + inb(HID3),HID_REV); */ if ( inb(EBCNTRL) != EBCNTRL_VALUE ) { @@ -108,14 +156,14 @@ int aha1740_test_port() if ( inb(PORTADR) & PORTADDR_ENH ) return 1; /* Okay, we're all set */ + printk("aha1740: Board detected, but not in enhanced mode, so disabled it.\n"); return 0; } -/* What's this little function for? */ const char *aha1740_info(void) { - static char buffer[] = ""; /* looks nicer without anything here */ + static char buffer[] = "Adaptec 174x (EISA)"; return buffer; } @@ -130,7 +178,8 @@ void aha1740_intr_handle(int foo) number_serviced = 0; - while(inb(G2STAT) & G2STAT_INTPEND){ + while(inb(G2STAT) & G2STAT_INTPEND) + { DEB(printk("aha1740_intr top of loop.\n")); adapstat = inb(G2INTST); outb(G2CNTRL_IRST,G2CNTRL); /* interrupt reset */ @@ -138,7 +187,6 @@ void aha1740_intr_handle(int foo) switch ( adapstat & G2INTST_MASK ) { case G2INTST_CCBRETRY: - printk("aha1740 complete with retry!\n"); case G2INTST_CCBERROR: case G2INTST_CCBGOOD: ecbptr = (void *) ( ((ulong) inb(MBOXIN0)) + @@ -196,46 +244,36 @@ int aha1740_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *)) int ecbno; DEB(int i); - DEB(if (target > 0 || SCpnt->lun > 0) { - SCpnt->result = DID_TIME_OUT << 16; - done(SCpnt); return 0;}); - if(*cmd == REQUEST_SENSE){ -#ifndef DEBUG - if (bufflen != 16) { - printk("Wrong buffer length supplied for request sense (%d)\n",bufflen); - panic("aha1740.c"); - }; -#endif - SCpnt->result = 0; - done(SCpnt); - return 0; - }; + if(*cmd == REQUEST_SENSE) + { + if (bufflen != 16) + { + printk("Wrong buffer length supplied for request sense (%d)\n",bufflen); + panic("aha1740.c"); + } + SCpnt->result = 0; + done(SCpnt); + return 0; + } #ifdef DEBUG if (*cmd == READ_10 || *cmd == WRITE_10) - i = xscsi2int(cmd+2); + i = xscsi2int(cmd+2); else if (*cmd == READ_6 || *cmd == WRITE_6) - i = scsi2int(cmd+2); - else - i = -1; - if (done) - printk("aha1740_queuecommand: dev %d cmd %02x pos %d len %d ", target, *cmd, i, bufflen); + i = scsi2int(cmd+2); else - printk("aha1740_command: dev %d cmd %02x pos %d len %d ", target, *cmd, i, bufflen); + i = -1; + printk("aha1740_queuecommand: dev %d cmd %02x pos %d len %d ", target, *cmd, i, bufflen); printk("scsi cmd:"); - for (i = 0; i < (*cmd<=0x1f?6:10); i++) printk("%02x ", cmd[i]); + for (i = 0; i < (COMMAND_SIZE(*cmd)); i++) printk("%02x ", cmd[i]); printk("\n"); -#ifdef 0 - if (*cmd == WRITE_10 || *cmd == WRITE_6) - return 0; /* we are still testing, so *don't* write */ -#endif #endif -/* locate an available ecb */ + /* locate an available ecb */ cli(); - ecbno = aha1740_last_ecb_used + 1; + ecbno = aha1740_last_ecb_used + 1; /* An optimization */ if (ecbno >= AHA1740_ECBS) ecbno = 0; do{ @@ -248,7 +286,7 @@ int aha1740_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *)) if( ecb[ecbno].cmdw ) panic("Unable to find empty ecb for aha1740.\n"); - ecb[ecbno].cmdw = AHA1740CMD_INIT; /* SCSI Initiator Command to reserve*/ + ecb[ecbno].cmdw = AHA1740CMD_INIT; /* SCSI Initiator Command doubles as reserved flag */ aha1740_last_ecb_used = ecbno; sti(); @@ -257,7 +295,7 @@ int aha1740_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *)) printk("Sending command (%d %x)...",ecbno, done); #endif - ecb[ecbno].cdblen = (*cmd<=0x1f)?6:10; /* SCSI Command Descriptor Block Length */ + ecb[ecbno].cdblen = COMMAND_SIZE(*cmd); /* SCSI Command Descriptor Block Length */ direction = 0; if (*cmd == READ_10 || *cmd == READ_6) @@ -267,66 +305,90 @@ int aha1740_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *)) memcpy(ecb[ecbno].cdb, cmd, ecb[ecbno].cdblen); - if (SCpnt->use_sg) { - struct scatterlist * sgpnt; - struct aha1740_chain * cptr; + if (SCpnt->use_sg) + { + struct scatterlist * sgpnt; + struct aha1740_chain * cptr; + int i; #ifdef DEBUG - unsigned char * ptr; + unsigned char * ptr; #endif - int i; - ecb[ecbno].sg = 1; /* SCSI Initiator Command w/scatter-gather*/ - SCpnt->host_scribble = scsi_malloc(512); - sgpnt = (struct scatterlist *) SCpnt->request_buffer; - cptr = (struct aha1740_chain *) SCpnt->host_scribble; - if (cptr == NULL) panic("aha1740.c: unable to allocate DMA memory\n"); - for(i=0; i<SCpnt->use_sg; i++) { - cptr[i].dataptr = (long) sgpnt[i].address; - cptr[i].datalen = sgpnt[i].length; - }; - ecb[ecbno].datalen = SCpnt->use_sg * sizeof(struct aha1740_chain); - ecb[ecbno].dataptr = (long) cptr; + ecb[ecbno].sg = 1; /* SCSI Initiator Command w/scatter-gather*/ + SCpnt->host_scribble = scsi_malloc(512); + sgpnt = (struct scatterlist *) SCpnt->request_buffer; + cptr = (struct aha1740_chain *) SCpnt->host_scribble; + if (cptr == NULL) panic("aha1740.c: unable to allocate DMA memory\n"); + for(i=0; i<SCpnt->use_sg; i++) + { + cptr[i].dataptr = (long) sgpnt[i].address; + cptr[i].datalen = sgpnt[i].length; + } + ecb[ecbno].datalen = SCpnt->use_sg * sizeof(struct aha1740_chain); + ecb[ecbno].dataptr = (long) cptr; #ifdef DEBUG - printk("cptr %x: ",cptr); - ptr = (unsigned char *) cptr; - for(i=0;i<24;i++) printk("%02x ", ptr[i]); + printk("cptr %x: ",cptr); + ptr = (unsigned char *) cptr; + for(i=0;i<24;i++) printk("%02x ", ptr[i]); #endif - } else { - SCpnt->host_scribble = NULL; - ecb[ecbno].datalen = bufflen; - ecb[ecbno].dataptr = (long) buff; - }; + } + else + { + SCpnt->host_scribble = NULL; + ecb[ecbno].datalen = bufflen; + ecb[ecbno].dataptr = (long) buff; + } ecb[ecbno].lun = SCpnt->lun; ecb[ecbno].ses = 1; /* Suppress underrun errors */ -/* ecb[ecbno].dat=1; */ /* Yes, check the data direction */ ecb[ecbno].dir= direction; ecb[ecbno].ars=1; /* Yes, get the sense on an error */ - ecb[ecbno].senselen = 12; /* Why 12? Eric? MAXSENSE? */ + ecb[ecbno].senselen = 12; ecb[ecbno].senseptr = (long) ecb[ecbno].sense; ecb[ecbno].statusptr = (long) ecb[ecbno].status; ecb[ecbno].done = done; ecb[ecbno].SCpnt = SCpnt; #ifdef DEBUG - { int i; - printk("aha1740_command: sending.. "); - for (i = 0; i < sizeof(ecb[ecbno])-10; i++) - printk("%02x ", ((unchar *)&ecb[ecbno])[i]); - }; + { + int i; + printk("aha1740_command: sending.. "); + for (i = 0; i < sizeof(ecb[ecbno])-10; i++) + printk("%02x ", ((unchar *)&ecb[ecbno])[i]); + } printk("\n"); #endif - if (done) { ulong adrs; - - if ( ! (inb(G2STAT) & G2STAT_MBXOUT) ) /* Spec claim's it's so fast */ - printk("aha1740_mbxout wait!\n"); /* that this is okay? It seems */ - while ( ! (inb(G2STAT) & G2STAT_MBXOUT) ); /* to work, so I'll leave it */ - adrs = (ulong) &(ecb[ecbno]); - outb((char) (adrs&0xff), MBOXOUT0); - outb((char) ((adrs>>8)&0xff), MBOXOUT1); - outb((char) ((adrs>>16)&0xff), MBOXOUT2); + if (done) + { /* You may question the code below, which contains potentially + non-terminating while loops with interrupts disabled. So did + I when I wrote it, but the Adaptec Spec says the card is so fast, + that this problem virtually never occurs so I've kept it. We + do printk a warning first, so that you'll know if it happens. + In practive the only time we've seen this message is when some- + thing else is in the driver was broken, like _makecode(), or + when a scsi device hung the scsi bus. Even under these conditions, + The loop actually only cycled < 3 times (we instrumented it). */ + ulong adrs; + + DEB(printk("aha1740[%d] critical section\n",ecbno)); + cli(); + if ( ! (inb(G2STAT) & G2STAT_MBXOUT) ) + { + printk("aha1740[%d]_mbxout wait!\n",ecbno); + cli(); /* printk may have done a sti()! */ + } + while ( ! (inb(G2STAT) & G2STAT_MBXOUT) ); /* Oh Well. */ + adrs = (ulong) &(ecb[ecbno]); /* Spit the command */ + outb((char) (adrs&0xff), MBOXOUT0); /* out, note this set */ + outb((char) ((adrs>>8)&0xff), MBOXOUT1); /* of outb's must be */ + outb((char) ((adrs>>16)&0xff), MBOXOUT2); /* atomic */ outb((char) ((adrs>>24)&0xff), MBOXOUT3); - if ( inb(G2STAT) & G2STAT_BUSY ) /* Again, allegedly fast */ - printk("aha1740_attn wait!\n"); - while ( inb(G2STAT) & G2STAT_BUSY ); + if ( inb(G2STAT) & G2STAT_BUSY ) + { + printk("aha1740[%d]_attn wait!\n",ecbno); + cli(); + } + while ( inb(G2STAT) & G2STAT_BUSY ); /* And Again! */ outb(ATTN_START | (target & 7), ATTN); /* Start it up */ + sti(); + DEB(printk("aha1740[%d] request queued.\n",ecbno)); } else printk("aha1740_queuecommand: done can't be NULL\n"); @@ -336,6 +398,7 @@ int aha1740_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *)) static volatile int internal_done_flag = 0; static volatile int internal_done_errcode = 0; + static void internal_done(Scsi_Cmnd * SCpnt) { internal_done_errcode = SCpnt->result; @@ -351,22 +414,14 @@ int aha1740_command(Scsi_Cmnd * SCpnt) return internal_done_errcode; } -/* Query the board for it's port addresses, etc. Actually, the irq_level is - all we care about for this board, since it's EISA */ +/* Query the board for it's irq_level. Nothing else matters + in enhanced mode on an EISA bus. */ -static int aha1740_getconfig() +void aha1740_getconfig(void) { - int iop, bios, scsi, dma; - static int iotab[] = { 0, 0, 0x130, 0x134, 0x230, 0x234, 0x330, 0x334 }; static int intab[] = { 9,10,11,12,0,14,15,0 }; - static int dmatab[] = { 0,5,6,7 }; - iop = iotab [ inb(PORTADR)&0x7 ]; - bios = inb(BIOSADR); irq_level = intab [ inb(INTDEF)&0x7 ]; - scsi = inb(SCSIDEF); - dma = dmatab[ (inb(BUSDEF)>>2) & 0x3 ]; - return 0; } int aha1740_detect(int hostnum) @@ -377,18 +432,16 @@ int aha1740_detect(int hostnum) for ( slot=MINEISA; slot <= MAXEISA; slot++ ) { base = SLOTBASE(slot); - if ( aha1740_test_port(base)) break; + if ( aha1740_test_port()) break; } if ( slot > MAXEISA ) return 0; - if (aha1740_getconfig() == -1) - return 0; + aha1740_getconfig(); if ( (inb(G2STAT) & (G2STAT_MBXOUT | G2STAT_BUSY) ) != G2STAT_MBXOUT ) - { + { /* If the card isn't ready, hard reset it */ outb(G2CNTRL_HRST,G2CNTRL); - /* 10 Msec Delay Here */ outb(0,G2CNTRL); } @@ -397,16 +450,25 @@ int aha1740_detect(int hostnum) DEB(printk("aha1740_detect: enable interrupt channel %d\n", irq_level)); - if (request_irq(irq_level,aha1740_intr_handle)) { - printk("Unable to allocate IRQ for adaptec controller.\n"); - return 0; - }; + if (request_irq(irq_level,aha1740_intr_handle)) + { + printk("Unable to allocate IRQ for adaptec controller.\n"); + return 0; + } return 1; } +/* Note: They following two functions do not apply very well to the Adaptec, +which basically manages it's own affairs quite well without our interference, +so I haven't put anything into them. I can faintly imagine someone with a +*very* badly behaved SCSI target (perhaps an old tape?) wanting the abort(), +but it hasn't happened yet, and doing aborts brings the Adaptec to it's +knees. I cannot (at this moment in time) think of any reason to reset the +card once it's running. So there. */ + int aha1740_abort(Scsi_Cmnd * SCpnt, int i) { - DEB(printk("aha1740_abort\n")); + DEB(printk("aha1740_abort called\n")); return 0; } @@ -416,13 +478,16 @@ int aha1740_reset(void) return 0; } -int aha1740_biosparam(int size, int dev, int* info){ +int aha1740_biosparam(int size, int dev, int* info) +{ DEB(printk("aha1740_biosparam\n")); info[0] = 64; info[1] = 32; info[2] = size >> 11; - if (info[2] >= 1024) info[2] = 1024; +/* if (info[2] >= 1024) info[2] = 1024; */ return 0; } - +/* Okay, you made it all the way through. As of this writing, 3/31/93, I'm +brad@saturn.gaylord.com or brad@bradpc.gaylord.com. I'll try to help as time +permits if you have any trouble with this driver. Happy Linuxing! */ diff --git a/kernel/blk_drv/scsi/aha1740.h b/kernel/blk_drv/scsi/aha1740.h index 178fdf0..ec25861 100644 --- a/kernel/blk_drv/scsi/aha1740.h +++ b/kernel/blk_drv/scsi/aha1740.h @@ -1,16 +1,21 @@ #ifndef _AHA1740_H -/* $Id: aha1740.h,v 1.1 1992/07/24 06:27:38 root Exp root $ +/* $Id$ * * Header file for the adaptec 1740 driver for Linux * + * With minor revisions 3/31/93 + * Written and (C) 1992,1993 Brad McLean. See aha1740.c + * for more info + * */ #include <linux/types.h> /* Eisa Enhanced mode operation - slot locating and addressing */ #define MINEISA 1 /* I don't have an EISA Spec to know these ranges, so I */ -#define MAXEISA 6 /* Just took my machine's specifications. Adjust to fit.*/ +#define MAXEISA 8 /* Just took my machine's specifications. Adjust to fit.*/ + /* I just saw an ad, and bumped this from 6 to 8 */ #define SLOTBASE(x) ((x << 12)+ 0xc80 ) #define BASE (base) @@ -31,7 +36,7 @@ #define HID_MFG "ADP" #define HID_PRD 0 -#define HID_REV 1 +#define HID_REV 2 #define EBCNTRL_VALUE 1 #define PORTADDR_ENH 0x80 /* READ */ @@ -80,7 +85,7 @@ struct aha1740_chain { ulong datalen; /* Size of this part of chain */ }; -/* These belong in scsi.h also */ +/* These belong in scsi.h */ #define any2scsi(up, p) \ (up)[0] = (((unsigned long)(p)) >> 16) ; \ (up)[1] = (((unsigned long)(p)) >> 8); \ @@ -155,11 +160,11 @@ const char *aha1740_info(void); int aha1740_reset(void); int aha1740_biosparam(int, int, int*); -#define AHA1740_ECBS 64 +#define AHA1740_ECBS 32 #define AHA1740_SCATTER 16 #ifndef NULL - #define NULL 0 +#define NULL 0 #endif #define AHA1740 {"Adaptec 1740", aha1740_detect, \ diff --git a/kernel/blk_drv/scsi/hosts.c b/kernel/blk_drv/scsi/hosts.c index 02da3fc..50c9c3f 100644 --- a/kernel/blk_drv/scsi/hosts.c +++ b/kernel/blk_drv/scsi/hosts.c @@ -67,6 +67,13 @@ static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/hos * during detection. */ +/* This is a placeholder for controllers that are not configured into + the system - we do this to ensure that the controller numbering is + always consistent, no matter how the kernel is configured. */ + +#define NO_CONTROLLER {NULL, NULL, NULL, NULL, NULL, NULL, NULL, \ + NULL, NULL, 0, 0, 0, 0, 0, 0} + /* * When figure is run, we don't want to link to any object code. Since * the macro for each host will contain function pointers, we cannot diff --git a/kernel/blk_drv/scsi/scsi.c b/kernel/blk_drv/scsi/scsi.c index 13187ac..78a1c10 100644 --- a/kernel/blk_drv/scsi/scsi.c +++ b/kernel/blk_drv/scsi/scsi.c @@ -244,6 +244,8 @@ static void scan_scsis (void) scsi_devices[NR_SCSI_DEVICES]. removable = (0x80 & scsi_result[1]) >> 7; + scsi_devices[NR_SCSI_DEVICES].lockable = + scsi_devices[NR_SCSI_DEVICES].removable; scsi_devices[NR_SCSI_DEVICES]. changed = 0; scsi_devices[NR_SCSI_DEVICES]. @@ -309,7 +311,10 @@ static void scan_scsis (void) /* These devices need this "key" to unlock the device so we can use it */ if(strncmp("INSITE", &scsi_result[8], 6) == 0 && - strncmp("Floptical F*8I", &scsi_result[16], 16) == 0) { + (strncmp("Floptical F*8I", &scsi_result[16], 16) == 0 + ||strncmp("I325VM", &scsi_result[16], 6) == 0)) { + printk("Unlocked floptical drive.\n"); + scsi_devices[NR_SCSI_DEVICES].lockable = 0; scsi_cmd[0] = MODE_SENSE; scsi_cmd[1] = (lun << 5) & 0xe0; scsi_cmd[2] = 0x2e; @@ -464,8 +469,8 @@ Scsi_Cmnd * allocate_device (struct request ** reqp, int index, int wait) if (reqp) req = *reqp; - if (req && (dev = req->dev) <= 0) - panic("Invalid device in allocate_device"); + /* See if this request has already been queued by an interrupt routine */ + if (req && (dev = req->dev) <= 0) return NULL; host = scsi_devices[index].host_no; @@ -481,7 +486,10 @@ Scsi_Cmnd * allocate_device (struct request ** reqp, int index, int wait) }; cli(); /* See if this request has already been queued by an interrupt routine */ - if (req && ((req->dev < 0) || (req->dev != dev))) return NULL; + if (req && ((req->dev < 0) || (req->dev != dev))) { + sti(); + return NULL; + }; if (!SCpnt || SCpnt->request.dev >= 0) /* Might have changed */ { sti(); @@ -1407,7 +1415,8 @@ unsigned long scsi_dev_init (unsigned long memory_start,unsigned long memory_end if(scsi_hosts[host].unchecked_isa_dma && memory_end > ISA_DMA_THRESHOLD && scsi_devices[i].type != TYPE_TAPE) { - dma_sectors += 32 * scsi_hosts[host].cmd_per_lun; + dma_sectors += (BLOCK_SIZE >> 9) * scsi_hosts[host].sg_tablesize * + scsi_hosts[host].cmd_per_lun; need_isa_buffer++; }; }; diff --git a/kernel/blk_drv/scsi/scsi.h b/kernel/blk_drv/scsi/scsi.h index e86b141..7bc7663 100644 --- a/kernel/blk_drv/scsi/scsi.h +++ b/kernel/blk_drv/scsi/scsi.h @@ -76,7 +76,28 @@ #define MODE_SELECT_10 0x55 #define MODE_SENSE_10 0x5a -#define COMMAND_SIZE(opcode) ((opcode) ? ((opcode) > 0x20 ? 10 : 6) : 0) +static __inline__ int COMMAND_SIZE (int opcode) { + int group = (opcode >> 5) & 7; + switch (group) { + case 0: + return 6; + case 1: + case 2: + return 10; + case 3: + case 4: + printk("COMMAND_SIZE : reserved command group %d\n", group); + panic (""); + case 5: + return 12; + default: +#ifdef DEBUG + printk("COMMAND_SIZE : vendor specific command group %d - assuming" + " 10 bytes\n", group); +#endif + return 10; + } +} /* MESSAGE CODES @@ -258,6 +279,7 @@ typedef struct scsi_device { unsigned random:1; unsigned changed:1; /* Data invalid due to media change */ unsigned busy:1; /* Used to prevent races */ + unsigned lockable:1; /* Able to prevent media removal */ } Scsi_Device; /* Use these to separate status msg and our bytes @@ -348,7 +370,7 @@ typedef struct scsi_cmnd { * abort, etc are in process. */ - unsigned char internal_timeout; + unsigned volatile char internal_timeout; unsigned flags; @@ -459,6 +481,24 @@ static void end_scsi_request(Scsi_Cmnd * SCpnt, int uptodate, int sectors) wake_up(&scsi_devices[SCpnt->index].device_wait); return; } + + +/* This is just like INIT_REQUEST, but we need to be aware of the fact + that an interrupt may start another request, so we run this with interrupts + turned off */ + +#define INIT_SCSI_REQUEST \ + if (!CURRENT) {\ + CLEAR_INTR; \ + sti(); \ + return; \ + } \ + if (MAJOR(CURRENT->dev) != MAJOR_NR) \ + panic(DEVICE_NAME ": request list destroyed"); \ + if (CURRENT->bh) { \ + if (!CURRENT->bh->b_lock) \ + panic(DEVICE_NAME ": block not locked"); \ + } #endif #define SCSI_SLEEP(QUEUE, CONDITION) { \ @@ -476,4 +516,3 @@ sleep_repeat: \ }; } #endif - diff --git a/kernel/blk_drv/scsi/scsi_ioctl.c b/kernel/blk_drv/scsi/scsi_ioctl.c index b3724ef..45142d1 100644 --- a/kernel/blk_drv/scsi/scsi_ioctl.c +++ b/kernel/blk_drv/scsi/scsi_ioctl.c @@ -29,10 +29,14 @@ static int ioctl_probe(int dev, void *buffer) { int temp; int len; + char * string; if ((temp = scsi_hosts[dev].present) && buffer) { len = get_fs_long ((int *) buffer); - memcpy_tofs (buffer, scsi_hosts[dev].info(), len); + string = scsi_hosts[dev].info(); + if (len > strlen(string)) len = strlen(string)+1; + verify_area(VERIFY_WRITE, buffer, len); + memcpy_tofs (buffer, string, len); } return temp; } @@ -101,7 +105,8 @@ static int ioctl_internal_command(Scsi_Device *dev, char * cmd) if(driver_byte(SCpnt->result) != 0) switch(SCpnt->sense_buffer[2] & 0xf) { case ILLEGAL_REQUEST: - printk("SCSI device (ioctl) reports ILLEGAL REQUEST.\n"); + if(cmd[0] == ALLOW_MEDIUM_REMOVAL) dev->lockable = 0; + else printk("SCSI device (ioctl) reports ILLEGAL REQUEST.\n"); break; case NOT_READY: /* This happens if there is no disc in drive */ if(dev->removable){ @@ -225,12 +230,17 @@ int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg) return -ENODEV; switch (cmd) { + case SCSI_IOCTL_GET_IDLUN: + verify_area(VERIFY_WRITE, (void *) arg, sizeof(int)); + put_fs_long(dev->id + (dev->lun << 8) + + (dev->host_no << 16), (long *) arg); + return 0; case SCSI_IOCTL_PROBE_HOST: return ioctl_probe(dev->host_no, arg); case SCSI_IOCTL_SEND_COMMAND: return ioctl_command((Scsi_Device *) dev, arg); case SCSI_IOCTL_DOORLOCK: - if (!dev->removable) return 0; + if (!dev->removable || !dev->lockable) return 0; scsi_cmd[0] = ALLOW_MEDIUM_REMOVAL; scsi_cmd[1] = dev->lun << 5; scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0; @@ -238,7 +248,7 @@ int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg) return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd); break; case SCSI_IOCTL_DOORUNLOCK: - if (!dev->removable) return 0; + if (!dev->removable || !dev->lockable) return 0; scsi_cmd[0] = ALLOW_MEDIUM_REMOVAL; scsi_cmd[1] = dev->lun << 5; scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0; diff --git a/kernel/blk_drv/scsi/scsi_ioctl.h b/kernel/blk_drv/scsi/scsi_ioctl.h index 531f27a..c25ed86 100644 --- a/kernel/blk_drv/scsi/scsi_ioctl.h +++ b/kernel/blk_drv/scsi/scsi_ioctl.h @@ -8,6 +8,7 @@ the cdrom */ #define SCSI_IOCTL_DOORLOCK 0x5380 /* lock the eject mechanism */ #define SCSI_IOCTL_DOORUNLOCK 0x5381 /* unlock the mechanism */ +#define SCSI_IOCTL_GET_IDLUN 0x5382 #define SCSI_REMOVAL_PREVENT 1 #define SCSI_REMOVAL_ALLOW 0 diff --git a/kernel/blk_drv/scsi/sd.c b/kernel/blk_drv/scsi/sd.c index 47d3767..889a5f9 100644 --- a/kernel/blk_drv/scsi/sd.c +++ b/kernel/blk_drv/scsi/sd.c @@ -322,9 +322,13 @@ static void do_sd_request (void) struct request * req = NULL; int flag = 0; while (1==1){ - if (CURRENT != NULL && CURRENT->dev == -1) return; + cli(); + if (CURRENT != NULL && CURRENT->dev == -1) { + sti(); + return; + }; - INIT_REQUEST; + INIT_SCSI_REQUEST; /* We have to be careful here. allocate_device will get a free pointer, but there is no guarantee that it is queueable. In normal usage, we want to @@ -341,6 +345,7 @@ static void do_sd_request (void) SCpnt = allocate_device(&CURRENT, rscsi_disks[DEVICE_NR(MINOR(CURRENT->dev))].device->index, 0); else SCpnt = NULL; + sti(); /* This is a performance enhancement. We dig down into the request list and try and find a queueable request (i.e. device not busy, and host able to @@ -562,6 +567,18 @@ repeat: cmd[1] = (SCpnt->lun << 5) & 0xe0; + if (rscsi_disks[dev].sector_size == 1024){ + if(block & 1) panic("sd.c:Bad block number requested"); + if(this_count & 1) panic("sd.c:Bad block number requested"); + block = block >> 1; + this_count = this_count >> 1; + }; + + if (rscsi_disks[dev].sector_size == 256){ + block = block << 1; + this_count = this_count << 1; + }; + if (((this_count > 0xff) || (block > 0x1fffff)) && rscsi_disks[dev].ten) { if (this_count > 0xffff) @@ -588,7 +605,8 @@ repeat: cmd[5] = 0; } - scsi_do_cmd (SCpnt, (void *) cmd, buff, this_count << 9, + scsi_do_cmd (SCpnt, (void *) cmd, buff, + this_count * rscsi_disks[dev].sector_size, rw_intr, SD_TIMEOUT, MAX_RETRIES); } @@ -660,6 +678,8 @@ static int sd_init_onedisk(int i) cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0; memset ((void *) &cmd[2], 0, 8); SCpnt->request.dev = 0xffff; /* Mark as really busy again */ + SCpnt->sense_buffer[0] = 0; + SCpnt->sense_buffer[2] = 0; scsi_do_cmd (SCpnt, (void *) cmd, (void *) buffer, @@ -717,8 +737,15 @@ static int sd_init_onedisk(int i) printk("sd%d : sense not available. \n", i); printk("sd%d : block size assumed to be 512 bytes, disk size 1GB. \n", i); - rscsi_disks[i].capacity = 0xfffff; + rscsi_disks[i].capacity = 0x1fffff; rscsi_disks[i].sector_size = 512; + + /* Set dirty bit for removable devices if not ready - sometimes drives + will not report this properly. */ + if(rscsi_disks[i].device->removable && + SCpnt->sense_buffer[2] == NOT_READY) + rscsi_disks[i].device->changed = 1; + } else { @@ -727,10 +754,12 @@ static int sd_init_onedisk(int i) (buffer[2] << 8) | buffer[3]; - if ((rscsi_disks[i].sector_size = (buffer[4] << 24) | - (buffer[5] << 16) | - (buffer[6] << 8) | - buffer[7]) != 512) + rscsi_disks[i].sector_size = (buffer[4] << 24) | + (buffer[5] << 16) | (buffer[6] << 8) | buffer[7]; + + if (rscsi_disks[i].sector_size != 512 && + rscsi_disks[i].sector_size != 1024 && + rscsi_disks[i].sector_size != 256) { printk ("sd%d : unsupported sector size %d.\n", i, rscsi_disks[i].sector_size); @@ -745,6 +774,10 @@ static int sd_init_onedisk(int i) return i; }; } + if(rscsi_disks[i].sector_size == 1024) + rscsi_disks[i].capacity <<= 1; /* Change this into 512 byte sectors */ + if(rscsi_disks[i].sector_size == 256) + rscsi_disks[i].capacity >>= 1; /* Change this into 512 byte sectors */ } rscsi_disks[i].ten = 1; diff --git a/kernel/blk_drv/scsi/seagate.c b/kernel/blk_drv/scsi/seagate.c index f9598e9..400d06c 100644 --- a/kernel/blk_drv/scsi/seagate.c +++ b/kernel/blk_drv/scsi/seagate.c @@ -64,9 +64,9 @@ static volatile int st0x_aborted=0; /* static unsigned char controller_type; /* set to SEAGATE for ST0x boards or FD for TMC-88x boards */ #define retcode(result) (((result) << 16) | (message << 8) | status) -#define STATUS (*(unsigned char *) st0x_cr_sr) +#define STATUS (*(volatile unsigned char *) st0x_cr_sr) #define CONTROL STATUS -#define DATA (*(unsigned char *) st0x_dr) +#define DATA (*(volatile unsigned char *) st0x_dr) #ifndef OVERRIDE static const char * seagate_bases[] = {(char *) 0xc8000, (char *) 0xca000, (char *) 0xcc000, (char *) 0xce000, (char *) 0xce000, diff --git a/kernel/blk_drv/scsi/sr.c b/kernel/blk_drv/scsi/sr.c index 24140d9..22df4b7 100644 --- a/kernel/blk_drv/scsi/sr.c +++ b/kernel/blk_drv/scsi/sr.c @@ -28,7 +28,7 @@ #include "sr.h" #include "scsi_ioctl.h" /* For the door lock/unlock commands */ -#define MAX_RETRIES 0 +#define MAX_RETRIES 1 #define SR_TIMEOUT 250 int NR_SR=0; @@ -279,14 +279,19 @@ static void do_sr_request (void) int flag = 0; while (1==1){ - if (CURRENT != NULL && CURRENT->dev == -1) return; + cli(); + if (CURRENT != NULL && CURRENT->dev == -1) { + sti(); + return; + }; - INIT_REQUEST; + INIT_SCSI_REQUEST; if (flag++ == 0) SCpnt = allocate_device(&CURRENT, scsi_CDs[DEVICE_NR(MINOR(CURRENT->dev))].device->index, 0); else SCpnt = NULL; + sti(); /* This is a performance enhancement. We dig down into the request list and @@ -542,15 +547,21 @@ are any multiple of 512 bytes long. */ } }; - block = block >> 2; /* These are the sectors that the cdrom uses */ + if (scsi_CDs[dev].sector_size == 2048) + block = block >> 2; /* These are the sectors that the cdrom uses */ + else + block = block & 0xfffffffc; + realcount = (this_count + 3) / 4; + if (scsi_CDs[dev].sector_size == 512) realcount = realcount << 2; + if (((realcount > 0xff) || (block > 0x1fffff)) && scsi_CDs[dev].ten) { if (realcount > 0xffff) { realcount = 0xffff; - this_count = realcount * 4; + this_count = realcount * (scsi_CDs[dev].sector_size >> 9); } cmd[0] += READ_10 - READ_6 ; @@ -567,7 +578,7 @@ are any multiple of 512 bytes long. */ if (realcount > 0xff) { realcount = 0xff; - this_count = realcount * 4; + this_count = realcount * (scsi_CDs[dev].sector_size >> 9); } cmd[1] |= (unsigned char) ((block >> 16) & 0x1f); @@ -578,11 +589,12 @@ are any multiple of 512 bytes long. */ } #ifdef DEBUG - printk("ReadCD: %d %d %d\n",block, realcount, buffer); + printk("ReadCD: %d %d %d %d\n",block, realcount, buffer, this_count); #endif SCpnt->this_count = this_count; - scsi_do_cmd (SCpnt, (void *) cmd, buffer, realcount << 11, + scsi_do_cmd (SCpnt, (void *) cmd, buffer, + realcount * scsi_CDs[dev].sector_size, rw_intr, SR_TIMEOUT, MAX_RETRIES); } @@ -597,9 +609,29 @@ void sr_attach(Scsi_Device * SDp){ if(NR_SR > MAX_SR) panic ("scsi_devices corrupt (sr)"); }; +static void sr_init_done (Scsi_Cmnd * SCpnt) +{ + struct request * req; + struct task_struct * p; + + req = &SCpnt->request; + req->dev = 0xfffe; /* Busy, but indicate request done */ + + if ((p = req->waiting) != NULL) { + req->waiting = NULL; + p->state = TASK_RUNNING; + if (p->counter > current->counter) + need_resched = 1; + } +} + unsigned long sr_init(unsigned long memory_start, unsigned long memory_end) { int i; + unsigned char cmd[10]; + unsigned char buffer[513]; + int the_result, retries; + Scsi_Cmnd * SCpnt; if (register_blkdev(MAJOR_NR,"sr",&sr_fops)) { printk("Unable to get major %d for SCSI-CD\n",MAJOR_NR); @@ -613,8 +645,56 @@ unsigned long sr_init(unsigned long memory_start, unsigned long memory_end) for (i = 0; i < NR_SR; ++i) { - scsi_CDs[i].capacity = 0x1fffff; - scsi_CDs[i].sector_size = 2048; + SCpnt = allocate_device(NULL, scsi_CDs[i].device->index, 1); + + retries = 3; + do { + cmd[0] = READ_CAPACITY; + cmd[1] = (scsi_CDs[i].device->lun << 5) & 0xe0; + memset ((void *) &cmd[2], 0, 8); + SCpnt->request.dev = 0xffff; /* Mark as really busy */ + + scsi_do_cmd (SCpnt, + (void *) cmd, (void *) buffer, + 512, sr_init_done, SR_TIMEOUT, + MAX_RETRIES); + + if (current == task[0]) + while(SCpnt->request.dev != 0xfffe); + else + if (SCpnt->request.dev != 0xfffe){ + SCpnt->request.waiting = current; + current->state = TASK_UNINTERRUPTIBLE; + while (SCpnt->request.dev != 0xfffe) schedule(); + }; + + the_result = SCpnt->result; + retries--; + + } while(the_result && retries); + + SCpnt->request.dev = -1; /* Mark as not busy */ + + wake_up(&scsi_devices[SCpnt->index].device_wait); + if (the_result) { + scsi_CDs[i].capacity = 0x1fffff; + scsi_CDs[i].sector_size = 2048; + } else { + scsi_CDs[i].capacity = (buffer[0] << 24) | + (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; + scsi_CDs[i].sector_size = (buffer[4] << 24) | + (buffer[5] << 16) | (buffer[6] << 8) | buffer[7]; + if(scsi_CDs[i].sector_size != 2048 && + scsi_CDs[i].sector_size != 512) { + printk ("scd%d : unsupported sector size %d.\n", + i, scsi_CDs[i].sector_size); + scsi_CDs[i].capacity = 0; + }; + if(scsi_CDs[i].sector_size == 2048) + scsi_CDs[i].capacity *= 4; + }; + + printk("Scd sectorsize = %d bytes\n", scsi_CDs[i].sector_size); scsi_CDs[i].use = 1; scsi_CDs[i].ten = 1; scsi_CDs[i].remap = 1; diff --git a/kernel/blk_drv/scsi/st.c b/kernel/blk_drv/scsi/st.c index 29e37a1..bfe0539 100644 --- a/kernel/blk_drv/scsi/st.c +++ b/kernel/blk_drv/scsi/st.c @@ -1197,7 +1197,7 @@ static int st_ioctl(struct inode * inode,struct file * file, return result; } else - return (-EINVAL); + return scsi_ioctl(scsi_tapes[dev].device, cmd_in, (void *) arg); } diff --git a/kernel/blk_drv/scsi/ultrastor.c b/kernel/blk_drv/scsi/ultrastor.c index c41968a..6bc1a00 100644 --- a/kernel/blk_drv/scsi/ultrastor.c +++ b/kernel/blk_drv/scsi/ultrastor.c @@ -403,7 +403,7 @@ int ultrastor_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) memset(&mscp.command_link, 0, sizeof(mscp.command_link)); /*???*/ mscp.scsi_command_link_id = 0; /*???*/ mscp.length_of_sense_byte = 0; /*???*/ - mscp.length_of_scsi_cdbs = ((SCpnt->cmnd[0] <= 0x1F) ? 6 : 10); + mscp.length_of_scsi_cdbs = COMMAND_SIZE(*(unsigned char *)SCpnt->cmnd); memcpy(mscp.scsi_cdbs, SCpnt->cmnd, mscp.length_of_scsi_cdbs); mscp.adapter_status = 0; mscp.target_status = 0; @@ -496,8 +496,8 @@ int ultrastor_biosparam(int size, int dev, int *info) info[0] = config.heads; info[1] = config.sectors; info[2] = (size + (s - 1)) / s; - if (info[2] > 1024) - info[2] = 1024; +/* if (info[2] > 1024) + info[2] = 1024; */ return 0; } diff --git a/kernel/blk_drv/scsi/wd7000.c b/kernel/blk_drv/scsi/wd7000.c index db402ad..234ae7f 100644 --- a/kernel/blk_drv/scsi/wd7000.c +++ b/kernel/blk_drv/scsi/wd7000.c @@ -357,7 +357,7 @@ int wd7000_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *)) short cdblen; cdb = (unchar *) SCpnt->cmnd; - cdblen = (*cdb <= 0x1f ? 6 : 10); + cdblen = COMMAND_SIZE(*cdb); idlun = ((SCpnt->target << 5) & 0xe0) | (SCpnt->lun & 7); SCpnt->scsi_done = done; SCpnt->SCp.phase = 1; @@ -580,7 +580,7 @@ int wd7000_abort(Scsi_Cmnd * SCpnt, int i) printk("wd7000_abort: Scsi_Cmnd = 0x%08x, code = %d ", SCpnt, i); printk("id %d lun %d cdb", SCpnt->target, SCpnt->lun); { int j; unchar *cdbj = (unchar *) SCpnt->cmnd; - for (j=0; j < (*cdbj <= 0x1f?6:10); j++) printk(" %02x", *(cdbj++)); + for (j=0; j < COMMAND_SIZE(*cdbj); j++) printk(" %02x", *(cdbj++)); printk(" result %08x\n", SCpnt->result); } #endif @@ -606,7 +606,7 @@ int wd7000_biosparam(int size, int dev, int* info) info[0] = 64; info[1] = 32; info[2] = (size + 2047) >> 11; - if (info[2] >= 1024) info[2] = 1024; +/* if (info[2] >= 1024) info[2] = 1024; */ return 0; } diff --git a/kernel/chr_drv/keyboard.c b/kernel/chr_drv/keyboard.c index 30d4e83..033831a 100644 --- a/kernel/chr_drv/keyboard.c +++ b/kernel/chr_drv/keyboard.c @@ -40,7 +40,9 @@ extern void do_keyboard_interrupt(void); extern void ctrl_alt_del(void); extern void change_console(unsigned int new_console); -extern void fake_keyboard_interrupt(void); + +#define fake_keyboard_interrupt() \ +__asm__ __volatile__("int $0x21") unsigned long kbd_flags = 0; unsigned long kbd_dead_keys = 0; diff --git a/kernel/chr_drv/tty_io.c b/kernel/chr_drv/tty_io.c index aed9428..2017cda 100644 --- a/kernel/chr_drv/tty_io.c +++ b/kernel/chr_drv/tty_io.c @@ -243,6 +243,8 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops) continue; if (filp->f_rdev != dev) continue; + if (filp->f_inode && filp->f_inode->i_rdev == 0x0400) + continue; if (filp->f_op != &tty_fops) continue; filp->f_op = fops; diff --git a/kernel/chr_drv/tty_ioctl.c b/kernel/chr_drv/tty_ioctl.c index 0ff7f97..d9cd91e 100644 --- a/kernel/chr_drv/tty_ioctl.c +++ b/kernel/chr_drv/tty_ioctl.c @@ -387,8 +387,9 @@ int tty_ioctl(struct inode * inode, struct file * file, case TIOCNXCL: return -EINVAL; /* not implemented */ case TIOCSCTTY: - if (current->leader && current->tty < 0 - && tty->session == 0) { + if ((current->leader && current->tty < 0 && + tty->session == 0) || + (arg == 1 && suser())) { current->tty = dev; tty->session = current->session; tty->pgrp = current->pgrp; diff --git a/kernel/exit.c b/kernel/exit.c index 4a1e02a..5255386 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -98,7 +98,7 @@ int bad_task_ptr(struct task_struct *p) * This routine scans the pid tree and make sure the rep invarient still * holds. Used for debugging only, since it's very slow.... * - * It looks a lot scarier than it really is.... we're doing ænothing more + * It looks a lot scarier than it really is.... we're doing nothing more * than verifying the doubly-linked list found in p_ysptr and p_osptr, * and checking it corresponds with the process tree defined by p_cptr and * p_pptr; diff --git a/kernel/irq.c b/kernel/irq.c index 2e5d29f..9d503ae 100644 --- a/kernel/irq.c +++ b/kernel/irq.c @@ -125,11 +125,6 @@ static void (*bad_interrupt[16])(void) = { bad_IRQ14_interrupt, bad_IRQ15_interrupt }; -void fake_keyboard_interrupt(void) -{ - IRQ1_interrupt(); -} - /* * Initial irq handlers. */ diff --git a/kernel/sched.c b/kernel/sched.c index 992a3fb..6fba0a8 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -74,6 +74,8 @@ void math_state_restore(void) { if (last_task_used_math == current) return; + timer_table[COPRO_TIMER].expires = jiffies+50; + timer_active |= 1<<COPRO_TIMER; if (last_task_used_math) { __asm__("fnsave %0"::"m" (last_task_used_math->tss.i387)); } @@ -85,6 +87,7 @@ void math_state_restore(void) __asm__("fninit"::); current->used_math=1; } + timer_active &= ~(1<<COPRO_TIMER); } /* @@ -138,39 +141,11 @@ void schedule(void) switch_to(next); } -/* - * This is a little tricky because POSIX pause (3.4.2.1) should only - * return for a caught signal or a signal that terminates the process. - * We just block ignored signals or "harmless" default signals. - * For suspending signals, we must return so that they are handled - * and then pause again. -- jrs - */ - int sys_pause(void) { - unsigned long old_blocked; - unsigned long mask; - int sig; - struct sigaction * sa = current->sigaction; - - old_blocked = current->blocked; - /* block everything we can that shouldn't interrupt pause */ - for (mask = 1, sig = 1; mask; sa++, mask += mask, sig++) - if (sa->sa_handler == SIG_IGN || (sa->sa_handler == SIG_DFL - && (sig == SIGCONT || sig == SIGCHLD || sig == SIGWINCH))) - current->blocked |= mask; - do { - current->state = TASK_INTERRUPTIBLE; - schedule(); - } while (!(current->signal & ~current->blocked)); - /* if a suspending signal interrupted us we must restart */ - if (!(current->signal & ~current->blocked & - ~(_S(SIGSTOP) | _S(SIGTSTP) | _S(SIGTTIN) | _S(SIGTTOU)))) { - current->blocked = old_blocked; - return -ERESTARTSYS; - } - current->blocked = old_blocked; - return -EINTR; + current->state = TASK_INTERRUPTIBLE; + schedule(); + return -ERESTARTNOHAND; } /* diff --git a/kernel/signal.c b/kernel/signal.c index f90b7a2..cea610c 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -10,6 +10,7 @@ #include <linux/errno.h> #include <linux/wait.h> #include <linux/ptrace.h> +#include <linux/unistd.h> #include <asm/segment.h> @@ -18,6 +19,7 @@ #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) extern int core_dump(long signr,struct pt_regs * regs); +int do_signal(unsigned long oldmask, struct pt_regs * regs); int sys_sgetmask(void) { @@ -42,47 +44,23 @@ int sys_sigpending(sigset_t *set) return error; } -/* atomically swap in the new signal mask, and wait for a signal. - * - * we need to play some games with syscall restarting. We get help - * from the syscall library interface. Note that we need to coordinate - * the calling convention with the libc routine. - * - * "set" is just the sigmask as described in 1003.1-1988, 3.3.7. - * It is assumed that sigset_t can be passed as a 32 bit quantity. - * - * "restart" holds a restart indication. If it's 1, then we - * install the old mask, and return normally. If it's zero, we store - * the current mask in old_mask and block until a signal comes in. - * If it's 2, then it's a signal we must handle but not return from. - * - * We are careful to prevent a rouge restart from user space from fooling - * us into blocking SIGKILL or SIGSTOP. +/* + * atomically swap in the new signal mask, and wait for a signal. */ -int sys_sigsuspend(volatile int restart, volatile unsigned long old_mask, unsigned long set) +int sys_sigsuspend(volatile int restart, volatile unsigned long oldmask, unsigned long set) { - extern int sys_pause(void); + unsigned long mask; + struct pt_regs * regs = (struct pt_regs *) &restart; - switch (restart) { - case 0: - /* we're not restarting. do the work */ - restart = 1; - old_mask = current->blocked; - current->blocked = set & _BLOCKABLE; - break; - case 1: - /* we're restarting to restore and exit */ - current->blocked = old_mask & _BLOCKABLE; - return -EINTR; - case 2: - /* we're restarting but staying paused */ - restart = 1; - break; + mask = current->blocked; + current->blocked = set & _BLOCKABLE; + regs->eax = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(mask,regs)) + return -EINTR; } - /* pause returns after a signal arrives */ - if (sys_pause() == -ERESTARTSYS) - restart = 2; - return -ERESTARTNOINTR; /* handle the signal, and come back */ } /* @@ -119,7 +97,7 @@ static void check_pending(int signum) } } -int sys_signal(int signum, long handler, long restorer) +int sys_signal(int signum, long handler) { struct sigaction tmp; @@ -128,7 +106,7 @@ int sys_signal(int signum, long handler, long restorer) tmp.sa_handler = (void (*)(int)) handler; tmp.sa_mask = 0; tmp.sa_flags = SA_ONESHOT | SA_NOMASK | SA_INTERRUPT; - tmp.sa_restorer = (void (*)(void)) restorer; + tmp.sa_restorer = NULL; handler = (long) current->sigaction[signum-1].sa_handler; current->sigaction[signum-1] = tmp; check_pending(signum); @@ -165,26 +143,75 @@ int sys_sigaction(int signum, const struct sigaction * action, extern int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options); +void sys_sigreturn(int signr, unsigned long oldmask, unsigned long unused) +{ + current->blocked = oldmask & _BLOCKABLE; +} + +/* + * This routine sets up the return stack for the first signal found + * (== last delivered). It makes room for the registers we need to save, + * but the actual saving is left until the very last moment when we + * know whether we can restart system calls etc. + */ +static unsigned long * setup_first(struct pt_regs * regs, + int signr, unsigned long sa_handler, unsigned long oldmask) +{ + unsigned long * tmp_esp; + + regs->esp -= 18*4; + tmp_esp = (unsigned long *) regs->esp; + verify_area(VERIFY_WRITE,tmp_esp,18*4); +/* set up the "normal" stack seen by the signal handler */ + put_fs_long(regs->esp+15*4,tmp_esp); /* points to the stack.. */ + put_fs_long(signr,tmp_esp+1); /* parameter to handler and sigreturn */ + put_fs_long(0,tmp_esp+2); /* third parameter to sigreturn */ + put_fs_long(oldmask,tmp_esp+3); /* second .. */ + put_fs_long(__NR_sigreturn,tmp_esp+4); /* sigreturn number.. */ +/* save this frame so that we later can fill in the saved registers */ + return tmp_esp+5; +} + +/* + * This sets up the stack for any stacked signals other than the + * first one: no need to restore registers etc, as that is done + * by the very last signal handler return code.. + */ +static void setup_other(unsigned long eip, struct pt_regs * regs, int signr, + unsigned long sa_handler, unsigned long oldmask) +{ + unsigned long * tmp_esp; + + regs->esp -= 9*4; + tmp_esp = (unsigned long *) regs->esp; + verify_area(VERIFY_WRITE,tmp_esp,9*4); +/* set up the "normal" stack seen by the signal handler */ + put_fs_long(regs->esp+6*4,tmp_esp); /* points to the stack.. */ + put_fs_long(signr,tmp_esp+1); /* parameter to handler and sigreturn */ + put_fs_long(0,tmp_esp+2); /* third parameter to sigreturn */ + put_fs_long(oldmask,tmp_esp+3); /* second .. */ + put_fs_long(__NR_sigreturn,tmp_esp+4); /* sigreturn number.. */ + put_fs_long(eip,tmp_esp+5); /* return address */ +/* set up the return code... */ + put_fs_long(0x58595a5b,tmp_esp+6); /* pop bx,dx,cx,ax */ + put_fs_long(0x909080cd,tmp_esp+7); /* int $0x80 + nop + nop */ + put_fs_long(0x000cc290,tmp_esp+8); /* nop + "ret 12" */ +} + /* * Note that 'init' is a special process: it doesn't get signals it doesn't * want to handle. Thus you cannot kill init even with a SIGKILL even by * mistake. */ -void do_signal(struct pt_regs * regs) +int do_signal(unsigned long oldmask, struct pt_regs * regs) { + unsigned long *frame = NULL; + unsigned long eip = 0; unsigned long signr; unsigned long sa_handler; - long old_eip = regs->eip; struct sigaction * sa; - int longs; - unsigned long * tmp_esp; - if (regs->orig_eax >= 0 && regs->eax == -ERESTARTNOINTR) { - regs->eax = regs->orig_eax; - regs->eip = old_eip -= 2; - } - signr = current->signal & ~current->blocked; - do { + while ((signr = current->signal & ~current->blocked)) { __asm__("bsf %2,%1\n\t" "btrl %1,%0" :"=m" (current->signal),"=r" (signr) @@ -203,15 +230,10 @@ void do_signal(struct pt_regs * regs) if (current->pid == 1) continue; switch (signr) { - case SIGCONT: - case SIGCHLD: - case SIGWINCH: + case SIGCONT: case SIGCHLD: case SIGWINCH: continue; - case SIGSTOP: - case SIGTSTP: - case SIGTTIN: - case SIGTTOU: + case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU: current->state = TASK_STOPPED; current->exit_code = signr; if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags & @@ -220,12 +242,8 @@ void do_signal(struct pt_regs * regs) schedule(); continue; - case SIGQUIT: - case SIGILL: - case SIGTRAP: - case SIGIOT: - case SIGFPE: - case SIGSEGV: + case SIGQUIT: case SIGILL: case SIGTRAP: + case SIGIOT: case SIGFPE: case SIGSEGV: if (core_dump(signr,regs)) signr |= 0x80; /* fall through */ @@ -235,39 +253,49 @@ void do_signal(struct pt_regs * regs) } } /* - * OK, we're invoking a handler + * OK, we're invoking a handler */ - if (regs->orig_eax >= 0 && regs->eax == -ERESTARTSYS) { - if (sa->sa_flags & SA_INTERRUPT) + if (regs->orig_eax >= 0) { + if (regs->eax == -ERESTARTNOHAND || + (regs->eax == -ERESTARTSYS && (sa->sa_flags & SA_INTERRUPT))) regs->eax = -EINTR; - else { - regs->eax = regs->orig_eax; - regs->eip = old_eip -= 2; - } } if (sa->sa_flags & SA_ONESHOT) sa->sa_handler = NULL; - regs->eip = sa_handler; - longs = (sa->sa_flags & SA_NOMASK)?(7*4):(8*4); - regs->esp -= longs; - tmp_esp = (unsigned long *) regs->esp; - verify_area(VERIFY_WRITE,tmp_esp,longs); - put_fs_long((long) sa->sa_restorer,tmp_esp++); - put_fs_long(signr,tmp_esp++); - if (!(sa->sa_flags & SA_NOMASK)) - put_fs_long(current->blocked,tmp_esp++); - put_fs_long(regs->eax,tmp_esp++); - put_fs_long(regs->ecx,tmp_esp++); - put_fs_long(regs->edx,tmp_esp++); - put_fs_long(regs->eflags,tmp_esp++); - put_fs_long(old_eip,tmp_esp++); - current->blocked |= sa->sa_mask; /* force a supervisor-mode page-in of the signal handler to reduce races */ __asm__("testb $0,%%fs:%0"::"m" (*(char *) sa_handler)); - return; - } while ((signr = current->signal & ~current->blocked)); - if (regs->orig_eax >= 0 && regs->eax == -ERESTARTSYS) { + if (!frame) { + frame = setup_first(regs,signr,sa_handler,oldmask); + } else + setup_other(eip,regs,signr,sa_handler,oldmask); + eip = sa_handler; + current->blocked |= sa->sa_mask; + oldmask |= sa->sa_mask; + } + if (regs->orig_eax >= 0 && + (regs->eax == -ERESTARTNOHAND || + regs->eax == -ERESTARTSYS || + regs->eax == -ERESTARTNOINTR)) { regs->eax = regs->orig_eax; - regs->eip = old_eip -= 2; + regs->eip -= 2; } + if (!frame) /* no handlers installed - return 0 */ + return 0; +/* save registers if one or more handlers are called.. */ + put_fs_long(regs->edi,frame); /* suitable order for "popad" */ + put_fs_long(regs->esi,frame+1); + put_fs_long(regs->ebp,frame+2); /* using 'frame++' instead of the 'frame+x' */ + put_fs_long(regs->esp,frame+3); /* form used now results in atrocious code */ + put_fs_long(regs->ebx,frame+4); /* due to gcc not being very good at optimizing */ + put_fs_long(regs->edx,frame+5); /* things with inline-assembly/functions.. */ + put_fs_long(regs->ecx,frame+6); + put_fs_long(regs->eax,frame+7); + put_fs_long(regs->eflags,frame+8); /* flags */ + put_fs_long(regs->eip,frame+9); /* original return address */ +/* set up the return code... */ + put_fs_long(0x58595a5b,frame+10); /* pop bx,dx,cx,ax */ + put_fs_long(0x906180cd,frame+11); /* int $0x80 + popad + nop */ + put_fs_long(0x000cc29d,frame+12); /* popfl + "ret 12" */ + regs->eip = eip; /* "return" to the first handler */ + return 1; } diff --git a/kernel/sys_call.S b/kernel/sys_call.S index 9b10c82..d16ec17 100644 --- a/kernel/sys_call.S +++ b/kernel/sys_call.S @@ -184,16 +184,20 @@ signal_return: pushl %ebx testl $VM_MASK,EFLAGS(%ebx) jne v86_signal_return + pushl blocked(%eax) call _do_signal popl %ebx + popl %ebx RESTORE_ALL .align 4 v86_signal_return: call _save_v86_state movl %eax,%esp pushl %eax + pushl blocked(%eax) call _do_signal popl %ebx + popl %ebx RESTORE_ALL .align 4 |