Commit 8151e895 authored by Linus Torvalds's avatar Linus Torvalds

Import 1.1.32

parent 3dd3f561
VERSION = 1
PATCHLEVEL = 1
SUBLEVEL = 31
SUBLEVEL = 32
all: Version zImage
......
......@@ -81,7 +81,6 @@ if [ "$CONFIG_SLIP" = "y" ]; then
fi
bool 'PPP (point-to-point) support' CONFIG_PPP n
bool 'PLIP (parallel port) support' CONFIG_PLIP n
bool 'SK_G16 support' CONFIG_SK_G16 n
bool 'Load balancing support (experimental)' CONFIG_SLAVE_BALANCING n
bool 'Do you want to be offered ALPHA test drivers' CONFIG_NET_ALPHA n
bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC n
......@@ -110,6 +109,7 @@ if [ "$CONFIG_NET_ISA" = "y" ]; then
fi
bool 'HP PCLAN support' CONFIG_HPLAN n
bool 'NE2000/NE1000 support' CONFIG_NE2000 y
bool 'SK_G16 support' CONFIG_SK_G16 n
fi
bool 'EISA and on board controllers' CONFIG_NET_EISA n
if [ "$CONFIG_NET_ALPHA" = "y" ]; then
......
IDE Performance Enhancements Version 2.0
============================ ===========
This version of hd.c includes support for two optional features:
(1) The disk I/O routines can now run with interrupts unmasked
most of the time, making them much friendlier to high
speed serial ports and other system activity.
(2) Support is included for IDE "Multiple Sector Mode", the use
of which can reduce disk I/O kernel overhead by 10-30%
on many systems, with a corresponding 10-20% increase in
data throughput.
By default, both features are DISABLED, for compatibility with
systems on which they may cause troubles.
The IRQ unmasking has been known to CORRUPT FILESYSTEMS in the
past on systems with strange hard drives. Backup before trying!
It works on most systems, but use at your own risk!!
Drives which support "Multiple Sector Mode" are identified by the
kernel at boot time, and a message is displayed indicating the
largest possible setting for "MaxMult". I recommend using settings
of 8, 16, or 32. Many drives also support non-powers of two,
but many other drives do not -- try strange values at your own risk!
For more detailed boot-time information about your drive, change
the definition of VERBOSE_DRIVE_INFO from 0 to 1 near the top
of hd.c and rebuild/reinstall the kernel.
Some drives (mostly older CONNER drives) do not implement multiple mode
correctly, and data corruption may occur.. but if you wait long enough
the error recovery logic *should* be able to recover eventually.
To try this out more safely, mount the drive's partitions read-only
before using hdparm (see below) for the first time. If it doesn't
work, email me (mlord@bnr.ca) with the drive name as displayed at
boot time, so I can warn others and possibly add a hook to the code.
To enable the features, a small program is included: hdparm.c
This one is *different* from previous versions -- be sure to recompile it!
Compile this using cc -O -o /usr/bin/hdparm hdparm.c
and then use it to enable/disable the new features, as follows:
To turn on 16-sector multiple mode, with interrupt unmasking:
hdparm /dev/hda 16 1
To view the current settings:
hdparm /dev/hda
If you have more than one drive, a separate command would need to be issued for the second drive as well, using the same or different
settings as desired:
hdparm /dev/hdb 16 1
To turn off both features on the first drive, use:
hdparm /dev/hda 0 0
To benchmark the performance difference, try:
hdparm /dev/hda 0 0
sync
time dd if=/dev/hda of=/dev/null bs=1024k count=30
time dd if=/dev/hda of=/dev/null bs=1024k count=30
time dd if=/dev/hda of=/dev/null bs=1024k count=30
hdparm /dev/hda 16 1
sync
time dd if=/dev/hda of=/dev/null bs=1024k count=30
time dd if=/dev/hda of=/dev/null bs=1024k count=30
time dd if=/dev/hda of=/dev/null bs=1024k count=30
This gives before and after views. Compare the total elapsed times,
as well as the percent of CPU used in each case. Run several trials
to ensure/verify consistent results. Some drives are actually *slower*
with multiple mode enabled, but those are very rare indeed. Most systems
experience a 10-30% increase in throughput, with a corresponding 5-50%
decrease in kernel/system CPU usage.
If you are using linux kernel 1.1.4 or higher (with the "cluster" code),
then you may not notice a big difference with the above tests. However,
I have noticed that the iozone benchmark program does seem to work fairly
reliably under kernels with the "cluster" code, so you could try that
instead (under older kernels, iozone seems to give wildly varying results
from trial to trial, at least on my system).
To have your favorite settings installed automatically at boot time,
place the hdparm command(s) into the /etc/rc.d/rc.local file.
Alternatively, one could modify the DEFAULTs near the top of hd.c
and rebuild and install the new kernel.
Enjoy,
mlord@bnr.ca
......@@ -14,9 +14,15 @@
*
* Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
* in the early extended-partition checks and added DM partitions
*
* IDE IRQ-unmask & drive-id & multiple-mode code added by Mark Lord.
*/
#define DEFAULT_MULT_COUNT 0 /* set to 0 to disable multiple mode at boot */
#define DEFAULT_UNMASK_INTR 0 /* set to 0 to *NOT* unmask irq's more often */
#define VERBOSE_DRIVE_INFO 0 /* set to 1 for more drive info at boot time */
#include <asm/irq.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
......@@ -211,6 +217,128 @@ static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
outb_p(cmd,++port);
}
static void hd_request (void);
unsigned int identified [MAX_HD] = {0,}; /* 1 = drive ID already displayed */
unsigned int unmask_intr [MAX_HD] = {0,}; /* 1 = unmask IRQs during I/O */
unsigned int max_mult [MAX_HD] = {0,}; /* max sectors for MultMode */
unsigned int mult_req [MAX_HD] = {0,}; /* requested MultMode count */
unsigned int mult_count [MAX_HD] = {0,}; /* currently enabled MultMode count */
struct request WCURRENT;
static void rawstring (char *prefix, char *s, int n)
{
if (prefix)
printk(prefix);
if (s && *s) {
int i;
for (i=0; i < n && s[i^1] == ' '; ++i); /* skip blanks */
for (; i < n && s[i^1]; ++i) /* flip bytes */
if (s[i^1] != ' ' || ((i+1) < n && s[(i+1)^1] != ' '))
printk("%c",s[i^1]);
}
}
#if VERBOSE_DRIVE_INFO
char *cfg_str[] =
{ "", " HardSect", " SoftSect", " NotMFM", " HdSw>15uSec", " SpinMotCtl",
" Fixed", " Removeable", " DTR<=5Mbs", " DTR>5Mbs", " DTR>10Mbs",
" RotSpdTol>.5%", " dStbOff", " TrkOff", " FmtGapReq", "",
};
char *ioready[] = {"no", "?", "yes", "on/off"};
char *SlowMedFast[] = {"slow", "medium", "fast"};
char *BuffType[] = {"?", "1Sect", "DualPort", "DualPortCache"};
#define YN(b) (((b)==0)?"no":"yes")
static void dmpstr (char *prefix, unsigned int i, char *s[], unsigned int maxi)
{
printk(prefix);
printk( (i > maxi) ? "?" : s[i] );
}
static void dump_identity (unsigned int dev, unsigned short ib[])
{
int i;
char dashes[] = "\n+-------------------------------------------------------------------+\n";
printk (dashes);
printk ("hd%c: Drive Identification Info:\n", dev+'a');
rawstring (" Model=",(char *)&ib[27],40);
rawstring (", FwRev=",(char *)&ib[23],8);
rawstring (", SerialNo=",(char *)&ib[10],20);
printk ("\n Config={");
for (i=0; i<=15; i++) if (ib[0] & (1<<i)) printk (cfg_str[i]);
printk (" }\n");
printk (" Default c/h/s=%d/%d/%d, TrkSize=%d, SectSize=%d, ECCbytes=%d\n",
ib[1],ib[3],ib[6],ib[4],ib[5], ib[22]);
dmpstr (" BuffType=",ib[20],BuffType,3);
ib[47] &= 0xFF;
printk (", BuffSize=%dKB, MaxMultSect=%d\n", ib[21]/2, ib[47]);
printk (" Features: DblWordIO=%s, IORDY=%s, LBA=%s, DMA=%s",
YN(ib[48]&1),ioready[(ib[49]&0xC00)>>10],YN(ib[49]&0x200),YN(ib[49]&0x100));
dmpstr (", tPIO=",ib[51]>>8,SlowMedFast,2);
if (ib[49]&0x100 && (ib[53]&1))
dmpstr (", tDMA=",ib[52]>>8,SlowMedFast,2);
printk ("\n (%s): Current c/h/s=%d/%d/%d, TotSect=%d",
(((ib[53]&1)==0)?"maybe":"valid"),
ib[54],ib[55],ib[56],*(int *)&ib[57]);
if (ib[49]&0x200)
printk (", MaxLBAsect=%d", *(int *)&ib[60]);
printk("\n CurMultSect=%d%s",ib[59]&0xFF,(ib[59]&0x100)?"":"?");
if (ib[49]&0x100)
printk (", DMA-1w=%04X, DMA-mw=%04X", ib[62], ib[63]);
printk ("%s\n",dashes);
}
#endif /* VERBOSE_DRIVE_INFO */
static void identify_intr(void)
{
unsigned int dev = DEVICE_NR(CURRENT->dev);
unsigned short ib[64], stat = inb_p(HD_STATUS);
if (unmask_intr[dev])
sti();
if (stat & (BUSY_STAT|ERR_STAT))
printk (" hd%c: multiple mode not supported\n", dev+'a');
else {
insw(HD_DATA,(char *)ib,64); /* get first 128 ID bytes */
#if VERBOSE_DRIVE_INFO
dump_identity(dev, ib);
#endif
printk (" hd%c: ", dev+'a');
rawstring(NULL, (char *)&ib[27], 40);
max_mult[dev] = ib[47] & 0xff;
printk (" (%dMB IDE w/%dKB Cache, MaxMult=%d)\n",
ib[1]*ib[3]*ib[6] / 2048, ib[21]>>1, max_mult[dev]);
insw(HD_DATA,(char *)ib,64); /* flush remaining 384 ID bytes */
insw(HD_DATA,(char *)ib,64);
insw(HD_DATA,(char *)ib,64);
}
hd_request();
return;
}
static void set_multmode_intr(void)
{
unsigned int dev = DEVICE_NR(CURRENT->dev), stat = inb_p(HD_STATUS);
if (unmask_intr[dev])
sti();
if (stat & (BUSY_STAT|ERR_STAT)) {
mult_req[dev] = mult_count[dev] = 0;
printk (" hd%c: set multiple mode failed\n", dev+'a');
} else {
if ((mult_count[dev] = mult_req[dev]))
printk (" hd%c: enabled %d-sector multiple mode\n",
dev+'a', mult_count[dev]);
else
printk (" hd%c: disabled multiple mode\n", dev+'a');
}
hd_request();
return;
}
static int drive_busy(void)
{
unsigned int i;
......@@ -254,14 +382,21 @@ static void reset_hd(void)
if (reset)
goto repeat;
}
i++;
if (i < NR_HD) {
if (++i < NR_HD) {
if (unmask_intr[i]) {
printk("hd%c: disabled irq-unmasking\n",i+'a');
unmask_intr[i] = 0;
}
if (mult_req[i] || mult_count[i]) {
printk("hd%c: disabled multiple mode\n",i+'a');
mult_req[i] = mult_count[i] = 0;
}
hd_out(i,hd_info[i].sect,hd_info[i].sect,hd_info[i].head-1,
hd_info[i].cyl,WIN_SPECIFY,&reset_hd);
if (reset)
goto repeat;
} else
do_hd_request();
hd_request();
}
/*
......@@ -313,9 +448,11 @@ static inline int wait_DRQ(void)
static void read_intr(void)
{
int i;
int retries = 100000;
unsigned int dev = DEVICE_NR(CURRENT->dev);
int i, retries = 100000, msect, nsect;
if (unmask_intr[dev])
sti(); /* permit other IRQs during xfer */
do {
i = (unsigned) inb_p(HD_STATUS);
if (i & BUSY_STAT)
......@@ -326,42 +463,111 @@ static void read_intr(void)
goto ok_to_read;
} while (--retries > 0);
sti();
printk("HD: read_intr: status = 0x%02x\n",i);
printk("hd%c: read_intr: status = 0x%02x\n",dev+'a',i);
if (i & ERR_STAT) {
hd_error = (unsigned) inb(HD_ERROR);
printk("HD: read_intr: error = 0x%02x\n",hd_error);
printk("hd%c: read_intr: error = 0x%02x\n",dev+'a',hd_error);
}
bad_rw_intr();
cli();
do_hd_request();
hd_request();
return;
ok_to_read:
insw(HD_DATA,CURRENT->buffer,256);
msect = mult_count[dev];
read_next:
if (msect) {
if ((nsect = CURRENT->current_nr_sectors) > msect)
nsect = msect;
msect -= nsect;
} else
nsect = 1;
insw(HD_DATA,CURRENT->buffer,nsect<<8);
CURRENT->sector += nsect;
CURRENT->buffer += nsect<<9;
CURRENT->errors = 0;
CURRENT->buffer += 512;
CURRENT->sector++;
i = --CURRENT->nr_sectors;
--CURRENT->current_nr_sectors;
i = (CURRENT->nr_sectors -= nsect);
#ifdef DEBUG
printk("hd%d : sector = %d, %d remaining to buffer = %08x\n",
MINOR(CURRENT->dev), CURRENT->sector, i, CURRENT->
buffer);
printk("hd%c: read: sectors(%ld-%ld), remaining=%ld, buffer=%08lx\n",
dev+'a', CURRENT->sector, CURRENT->sector+nsect,
CURRENT->nr_sectors, (long) CURRENT->buffer+(nsect<<9));
#endif
if (!i || (CURRENT->bh && !SUBSECTOR(i)))
if ((CURRENT->current_nr_sectors -= nsect) <= 0)
end_request(1);
if (i > 0) {
if (msect)
goto read_next;
SET_INTR(&read_intr);
sti();
return;
}
(void) inb_p(HD_STATUS);
#if (HD_DELAY > 0)
last_req = read_timer();
#endif
do_hd_request();
if (CURRENT)
hd_request();
return;
}
static inline void multwrite (unsigned int dev)
{
unsigned int mcount = mult_count[dev];
while (mcount--) {
outsw(HD_DATA,WCURRENT.buffer,256);
if (!--WCURRENT.nr_sectors)
return;
WCURRENT.buffer += 512;
if (!--WCURRENT.current_nr_sectors) {
WCURRENT.bh = WCURRENT.bh->b_reqnext;
if (WCURRENT.bh == NULL)
panic("buffer list corrupted\n");
WCURRENT.current_nr_sectors = WCURRENT.bh->b_size>>9;
WCURRENT.buffer = WCURRENT.bh->b_data;
}
}
}
static void multwrite_intr(void)
{
int i;
unsigned int dev = DEVICE_NR(WCURRENT.dev);
if (unmask_intr[dev])
sti();
if (((i = inb_p(HD_STATUS)) & STAT_MASK) == STAT_OK) {
if (i & DRQ_STAT) {
if (WCURRENT.nr_sectors) {
multwrite(dev);
SET_INTR(&multwrite_intr);
return;
}
} else {
if (!WCURRENT.nr_sectors) { /* all done? */
for (i = CURRENT->nr_sectors; i > 0;){
i -= CURRENT->current_nr_sectors;
end_request(1);
}
#if (HD_DELAY > 0)
last_req = read_timer();
#endif
if (CURRENT)
hd_request();
return;
}
}
}
sti();
printk("hd%c: multwrite_intr: status = 0x%02x\n",dev+'a',i);
if (i & ERR_STAT) {
hd_error = (unsigned) inb(HD_ERROR);
printk("hd:%c multwrite_intr: error = 0x%02x\n",dev+'a',hd_error);
}
bad_rw_intr();
cli();
hd_request();
}
static void write_intr(void)
{
int i;
......@@ -384,7 +590,7 @@ static void write_intr(void)
}
bad_rw_intr();
cli();
do_hd_request();
hd_request();
return;
ok_to_write:
CURRENT->sector++;
......@@ -401,7 +607,7 @@ static void write_intr(void)
#if (HD_DELAY > 0)
last_req = read_timer();
#endif
do_hd_request();
hd_request();
}
return;
}
......@@ -410,7 +616,7 @@ static void recal_intr(void)
{
if (win_result())
bad_rw_intr();
do_hd_request();
hd_request();
}
/*
......@@ -433,7 +639,7 @@ static void hd_times_out(void)
end_request(0);
}
do_hd_request();
hd_request();
}
/*
......@@ -443,7 +649,7 @@ static void hd_times_out(void)
* worst that can happen is that an unexpected HD-interrupt comes in and
* sets the "reset" variable and starts the timer)
*/
static void do_hd_request(void)
static void hd_request(void)
{
unsigned int block,dev;
unsigned int sec,head,cyl,track;
......@@ -462,7 +668,7 @@ static void do_hd_request(void)
nsect = CURRENT->nr_sectors;
if (dev >= (NR_HD<<6) || block >= hd[dev].nr_sects) {
#ifdef DEBUG
printk("hd%d : attempted read for sector %d past end of device at sector %d.\n",
printk("hd : attempted read for sector %d past end of device at sector %d.\n",
block, hd[dev].nr_sects);
#endif
end_request(0);
......@@ -475,17 +681,18 @@ static void do_hd_request(void)
head = track % hd_info[dev].head;
cyl = track / hd_info[dev].head;
#ifdef DEBUG
printk("hd%d : cyl = %d, head = %d, sector = %d, buffer = %08x\n",
dev, cyl, head, sec, CURRENT->buffer);
printk("hd%c : cyl = %d, head = %d, sector = %d, buffer = %08x\n",
dev+'a', cyl, head, sec, CURRENT->buffer);
#endif
cli();
if (!unmask_intr[dev])
cli();
if (reset) {
int i;
for (i=0; i < NR_HD; i++)
recalibrate[i] = 1;
cli(); /* better play it safe, as resets are the last resort */
reset_hd();
sti();
return;
}
if (recalibrate[dev]) {
......@@ -493,32 +700,70 @@ static void do_hd_request(void)
hd_out(dev,hd_info[dev].sect,0,0,0,WIN_RESTORE,&recal_intr);
if (reset)
goto repeat;
sti();
return;
}
if (CURRENT->cmd == WRITE) {
hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
if (!identified[dev]) {
identified[dev] = 1;
unmask_intr[dev] = DEFAULT_UNMASK_INTR;
mult_req[dev] = DEFAULT_MULT_COUNT;
hd_out(dev,0,0,0,0,WIN_IDENTIFY,&identify_intr);
if (reset)
goto repeat;
if (wait_DRQ()) {
printk("HD: do_hd_request: no DRQ\n");
bad_rw_intr();
return;
}
if (mult_req[dev] != mult_count[dev]) {
hd_out(dev,mult_req[dev],0,0,0,WIN_SETMULT,&set_multmode_intr);
if (reset)
goto repeat;
}
outsw(HD_DATA,CURRENT->buffer,256);
sti();
return;
}
if (CURRENT->cmd == READ) {
hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr);
unsigned int cmd = mult_count[dev] > 1 ? WIN_MULTREAD : WIN_READ;
hd_out(dev,nsect,sec,head,cyl,cmd,&read_intr);
if (reset)
goto repeat;
sti();
#ifdef DEBUG
printk("hd%c: reading %d sectors(%ld-%ld), buffer=%08lx\n",
dev+'a', nsect, CURRENT->sector,
CURRENT->sector+nsect-1, (long) CURRENT->buffer);
#endif
return;
}
if (CURRENT->cmd == WRITE) {
if (mult_count[dev])
hd_out(dev,nsect,sec,head,cyl,WIN_MULTWRITE,&multwrite_intr);
else
hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
if (reset)
goto repeat;
#ifdef DEBUG
printk("hd%c: writing %d sectors(%ld-%ld), buffer=%08lx\n",
dev+'a', nsect, CURRENT->sector,
CURRENT->sector+nsect-1, (long) CURRENT->buffer);
#endif
if (wait_DRQ()) {
printk("hd%c: hd_request: no DRQ\n", dev+'a');
bad_rw_intr();
goto repeat;
}
if (mult_count[dev]) {
WCURRENT = *CURRENT;
multwrite(dev);
} else {
outsw(HD_DATA,CURRENT->buffer,256);
}
return;
}
panic("unknown hd-command");
}
static void do_hd_request (void)
{
disable_irq(HD_IRQ);
hd_request();
enable_irq(HD_IRQ);
}
static int hd_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
......@@ -568,6 +813,53 @@ static int hd_ioctl(struct inode * inode, struct file * file,
case BLKRRPART: /* Re-read partition tables */
return revalidate_hddisk(inode->i_rdev, 1);
case HDIO_SETUNMASKINTR:
if (!arg) return -EINVAL;
err = verify_area(VERIFY_READ, (long *) arg, sizeof(long));
if (err)
return err;
unmask_intr[dev] = get_fs_long((long *) arg);
return 0;
case HDIO_GETUNMASKINTR:
if (!arg) return -EINVAL;
err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
if (err)
return err;
put_fs_long(unmask_intr[dev], (long *) arg);
return 0;
case HDIO_GETMULTCOUNT:
if (!arg) return -EINVAL;
err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
if (err)
return err;
put_fs_long(mult_count[dev], (long *) arg);
return 0;
case HDIO_SETMULTCOUNT:
{
unsigned long flags;
if (!arg) return -EINVAL;
err = verify_area(VERIFY_READ, (long *) arg, sizeof(long));
if (err)
return err;
arg = get_fs_long((long *) arg);
save_flags(flags);
cli(); /* a prior request might still be in progress */
if (arg > max_mult[dev])
err = -EINVAL; /* out of range for device */
else if (mult_req[dev] != mult_count[dev])
err = -EBUSY; /* busy, try again */
else {
mult_req[dev] = arg;
err = 0;
}
restore_flags(flags);
return err;
}
RO_IOCTLS(inode->i_rdev,arg);
default:
return -EINVAL;
......
......@@ -83,6 +83,7 @@ static struct termios *console_termios_locked[NR_CONSOLES];
int set_selection(const int arg);
int paste_selection(struct tty_struct *tty);
static void clear_selection(void);
static void highlight_pointer(const int currcons, const int where);
/* Variables for selection control. */
#define SEL_BUFFER_SIZE 4096
......@@ -1703,6 +1704,9 @@ void update_screen(int new_console)
return;
lock = 1;
kbdsave(new_console);
#ifdef CONFIG_SELECTION
highlight_pointer(fg_console,-1);
#endif /* CONFIG_SELECTION */
get_scrmem(fg_console);
fg_console = new_console;
set_scrmem(fg_console);
......@@ -1778,8 +1782,49 @@ static void highlight(const int currcons, const int s, const int e)
*p = (*p & 0x88) | ((*p << 4) & 0x70) | ((*p >> 4) & 0x07);
}
/* is c in range [a-zA-Z0-9_]? */
static inline int inword(const char c) { return (isalnum(c) || c == '_'); }
/* use complementary color to show the pointer */
static void highlight_pointer(const int currcons, const int where)
{
unsigned char *p;
static char *prev=NULL;
if (where==-1) /* remove the pointer */
{
if (prev)
{
*prev ^= 0x77;
prev=NULL;
}
}
else
{
p = (unsigned char *)origin - hwscroll_offset + where + 1;
*p ^= 0x77;
if (prev) *prev ^= 0x77; /* remove the previous one */
prev=p;
}
}
/*
* This function uses a 128-bit look up table
*/
static unsigned long inwordLut[4]={
0x00000000, /* control chars */
0x03FF0000, /* digits */
0x87FFFFFE, /* uppercase and '_' */
0x07FFFFFE /* lowercase */
};
static inline int inword(const char c) {
return ( inwordLut[(c>>5)&3] >> (c&0x1F) ) & 1;
}
/* set inwordLut conntents. Invoked by ioctl(). */
int sel_loadlut(const int arg)
{
memcpy_fromfs(inwordLut,(unsigned long *)(arg+4),16);
return 0;
}
/* does screen address p correspond to character at LH/RH edge of screen? */
static inline int atedge(const int p)
......@@ -1828,7 +1873,6 @@ int set_selection(const int arg)
switch (sel_mode)
{
case 0: /* character-by-character selection */
default:
new_sel_start = ps;
new_sel_end = pe;
break;
......@@ -1859,6 +1903,17 @@ int set_selection(const int arg)
new_sel_end = pe + video_size_row
- pe % video_size_row - 2;
break;
case 3: /* pointer highlight */
if (sel_cons != currcons)
{
highlight_pointer(sel_cons,-1);
clear_selection();
sel_cons = currcons;
}
highlight_pointer(sel_cons,pe);
return 0; /* nothing more */
default:
return -EINVAL;
}
/* select to end of line if on trailing space */
if (new_sel_end > new_sel_start &&
......@@ -1962,6 +2017,7 @@ int paste_selection(struct tty_struct *tty)
the selection. */
static void clear_selection()
{
highlight_pointer(sel_cons, -1); /* hide the pointer */
if (sel_start != -1)
{
highlight(sel_cons, sel_start, sel_end);
......
......@@ -86,7 +86,12 @@ static unsigned long key_down[8] = { 0, };
static int want_console = -1;
static int last_console = 0; /* last used VC */
static int dead_key_next = 0;
static int shift_state = 0;
/*
* In order to retrieve the shift_state (for the mouse server), either
* the variable must be global, or a new procedure must be create to
* return the value. I chose the former way.
*/
/*static*/ int shift_state = 0;
static int npadch = -1; /* -1 or number assembled on pad */
static unsigned char diacr = 0;
static char rep = 0; /* flag telling character repeat */
......
......@@ -420,7 +420,7 @@ long chr_dev_init(long mem_start, long mem_end)
#ifdef CONFIG_FTAPE
/* allocate NR_FTAPE_BUFFERS 32Kb buffers at aligned address */
ftape_big_buffer= (char*) ((mem_start + 0x7fff) & ~0x7fff);
printk( "ftape: allocated %d buffers alligned at: %p\n",
printk( "ftape: allocated %d buffers aligned at: %p\n",
NR_FTAPE_BUFFERS, ftape_big_buffer);
mem_start = (long) ftape_big_buffer + NR_FTAPE_BUFFERS * 0x8000;
#endif
......
......@@ -68,6 +68,8 @@
#ifdef CONFIG_SELECTION
extern int set_selection(const int arg);
extern int paste_selection(struct tty_struct *tty);
extern int sel_loadlut(const int arg);
extern int shift_state;
#endif /* CONFIG_SELECTION */
extern int do_screendump(int arg);
......@@ -1385,6 +1387,11 @@ static int tty_ioctl(struct inode * inode, struct file * file,
case 4:
unblank_screen();
return 0;
case 5:
return sel_loadlut(arg);
case 6:
put_fs_byte(shift_state,arg);
return 0;
#endif /* CONFIG_SELECTION */
default:
return -EINVAL;
......
......@@ -54,7 +54,9 @@ extern int el16_probe(struct device *);
extern int elplus_probe(struct device *);
extern int ac3200_probe(struct device *);
extern int e2100_probe(struct device *);
extern int SK_init(struct device *dev);
extern int ni52_probe(struct device *);
extern int ni65_probe(struct device *);
extern int SK_init(struct device *);
/* Detachable devices ("pocket adaptors" and special PCMCIA drivers). */
extern int atp_init(struct device *);
......@@ -129,6 +131,12 @@ ethif_probe(struct device *dev)
#endif
#if defined(CONFIG_SK_G16)
&& SK_init(dev)
#endif
#ifdef CONFIG_NI52
&& ni52_probe(dev)
#endif
#ifdef CONFIG_NI65
&& ni65_probe(dev)
#endif
&& 1 ) {
return 1; /* -ENODEV or -EAGAIN would be more accurate. */
......
......@@ -267,7 +267,6 @@ ppp_init(struct device *dev)
dev->mtu = PPP_MTU;
dev->hard_start_xmit = ppp_xmit;
dev->open = ppp_dev_open;
dev->do_ioctl = ppp_dev_ioctl;
dev->stop = ppp_dev_close;
dev->get_stats = ppp_get_stats;
dev->hard_header = ppp_header;
......@@ -280,6 +279,8 @@ ppp_init(struct device *dev)
#ifdef NET02D
dev->add_arp = ppp_add_arp;
dev->queue_xmit = dev_queue_xmit;
#else
dev->do_ioctl = ppp_dev_ioctl;
#endif
for (i = 0; i < DEV_NUMBUFFS; i++)
......@@ -605,6 +606,7 @@ ppp_dev_close(struct device *dev)
return 0;
}
#ifndef NET02D
static int ppp_dev_ioctl(struct device *dev, struct ifreq *ifr)
{
struct ppp *ppp = &ppp_ctrl[dev->base_addr];
......@@ -630,6 +632,7 @@ static int ppp_dev_ioctl(struct device *dev, struct ifreq *ifr)
return error;
}
#endif
/*************************************************************
* TTY OUTPUT
......@@ -650,7 +653,11 @@ ppp_output_done (void *ppp)
/* If the device is still up then enable the transmitter of the
next frame. */
if (((struct ppp *) ppp)->dev->flags & IFF_UP)
#ifndef NET02D
mark_bh (NET_BH);
#else
dev_tint (((struct ppp *) ppp)->dev);
#endif
/* enable any blocked process pending transmission */
wake_up_interruptible (&((struct ppp *) ppp)->write_wait);
......@@ -1124,9 +1131,7 @@ ppp_do_ip (struct ppp *ppp, unsigned short proto, unsigned char *c,
}
/* receive the frame through the network software */
while ((dev_rint(c, count, 0, ppp->dev) & ~1) != 0)
;
(void) dev_rint (c, count, 0, ppp->dev);
return 1;
}
......
......@@ -5,7 +5,7 @@
#
#
VERSION = 2.90
VERSION = 2.90-2
TARGET_OS = linux
.c.s:
......
......@@ -12,8 +12,8 @@ VoxWare v2.90 release notes
there are some new features required by a popular
application. In addition there is also support
for the GUS MAX and the 16 bit sampling option of GUS.
Also the Windows Sound System stuff is there but may not
work yet (may work with some WSS compatible cards).
The MSS/WSS support works now. At least with SG NX Pro 16.
********* IMPORTANT *****************************************
Linux 1.0 or later is required to by this driver version.
......@@ -65,6 +65,7 @@ contributors. (I could have forgotten some names.)
Markus Aroharju and
Risto Kankkunen Major contributions to the mixer support
of GUS v3.7.
Hunyue Yau Mixer support for SG NX Pro.
Marc Hoffman PSS support.
Regards,
......
......@@ -903,16 +903,14 @@ ad1848_interrupt (int irq)
int
probe_ms_sound (struct address_info *hw_config)
{
int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3;
if ((INB (hw_config->io_base + 3) & 0x04) == 0)
return 0; /* WSS ID test failed */
if (hw_config->irq > 11)
return;
return 0;
if (hw_config->dma > 3 || hw_config->dma == 2)
return;
if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3)
return 0;
return ad1848_detect (hw_config->io_base + 4);
}
......@@ -920,10 +918,12 @@ probe_ms_sound (struct address_info *hw_config)
long
attach_ms_sound (long mem_start, struct address_info *hw_config)
{
static unsigned char interrupt_bits[11] =
static unsigned char interrupt_bits[12] =
{-1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20};
char bits;
static unsigned char dma_bits[4] = {1, 2, 0, 3};
int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3;
if (!ad1848_detect (hw_config->io_base + 4))
......@@ -941,7 +941,7 @@ attach_ms_sound (long mem_start, struct address_info *hw_config)
if ((INB (version_port) & 0x40) == 0)
printk ("[IRQ?]");
OUTB (bits | hw_config->dma, config_port); /* Write IRQ+DMA setup */
OUTB (bits | dma_bits[hw_config->dma], config_port); /* Write IRQ+DMA setup */
ad1848_init ("MS Sound System", hw_config->io_base + 4,
hw_config->irq,
......
......@@ -322,6 +322,13 @@ main (int argc, char *argv[])
}
}
if (selected_options & B (OPT_SBPRO))
{
fprintf(stderr, "Do you want support for the mixer of SG NX Pro ? ");
if (think_positively (0))
printf("#define __SGNXPRO__\n");
}
if (selected_options & B (OPT_SB16))
selected_options |= B (OPT_SBPRO);
......@@ -333,7 +340,7 @@ main (int argc, char *argv[])
"if you wish to emulate the soundblaster and you have a DSPxxx.LD.\n"
"then you must include the LD in the kernel.\n"
"(do you wish to include a LD) ? ");
if (think_positively (1))
if (think_positively (0))
{
char path[512];
......
......@@ -278,6 +278,9 @@ struct sound_timer_operations {
#endif
#ifndef EXCLUDE_MSS
{SNDCARD_MSS, {MSS_BASE, MSS_IRQ, MSS_DMA}, SND_DEFAULT_ENABLE},
# ifdef MSS2_BASE
{SNDCARD_MSS, {MSS2_BASE, MSS2_IRQ, MSS2_DMA}, SND_DEFAULT_ENABLE},
# endif
#endif
#if !defined(EXCLUDE_UART6850) && !defined(EXCLUDE_MIDI)
......
......@@ -320,7 +320,7 @@ DMAbuf_getrdbuffer (int dev, char **buf, int *len)
if (!(dmap->flags & DMA_ALLOC_DONE))
reorganize_buffers (dev);
if (dmap->dma_mode)
if (!dmap->dma_mode)
{
int err;
......
/*
* sound/sb_dsp.c
*
* The low level driver for the SoundBlaster DSP chip.
* The low level driver for the SoundBlaster DSP chip (SB1.0 to 2.1, SB Pro).
*
* Copyright by Hannu Savolainen 1993
* Copyright by Hannu Savolainen 1994
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
......@@ -25,6 +25,10 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Modified:
* Hunyue Yau Jan 6 1994
* Added code to support Sound Galaxy NX Pro
*
*/
#include "sound_config.h"
......@@ -756,6 +760,7 @@ long
sb_dsp_init (long mem_start, struct address_info *hw_config)
{
int i;
int mixer_type = 0;
sbc_major = sbc_minor = 0;
sb_dsp_command (0xe1); /*
......@@ -786,7 +791,7 @@ sb_dsp_init (long mem_start, struct address_info *hw_config)
#ifndef EXCLUDE_SBPRO
if (sbc_major >= 3)
sb_mixer_init (sbc_major);
mixer_type = sb_mixer_init (sbc_major);
#endif
#ifndef EXCLUDE_YM8312
......@@ -799,7 +804,16 @@ sb_dsp_init (long mem_start, struct address_info *hw_config)
if (sbc_major >= 3)
{
#ifndef SCO
sprintf (sb_dsp_operations.name, "SoundBlaster Pro %d.%d", sbc_major, sbc_minor);
# ifdef __SGNXPRO__
if (mixer_type == 2)
{
sprintf (sb_dsp_operations.name, "Sound Galaxy NX Pro %d.%d", sbc_major, sbc_minor);
}
else
# endif
{
sprintf (sb_dsp_operations.name, "SoundBlaster Pro %d.%d", sbc_major, sbc_minor);
}
#endif
}
else
......
......@@ -4,7 +4,7 @@
*
* The low level mixer driver for the SoundBlaster Pro and SB16 cards.
*
* Copyright by Hannu Savolainen 1993
* Copyright by Hannu Savolainen 1994
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
......@@ -26,6 +26,10 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Modified:
* Hunyue Yau Jan 6 1994
* Added code to support the Sound Galaxy NX Pro mixer.
*
*/
#include "sound_config.h"
......@@ -91,9 +95,21 @@ sb_mixer_set_stereo (int mode)
| (mode ? STEREO_DAC : MONO_DAC)));
}
/*
* Returns:
* 0 No mixer detected.
* 1 Only a plain Sound Blaster Pro style mixer detected.
* 2 The Sound Galaxy NX Pro mixer detected.
*/
static int
detect_mixer (void)
{
#ifdef __SGNXPRO__
int oldbass, oldtreble;
#endif
int retcode = 1;
/*
* Detect the mixer by changing parameters of two volume channels. If the
* values read back match with the values written, the mixer is there (is
......@@ -109,7 +125,30 @@ detect_mixer (void)
if (sb_getmixer (VOC_VOL) != 0x33)
return 0;
return 1;
#ifdef __SGNXPRO__
/* Attempt to detect the SG NX Pro by check for valid bass/treble
* registers.
*/
oldbass = sb_getmixer (BASS_LVL);
oldtreble = sb_getmixer (TREBLE_LVL);
sb_setmixer (BASS_LVL, 0xaa);
sb_setmixer (TREBLE_LVL, 0x55);
if ((sb_getmixer (BASS_LVL) != 0xaa) ||
(sb_getmixer (TREBLE_LVL) != 0x55))
{
retcode = 1; /* 1 == Only SB Pro detected */
}
else
retcode = 2; /* 2 == SG NX Pro detected */
/* Restore register in either case since SG NX Pro has EEPROM with
* 'preferred' values stored.
*/
sb_setmixer (BASS_LVL, oldbass);
sb_setmixer (TREBLE_LVL, oldtreble);
#endif
return retcode;
}
static void
......@@ -350,17 +389,23 @@ sb_mixer_reset (void)
set_recmask (SOUND_MASK_MIC);
}
void
/*
* Returns a code depending on whether a SG NX Pro was detected.
* 1 == Plain SB Pro
* 2 == SG NX Pro detected.
* 3 == SB16
*
* Used to update message.
*/
int
sb_mixer_init (int major_model)
{
sb_setmixer (0x00, 0); /*
* Reset mixer
*/
int mixer_type = 0;
if (!detect_mixer ())
return; /*
* No mixer. Why?
*/
sb_setmixer (0x00, 0); /* Reset mixer */
if (!(mixer_type = detect_mixer ()))
return 0; /* No mixer. Why? */
mixer_initialized = 1;
mixer_model = major_model;
......@@ -369,9 +414,21 @@ sb_mixer_init (int major_model)
{
case 3:
mixer_caps = SOUND_CAP_EXCL_INPUT;
supported_devices = SBPRO_MIXER_DEVICES;
supported_rec_devices = SBPRO_RECORDING_DEVICES;
iomap = &sbpro_mix;
#ifdef __SGNXPRO__
if (mixer_type == 2) /* A SGNXPRO was detected */
{
supported_devices = SGNXPRO_MIXER_DEVICES;
supported_rec_devices = SGNXPRO_RECORDING_DEVICES;
iomap = &sgnxpro_mix;
}
else
#endif
{
supported_devices = SBPRO_MIXER_DEVICES;
supported_rec_devices = SBPRO_RECORDING_DEVICES;
iomap = &sbpro_mix;
mixer_type = 1;
}
break;
case 4:
......@@ -379,16 +436,18 @@ sb_mixer_init (int major_model)
supported_devices = SB16_MIXER_DEVICES;
supported_rec_devices = SB16_RECORDING_DEVICES;
iomap = &sb16_mix;
mixer_type = 3;
break;
default:
printk ("SB Warning: Unsupported mixer type\n");
return;
return 0;
}
if (num_mixers < MAX_MIXER_DEV)
mixer_devs[num_mixers++] = &sb_mixer_operations;
sb_mixer_reset ();
return mixer_type;
}
#endif
......@@ -24,13 +24,29 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Modified:
* Hunyue Yau Jan 6 1994
* Added defines for the Sound Galaxy NX Pro mixer.
*
*/
#define SBPRO_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
/* Same as SB Pro, unless I find otherwise */
#define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES
#define SBPRO_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD | SOUND_MASK_VOLUME)
/* SG NX Pro has treble and bass settings on the mixer. The 'speaker'
* channel is the COVOX/DisneySoundSource emulation volume control
* on the mixer. It does NOT control speaker volume. Should have own
* mask eventually?
*/
#define SGNXPRO_MIXER_DEVICES (SBPRO_MIXER_DEVICES|SOUND_MASK_BASS| \
SOUND_MASK_TREBLE|SOUND_MASK_SPEAKER )
#define SB16_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD)
......@@ -62,6 +78,13 @@
#define IRQ_STAT 0x82
#define OPSW 0x3c
/*
* Additional registers on the SG NX Pro
*/
#define COVOX_VOL 0x42
#define TREBLE_LVL 0x44
#define BASS_LVL 0x46
#define FREQ_HI (1 << 3)/* Use High-frequency ANFI filters */
#define FREQ_LOW 0 /* Use Low-frequency ANFI filters */
#define FILT_ON 0 /* Yes, 0 to turn it on, 1 for off */
......@@ -108,6 +131,23 @@ MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0)
};
#ifdef __SGNXPRO__
mixer_tab sgnxpro_mix = {
MIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4),
MIX_ENT(SOUND_MIXER_BASS, 0x46, 2, 3, 0x00, 0, 0),
MIX_ENT(SOUND_MIXER_TREBLE, 0x44, 2, 3, 0x00, 0, 0),
MIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4),
MIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4),
MIX_ENT(SOUND_MIXER_SPEAKER, 0x42, 2, 3, 0x00, 0, 0),
MIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4),
MIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0),
MIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4),
MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0)
};
#endif
mixer_tab sb16_mix = {
MIX_ENT(SOUND_MIXER_VOLUME, 0x30, 7, 5, 0x31, 7, 5),
MIX_ENT(SOUND_MIXER_BASS, 0x46, 7, 4, 0x47, 7, 4),
......
......@@ -140,7 +140,7 @@ void sb_midi_init(int model);
void sb_setmixer (unsigned int port, unsigned int value);
int sb_getmixer (unsigned int port);
void sb_mixer_set_stereo(int mode);
void sb_mixer_init(int major_model);
int sb_mixer_init(int major_model);
/* From opl3.c */
int opl3_detect (int ioaddr);
......
......@@ -180,7 +180,7 @@ If your card has nonstandard I/O address or IRQ number, change defines
* In v3.0 it's /dev/sndproc but this could be a temporary solution.
*/
#define SND_NDEVS 64 /* Number of supported devices */
#define SND_NDEVS 256 /* Number of supported devices */
#define SND_DEV_CTL 0 /* Control port /dev/mixer */
#define SND_DEV_SEQ 1 /* Sequencer output /dev/sequencer (FM
synthesizer and MIDI output) */
......@@ -199,7 +199,7 @@ If your card has nonstandard I/O address or IRQ number, change defines
#define ON 1
#define OFF 0
#define MAX_AUDIO_DEV 4
#define MAX_AUDIO_DEV 5
#define MAX_MIXER_DEV 2
#define MAX_SYNTH_DEV 3
#define MAX_MIDI_DEV 6
......
......@@ -42,6 +42,12 @@
#define WIN_DIAGNOSE 0x90
#define WIN_SPECIFY 0x91
#define WIN_MULTREAD 0xC4 /* read multiple sectors */
#define WIN_MULTWRITE 0xC5 /* write multiple sectors */
#define WIN_SETMULT 0xC6 /* enable read multiple */
#define WIN_IDENTIFY 0xEC /* ask drive to identify itself */
#define WIN_SETFEATURES 0xEF /* set special drive features */
/* Bits for HD_ERROR */
#define MARK_ERR 0x01 /* Bad address mark */
#define TRK0_ERR 0x02 /* couldn't find track 0 */
......@@ -61,4 +67,9 @@ struct hd_geometry {
unsigned short cylinders;
unsigned long start;
};
#define HDIO_GETUNMASKINTR 0x302
#define HDIO_SETUNMASKINTR 0x303
#define HDIO_GETMULTCOUNT 0x304
#define HDIO_SETMULTCOUNT 0x305
#define HDIO_SETFEATURE 0x306
#endif
......@@ -145,10 +145,10 @@ extern int unmap_page_range(unsigned long from, unsigned long size);
extern int remap_page_range(unsigned long from, unsigned long to, unsigned long size, int mask);
extern int zeromap_page_range(unsigned long from, unsigned long size, int mask);
extern void do_wp_page(unsigned long error_code, unsigned long address,
struct task_struct *tsk);
extern void do_no_page(unsigned long error_code, unsigned long address,
struct task_struct *tsk);
extern void do_wp_page(struct vm_area_struct * vma, unsigned long address,
unsigned long error_code);
extern void do_no_page(struct vm_area_struct * vma, unsigned long address,
unsigned long error_code);
extern unsigned long paging_init(unsigned long start_mem, unsigned long end_mem);
extern void mem_init(unsigned long low_start_mem,
......
......@@ -6,6 +6,8 @@
#define __WCLONE 0x80000000
#ifdef __KERNEL__
struct wait_queue {
struct task_struct * task;
struct wait_queue * next;
......@@ -31,4 +33,6 @@ typedef struct select_table_struct {
#define __MAX_SELECT_TABLE_ENTRIES (4096 / sizeof (struct select_table_entry))
#endif /* __KERNEL__ */
#endif
......@@ -81,24 +81,20 @@ static inline int put_stack_long(struct task_struct *task, int offset,
* tables. NOTE! You should check that the long isn't on a page boundary,
* and that it is in the task area before calling this: this routine does
* no checking.
*
* NOTE2! This uses "tsk->tss.cr3" even though we know it's currently always
* zero. This routine shouldn't have to change when we make a better mm.
*/
static unsigned long get_long(struct task_struct * tsk,
unsigned long addr)
static unsigned long get_long(struct vm_area_struct * vma, unsigned long addr)
{
unsigned long page;
repeat:
page = *PAGE_DIR_OFFSET(tsk->tss.cr3,addr);
page = *PAGE_DIR_OFFSET(vma->vm_task->tss.cr3, addr);
if (page & PAGE_PRESENT) {
page &= PAGE_MASK;
page += PAGE_PTR(addr);
page = *((unsigned long *) page);
}
if (!(page & PAGE_PRESENT)) {
do_no_page(0,addr,tsk);
do_no_page(vma, addr, 0);
goto repeat;
}
/* this is a hack for non-kernel-mapped video buffers and similar */
......@@ -118,14 +114,14 @@ static unsigned long get_long(struct task_struct * tsk,
* Now keeps R/W state of page so that a text page stays readonly
* even if a debugger scribbles breakpoints into it. -M.U-
*/
static void put_long(struct task_struct * tsk, unsigned long addr,
static void put_long(struct vm_area_struct * vma, unsigned long addr,
unsigned long data)
{
unsigned long page, pte = 0;
int readonly = 0;
repeat:
page = *PAGE_DIR_OFFSET(tsk->tss.cr3,addr);
page = *PAGE_DIR_OFFSET(vma->vm_task->tss.cr3, addr);
if (page & PAGE_PRESENT) {
page &= PAGE_MASK;
page += PAGE_PTR(addr);
......@@ -133,13 +129,13 @@ static void put_long(struct task_struct * tsk, unsigned long addr,
page = *((unsigned long *) page);
}
if (!(page & PAGE_PRESENT)) {
do_no_page(0 /* PAGE_RW */ ,addr,tsk);
do_no_page(vma, addr, 0 /* PAGE_RW */);
goto repeat;
}
if (!(page & PAGE_RW)) {
if(!(page & PAGE_COW))
if (!(page & PAGE_COW))
readonly = 1;
do_wp_page(PAGE_RW | PAGE_PRESENT,addr,tsk);
do_wp_page(vma, addr, PAGE_RW | PAGE_PRESENT);
goto repeat;
}
/* this is a hack for non-kernel-mapped video buffers and similar */
......@@ -150,12 +146,34 @@ static void put_long(struct task_struct * tsk, unsigned long addr,
page &= PAGE_MASK;
page += addr & ~PAGE_MASK;
*(unsigned long *) page = data;
if(readonly) {
if (readonly) {
*(unsigned long *) pte &=~ (PAGE_RW|PAGE_COW);
invalidate();
}
}
static struct vm_area_struct * find_vma(struct task_struct * tsk, unsigned long addr)
{
struct vm_area_struct * vma;
addr &= PAGE_MASK;
for (vma = current->mm->mmap ; ; vma = vma->vm_next) {
if (!vma)
return NULL;
if (vma->vm_end > addr)
break;
}
if (vma->vm_start <= addr)
return vma;
if (!(vma->vm_flags & VM_GROWSDOWN))
return NULL;
if (vma->vm_end - addr > tsk->rlim[RLIMIT_STACK].rlim_cur)
return NULL;
vma->vm_offset -= vma->vm_start - addr;
vma->vm_start = addr;
return vma;
}
/*
* This routine checks the page boundaries, and that the offset is
* within the task area. It then calls get_long() to read a long.
......@@ -163,13 +181,21 @@ static void put_long(struct task_struct * tsk, unsigned long addr,
static int read_long(struct task_struct * tsk, unsigned long addr,
unsigned long * result)
{
unsigned long low,high;
struct vm_area_struct * vma = find_vma(tsk, addr);
if (addr > TASK_SIZE-sizeof(long))
if (!vma)
return -EIO;
if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) {
low = get_long(tsk,addr & ~(sizeof(long)-1));
high = get_long(tsk,(addr+sizeof(long)) & ~(sizeof(long)-1));
unsigned long low,high;
struct vm_area_struct * vma_high = vma;
if (addr + sizeof(long) >= vma->vm_end) {
vma_high = vma->vm_next;
if (!vma_high || vma_high->vm_start != vma->vm_end)
return -EIO;
}
low = get_long(vma, addr & ~(sizeof(long)-1));
high = get_long(vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1));
switch (addr & (sizeof(long)-1)) {
case 1:
low >>= 8;
......@@ -186,7 +212,7 @@ static int read_long(struct task_struct * tsk, unsigned long addr,
}
*result = low;
} else
*result = get_long(tsk,addr);
*result = get_long(vma, addr);
return 0;
}
......@@ -197,13 +223,21 @@ static int read_long(struct task_struct * tsk, unsigned long addr,
static int write_long(struct task_struct * tsk, unsigned long addr,
unsigned long data)
{
unsigned long low,high;
struct vm_area_struct * vma = find_vma(tsk, addr);
if (addr > TASK_SIZE-sizeof(long))
if (!vma)
return -EIO;
if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) {
low = get_long(tsk,addr & ~(sizeof(long)-1));
high = get_long(tsk,(addr+sizeof(long)) & ~(sizeof(long)-1));
unsigned long low,high;
struct vm_area_struct * vma_high = vma;
if (addr + sizeof(long) >= vma->vm_end) {
vma_high = vma->vm_next;
if (!vma_high || vma_high->vm_start != vma->vm_end)
return -EIO;
}
low = get_long(vma, addr & ~(sizeof(long)-1));
high = get_long(vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1));
switch (addr & (sizeof(long)-1)) {
case 0: /* shouldn't happen, but safety first */
low = data;
......@@ -227,10 +261,10 @@ static int write_long(struct task_struct * tsk, unsigned long addr,
high |= data >> 8;
break;
}
put_long(tsk,addr & ~(sizeof(long)-1),low);
put_long(tsk,(addr+sizeof(long)) & ~(sizeof(long)-1),high);
put_long(vma, addr & ~(sizeof(long)-1),low);
put_long(vma_high, (addr+sizeof(long)) & ~(sizeof(long)-1),high);
} else
put_long(tsk,addr,data);
put_long(vma, addr, data);
return 0;
}
......
......@@ -148,7 +148,6 @@ asmlinkage void math_emulate(long arg)
unsigned long itimer_ticks = 0;
unsigned long itimer_next = ~0;
static unsigned long lost_ticks = 0;
/*
* 'schedule()' is the scheduler function. It's a very simple and nice
......@@ -361,54 +360,76 @@ void sleep_on(struct wait_queue **p)
__sleep_on(p,TASK_UNINTERRUPTIBLE);
}
static struct timer_list * next_timer = NULL;
/*
* The head for the timer-list has a "expires" field of MAX_UINT,
* and the sorting routine counts on this..
*/
static struct timer_list timer_head = { &timer_head, &timer_head, ~0, 0, NULL };
#define SLOW_BUT_DEBUGGING_TIMERS 1
void add_timer(struct timer_list * timer)
{
unsigned long flags;
struct timer_list ** p;
struct timer_list *p;
if (!timer)
#if SLOW_BUT_DEBUGGING_TIMERS
if (timer->next || timer->prev) {
printk("add_timer() called with non-zero list from %08lx\n",
((unsigned long *) &timer)[-1]);
return;
timer->next = NULL;
p = &next_timer;
}
#endif
p = &timer_head;
timer->expires += jiffies;
save_flags(flags);
cli();
while (*p) {
if ((*p)->expires > timer->expires) {
(*p)->expires -= timer->expires;
timer->next = *p;
break;
}
timer->expires -= (*p)->expires;
p = &(*p)->next;
}
*p = timer;
do {
p = p->next;
} while (timer->expires > p->expires);
timer->next = p;
timer->prev = p->prev;
p->prev = timer;
timer->prev->next = timer;
restore_flags(flags);
}
int del_timer(struct timer_list * timer)
{
unsigned long flags;
unsigned long expires = 0;
struct timer_list **p;
#if SLOW_BUT_DEBUGGING_TIMERS
struct timer_list * p;
p = &next_timer;
p = &timer_head;
save_flags(flags);
cli();
while (*p) {
if (*p == timer) {
if ((*p = timer->next) != NULL)
(*p)->expires += timer->expires;
timer->expires += expires;
while ((p = p->next) != &timer_head) {
if (p == timer) {
timer->next->prev = timer->prev;
timer->prev->next = timer->next;
timer->next = timer->prev = NULL;
restore_flags(flags);
timer->expires -= jiffies;
return 1;
}
expires += (*p)->expires;
p = &(*p)->next;
}
if (p->next || p->prev)
printk("del_timer() called with timer not initialized\n");
restore_flags(flags);
return 0;
#else
save_flags(flags);
cli();
if (timer->next) {
timer->next->prev = timer->prev;
timer->prev->next = timer->next;
timer->next = timer->prev = NULL;
restore_flags(flags);
timer->expires -= jiffies;
return 1;
}
restore_flags(flags);
return 0;
#endif
}
unsigned long timer_active = 0;
......@@ -528,12 +549,15 @@ static void timer_bh(void * unused)
{
unsigned long mask;
struct timer_struct *tp;
struct timer_list * timer;
cli();
while (next_timer && next_timer->expires == 0) {
void (*fn)(unsigned long) = next_timer->function;
unsigned long data = next_timer->data;
next_timer = next_timer->next;
while ((timer = timer_head.next) != &timer_head && timer->expires < jiffies) {
void (*fn)(unsigned long) = timer->function;
unsigned long data = timer->data;
timer->next->prev = timer->prev;
timer->prev->next = timer->next;
timer->next = timer->prev = NULL;
sti();
fn(data);
cli();
......@@ -667,16 +691,8 @@ static void do_timer(struct pt_regs * regs)
itimer_ticks++;
if (itimer_ticks > itimer_next)
need_resched = 1;
if (next_timer) {
if (next_timer->expires) {
next_timer->expires--;
if (!next_timer->expires)
mark_bh(TIMER_BH);
} else {
lost_ticks++;
mark_bh(TIMER_BH);
}
}
if (timer_head.next->expires < jiffies)
mark_bh(TIMER_BH);
if (tq_timer != &tq_last)
mark_bh(TQUEUE_BH);
sti();
......
......@@ -554,20 +554,17 @@ unsigned long put_dirty_page(struct task_struct * tsk, unsigned long page, unsig
* to a shared page. It is done by copying the page to a new address
* and decrementing the shared-page counter for the old page.
*
* Note that we do many checks twice (look at do_wp_page()), as
* we have to be careful about race-conditions.
*
* Goto-purists beware: the only reason for goto's here is that it results
* in better assembly code.. The "default" path will see no jumps at all.
*/
static void __do_wp_page(unsigned long error_code, unsigned long address,
struct task_struct * tsk)
void do_wp_page(struct vm_area_struct * vma, unsigned long address,
unsigned long error_code)
{
unsigned long *pde, pte, old_page, prot;
unsigned long new_page;
new_page = __get_free_page(GFP_KERNEL);
pde = PAGE_DIR_OFFSET(tsk->tss.cr3,address);
pde = PAGE_DIR_OFFSET(vma->vm_task->tss.cr3,address);
pte = *pde;
if (!(pte & PAGE_PRESENT))
goto end_wp_page;
......@@ -582,13 +579,13 @@ static void __do_wp_page(unsigned long error_code, unsigned long address,
goto bad_wp_page;
if (old_page & PAGE_RW)
goto end_wp_page;
tsk->mm->min_flt++;
vma->vm_task->mm->min_flt++;
prot = (old_page & ~PAGE_MASK) | PAGE_RW;
old_page &= PAGE_MASK;
if (mem_map[MAP_NR(old_page)] != 1) {
if (new_page) {
if (mem_map[MAP_NR(old_page)] & MAP_PAGE_RESERVED)
++tsk->mm->rss;
++vma->vm_task->mm->rss;
copy_page(old_page,new_page);
*(unsigned long *) pte = new_page | prot;
free_page(old_page);
......@@ -596,7 +593,7 @@ static void __do_wp_page(unsigned long error_code, unsigned long address,
return;
}
free_page(old_page);
oom(tsk);
oom(vma->vm_task);
*(unsigned long *) pte = BAD_PAGE | prot;
invalidate();
return;
......@@ -609,12 +606,12 @@ static void __do_wp_page(unsigned long error_code, unsigned long address,
bad_wp_page:
printk("do_wp_page: bogus page at address %08lx (%08lx)\n",address,old_page);
*(unsigned long *) pte = BAD_PAGE | PAGE_SHARED;
send_sig(SIGKILL, tsk, 1);
send_sig(SIGKILL, vma->vm_task, 1);
goto end_wp_page;
bad_wp_pagetable:
printk("do_wp_page: bogus page-table at address %08lx (%08lx)\n",address,pte);
*pde = BAD_PAGETABLE | PAGE_TABLE;
send_sig(SIGKILL, tsk, 1);
send_sig(SIGKILL, vma->vm_task, 1);
end_wp_page:
if (new_page)
free_page(new_page);
......@@ -622,63 +619,12 @@ static void __do_wp_page(unsigned long error_code, unsigned long address,
}
/*
* check that a page table change is actually needed, and call
* the low-level function only in that case..
* Ugly, ugly, but the goto's result in better assembly..
*/
void do_wp_page(unsigned long error_code, unsigned long address,
struct task_struct * tsk)
{
unsigned long page;
unsigned long * pg_table;
pg_table = PAGE_DIR_OFFSET(tsk->tss.cr3,address);
page = *pg_table;
if (!page)
return;
if ((page & PAGE_PRESENT) && page < high_memory) {
pg_table = (unsigned long *) ((page & PAGE_MASK) + PAGE_PTR(address));
page = *pg_table;
if (!(page & PAGE_PRESENT))
return;
if (page & PAGE_RW)
return;
if (!(page & PAGE_COW)) {
if ((error_code & PAGE_USER) && tsk == current) {
current->tss.cr2 = address;
current->tss.error_code = error_code;
current->tss.trap_no = 14;
send_sig(SIGSEGV, tsk, 1);
return;
}
}
if (mem_map[MAP_NR(page)] == 1) {
*pg_table |= PAGE_RW | PAGE_DIRTY;
invalidate();
return;
}
__do_wp_page(error_code, address, tsk);
return;
}
printk("bad page directory entry %08lx\n",page);
*pg_table = 0;
}
static int __verify_write(unsigned long start, unsigned long size)
{
size--;
size += start & ~PAGE_MASK;
size >>= PAGE_SHIFT;
start &= PAGE_MASK;
do {
do_wp_page(1,start,current);
start += PAGE_SIZE;
} while (size--);
return 0;
}
int verify_area(int type, const void * addr, unsigned long size)
{
struct vm_area_struct * vma;
unsigned long start = (unsigned long) addr;
/* If the current user space is mapped to kernel space (for the
* case where we use a fake user buffer with get_fs/set_fs()) we
......@@ -690,27 +636,52 @@ int verify_area(int type, const void * addr, unsigned long size)
for (vma = current->mm->mmap ; ; vma = vma->vm_next) {
if (!vma)
goto bad_area;
if (vma->vm_end > (unsigned long) addr)
if (vma->vm_end > start)
break;
}
if (vma->vm_start <= (unsigned long) addr)
if (vma->vm_start <= start)
goto good_area;
if (!(vma->vm_flags & VM_GROWSDOWN))
goto bad_area;
if (vma->vm_end - (unsigned long) addr > current->rlim[RLIMIT_STACK].rlim_cur)
if (vma->vm_end - start > current->rlim[RLIMIT_STACK].rlim_cur)
goto bad_area;
good_area:
while (vma->vm_end - (unsigned long) addr < size) {
struct vm_area_struct * next = vma->vm_next;
if (!next)
if (!wp_works_ok && type == VERIFY_WRITE)
goto check_wp_fault_by_hand;
for (;;) {
struct vm_area_struct * next;
if (type != VERIFY_READ && !(vma->vm_page_prot & (PAGE_COW | PAGE_RW)))
goto bad_area;
if (vma->vm_end != next->vm_start)
if (vma->vm_end - start >= size)
return 0;
next = vma->vm_next;
if (!next || vma->vm_end != next->vm_start)
goto bad_area;
vma = next;
}
if (wp_works_ok || type == VERIFY_READ || !size)
return 0;
return __verify_write((unsigned long) addr,size);
check_wp_fault_by_hand:
size--;
size += start & ~PAGE_MASK;
size >>= PAGE_SHIFT;
start &= PAGE_MASK;
for (;;) {
if (!(vma->vm_page_prot & (PAGE_COW | PAGE_RW)))
goto bad_area;
do_wp_page(vma, start, PAGE_PRESENT);
if (!size)
return 0;
size--;
start += PAGE_SIZE;
if (start < vma->vm_end)
continue;
vma = vma->vm_next;
if (!vma || vma->vm_start != start)
break;
}
bad_area:
return -EFAULT;
}
......@@ -885,12 +856,33 @@ static inline unsigned long get_empty_pgtable(struct task_struct * tsk,unsigned
return 0;
}
static void handle_no_page(struct vm_area_struct * vma,
unsigned long address, unsigned long error_code)
void do_no_page(struct vm_area_struct * vma, unsigned long address,
unsigned long error_code)
{
unsigned long page;
int prot;
unsigned long page, entry, prot;
page = get_empty_pgtable(vma->vm_task,address);
if (!page)
return;
page &= PAGE_MASK;
page += PAGE_PTR(address);
entry = *(unsigned long *) page;
if (entry & PAGE_PRESENT)
return;
if (entry) {
++vma->vm_task->mm->rss;
++vma->vm_task->mm->maj_flt;
swap_in((unsigned long *) page);
return;
}
address &= PAGE_MASK;
if (!vma->vm_ops || !vma->vm_ops->nopage) {
++vma->vm_task->mm->rss;
++vma->vm_task->mm->min_flt;
get_empty_page(vma->vm_task,address);
return;
}
page = get_free_page(GFP_KERNEL);
if (share_page(vma, address, error_code, page)) {
++vma->vm_task->mm->min_flt;
......@@ -904,8 +896,10 @@ static void handle_no_page(struct vm_area_struct * vma,
++vma->vm_task->mm->maj_flt;
++vma->vm_task->mm->rss;
page = vma->vm_ops->nopage(vma, address, page, error_code);
if (share_page(vma, address, error_code, 0))
if (share_page(vma, address, error_code, 0)) {
free_page(page);
return;
}
prot = vma->vm_page_prot;
if ((prot & PAGE_COW) && mem_map[MAP_NR(page)] > 1)
prot &= ~PAGE_RW;
......@@ -915,28 +909,20 @@ static void handle_no_page(struct vm_area_struct * vma,
oom(current);
}
void do_no_page(unsigned long error_code, unsigned long address,
struct task_struct *tsk)
/*
* This routine handles page faults. It determines the address,
* and the problem, and then passes it off to one of the appropriate
* routines.
*/
asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
{
unsigned long page, tmp;
struct vm_area_struct * vma;
unsigned long address;
unsigned long page;
page = get_empty_pgtable(tsk,address);
if (!page)
return;
page &= PAGE_MASK;
page += PAGE_PTR(address);
tmp = *(unsigned long *) page;
if (tmp & PAGE_PRESENT)
return;
if (tmp) {
++tsk->mm->rss;
++tsk->mm->maj_flt;
swap_in((unsigned long *) page);
return;
}
address &= PAGE_MASK;
for (vma = tsk->mm->mmap ; ; vma = vma->vm_next) {
/* get the address */
__asm__("movl %%cr2,%0":"=r" (address));
for (vma = current->mm->mmap ; ; vma = vma->vm_next) {
if (!vma)
goto bad_area;
if (vma->vm_end > address)
......@@ -946,81 +932,63 @@ void do_no_page(unsigned long error_code, unsigned long address,
goto good_area;
if (!(vma->vm_flags & VM_GROWSDOWN))
goto bad_area;
if (vma->vm_end - address > tsk->rlim[RLIMIT_STACK].rlim_cur)
if (vma->vm_end - address > current->rlim[RLIMIT_STACK].rlim_cur)
goto bad_area;
vma->vm_offset -= vma->vm_start - address;
vma->vm_start = address;
vma->vm_offset -= vma->vm_start - (address & PAGE_MASK);
vma->vm_start = (address & PAGE_MASK);
/*
* Ok, we have a good vm_area for this memory access, so
* we can handle it..
*/
good_area:
if (!vma->vm_ops || !vma->vm_ops->nopage) {
++tsk->mm->rss;
++tsk->mm->min_flt;
get_empty_page(tsk,address);
if (regs->eflags & VM_MASK) {
unsigned long bit = (address - 0xA0000) >> PAGE_SHIFT;
if (bit < 32)
current->screen_bitmap |= 1 << bit;
}
if (error_code & PAGE_PRESENT) {
if ((vma->vm_page_prot & (PAGE_RW | PAGE_COW | PAGE_PRESENT)) == PAGE_PRESENT)
goto bad_area;
#ifdef CONFIG_TEST_VERIFY_AREA
if (regs->cs == KERNEL_CS)
printk("WP fault at %08x\n", regs->eip);
#endif
do_wp_page(vma, address, error_code);
return;
}
handle_no_page(vma, address, error_code);
if (!(vma->vm_page_prot & PAGE_PRESENT))
goto bad_area;
do_no_page(vma, address, error_code);
return;
bad_area:
if (tsk != current)
goto kernel_needs_bad_page;
tsk->tss.cr2 = address;
tsk->tss.error_code = error_code;
tsk->tss.trap_no = 14;
send_sig(SIGSEGV,tsk,1);
if (error_code & 4) /* user level access? */
return;
kernel_needs_bad_page:
++tsk->mm->rss;
++tsk->mm->min_flt;
get_empty_page(tsk,address);
}
/*
* This routine handles page faults. It determines the address,
* and the problem, and then passes it off to one of the appropriate
* routines.
* Something tried to access memory that isn't in our memory map..
* Fix it, but check if it's kernel or user first..
*/
asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
{
unsigned long address;
unsigned long page;
unsigned int bit;
/* get the address */
__asm__("movl %%cr2,%0":"=r" (address));
if (address < TASK_SIZE) {
if (regs->eflags & VM_MASK) {
bit = (address - 0xA0000) >> PAGE_SHIFT;
if (bit < 32)
current->screen_bitmap |= 1 << bit;
}
if (error_code & PAGE_PRESENT) {
#ifdef CONFIG_TEST_VERIFY_AREA
if (regs->cs == KERNEL_CS)
printk("WP fault at %08x\n", regs->eip);
#endif
do_wp_page(error_code, address, current);
} else {
do_no_page(error_code, address, current);
}
bad_area:
if (error_code & PAGE_USER) {
current->tss.cr2 = address;
current->tss.error_code = error_code;
current->tss.trap_no = 14;
send_sig(SIGSEGV, current, 1);
return;
}
address -= TASK_SIZE;
if (wp_works_ok < 0 && address == 0 && (error_code & PAGE_PRESENT)) {
/*
* Oops. The kernel tried to access some bad page. We'll have to
* terminate things with extreme prejudice.
*/
if (wp_works_ok < 0 && address == TASK_SIZE && (error_code & PAGE_PRESENT)) {
wp_works_ok = 1;
pg0[0] = PAGE_SHARED;
printk("This processor honours the WP bit even when in supervisor mode. Good.\n");
return;
}
if (address < PAGE_SIZE) {
if ((unsigned long) (address-TASK_SIZE) < PAGE_SIZE) {
printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
pg0[0] = PAGE_SHARED;
} else
printk(KERN_ALERT "Unable to handle kernel paging request");
printk(" at kernel address %08lx\n",address);
address += TASK_SIZE;
printk(" at virtual address %08lx\n",address);
__asm__("movl %%cr3,%0" : "=r" (page));
printk(KERN_ALERT "current->tss.cr3 = %08lx, %%cr3 = %08lx\n",
current->tss.cr3, page);
......
......@@ -615,6 +615,7 @@ static int inet_create(struct socket *sock, int protocol)
sk->timeout = 0;
sk->broadcast = 0;
sk->localroute = 0;
sk->timer.next = sk->timer.prev = NULL;
sk->timer.data = (unsigned long)sk;
sk->timer.function = &net_timer;
skb_queue_head_init(&sk->back_log);
......
......@@ -752,7 +752,7 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
skb_queue_head_init(&entry->skb);
entry->next = arp_tables[hash];
arp_tables[hash] = entry;
entry->timer.next = entry->timer.prev = NULL;
sti();
}
......@@ -841,6 +841,7 @@ int arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev,
entry->next = arp_tables[hash];
entry->dev = dev;
arp_tables[hash] = entry;
entry->timer.next = entry->timer.prev = NULL;
entry->timer.function = arp_expire_request;
entry->timer.data = (unsigned long)entry;
entry->timer.expires = ARP_RES_TIME;
......@@ -1048,6 +1049,7 @@ static int arp_req_set(struct arpreq *req)
entry->hlen = hlen;
entry->htype = htype;
entry->next = arp_tables[hash];
entry->timer.next = entry->timer.prev = NULL;
arp_tables[hash] = entry;
skb_queue_head_init(&entry->skb);
}
......
......@@ -336,6 +336,8 @@ int dev_close(struct device *dev)
void dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
{
unsigned long flags;
int nitcount;
struct packet_type *ptype;
int where = 0; /* used to say if the packet should go */
/* at the front or the back of the */
/* queue - front is a retranmsit try */
......@@ -419,6 +421,17 @@ void dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
}
restore_flags(flags);
/* copy outgoing packets to any sniffer packet handlers */
for (nitcount = dev_nit, ptype = ptype_base; nitcount > 0 && ptype != NULL; ptype = ptype->next) {
if (ptype->type == htons(ETH_P_ALL)) {
struct sk_buff *skb2;
if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL)
break;
ptype->func(skb2, skb->dev, ptype);
nitcount--;
}
}
if (dev->hard_start_xmit(skb, dev) == 0) {
/*
* Packet is now solely the responsibility of the driver
......
......@@ -681,8 +681,7 @@ struct sk_buff * tcp_dequeue_partial(struct sock * sk)
save_flags(flags);
cli();
skb = sk->partial;
if (skb)
{
if (skb) {
sk->partial = NULL;
del_timer(&sk->partial_timer);
}
......@@ -711,6 +710,7 @@ void tcp_enqueue_partial(struct sk_buff * skb, struct sock * sk)
if (tmp)
del_timer(&sk->partial_timer);
sk->partial = skb;
sk->partial_timer.next = sk->partial_timer.prev = NULL;
sk->partial_timer.expires = HZ;
sk->partial_timer.function = (void (*)(unsigned long)) tcp_send_partial;
sk->partial_timer.data = (unsigned long) sk;
......@@ -1987,6 +1987,7 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb,
newsk->urg_data = 0;
newsk->retransmits = 0;
newsk->destroy = 0;
newsk->timer.next = newsk->timer.prev = NULL;
newsk->timer.data = (unsigned long)newsk;
newsk->timer.function = &net_timer;
newsk->dummy_th.source = skb->h.th->dest;
......@@ -2217,6 +2218,15 @@ static void tcp_close(struct sock *sk, int timeout)
* XXX if retransmit count reaches limit, is tcp_close()
* called with timeout == 1 ? if not, we need to fix that.
*/
if (!timeout) {
int timer_active;
timer_active = del_timer(&sk->timer);
if (timer_active)
add_timer(&sk->timer);
else
reset_timer(sk, TIME_CLOSE, 4 * sk->rto);
}
#ifdef NOTDEF
/*
* Start a timer.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment