diff options
author | Linus Torvalds <torvalds@cc.helsinki.fi> | 1994-01-13 21:08:25 +0000 |
---|---|---|
committer | Nicolas Pitre <nico@cam.org> | 2007-08-19 14:19:26 -0400 |
commit | 5f295b33cf4ea277617047421018a6cd75cc5dff (patch) | |
tree | 2851c8b70dda15759fac8f1b310402391fb16ada | |
parent | 9ee059ad6f92a777f874ddf472cd36aa9a901c16 (diff) | |
download | archive-5f295b33cf4ea277617047421018a6cd75cc5dff.tar.gz |
ALPHA-pl14p
47 files changed, 1201 insertions, 691 deletions
@@ -1,6 +1,6 @@ VERSION = 0.99 PATCHLEVEL = 14 -ALPHA = o +ALPHA = p all: Version zImage diff --git a/drivers/FPU-emu/Makefile b/drivers/FPU-emu/Makefile index 78ebb64..3563c1d 100644 --- a/drivers/FPU-emu/Makefile +++ b/drivers/FPU-emu/Makefile @@ -4,14 +4,15 @@ #DEBUG = -DDEBUGGING DEBUG = +PARANOID = -DPARANOID REENTRANT = -DREENTRANT_FPU -CFLAGS := $(CFLAGS) -DPARANOID $(DEBUG) -fno-builtin +CFLAGS := $(CFLAGS) $(PARANOID) $(DEBUG) -fno-builtin .c.o: $(CC) $(CFLAGS) $(MATH_EMULATION) -c $< .S.o: - $(CC) -D__ASSEMBLER__ $(REENTRANT) -c $< + $(CC) -D__ASSEMBLER__ $(PARANOID) $(REENTRANT) -c $< .s.o: $(CC) -c $< diff --git a/drivers/FPU-emu/README b/drivers/FPU-emu/README index 0b1a6f3..8493558 100644 --- a/drivers/FPU-emu/README +++ b/drivers/FPU-emu/README @@ -1,7 +1,7 @@ +---------------------------------------------------------------------------+ | wm-FPU-emu an FPU emulator for 80386 and 80486SX microprocessors. | | | - | Copyright (C) 1992,1993 | + | Copyright (C) 1992,1993,1994 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail billm@vaxc.cc.monash.edu.au | | | @@ -47,7 +47,7 @@ Please report bugs, etc to me at: --Bill Metzenthen - Sept 1993 + Jan 1994 ----------------------- Internals of wm-FPU-emu ----------------------- @@ -305,7 +305,8 @@ Daniel Carosone, danielce@ee.mu.oz.au cae@jpmorgan.com Hamish Coleman, t933093@minyos.xx.rmit.oz.au Bruce Evans, bde@kralizec.zeta.org.au - +Timo Korvola, Timo.Korvola@hut.fi + ...and numerous others who responded to my request for help with a real 80486. diff --git a/drivers/FPU-emu/errors.c b/drivers/FPU-emu/errors.c index cd3c03a..8506997 100644 --- a/drivers/FPU-emu/errors.c +++ b/drivers/FPU-emu/errors.c @@ -3,7 +3,7 @@ | | | The error handling functions for wm-FPU-emu | | | - | Copyright (C) 1992,1993 | + | Copyright (C) 1992,1993,1994 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail billm@vaxc.cc.monash.edu.au | | | @@ -208,7 +208,7 @@ static struct { 0x125 in fpu_trig.c 0x126 in fpu_entry.c 0x127 in poly_2xm1.c - 0x2nn in an *.s file: + 0x2nn in an *.S file: 0x201 in reg_u_add.S, reg_round.S 0x202 in reg_u_div.S 0x203 in reg_u_div.S @@ -227,6 +227,8 @@ static struct { 0x216 in reg_round.S 0x217 in reg_round.S 0x218 in reg_round.S + 0x220 in reg_norm.S + 0x221 in reg_norm.S */ void exception(int n) diff --git a/drivers/FPU-emu/fpu_trig.c b/drivers/FPU-emu/fpu_trig.c index 76db016..1de4317 100644 --- a/drivers/FPU-emu/fpu_trig.c +++ b/drivers/FPU-emu/fpu_trig.c @@ -3,7 +3,7 @@ | | | Implementation of the FPU "transcendental" functions. | | | - | Copyright (C) 1992,1993 | + | Copyright (C) 1992,1993,1994 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail billm@vaxc.cc.monash.edu.au | | | @@ -88,9 +88,19 @@ static int trig_arg(FPU_REG *X, int even) 128 bits precision. */ significand(&tmp) = q + 1; tmp.exp = EXP_BIAS + 63; + tmp.tag = TW_Valid; normalize(&tmp); reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION); reg_add(X, &tmp, X, FULL_PRECISION); + if ( X->sign == SIGN_NEG ) + { + /* CONST_PI2extra is negative, so the result of the addition + can be negative. This means that the argument is actually + in a different quadrant. The correction is always < pi/2, + so it can't overflow into yet another quadrant. */ + X->sign = SIGN_POS; + q++; + } } #endif BETTER_THAN_486 } @@ -107,9 +117,23 @@ static int trig_arg(FPU_REG *X, int even) 128 bits precision. */ significand(&tmp) = q; tmp.exp = EXP_BIAS + 63; + tmp.tag = TW_Valid; normalize(&tmp); reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION); reg_sub(X, &tmp, X, FULL_PRECISION); + if ( (X->exp == CONST_PI2.exp) && + ((X->sigh > CONST_PI2.sigh) + || ((X->sigh == CONST_PI2.sigh) + && (X->sigl > CONST_PI2.sigl))) ) + { + /* CONST_PI2extra is negative, so the result of the + subtraction can be larger than pi/2. This means + that the argument is actually in a different quadrant. + The correction is always < pi/2, so it can't overflow + into yet another quadrant. */ + reg_sub(&CONST_PI, X, X, FULL_PRECISION); + q++; + } } } #endif BETTER_THAN_486 diff --git a/drivers/FPU-emu/reg_norm.S b/drivers/FPU-emu/reg_norm.S index 8ce60a2..9b7a9d7 100644 --- a/drivers/FPU-emu/reg_norm.S +++ b/drivers/FPU-emu/reg_norm.S @@ -1,7 +1,7 @@ /*---------------------------------------------------------------------------+ | reg_norm.S | | | - | Copyright (C) 1992,1993 | + | Copyright (C) 1992,1993,1994 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail billm@vaxc.cc.monash.edu.au | | | @@ -29,6 +29,17 @@ _normalize: movl PARAM1,%ebx +#ifdef PARANOID + cmpb TW_Valid,TAG(%ebx) + je L_ok + + pushl $0x220 + call _exception + addl $4,%esp + +L_ok: +#endif PARANOID + movl SIGH(%ebx),%edx movl SIGL(%ebx),%eax @@ -98,6 +109,17 @@ _normalize_nuo: movl PARAM1,%ebx +#ifdef PARANOID + cmpb TW_Valid,TAG(%ebx) + je L_ok_nuo + + pushl $0x221 + call _exception + addl $4,%esp + +L_ok_nuo: +#endif PARANOID + movl SIGH(%ebx),%edx movl SIGL(%ebx),%eax diff --git a/drivers/FPU-emu/version.h b/drivers/FPU-emu/version.h index 587f364..5ae1f49 100644 --- a/drivers/FPU-emu/version.h +++ b/drivers/FPU-emu/version.h @@ -2,12 +2,12 @@ | version.h | | | | | - | Copyright (C) 1992,1993 | + | Copyright (C) 1992,1993,1994 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | | Australia. E-mail billm@vaxc.cc.monash.edu.au | | | | | +---------------------------------------------------------------------------*/ -#define FPU_VERSION "wm-FPU-emu version BETA 1.6" +#define FPU_VERSION "wm-FPU-emu version Beta 1.7" diff --git a/drivers/block/cdu31a.c b/drivers/block/cdu31a.c index 4a56d16..063da62 100644 --- a/drivers/block/cdu31a.c +++ b/drivers/block/cdu31a.c @@ -80,11 +80,13 @@ #define MAJOR_NR CDU31A_CDROM_MAJOR #include "blk.h" +#define CDU31A_MAX_CONSECUTIVE_ATTENTIONS 10 static unsigned short cdu31a_addresses[] = { 0x340, /* Standard configuration Sony Interface */ 0x1f88, /* Fusion CD-16 */ + 0x230, /* SoundBlaster 16 card */ 0x360, /* Secondary standard Sony Interface */ 0x320, /* Secondary standard Sony Interface */ 0x330, /* Secondary standard Sony Interface */ @@ -96,6 +98,13 @@ static int handle_sony_cd_attention(void); static int read_subcode(void); static void sony_get_toc(void); static int scd_open(struct inode *inode, struct file *filp); +static void do_sony_cd_cmd(unsigned char cmd, + unsigned char *params, + unsigned int num_params, + unsigned char *result_buffer, + unsigned int *result_size); +static void size_to_buf(unsigned int size, + unsigned char *buf); /* The base I/O address of the Sony Interface. This is a variable (not a @@ -116,7 +125,6 @@ static volatile unsigned short sony_cd_read_reg; static volatile unsigned short sony_cd_fifost_reg; -static int initialized = 0; /* Has the drive been initialized? */ static int sony_disc_changed = 1; /* Has the disk been changed since the last check? */ static int sony_toc_read = 0; /* Has the table of contents been @@ -306,6 +314,71 @@ write_cmd(unsigned char cmd) outb(SONY_RES_RDY_INT_EN_BIT, sony_cd_control_reg); } +/* + * Set the drive parameters so the drive will auto-spin-up when a + * disk is inserted. + */ +static void +set_drive_params(void) +{ + unsigned char res_reg[2]; + unsigned int res_size; + unsigned char params[3]; + + + params[0] = SONY_SD_MECH_CONTROL; + params[1] = 0x03; + do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, + params, + 2, + res_reg, + &res_size); + if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20)) + { + printk(" Unable to set mechanical parameters: 0x%2.2x\n", res_reg[1]); + } +} + +/* + * This code will reset the drive and attempt to restore sane parameters. + */ +static void +restart_on_error(void) +{ + unsigned char res_reg[2]; + unsigned int res_size; + unsigned int retry_count; + + + printk("cdu31a: Resetting drive on error\n"); + reset_drive(); + retry_count = jiffies + SONY_RESET_TIMEOUT; + while ((retry_count > jiffies) && (!is_attention())) + { + sony_sleep(); + } + set_drive_params(); + do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); + if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20)) + { + printk("cdu31a: Unable to spin up drive: 0x%2.2x\n", res_reg[1]); + } + + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 200; + schedule(); + + do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, &res_size); + if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20)) + { + printk("cdu31a: Unable to read TOC: 0x%2.2x\n", res_reg[1]); + } + sony_get_toc(); + if (!sony_toc_read) + { + printk("cdu31a: Unable to get TOC data\n"); + } +} /* * This routine writes data to the parameter register. Since this should @@ -459,6 +532,39 @@ get_result(unsigned char *result_buffer, } } +/* + * Read in a 2048 byte block of data. + */ +static void +read_data_block(unsigned char *data, + unsigned char *result_buffer, + unsigned int *result_size) +{ + int i; + unsigned int retry_count; + + for (i=0; i<2048; i++) + { + retry_count = jiffies + SONY_JIFFIES_TIMEOUT; + while ((retry_count > jiffies) && (!is_data_requested())) + { + while (handle_sony_cd_attention()) + ; + + sony_sleep(); + } + if (!is_data_requested()) + { + result_buffer[0] = 0x20; + result_buffer[1] = SONY_TIMEOUT_OP_ERR; + *result_size = 2; + return; + } + + *data = read_data_register(); + data++; + } +} /* * This routine issues a read data command and gets the data. I don't @@ -469,35 +575,37 @@ get_result(unsigned char *result_buffer, * received at any time and should be handled immediately (at least * between every 2048 byte block) to check for errors, we can't wait * until all the data is read. + * + * This routine returns the total number of sectors read. It will + * not return an error if it reads at least one sector successfully. */ -static void -get_data(unsigned char *data, +static unsigned int +get_data(unsigned char *orig_data, unsigned char *params, /* 6 bytes with the MSF start address and number of sectors to read. */ - unsigned int data_size, + unsigned int orig_data_size, unsigned char *result_buffer, unsigned int *result_size) { - int i; unsigned int cur_offset; unsigned int retry_count; int result_read; int num_retries; + unsigned int num_sectors_read = 0; + unsigned char *data = orig_data; + unsigned int data_size = orig_data_size; cli(); - if (current != has_cd_task) /* Allow recursive calls to this routine */ + while (sony_inuse) { - while (sony_inuse) + interruptible_sleep_on(&sony_wait); + if (current->signal & ~current->blocked) { - interruptible_sleep_on(&sony_wait); - if (current->signal & ~current->blocked) - { - result_buffer[0] = 0x20; - result_buffer[1] = SONY_SIGNAL_OP_ERR; - *result_size = 2; - return; - } + result_buffer[0] = 0x20; + result_buffer[1] = SONY_SIGNAL_OP_ERR; + *result_size = 2; + return 0; } } sony_inuse = 1; @@ -506,6 +614,8 @@ get_data(unsigned char *data, num_retries = 0; retry_data_operation: + result_buffer[0] = 0; + result_buffer[1] = 0; /* * Clear any outstanding attentions and wait for the drive to @@ -522,115 +632,120 @@ retry_data_operation: while (handle_sony_cd_attention()) ; } + if (is_busy()) { result_buffer[0] = 0x20; result_buffer[1] = SONY_TIMEOUT_OP_ERR; *result_size = 2; - goto get_data_end; } + else + { + /* Issue the command */ + clear_result_ready(); + clear_param_reg(); - /* Issue the command */ - clear_result_ready(); - clear_param_reg(); - - write_params(params, 6); - write_cmd(SONY_READ_CMD); + write_params(params, 6); + write_cmd(SONY_READ_CMD); - /* - * Read the data from the drive one 2048 byte sector at a time. Handle - * any results received between sectors, if an error result is returned - * terminate the operation immediately. - */ - cur_offset = 0; - result_read = 0; - while (data_size > 0) - { - /* Wait for the drive to tell us we have something */ - retry_count = jiffies + SONY_JIFFIES_TIMEOUT; - while ((retry_count > jiffies) && (!(is_result_ready() || is_data_ready()))) - { - while (handle_sony_cd_attention()) - ; - - sony_sleep(); - } - if (!(is_result_ready() || is_data_ready())) + /* + * Read the data from the drive one 2048 byte sector at a time. Handle + * any results received between sectors, if an error result is returned + * terminate the operation immediately. + */ + cur_offset = 0; + result_read = 0; + while ((data_size > 0) && (result_buffer[0] == 0)) { - result_buffer[0] = 0x20; - result_buffer[1] = SONY_TIMEOUT_OP_ERR; - *result_size = 2; - goto get_data_end; - } + /* Wait for the drive to tell us we have something */ + retry_count = jiffies + SONY_JIFFIES_TIMEOUT; + while ((retry_count > jiffies) && (!(is_result_ready() || is_data_ready()))) + { + while (handle_sony_cd_attention()) + ; + + sony_sleep(); + } + if (!(is_result_ready() || is_data_ready())) + { + result_buffer[0] = 0x20; + result_buffer[1] = SONY_TIMEOUT_OP_ERR; + *result_size = 2; + } - /* Handle results first */ - if (is_result_ready()) - { - result_read = 1; - get_result(result_buffer, result_size); - if ((*result_size < 2) || (result_buffer[0] != 0)) + /* Handle results first */ + else if (is_result_ready()) { - goto get_data_end; + result_read = 1; + get_result(result_buffer, result_size); } - } - else /* Handle data next */ - { - /* - * The drive has to be polled for status on a byte-by-byte basis - * to know if the data is ready. Yuck. I really wish I could use DMA. - */ - clear_data_ready(); - for (i=0; i<2048; i++) + else /* Handle data next */ { - retry_count = jiffies + SONY_JIFFIES_TIMEOUT; - while ((retry_count > jiffies) && (!is_data_requested())) - { - while (handle_sony_cd_attention()) - ; - - sony_sleep(); - } - if (!is_data_requested()) - { - result_buffer[0] = 0x20; - result_buffer[1] = SONY_TIMEOUT_OP_ERR; - *result_size = 2; - goto get_data_end; - } - - *data = read_data_register(); - data++; - data_size--; + /* + * The drive has to be polled for status on a byte-by-byte basis + * to know if the data is ready. Yuck. I really wish I could use DMA. + */ + clear_data_ready(); + read_data_block(data, result_buffer, result_size); + data += 2048; + data_size -= 2048; + cur_offset = cur_offset + 2048; + num_sectors_read++; } - cur_offset = cur_offset + 2048; } - } - /* Make sure the result has been read */ - if (!result_read) - { - get_result(result_buffer, result_size); + /* Make sure the result has been read */ + if (!result_read) + { + get_result(result_buffer, result_size); + } } -get_data_end: if ( ((result_buffer[0] & 0x20) == 0x20) + && (result_buffer[1] != SONY_NOT_SPIN_ERR) /* No retry when not spin */ && (num_retries < MAX_CDU31A_RETRIES)) { + /* + * If an error occurs, go back and only read one sector at the + * given location. Hopefully the error occurred on an unused + * sector after the first one. It is hard to say which sector + * the error occurred on because the drive returns status before + * the data transfer is finished and doesn't say which sector. + */ + data_size = 2048; + data = orig_data; + num_sectors_read = 0; + size_to_buf(1, ¶ms[3]); + num_retries++; - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + 10; /* Wait .1 seconds on retries */ - schedule(); + /* Issue a reset on an error (the second time), othersize just delay */ + if (num_retries == 2) + { + restart_on_error(); + } + else + { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 10; + schedule(); + } + + /* Restart the operation. */ goto retry_data_operation; } has_cd_task = NULL; sony_inuse = 0; wake_up_interruptible(&sony_wait); + + return(num_sectors_read); } /* - * Do a command that does not involve data transfer. + * Do a command that does not involve data transfer. This routine must + * be re-entrant from the same task to support being called from the + * data operation code when an error occurs. */ static void do_sony_cd_cmd(unsigned char cmd, @@ -641,6 +756,7 @@ do_sony_cd_cmd(unsigned char cmd, { unsigned int retry_count; int num_retries; + int recursive_call; cli(); @@ -657,9 +773,14 @@ do_sony_cd_cmd(unsigned char cmd, return; } } + sony_inuse = 1; + has_cd_task = current; + recursive_call = 0; + } + else + { + recursive_call = 1; } - sony_inuse = 1; - has_cd_task = current; sti(); num_retries = 0; @@ -681,18 +802,18 @@ retry_cd_operation: result_buffer[0] = 0x20; result_buffer[1] = SONY_TIMEOUT_OP_ERR; *result_size = 2; - goto do_cmd_end; } + else + { + clear_result_ready(); + clear_param_reg(); - clear_result_ready(); - clear_param_reg(); - - write_params(params, num_params); - write_cmd(cmd); + write_params(params, num_params); + write_cmd(cmd); - get_result(result_buffer, result_size); + get_result(result_buffer, result_size); + } -do_cmd_end: if ( ((result_buffer[0] & 0x20) == 0x20) && (num_retries < MAX_CDU31A_RETRIES)) { @@ -703,26 +824,41 @@ do_cmd_end: goto retry_cd_operation; } - has_cd_task = NULL; - sony_inuse = 0; - wake_up_interruptible(&sony_wait); + if (!recursive_call) + { + has_cd_task = NULL; + sony_inuse = 0; + wake_up_interruptible(&sony_wait); + } } /* * Handle an attention from the drive. This will return 1 if it found one * or 0 if not (if one is found, the caller might want to call again). + * + * This routine counts the number of consecutive times it is called + * (since this is always called from a while loop until it returns + * a 0), and returns a 0 if it happens too many times. This will help + * prevent a lockup. */ static int handle_sony_cd_attention(void) { unsigned char atten_code; - unsigned char res_reg[2]; - unsigned int res_size; + static int num_consecutive_attentions = 0; if (is_attention()) { + if (num_consecutive_attentions > CDU31A_MAX_CONSECUTIVE_ATTENTIONS) + { + printk("cdu31a: Too many consecutive attentions: %d\n", + num_consecutive_attentions); + num_consecutive_attentions = 0; + return(0); + } + clear_attention(); atten_code = read_result_register(); @@ -735,11 +871,6 @@ handle_sony_cd_attention(void) sony_audio_status = CDROM_AUDIO_NO_STATUS; sony_first_block = -1; sony_last_block = -1; - if (initialized) - { - do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); - sony_get_toc(); - } break; case SONY_AUDIO_PLAY_DONE_ATTN: @@ -758,9 +889,12 @@ handle_sony_cd_attention(void) sony_audio_status = CDROM_AUDIO_ERROR; break; } + + num_consecutive_attentions++; return(1); } + num_consecutive_attentions = 0; return(0); } @@ -862,6 +996,7 @@ do_cdu31a_request(void) while (1) { +cdu31a_request_startover: /* * The beginning here is stolen from the hard disk driver. I hope * its right. @@ -878,7 +1013,7 @@ do_cdu31a_request(void) if (dev != 0) { end_request(0); - continue; + goto cdu31a_request_startover; } switch(CURRENT->cmd) @@ -891,12 +1026,12 @@ do_cdu31a_request(void) if ((block / 4) >= sony_toc->lead_out_start_lba) { end_request(0); - return; + goto cdu31a_request_startover; } if (((block + nsect) / 4) >= sony_toc->lead_out_start_lba) { end_request(0); - return; + goto cdu31a_request_startover; } while (nsect > 0) @@ -916,12 +1051,10 @@ do_cdu31a_request(void) */ if (((block / 4) + sony_buffer_sectors) >= sony_toc->lead_out_start_lba) { - sony_last_block = (sony_toc->lead_out_start_lba * 4) - 1; read_size = sony_toc->lead_out_start_lba - (block / 4); } else { - sony_last_block = sony_first_block + (sony_buffer_sectors * 4) - 1; read_size = sony_buffer_sectors; } size_to_buf(read_size, ¶ms[3]); @@ -932,7 +1065,12 @@ do_cdu31a_request(void) */ spin_up_retry = 0; try_read_again: - get_data(sony_buffer, params, (read_size * 2048), res_reg, &res_size); + sony_last_block = sony_first_block + + (get_data(sony_buffer, + params, + (read_size * 2048), + res_reg, + &res_size) * 4) - 1; if ((res_size < 2) || (res_reg[0] != 0)) { if ((res_reg[1] == SONY_NOT_SPIN_ERR) && (!spin_up_retry)) @@ -946,7 +1084,7 @@ try_read_again: sony_first_block = -1; sony_last_block = -1; end_request(0); - return; + goto cdu31a_request_startover; } } @@ -1569,7 +1707,7 @@ static char *load_mech[] = { "caddy", "tray", "pop-up", "unknown" }; /* Read-ahead buffer sizes for different drives. These are just arbitrary values, I don't know what is really optimum. */ -static unsigned int mem_size[] = { 4096, 8192, 16384, 2048 }; +static unsigned int mem_size[] = { 16384, 16384, 16384, 2048 }; void get_drive_configuration(unsigned short base_io, @@ -1640,8 +1778,6 @@ unsigned long cdu31a_init(unsigned long mem_start, unsigned long mem_end) { struct s_sony_drive_config drive_config; - unsigned char params[3]; - unsigned char res_reg[2]; unsigned int res_size; int i; int drive_found; @@ -1695,17 +1831,7 @@ cdu31a_init(unsigned long mem_start, unsigned long mem_end) } printk("\n"); - params[0] = SONY_SD_MECH_CONTROL; - params[1] = 0x03; - do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, - params, - 2, - res_reg, - &res_size); - if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20)) - { - printk(" Unable to set mechanical parameters: 0x%2.2x\n", res_reg[1]); - } + set_drive_params(); blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */ @@ -1716,8 +1842,6 @@ cdu31a_init(unsigned long mem_start, unsigned long mem_end) mem_start += sizeof(*last_sony_subcode); sony_buffer = (unsigned char *) mem_start; mem_start += sony_buffer_size; - - initialized = 1; } i++; diff --git a/drivers/char/Makefile b/drivers/char/Makefile index b72485c..4148459 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -48,7 +48,7 @@ OBJS := $(OBJS) msbusmouse.o SRCS := $(SRCS) msbusmouse.c endif -ifdef CONFIG_QUICKPORT_MOUSE +ifdef CONFIG_82C710_MOUSE CONFIG_PSMOUSE = CONFIG_PSMOUSE endif diff --git a/drivers/char/mouse.c b/drivers/char/mouse.c index debc6ea..107405f 100644 --- a/drivers/char/mouse.c +++ b/drivers/char/mouse.c @@ -45,7 +45,7 @@ static int mouse_open(struct inode * inode, struct file * file) file->f_op = &bus_mouse_fops; break; #endif -#if defined CONFIG_PSMOUSE || defined CONFIG_QUICKPORT_MOUSE +#if defined CONFIG_PSMOUSE || defined CONFIG_82C710_MOUSE case PSMOUSE_MINOR: file->f_op = &psaux_fops; break; @@ -83,7 +83,7 @@ unsigned long mouse_init(unsigned long kmem_start) #ifdef CONFIG_BUSMOUSE kmem_start = bus_mouse_init(kmem_start); #endif -#if defined CONFIG_PSMOUSE || defined CONFIG_QUICKPORT_MOUSE +#if defined CONFIG_PSMOUSE || defined CONFIG_82C710_MOUSE kmem_start = psaux_init(kmem_start); #endif #ifdef CONFIG_MS_BUSMOUSE diff --git a/drivers/char/psaux.c b/drivers/char/psaux.c index 487b16d..da133b3 100644 --- a/drivers/char/psaux.c +++ b/drivers/char/psaux.c @@ -68,7 +68,7 @@ #define AUX_DISABLE_DEV 0xf5 /* disable aux device */ #define AUX_RESET 0xff /* reset aux device */ -#define MAX_RETRIES 30 /* some aux operations take long time*/ +#define MAX_RETRIES 60 /* some aux operations take long time*/ #define AUX_IRQ 12 #define AUX_BUF_SIZE 2048 diff --git a/drivers/char/serial.c b/drivers/char/serial.c index c25c17b..0330087 100644 --- a/drivers/char/serial.c +++ b/drivers/char/serial.c @@ -1574,7 +1574,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, */ if (info->flags & ASYNC_CLOSING) { interruptible_sleep_on(&info->close_wait); - return -EAGAIN; + return -ERESTARTSYS; } /* @@ -1632,7 +1632,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, current->state = TASK_INTERRUPTIBLE; if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)) { - retval = -EAGAIN; + retval = -ERESTARTSYS; break; } if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 5422dcc..77fb28a 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1315,6 +1315,7 @@ static int tty_open(struct inode * inode, struct file * filp) int major, minor; int noctty, retval; +retry_open: minor = MINOR(inode->i_rdev); major = MAJOR(inode->i_rdev); noctty = filp->f_flags & O_NOCTTY; @@ -1372,7 +1373,12 @@ static int tty_open(struct inode * inode, struct file * filp) #endif release_dev(minor, filp); - return retval; + if (retval != -ERESTARTSYS) + return retval; + if (current->signal & ~current->blocked) + return retval; + schedule(); + goto retry_open; } if (!noctty && current->leader && diff --git a/drivers/net/slip.c b/drivers/net/slip.c index b72dbca..2753824 100644 --- a/drivers/net/slip.c +++ b/drivers/net/slip.c @@ -881,6 +881,37 @@ slip_open(struct tty_struct *tty) return(sl->line); } + +static struct enet_statistics * +sl_get_stats(struct device *dev) +{ + static struct enet_statistics stats; + struct slip *sl; + struct slcompress *comp; + + /* Find the correct SLIP channel to use. */ + sl = &sl_ctrl[dev->base_addr]; + if (! sl) + return NULL; + + memset(&stats, 0, sizeof(struct enet_statistics)); + + stats.rx_packets = sl->rpacket; + stats.rx_over_errors = sl->roverrun; + stats.tx_packets = sl->spacket; + stats.tx_dropped = sl->sbusy; + stats.rx_errors = sl->errors; + + comp = sl->slcomp; + if (comp) { + stats.rx_fifo_errors = comp->sls_i_compressed; + stats.rx_dropped = comp->sls_i_tossed; + stats.tx_fifo_errors = comp->sls_o_compressed; + stats.collisions = comp->sls_o_misses; + } + + return (&stats); +} /* * Close down a SLIP channel. @@ -1193,6 +1224,7 @@ slip_init(struct device *dev) dev->hard_header = sl_header; dev->add_arp = sl_add_arp; dev->type_trans = sl_type_trans; + dev->get_stats = sl_get_stats; #ifdef HAVE_SET_MAC_ADDR #ifdef CONFIG_AX25 dev->set_mac_address = sl_set_mac_address; diff --git a/fs/binfmt_coff.c b/fs/binfmt_coff.c index 53b5eda..bf57016 100644 --- a/fs/binfmt_coff.c +++ b/fs/binfmt_coff.c @@ -38,6 +38,12 @@ * Al Longyear (longyear@sii.com) * Removed erroneous code which mistakenly folded .data with .bss for * a shared library. + * + * 8 Janurary 1994 + * Al Longyear (longyear@sii.com) + * Corrected problem with read of library section returning the byte + * count rather than zero. This was a change between the pl12 and + * pl14 kernels which slipped by me. */ #include <linux/fs.h> @@ -650,12 +656,21 @@ preload_library (struct linux_binprm *exe_bprm, buffer, /* Buffer for read */ nbytes); /* Byte count reqd. */ set_fs (old_fs); /* Restore the selector */ +/* + * Check the result. The value returned is the byte count actaully read. + */ + if (status >= 0 && status != nbytes) { +#ifdef COFF_DEBUG + printk ("read of lib section was short\n"); +#endif + status = -ENOEXEC; + } } /* * At this point, go through the list of libraries in the data area. */ phdr = (COFF_SLIBHD *) buffer; - while (status == 0 && nbytes > COFF_SLIBSZ) { + while (status >= 0 && nbytes > COFF_SLIBSZ) { int entry_size = COFF_LONG (phdr->sl_entsz) * sizeof (long); int header_size = COFF_LONG (phdr->sl_pathndx) * sizeof (long); /* diff --git a/fs/ext2/CHANGES b/fs/ext2/CHANGES index 258bf23..08b7b64 100644 --- a/fs/ext2/CHANGES +++ b/fs/ext2/CHANGES @@ -4,8 +4,7 @@ Changes from version 0.4a to version 0.4b - Clean up of balloc.c and ialloc.c. - More consistency checks. - Block preallocation added by Stephen Tweedie. - - Direct reads of directories disallowed if CONFIG_EXT2_FS_DIR_READ not - defined. + - Direct reads of directories disallowed. - Readahead implemented in readdir by Stephen Tweedie. - Bugs in block and inodes allocation fixed. - Readahead implemented in ext2_find_entry by Chip Salzenberg. diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index 9cfff70..84830b8 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -16,32 +16,23 @@ #include <asm/segment.h> -#include <linux/autoconf.h> #include <linux/errno.h> #include <linux/fs.h> #include <linux/ext2_fs.h> #include <linux/sched.h> #include <linux/stat.h> -#ifndef CONFIG_EXT2_FS_DIR_READ static int ext2_dir_read (struct inode * inode, struct file * filp, char * buf, int count) { return -EISDIR; } -#else -int ext2_file_read (struct inode *, struct file *, char *, int); -#endif static int ext2_readdir (struct inode *, struct file *, struct dirent *, int); static struct file_operations ext2_dir_operations = { NULL, /* lseek - default */ -#ifdef CONFIG_EXT2_FS_DIR_READ - ext2_file_read, /* read */ -#else ext2_dir_read, /* read */ -#endif NULL, /* write - bad */ ext2_readdir, /* readdir */ NULL, /* select - default */ diff --git a/fs/ext2/file.c b/fs/ext2/file.c index e0e9e98..4f7fa7b 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -17,7 +17,6 @@ #include <asm/segment.h> #include <asm/system.h> -#include <linux/autoconf.h> #include <linux/errno.h> #include <linux/fs.h> #include <linux/ext2_fs.h> @@ -34,10 +33,7 @@ #include <linux/fs.h> #include <linux/ext2_fs.h> -#ifndef CONFIG_EXT2_FS_DIR_READ -static -#endif -int ext2_file_read (struct inode *, struct file *, char *, int); +static int ext2_file_read (struct inode *, struct file *, char *, int); static int ext2_file_write (struct inode *, struct file *, char *, int); static void ext2_release_file (struct inode *, struct file *); @@ -76,10 +72,7 @@ struct inode_operations ext2_file_inode_operations = { ext2_permission /* permission */ }; -#ifndef CONFIG_EXT2_FS_DIR_READ -static -#endif -int ext2_file_read (struct inode * inode, struct file * filp, +static int ext2_file_read (struct inode * inode, struct file * filp, char * buf, int count) { int read, left, chars; @@ -97,7 +90,7 @@ int ext2_file_read (struct inode * inode, struct file * filp, return -EINVAL; } sb = inode->i_sb; - if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode)) { + if (!S_ISREG(inode->i_mode)) { ext2_warning (sb, "ext2_file_read", "mode = %07o", inode->i_mode); return -EINVAL; diff --git a/fs/ext2/super.c b/fs/ext2/super.c index d04caea..6c7e7d2 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -274,7 +274,7 @@ static void ext2_setup_super (struct super_block * sb, printk ("EXT2-fs warning: mounting fs with errors, " "running e2fsck is recommended\n"); else if (es->s_max_mnt_count >= 0 && - es->s_mnt_count >= es->s_max_mnt_count) + es->s_mnt_count >= (unsigned short) es->s_max_mnt_count) printk ("EXT2-fs warning: maximal mount count reached, " "running e2fsck is recommended\n"); if (!(sb->s_flags & MS_RDONLY)) { diff --git a/fs/sysv/README b/fs/sysv/README index f42e57d..6257a0d 100644 --- a/fs/sysv/README +++ b/fs/sysv/README @@ -4,16 +4,11 @@ It implements all of - SystemV/386 FS, - Coherent FS. -This is version alpha 5. +This is version beta 1. To install: -* You need Linux 0.99.14. -* Go to /usr/src/linux, unpack the tar file there, and patch the Linux source: - patch -p1 < sysvfs.cdif - To build the Linux kernel with the patches: - make config - make depend - make +* Answer the 'System V and Coherent filesystem support' question with 'y' + when configuring the kernel. * To mount a disk or a partition, use mount [-r] -t sysv device mountpoint The file system type names diff --git a/fs/sysv/ialloc.c b/fs/sysv/ialloc.c index c8cc98b..cfa5694 100644 --- a/fs/sysv/ialloc.c +++ b/fs/sysv/ialloc.c @@ -65,7 +65,8 @@ void sysv_free_inode(struct inode * inode) return; } if (!(bh = sysv_bread(sb, inode->i_dev, sb->sv_firstinodezone + ((ino-1) >> sb->sv_inodes_per_block_bits), &bh_data))) { - panic("sysv_free_inode: unable to read inode block"); /* FIXME: too severe? */ + printk("sysv_free_inode: unable to read inode block on device %d/%d\n",MAJOR(inode->i_dev),MINOR(inode->i_dev)); + clear_inode(inode); return; } raw_inode = (struct sysv_inode *) bh_data + ((ino-1) & sb->sv_inodes_per_block_1); diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index 4e2af91..d9eb848 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -316,7 +316,7 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data, sb->s_dev=0; unlock_super(sb); if (!silent) - printk("VFS: unable to read Xenix/SystemV/Coherent superblock\n"); + printk("VFS: unable to read Xenix/SystemV/Coherent superblock on device %d/%d\n",MAJOR(dev),MINOR(dev)); return NULL; ok: diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h index 3c384b1..7b7bfbb 100644 --- a/include/linux/ext2_fs.h +++ b/include/linux/ext2_fs.h @@ -52,7 +52,7 @@ /* * The second extended file system version */ -#define EXT2FS_DATE "94/01/05" +#define EXT2FS_DATE "94/01/08" #define EXT2FS_VERSION "0.4b" /* diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h index 2d44fb1..2fabe9b 100644 --- a/include/linux/if_ether.h +++ b/include/linux/if_ether.h @@ -31,7 +31,8 @@ #define ETH_P_PUP 0x0400 /* Xerox PUP packet */ #define ETH_P_IP 0x0800 /* Internet Protocol packet */ #define ETH_P_ARP 0x0806 /* Address Resolution packet */ -#define ETH_P_RARP 0x0835 /* Reverse Addr Res packet */ +#define ETH_P_RARP 0x8035 /* Reverse Addr Res packet */ +#define ETH_P_X25 0x0805 /* CCITT X.25 */ #define ETH_P_IPX 0x8137 /* IPX over DIX */ #define ETH_P_802_3 0x0001 /* Dummy type for 802.3 frames */ #define ETH_P_AX25 0x0002 /* Dummy protocol id for AX.25 */ diff --git a/include/linux/socket.h b/include/linux/socket.h index 4e5fdbd..e6a520b 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -30,10 +30,14 @@ struct linger { #define AF_UNSPEC 0 #define AF_UNIX 1 #define AF_INET 2 +#define AF_AX25 3 +#define AF_IPX 4 /* Protocol families, same as address families. */ #define PF_UNIX AF_UNIX #define PF_INET AF_INET +#define PF_AX25 AF_AX25 +#define PF_IPX AF_IPX /* Flags we can use with send/ and recv. */ #define MSG_OOB 1 @@ -41,6 +45,10 @@ struct linger { /* Setsockoptions(2) level. */ #define SOL_SOCKET 1 +#define SOL_IP 2 +#define SOL_IPX 3 +#define SOL_AX25 4 +#define SOL_TCP 5 /* For setsockoptions(2) */ #define SO_DEBUG 1 @@ -56,6 +64,19 @@ struct linger { #define SO_NO_CHECK 11 #define SO_PRIORITY 12 #define SO_LINGER 13 +/* IP options */ +#define IP_TOS 1 +#define IPTOS_LOWDELAY 0x10 +#define IPTOS_THROUGHPUT 0x08 +#define IPTOS_RELIABILITY 0x04 +#define IP_TTL 2 +/* IPX options */ +#define IPX_TYPE 1 +/* AX.25 options */ +#define AX25_WINDOW 1 +/* TCP options */ +#define TCP_MSS 1 +#define TCP_NODELAY 2 /* The various priorities. */ #define SOPRI_INTERACTIVE 0 diff --git a/include/linux/sockios.h b/include/linux/sockios.h index 499b219..2090c33 100644 --- a/include/linux/sockios.h +++ b/include/linux/sockios.h @@ -28,7 +28,7 @@ struct ip_config { unsigned long paddr; unsigned long router; unsigned long net; - unsigned int up:1,destroy:1; + unsigned long up:1,destroy:1; }; #endif /* FIXME: */ diff --git a/init/main.c b/init/main.c index c816f2c..15e3f6b 100644 --- a/init/main.c +++ b/init/main.c @@ -235,7 +235,7 @@ static void calibrate_delay(void) "r" (ticks), "0" (loops_per_sec) :"dx"); - printk("ok - %lu.%02lu BogoMips (tm)\n", + printk("ok - %lu.%02lu BogoMips\n", loops_per_sec/500000, (loops_per_sec/5000) % 100); return; diff --git a/net/inet/arp.c b/net/inet/arp.c index 0b69e1f..d559bcb 100644 --- a/net/inet/arp.c +++ b/net/inet/arp.c @@ -36,6 +36,9 @@ * Alan Cox : skb->link3 maintained by letting the other xmit queue kill the packet. * Alan Cox : Knows about type 3 devices (AX.25) using an AX.25 protocol ID not the ethernet * one. + * Dominik Kubla : Better checking + * Tegge : Assorted corrections on cross port stuff + * Alan Cox : ATF_PERM was backwards! - might be useful now (sigh) * * To Fix: * : arp response allocates an skbuff to send. However there is a perfectly @@ -386,7 +389,7 @@ static struct arp_table *arp_lookup_proxy(unsigned long paddr) /* Delete an ARP mapping entry in the cache. */ void -arp_destroy(unsigned long paddr) +arp_destructor(unsigned long paddr, int force) { struct arp_table *apt; struct arp_table **lapt; @@ -406,6 +409,8 @@ arp_destroy(unsigned long paddr) lapt = &arp_tables[hash]; while ((apt = *lapt) != NULL) { if (apt->ip == paddr) { + if((apt->flags&ATF_PERM) && !force) + return; *lapt = apt->next; if(apt->flags&ATF_PUBL) arp_proxies--; @@ -418,6 +423,23 @@ arp_destroy(unsigned long paddr) sti(); } +/* + * Kill an entry - eg for ioctl() + */ + +void arp_destroy(unsigned long paddr) +{ + arp_destructor(paddr,1); +} + +/* + * Delete a possibly invalid entry (see timer.c) + */ + +void arp_destroy_maybe(unsigned long paddr) +{ + arp_destructor(paddr,0); +} /* Create an ARP entry. The caller should check for duplicates! */ static struct arp_table * @@ -536,16 +558,16 @@ arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) return(0); } - /* - * A broadcast arp, ignore it - */ +/* + * A broadcast arp, ignore it + */ - if((dst&0xFF)==0xFF) + if(chk_addr(dst)==IS_BROADCAST) { kfree_skb(skb, FREE_READ); return 0; } - + memcpy(&dst, ptr + (arp->ar_hln * 2) + arp->ar_pln, arp->ar_pln); if ((addr_hint=chk_addr(dst)) != IS_MYADDR && arp_proxies==0) { DPRINTF((DBG_ARP, "ARP: request was not for me!\n")); @@ -654,8 +676,8 @@ arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev, * just pretend we did not find it, and then arp_send will * verify the address for us. */ - if ((!(apt->flags & ATF_PERM)) || - (!before(apt->last_used, jiffies+ARP_TIMEOUT) && apt->hlen != 0)) { + if ((apt->flags & ATF_PERM) || + (apt->last_used < jiffies+ARP_TIMEOUT && apt->hlen != 0)) { apt->last_used = jiffies; memcpy(haddr, apt->ha, dev->addr_len); return(0); @@ -814,6 +836,11 @@ arp_req_set(struct arpreq *req) htype = ARPHRD_ETHER; hlen = ETH_ALEN; break; + case ARPHRD_AX25: + htype = ARPHRD_AX25; + hlen = 7; + break; + default: return(-EPFNOSUPPORT); } diff --git a/net/inet/arp.h b/net/inet/arp.h index c75c6cf..57f41ac 100644 --- a/net/inet/arp.h +++ b/net/inet/arp.h @@ -59,5 +59,6 @@ extern void arp_add_broad(unsigned long addr, struct device *dev); extern void arp_queue(struct sk_buff *skb); extern int arp_get_info(char *buffer); extern int arp_ioctl(unsigned int cmd, void *arg); +extern void arp_destroy_maybe(unsigned long paddr); #endif /* _ARP_H */ diff --git a/net/inet/datagram.c b/net/inet/datagram.c index 931d9f3..7d0687e 100644 --- a/net/inet/datagram.c +++ b/net/inet/datagram.c @@ -2,7 +2,7 @@ * SUCS NET2 Debugged. * * Generic datagram handling routines. These are generic for all protocols. Possibly a generic IP version on top - * of these would make sense. Not tonight however 8-). + * of these would make sense. Not tonight however 8-). * This is used because UDP, RAW, PACKET and the to be released IPX layer all have identical select code and mostly * identical recvfrom() code. So we share it here. The select was shared before but buried in udp.c so I moved it. * @@ -13,6 +13,7 @@ * Alan Cox : Rewrote skb_read_datagram to avoid the skb_peek_copy stuff. * Alan Cox : Added support for SOCK_SEQPACKET. IPX can no longer use the SO_TYPE hack but * AX.25 now works right, and SPX is feasible. + * Alan Cox : Fixed write select of non IP protocol crash. */ #include <linux/config.h> @@ -35,7 +36,7 @@ #include "udp.h" #include "skbuff.h" #include "sock.h" - + /* * Get a datagram skbuff, understands the peeking, nonblocking wakeups and possible @@ -46,16 +47,16 @@ */ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock, int *err) -{ +{ struct sk_buff *skb; - + /* Socket is inuse - so the timer doesn't attack it */ restart: sk->inuse = 1; - while(sk->rqueue == NULL) /* No data */ - { - /* If we are shutdown then no more data is going to appear. We are done */ - if (sk->shutdown & RCV_SHUTDOWN) + while(sk->rqueue == NULL) /* No data */ + { + /* If we are shutdown then no more data is going to appear. We are done */ + if (sk->shutdown & RCV_SHUTDOWN) { release_sock(sk); *err=0; @@ -69,7 +70,7 @@ restart: sk->err=0; return NULL; } - + /* Sequenced packets can come disconnected. If so we report the problem */ if(sk->type==SOCK_SEQPACKET && sk->state!=TCP_ESTABLISHED) { @@ -77,24 +78,24 @@ restart: *err=-ENOTCONN; return NULL; } - + /* User doesn't want to wait */ - if (noblock) + if (noblock) { release_sock(sk); *err=-EAGAIN; return NULL; } release_sock(sk); - - /* Interrupts off so that no packet arrives before we begin sleeping. + + /* Interrupts off so that no packet arrives before we begin sleeping. Otherwise we might miss our wake up */ cli(); - if (sk->rqueue == NULL) + if (sk->rqueue == NULL) { interruptible_sleep_on(sk->sleep); /* Signals may need a restart of the syscall */ - if (current->signal & ~current->blocked) + if (current->signal & ~current->blocked) { sti(); *err=-ERESTARTSYS; @@ -115,26 +116,26 @@ restart: } /* Again only user level code calls this function, so nothing interrupt level will suddenely eat the rqueue */ - if (!(flags & MSG_PEEK)) + if (!(flags & MSG_PEEK)) { - skb=skb_dequeue(&sk->rqueue); - if(skb!=NULL) - skb->users++; + skb=skb_dequeue(&sk->rqueue); + if(skb!=NULL) + skb->users++; else goto restart; /* Avoid race if someone beats us to the data */ } else { - cli(); - skb=skb_peek(&sk->rqueue); - if(skb!=NULL) - skb->users++; - sti(); - if(skb==NULL) /* shouldn't happen but .. */ - *err=-EAGAIN; + cli(); + skb=skb_peek(&sk->rqueue); + if(skb!=NULL) + skb->users++; + sti(); + if(skb==NULL) /* shouldn't happen but .. */ + *err=-EAGAIN; } return skb; -} +} void skb_free_datagram(struct sk_buff *skb) { @@ -156,7 +157,7 @@ void skb_free_datagram(struct sk_buff *skb) void skb_copy_datagram(struct sk_buff *skb, int offset, char *to, int size) { - /* We will know all about the fraglist options to allow >4K receives + /* We will know all about the fraglist options to allow >4K receives but not this release */ memcpy_tofs(to,skb->h.raw+offset,size); } @@ -165,11 +166,11 @@ void skb_copy_datagram(struct sk_buff *skb, int offset, char *to, int size) * Datagram select: Again totally generic. Moved from udp.c * Now does seqpacket. */ - + int datagram_select(struct sock *sk, int sel_type, select_table *wait) { select_wait(sk->sleep, wait); - switch(sel_type) + switch(sel_type) { case SEL_IN: if (sk->type==SOCK_SEQPACKET && sk->state==TCP_CLOSE) @@ -177,7 +178,7 @@ int datagram_select(struct sock *sk, int sel_type, select_table *wait) /* Connection closed: Wake up */ return(1); } - if (sk->rqueue != NULL || sk->err != 0) + if (sk->rqueue != NULL || sk->err != 0) { /* This appears to be consistent with other stacks */ return(1); @@ -185,16 +186,20 @@ int datagram_select(struct sock *sk, int sel_type, select_table *wait) return(0); case SEL_OUT: - if (sk->prot->wspace(sk) >= MIN_WRITE_SPACE) + if (sk->prot->wspace(sk) >= MIN_WRITE_SPACE) + { + return(1); + } + if (sk->prot==NULL && sk->sndbuf-sk->wmem_alloc >= MIN_WRITE_SPACE) { return(1); } return(0); - + case SEL_EX: if (sk->err) return(1); /* Socket has gone into error state (eg icmp error) */ return(0); - } - return(0); + } + return(0); } diff --git a/net/inet/dev.c b/net/inet/dev.c index e867ae3..0ce0e7b 100644 --- a/net/inet/dev.c +++ b/net/inet/dev.c @@ -26,6 +26,10 @@ * a) actually works for all A/B nets * b) doesn't forward off the same interface. * Alan Cox: Multiple extra protocols + * Alan Cox: Fixed ifconfig up of dud device setting the up flag + * Alan Cox: Fixed verify_area errors + * Alan Cox: Removed IP_SET_DEV as per Fred's comment. I hope this doesn't give + * anything away 8) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -709,9 +713,12 @@ dev_ifconf(char *arg) struct device *dev; char *pos; int len; + int err; /* Fetch the caller's info block. */ - verify_area(VERIFY_WRITE, arg, sizeof(struct ifconf)); + err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifconf)); + if(err) + return err; memcpy_fromfs(&ifc, arg, sizeof(struct ifconf)); len = ifc.ifc_len; pos = ifc.ifc_buf; @@ -800,7 +807,9 @@ dev_ifsioc(void *arg, unsigned int getset) int ret; /* Fetch the caller's info block. */ - verify_area(VERIFY_WRITE, arg, sizeof(struct ifreq)); + int err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifreq)); + if(err) + return err; memcpy_fromfs(&ifr, arg, sizeof(struct ifreq)); /* See which interface the caller is talking about. */ @@ -827,8 +836,12 @@ dev_ifsioc(void *arg, unsigned int getset) if ((old_flags & IFF_UP) && ((dev->flags & IFF_UP) == 0)) { ret = dev_close(dev); } else + { ret = (! (old_flags & IFF_UP) && (dev->flags & IFF_UP)) ? dev_open(dev) : 0; + if(ret<0) + dev->flags&=~IFF_UP; /* Didnt open so down the if */ + } } break; case SIOCGIFADDR: @@ -946,55 +959,9 @@ dev_ioctl(unsigned int cmd, void *arg) int ret; switch(cmd) { - case IP_SET_DEV: - { /* Maintain backwards-compatibility, to be deleted for 1.00. */ - struct device *dev; - /* The old 'struct ip_config'. */ - struct ip_config { - char name[MAX_IP_NAME]; - unsigned long paddr, router, net,up:1,destroy:1; - } ipc; - int retval, loopback; - - printk("INET: Warning: old-style ioctl(IP_SET_DEV) called!\n"); - if (!suser()) - return (-EPERM); - - verify_area (VERIFY_WRITE, arg, sizeof (ipc)); - memcpy_fromfs(&ipc, arg, sizeof (ipc)); - ipc.name[MAX_IP_NAME-1] = 0; - loopback = (strcmp(ipc.name, "loopback") == 0); - dev = dev_get( loopback ? "lo" : ipc.name); - if (dev == NULL) - return -EINVAL; - ipc.destroy = 0; - dev->pa_addr = ipc.paddr; - dev->family = AF_INET; - dev->pa_mask = get_mask(dev->pa_addr); - dev->pa_brdaddr = dev->pa_addr | ~dev->pa_mask; - if (ipc.net != 0xffffffff) { - dev->flags |= IFF_BROADCAST; - dev->pa_brdaddr = ipc.net; - } - - /* To be proper we should delete the route here. */ - if (ipc.up == 0) - return (dev->flags & IFF_UP != 0) ? dev_close(dev) : 0; - - if ((dev->flags & IFF_UP) == 0 - && (retval = dev_open(dev)) != 0) - return retval; - printk("%s: adding HOST route of %8.8lx.\n", dev->name, - htonl(ipc.paddr)); - rt_add(RTF_HOST, ipc.paddr, 0, 0, dev); - if (ipc.router != 0 && ipc.router != -1) { - rt_add(RTF_GATEWAY, ipc.paddr, 0, ipc.router, dev); - printk("%s: adding GATEWAY route of %8.8lx.\n", - dev->name, htonl(ipc.paddr)); - - } - return 0; - } + case IP_SET_DEV: + printk("Your network configuration program needs upgrading.\n"); + return -EINVAL; case SIOCGIFCONF: (void) dev_ifconf((char *) arg); ret = 0; diff --git a/net/inet/eth.c b/net/inet/eth.c index 01e2f51..d6d83eb 100644 --- a/net/inet/eth.c +++ b/net/inet/eth.c @@ -17,6 +17,7 @@ * Alan Cox : eth_header ntohs should be htons * Alan Cox : eth_rebuild_header missing an htons and * minor other things. + * Tegge : Arp bug fixes. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -124,7 +125,7 @@ eth_header(unsigned char *buff, struct device *dev, unsigned short type, cli(); memcpy(eth->h_source, &saddr, 4); /* No. Ask ARP to resolve the Ethernet address. */ - if (arp_find(eth->h_dest, daddr, dev, saddr)) + if (arp_find(eth->h_dest, daddr, dev, dev->pa_addr)) { sti(); if(type!=ETH_P_IP) @@ -155,7 +156,7 @@ eth_rebuild_header(void *buff, struct device *dev) DPRINTF((DBG_DEV, "ETH: RebuildHeader: SRC=%s ", in_ntoa(src))); DPRINTF((DBG_DEV, "DST=%s\n", in_ntoa(dst))); if(eth->h_proto!=htons(ETH_P_ARP)) /* This ntohs kind of helps a bit! */ - if (arp_find(eth->h_dest, dst, dev, src)) return(1); + if (arp_find(eth->h_dest, dst, dev, dev->pa_addr /* src */)) return(1); memcpy(eth->h_source, dev->dev_addr, dev->addr_len); return(0); } diff --git a/net/inet/icmp.c b/net/inet/icmp.c index 7b953d4..8150451 100644 --- a/net/inet/icmp.c +++ b/net/inet/icmp.c @@ -14,6 +14,7 @@ * Fixes: * Alan Cox : Generic queue usage. * Gerhard Koerting: ICMP addressing corrected + * Alan Cox : Use tos/ttl settings * * * This program is free software; you can redistribute it and/or @@ -108,7 +109,7 @@ icmp_send(struct sk_buff *skb_in, int type, int code, struct device *dev) /* Build Layer 2-3 headers for message back to source. */ offset = ip_build_header(skb, dev->pa_addr, iph->saddr, - &dev, IPPROTO_ICMP, NULL, len); + &dev, IPPROTO_ICMP, NULL, len, skb_in->ip_hdr->tos,255); if (offset < 0) { skb->sk = NULL; kfree_skb(skb, FREE_READ); @@ -255,7 +256,7 @@ icmp_echo(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, /* Build Layer 2-3 headers for message back to source */ offset = ip_build_header(skb2, daddr, saddr, &dev, - IPPROTO_ICMP, opt, len); + IPPROTO_ICMP, opt, len, skb->ip_hdr->tos,255); if (offset < 0) { printk("ICMP: Could not build IP Header for ICMP ECHO Response\n"); kfree_skb(skb2,FREE_WRITE); @@ -319,7 +320,7 @@ icmp_address(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, /* Build Layer 2-3 headers for message back to source */ offset = ip_build_header(skb2, daddr, saddr, &dev, - IPPROTO_ICMP, opt, len); + IPPROTO_ICMP, opt, len, skb->ip_hdr->tos,255); if (offset < 0) { printk("ICMP: Could not build IP Header for ICMP ADDRESS Response\n"); kfree_skb(skb2,FREE_WRITE); diff --git a/net/inet/ip.c b/net/inet/ip.c index 4c9f924..091998c 100644 --- a/net/inet/ip.c +++ b/net/inet/ip.c @@ -31,6 +31,9 @@ * Gerhard Koerting: IP interface addressing fix. * Linus Torvalds : More robustness checks * Alan Cox : Even more checks: Still not as robust as it ought to be + * Alan Cox : Save IP header pointer for later + * Alan Cox : ip option setting + * Alan Cox : Use ip_tos/ip_ttl settings * * To Fix: * IP option processing is mostly not needed. ip_forward needs to know about routing rules @@ -198,7 +201,7 @@ ip_send(struct sk_buff *skb, unsigned long daddr, int len, struct device *dev, */ int ip_build_header(struct sk_buff *skb, unsigned long saddr, unsigned long daddr, - struct device **dev, int type, struct options *opt, int len) + struct device **dev, int type, struct options *opt, int len, int tos, int ttl) { static struct options optmem; struct iphdr *iph; @@ -256,9 +259,9 @@ ip_build_header(struct sk_buff *skb, unsigned long saddr, unsigned long daddr, iph = (struct iphdr *)buff; iph->version = 4; - iph->tos = 0; + iph->tos = tos; iph->frag_off = 0; - iph->ttl = 32; + iph->ttl = ttl; iph->daddr = daddr; iph->saddr = saddr; iph->protocol = type; @@ -1267,6 +1270,8 @@ ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) } /* Point into the IP datagram, just past the header. */ + + skb->ip_hdr = iph; skb->h.raw += iph->ihl*4; hash = iph->protocol & (MAX_INET_PROTOS -1); for (ipprot = (struct inet_protocol *)inet_protos[hash]; @@ -1504,3 +1509,72 @@ int backoff(int n) } } +/* + * Socket option code for IP. This is the end of the line after any TCP,UDP etc options on + * an IP socket. + */ + +int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen) +{ + int val,err; + + if (optval == NULL) + return(-EINVAL); + + err=verify_area(VERIFY_READ, optval, sizeof(int)); + if(err) + return err; + + val = get_fs_long((unsigned long *)optval); + + if(level!=SOL_IP) + return -EOPNOTSUPP; + + switch(optname) + { + case IP_TOS: + if(val<0||val>255) + return -EINVAL; + sk->ip_tos=val; + return 0; + case IP_TTL: + if(val<1||val<255) + return -EINVAL; + sk->ip_ttl=val; + return 0; + /* IP_OPTIONS and friends go here eventually */ + default: + return(-ENOPROTOOPT); + } +} + +int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen) +{ + int val,err; + + if(level!=SOL_IP) + return -EOPNOTSUPP; + + switch(optname) + { + case IP_TOS: + val=sk->ip_tos; + break; + case IP_TTL: + val=sk->ip_ttl; + break; + default: + return(-ENOPROTOOPT); + } + err=verify_area(VERIFY_WRITE, optlen, sizeof(int)); + if(err) + return err; + put_fs_long(sizeof(int),(unsigned long *) optlen); + + err=verify_area(VERIFY_WRITE, optval, sizeof(int)); + if(err) + return err; + put_fs_long(val,(unsigned long *)optval); + + return(0); +} diff --git a/net/inet/ip.h b/net/inet/ip.h index 3c01cc8..7f2ecc0 100644 --- a/net/inet/ip.h +++ b/net/inet/ip.h @@ -69,7 +69,8 @@ extern int ip_build_header(struct sk_buff *skb, unsigned long saddr, unsigned long daddr, struct device **dev, int type, - struct options *opt, int len); + struct options *opt, int len, + int tos,int ttl); extern unsigned short ip_compute_csum(unsigned char * buff, int len); extern int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt); @@ -77,5 +78,7 @@ extern void ip_queue_xmit(struct sock *sk, struct device *dev, struct sk_buff *skb, int free); extern void ip_retransmit(struct sock *sk, int all); +extern int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen); +extern int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen); #endif /* _IP_H */ diff --git a/net/inet/packet.c b/net/inet/packet.c index c0585a6..ec00afe 100644 --- a/net/inet/packet.c +++ b/net/inet/packet.c @@ -18,6 +18,7 @@ * added. Also fixed the peek/read crash * from all old Linux datagram code. * Alan Cox : Uses the improved datagram code. + * Alan Cox : Added NULL's for socket options. * * * This program is free software; you can redistribute it and/or @@ -264,6 +265,8 @@ struct proto packet_prot = { NULL, packet_init, NULL, + NULL, /* No set/get socket options */ + NULL, 128, 0, {NULL,}, diff --git a/net/inet/raw.c b/net/inet/raw.c index 7533532..249d0b2 100644 --- a/net/inet/raw.c +++ b/net/inet/raw.c @@ -19,6 +19,10 @@ * Alan Cox : Checks sk->broadcast. * Alan Cox : Uses skb_free_datagram/skb_copy_datagram * Alan Cox : Raw passes ip options too + * Alan Cox : Setsocketopt added + * Alan Cox : Fixed error return for broadcasts + * Alan Cox : Removed wake_up calls + * Alan Cox : Use ttl/tos * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -76,7 +80,7 @@ raw_err (int err, unsigned char *header, unsigned long daddr, } sk->err = icmp_err_convert[err & 0xff].errno; - wake_up(sk->sleep); + sk->error_report(sk); return; } @@ -123,7 +127,7 @@ raw_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, } sk->rmem_alloc += skb->mem_len; skb_queue_tail(&sk->rqueue,skb); - wake_up(sk->sleep); + sk->data_ready(sk,skb->len); release_sock(sk); return(0); } @@ -169,7 +173,7 @@ raw_sendto(struct sock *sk, unsigned char *from, int len, if (sin.sin_port == 0) sin.sin_port = sk->protocol; if (sk->broadcast == 0 && chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST) - return -ENETUNREACH; + return -EACCES; sk->inuse = 1; skb = NULL; @@ -214,7 +218,7 @@ raw_sendto(struct sock *sk, unsigned char *from, int len, tmp = sk->prot->build_header(skb, sk->saddr, sin.sin_addr.s_addr, &dev, - sk->protocol, sk->opt, skb->mem_len); + sk->protocol, sk->opt, skb->mem_len, sk->ip_tos,sk->ip_ttl); if (tmp < 0) { DPRINTF((DBG_RAW, "raw_sendto: error building ip header.\n")); kfree_skb(skb,FREE_WRITE); @@ -330,6 +334,13 @@ raw_recvfrom(struct sock *sk, unsigned char *to, int len, return err; put_fs_long(sizeof(*sin), addr_len); } + if(sin) + { + err=verify_area(VERIFY_WRITE, sin, sizeof(*sin)); + if(err) + return err; + } + err=verify_area(VERIFY_WRITE,to,len); if(err) return err; @@ -348,7 +359,6 @@ raw_recvfrom(struct sock *sk, unsigned char *to, int len, addr.sin_family = AF_INET; addr.sin_addr.s_addr = skb->daddr; - verify_area(VERIFY_WRITE, sin, sizeof(*sin)); memcpy_tofs(sin, &addr, sizeof(*sin)); } @@ -390,6 +400,8 @@ struct proto raw_prot = { NULL, raw_init, NULL, + ip_setsockopt, + ip_getsockopt, 128, 0, {NULL,}, diff --git a/net/inet/route.c b/net/inet/route.c index 269c80d..c6bcdce 100644 --- a/net/inet/route.c +++ b/net/inet/route.c @@ -121,21 +121,27 @@ void rt_flush(struct device *dev) * number of zero 8-bit net numbers, otherwise we use the "default" * masks judging by the destination address and our device netmask. */ +static inline unsigned long default_mask(unsigned long dst) +{ + dst = ntohl(dst); + if (IN_CLASSA(dst)) + return htonl(IN_CLASSA_NET); + if (IN_CLASSB(dst)) + return htonl(IN_CLASSB_NET); + return htonl(IN_CLASSC_NET); +} + static unsigned long guess_mask(unsigned long dst, struct device * dev) { unsigned long mask = 0xffffffff; +/* this is a rather ugly optimization: works only on little-endian machines */ while (mask & dst) - mask >>= 8; + mask <<= 8; if (mask) return ~mask; - dst = ntohl(dst); - if (IN_CLASSA(dst)) - mask = htonl(IN_CLASSA_NET); - else if (IN_CLASSB(dst)) - mask = htonl(IN_CLASSB_NET); - else - mask = htonl(IN_CLASSC_NET); +/* ok, no more hacks.. */ + mask = default_mask(dst); if (dev->flags & IFF_POINTOPOINT) return mask; if ((dst ^ dev->pa_addr) & mask) diff --git a/net/inet/skbuff.c b/net/inet/skbuff.c index 1f3299d..2898729 100644 --- a/net/inet/skbuff.c +++ b/net/inet/skbuff.c @@ -13,7 +13,7 @@ * and memory leak hunting. * Alan Cox : More generic kfree handler */ - + #include <linux/config.h> #include <linux/types.h> #include <linux/kernel.h> @@ -44,14 +44,14 @@ /* * Resource tracking variables */ - + volatile unsigned long net_memory=0; volatile unsigned long net_skbcount=0; /* * Debugging paranoia. Can go later when this crud stack works - */ - + */ + void skb_check(struct sk_buff *skb, int line, char *file) @@ -81,20 +81,20 @@ void skb_check(struct sk_buff *skb, int line, char *file) /* * Insert an sk_buff at the start of a list. */ - + void skb_queue_head(struct sk_buff *volatile* list,struct sk_buff *newsk) { unsigned long flags; - - IS_SKB(newsk); + + IS_SKB(newsk); if(newsk->list) printk("Suspicious queue head: sk_buff on list!\n"); save_flags(flags); cli(); newsk->list=list; - + newsk->next=*list; - + if(*list) newsk->prev=(*list)->prev; else @@ -110,14 +110,14 @@ void skb_queue_head(struct sk_buff *volatile* list,struct sk_buff *newsk) /* * Insert an sk_buff at the end of a list. */ - + void skb_queue_tail(struct sk_buff *volatile* list, struct sk_buff *newsk) { unsigned long flags; - + if(newsk->list) printk("Suspicious queue tail: sk_buff on list!\n"); - + IS_SKB(newsk); save_flags(flags); cli(); @@ -137,7 +137,7 @@ void skb_queue_tail(struct sk_buff *volatile* list, struct sk_buff *newsk) *list=newsk; } IS_SKB(newsk->prev); - IS_SKB(newsk->next); + IS_SKB(newsk->next); restore_flags(flags); } @@ -146,21 +146,21 @@ void skb_queue_tail(struct sk_buff *volatile* list, struct sk_buff *newsk) * Remove an sk_buff from a list. This routine is also interrupt safe * so you can grab read and free buffers as another process adds them. */ - + struct sk_buff *skb_dequeue(struct sk_buff *volatile* list) { long flags; struct sk_buff *result; - + save_flags(flags); cli(); - + if(*list==NULL) { restore_flags(flags); return(NULL); } - + result=*list; if(result->next==result) *list=NULL; @@ -173,7 +173,7 @@ struct sk_buff *skb_dequeue(struct sk_buff *volatile* list) IS_SKB(result); restore_flags(flags); - + if(result->list!=list) printk("Dequeued packet has invalid list pointer\n"); @@ -186,19 +186,19 @@ struct sk_buff *skb_dequeue(struct sk_buff *volatile* list) /* * Insert a packet before another one in a list. */ - + void skb_insert(struct sk_buff *old, struct sk_buff *newsk) { unsigned long flags; IS_SKB(old); IS_SKB(newsk); - + if(!old->list) printk("insert before unlisted item!\n"); if(newsk->list) printk("inserted item is already on a list.\n"); - + save_flags(flags); cli(); newsk->list=old->list; @@ -206,18 +206,18 @@ void skb_insert(struct sk_buff *old, struct sk_buff *newsk) newsk->prev=old->prev; newsk->next->prev=newsk; newsk->prev->next=newsk; - + restore_flags(flags); } /* * Place a packet after a given packet in a list. */ - + void skb_append(struct sk_buff *old, struct sk_buff *newsk) { unsigned long flags; - + IS_SKB(old); IS_SKB(newsk); @@ -225,7 +225,7 @@ void skb_append(struct sk_buff *old, struct sk_buff *newsk) printk("append before unlisted item!\n"); if(newsk->list) printk("append item is already on a list.\n"); - + save_flags(flags); cli(); newsk->list=old->list; @@ -233,7 +233,7 @@ void skb_append(struct sk_buff *old, struct sk_buff *newsk) newsk->next=old->next; newsk->next->prev=newsk; newsk->prev->next=newsk; - + restore_flags(flags); } @@ -243,7 +243,7 @@ void skb_append(struct sk_buff *old, struct sk_buff *newsk) * MUST EXIST when you unlink. Thus a list must have its contents unlinked * _FIRST_. */ - + void skb_unlink(struct sk_buff *skb) { unsigned long flags; @@ -251,7 +251,7 @@ void skb_unlink(struct sk_buff *skb) cli(); IS_SKB(skb); - + if(skb->list) { skb->next->prev=skb->prev; @@ -290,7 +290,7 @@ void skb_new_list_head(struct sk_buff *volatile* list) while(skb!=*list); } } - + /* * Peek an sk_buff. Unlike most other operations you _MUST_ * be careful with this one. A peek leaves the buffer on the @@ -310,14 +310,14 @@ struct sk_buff *skb_peek(struct sk_buff *volatile* list) * anyway. Only the memcpy of upto 4K with ints off is not * as nice as I'd like. */ - + struct sk_buff *skb_peek_copy(struct sk_buff *volatile* list) { struct sk_buff *orig,*newsk; unsigned long flags; unsigned int len; /* Now for some games to avoid races */ - + do { save_flags(flags); @@ -336,7 +336,7 @@ struct sk_buff *skb_peek_copy(struct sk_buff *volatile* list) if(newsk==NULL) /* Oh dear... not to worry */ return NULL; - + save_flags(flags); cli(); if(skb_peek(list)!=orig) /* List changed go around another time */ @@ -349,7 +349,7 @@ struct sk_buff *skb_peek_copy(struct sk_buff *volatile* list) kfree_skb(newsk, FREE_WRITE); continue; } - + IS_SKB(orig); IS_SKB(newsk); memcpy(newsk,orig,len); @@ -364,11 +364,11 @@ struct sk_buff *skb_peek_copy(struct sk_buff *volatile* list) newsk->free=1; } while(0); - + restore_flags(flags); return(newsk); -} - +} + /* * Free an sk_buff. This still knows about things it should * not need to like protocols and sockets. @@ -376,68 +376,68 @@ struct sk_buff *skb_peek_copy(struct sk_buff *volatile* list) void kfree_skb(struct sk_buff *skb, int rw) { - if (skb == NULL) { - printk("kfree_skb: skb = NULL\n"); - return; - } - IS_SKB(skb); - if(skb->free == 2) - printk("Warning: kfree_skb passed an skb that nobody set the free flag on!\n"); - if(skb->list) - printk("Warning: kfree_skb passed an skb still on a list.\n"); - skb->magic = 0; - if (skb->sk) - { - if(skb->sk->prot!=NULL) - { - if (rw) - skb->sk->prot->rfree(skb->sk, skb->mem_addr, skb->mem_len); - else - skb->sk->prot->wfree(skb->sk, skb->mem_addr, skb->mem_len); - + if (skb == NULL) { + printk("kfree_skb: skb = NULL\n"); + return; } - else + IS_SKB(skb); + if(skb->free == 2) + printk("Warning: kfree_skb passed an skb that nobody set the free flag on!\n"); + if(skb->list) + printk("Warning: kfree_skb passed an skb still on a list.\n"); + skb->magic = 0; + if (skb->sk) + { + if(skb->sk->prot!=NULL) { - /* Non INET - default wmalloc/rmalloc handler */ - if (rw) - skb->sk->rmem_alloc-=skb->mem_len; + if (rw) + skb->sk->prot->rfree(skb->sk, skb->mem_addr, skb->mem_len); + else + skb->sk->prot->wfree(skb->sk, skb->mem_addr, skb->mem_len); + + } else - skb->sk->wmem_alloc-=skb->mem_len; - if(!skb->sk->dead) + { + /* Non INET - default wmalloc/rmalloc handler */ + if (rw) + skb->sk->rmem_alloc-=skb->mem_len; + else + skb->sk->wmem_alloc-=skb->mem_len; + if(!skb->sk->dead) wake_up(skb->sk->sleep); - kfree_skbmem(skb->mem_addr,skb->mem_len); + kfree_skbmem(skb->mem_addr,skb->mem_len); + } } - } - else - kfree_skbmem(skb->mem_addr, skb->mem_len); + else + kfree_skbmem(skb->mem_addr, skb->mem_len); } /* * Allocate a new skbuff. We do this ourselves so we can fill in a few 'private' * fields and also do memory statistics to find all the [BEEP] leaks. */ - + struct sk_buff *alloc_skb(unsigned int size,int priority) { - struct sk_buff *skb=(struct sk_buff *)kmalloc(size,priority); - if(skb==NULL) - return NULL; - skb->free= 2; /* Invalid so we pick up forgetful users */ - skb->list= 0; /* Not on a list */ - skb->truesize=size; - skb->mem_len=size; - skb->mem_addr=skb; - skb->fraglist=NULL; - net_memory+=size; - net_skbcount++; - skb->magic_debug_cookie=SK_GOOD_SKB; - skb->users=0; - return skb; + struct sk_buff *skb=(struct sk_buff *)kmalloc(size,priority); + if(skb==NULL) + return NULL; + skb->free= 2; /* Invalid so we pick up forgetful users */ + skb->list= 0; /* Not on a list */ + skb->truesize=size; + skb->mem_len=size; + skb->mem_addr=skb; + skb->fraglist=NULL; + net_memory+=size; + net_skbcount++; + skb->magic_debug_cookie=SK_GOOD_SKB; + skb->users=0; + return skb; } /* * Free an skbuff by memory - */ + */ void kfree_skbmem(void *mem,unsigned size) { @@ -451,4 +451,4 @@ void kfree_skbmem(void *mem,unsigned size) net_memory-=size; } } - + diff --git a/net/inet/skbuff.h b/net/inet/skbuff.h index 6d5f879..a32f5a9 100644 --- a/net/inet/skbuff.h +++ b/net/inet/skbuff.h @@ -60,6 +60,7 @@ struct sk_buff { ipx_packet *ipx; #endif } h; + struct iphdr *ip_hdr; /* For IPPROTO_RAW */ unsigned long mem_len; unsigned long len; unsigned long fraglen; diff --git a/net/inet/sock.c b/net/inet/sock.c index 7ad61c9..1781697 100644 --- a/net/inet/sock.c +++ b/net/inet/sock.c @@ -48,6 +48,9 @@ * Alan Cox : SO_LINGER supported * Alan Cox : Error reporting fixes * Anonymous : inet_create tidied up (sk->reuse setting) + * Alan Cox : inet sockets don't set sk->type! + * Alan Cox : Split socket option code + * Alan Cox : Callbacks * * To Fix: * @@ -312,7 +315,7 @@ destroy_sock(struct sock *sk) /* Incase it's sleeping somewhere. */ if (!sk->dead) - wake_up(sk->sleep); + sk->write_space(sk); remove_sock(sk); @@ -471,205 +474,236 @@ inet_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg) } } - -static int -inet_setsockopt(struct socket *sock, int level, int optname, +/* + * Set socket options on an inet socket. + */ + +static int inet_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen) { - struct sock *sk; - int val; - int err; - struct linger ling; - - /* This should really pass things on to the other levels. */ - if (level != SOL_SOCKET) return(-EOPNOTSUPP); + struct sock *sk = (struct sock *) sock->data; + if (level == SOL_SOCKET) + return sock_setsockopt(sk,level,optname,optval,optlen); + if (sk->prot->setsockopt==NULL) + return(-EOPNOTSUPP); + else + return sk->prot->setsockopt(sk,level,optname,optval,optlen); +} - sk = (struct sock *) sock->data; - if (sk == NULL) { - printk("Warning: sock->data = NULL: %d\n" ,__LINE__); - return(0); - } - if (optval == NULL) return(-EINVAL); - err=verify_area(VERIFY_READ, optval, sizeof(int)); - if(err) - return err; - - val = get_fs_long((unsigned long *)optval); - switch(optname) { - case SO_TYPE: - case SO_ERROR: - return(-ENOPROTOOPT); - - case SO_DEBUG: - sk->debug=val?1:0; - case SO_DONTROUTE: /* Still to be implemented */ - return(0); - case SO_BROADCAST: - sk->broadcast=val?1:0; - return 0; - case SO_SNDBUF: - if(val>32767) - val=32767; - if(val<256) - val=256; - sk->sndbuf=val; - return 0; - case SO_LINGER: - err=verify_area(VERIFY_READ,optval,sizeof(ling)); - if(err) - return err; - memcpy_fromfs(&ling,optval,sizeof(ling)); - if(ling.l_onoff==0) - sk->linger=0; - else - { - sk->lingertime=ling.l_linger; - sk->linger=1; - } - return 0; - case SO_RCVBUF: - if(val>32767) - val=32767; - if(val<256) - val=256; - sk->rcvbuf=val; - return(0); - case SO_REUSEADDR: - if (val) sk->reuse = 1; - else sk->reuse = 0; - return(0); - case SO_KEEPALIVE: - if (val) sk->keepopen = 1; - else sk->keepopen = 0; - return(0); +static int inet_getsockopt(struct socket *sock, int level, int optname, + char *optval, int *optlen) +{ + struct sock *sk = sock->data; + if (level == SOL_SOCKET) + return sock_getsockopt(sk,level,optname,optval,optlen); + if(sk->prot->getsockopt==NULL) + return(-EOPNOTSUPP); + else + return sk->prot->getsockopt(sk,level,optname,optval,optlen); +} - case SO_OOBINLINE: - if (val) sk->urginline = 1; - else sk->urginline = 0; - return(0); +/* + * This is meant for all protocols to use and covers goings on + * at the socket level. Everything here is generic. + */ - case SO_NO_CHECK: - if (val) sk->no_check = 1; - else sk->no_check = 0; - return(0); +int sock_setsockopt(struct sock *sk, int level, int optname, + char *optval, int optlen) +{ + int val; + int err; + struct linger ling; - case SO_PRIORITY: - if (val >= 0 && val < DEV_NUMBUFFS) { - sk->priority = val; - } else { - return(-EINVAL); - } - return(0); + if (optval == NULL) + return(-EINVAL); - default: - return(-ENOPROTOOPT); - } + err=verify_area(VERIFY_READ, optval, sizeof(int)); + if(err) + return err; + + val = get_fs_long((unsigned long *)optval); + switch(optname) + { + case SO_TYPE: + case SO_ERROR: + return(-ENOPROTOOPT); + + case SO_DEBUG: + sk->debug=val?1:0; + case SO_DONTROUTE: /* Still to be implemented */ + return(0); + case SO_BROADCAST: + sk->broadcast=val?1:0; + return 0; + case SO_SNDBUF: + if(val>32767) + val=32767; + if(val<256) + val=256; + sk->sndbuf=val; + return 0; + case SO_LINGER: + err=verify_area(VERIFY_READ,optval,sizeof(ling)); + if(err) + return err; + memcpy_fromfs(&ling,optval,sizeof(ling)); + if(ling.l_onoff==0) + sk->linger=0; + else + { + sk->lingertime=ling.l_linger; + sk->linger=1; + } + return 0; + case SO_RCVBUF: + if(val>32767) + val=32767; + if(val<256) + val=256; + sk->rcvbuf=val; + return(0); + + case SO_REUSEADDR: + if (val) + sk->reuse = 1; + else + sk->reuse = 0; + return(0); + + case SO_KEEPALIVE: + if (val) + sk->keepopen = 1; + else + sk->keepopen = 0; + return(0); + + case SO_OOBINLINE: + if (val) + sk->urginline = 1; + else + sk->urginline = 0; + return(0); + + case SO_NO_CHECK: + if (val) + sk->no_check = 1; + else + sk->no_check = 0; + return(0); + + case SO_PRIORITY: + if (val >= 0 && val < DEV_NUMBUFFS) + { + sk->priority = val; + } + else + { + return(-EINVAL); + } + return(0); + + default: + return(-ENOPROTOOPT); + } } -static int -inet_getsockopt(struct socket *sock, int level, int optname, - char *optval, int *optlen) -{ - struct sock *sk; - int val; - int err; - struct linger ling; - - /* This should really pass things on to the other levels. */ - if (level != SOL_SOCKET) return(-EOPNOTSUPP); +int sock_getsockopt(struct sock *sk, int level, int optname, + char *optval, int *optlen) +{ + int val; + int err; + struct linger ling; - sk = (struct sock *) sock->data; - if (sk == NULL) { - printk("Warning: sock->data = NULL: %d\n" ,__LINE__); - return(0); - } - - switch(optname) { - case SO_DEBUG: - val = sk->debug; - break; - - case SO_DONTROUTE: /* One last option to implement */ - val = 0; - break; + switch(optname) + { + case SO_DEBUG: + val = sk->debug; + break; - case SO_BROADCAST: - val= sk->broadcast; - break; + case SO_DONTROUTE: /* One last option to implement */ + val = 0; + break; - case SO_LINGER: + case SO_BROADCAST: + val= sk->broadcast; + break; - err=verify_area(VERIFY_WRITE,optval,sizeof(ling)); - if(err) - return err; - err=verify_area(VERIFY_WRITE,optlen,sizeof(int)); - if(err) - return err; - put_fs_long(sizeof(ling),(unsigned long *)optlen); - ling.l_onoff=sk->linger; - ling.l_linger=sk->lingertime; - memcpy_tofs(optval,&ling,sizeof(ling)); - return 0; + case SO_LINGER: + err=verify_area(VERIFY_WRITE,optval,sizeof(ling)); + if(err) + return err; + err=verify_area(VERIFY_WRITE,optlen,sizeof(int)); + if(err) + return err; + put_fs_long(sizeof(ling),(unsigned long *)optlen); + ling.l_onoff=sk->linger; + ling.l_linger=sk->lingertime; + memcpy_tofs(optval,&ling,sizeof(ling)); + return 0; - case SO_SNDBUF: - val=sk->sndbuf; - break; + case SO_SNDBUF: + val=sk->sndbuf; + break; - case SO_RCVBUF: - val =sk->rcvbuf; - break; - - case SO_REUSEADDR: - val = sk->reuse; - break; + case SO_RCVBUF: + val =sk->rcvbuf; + break; - case SO_KEEPALIVE: - val = sk->keepopen; - break; + case SO_REUSEADDR: + val = sk->reuse; + break; - case SO_TYPE: - if (sk->prot == &tcp_prot) val = SOCK_STREAM; - else val = SOCK_DGRAM; - break; + case SO_KEEPALIVE: + val = sk->keepopen; + break; - case SO_ERROR: - val = sk->err; - sk->err = 0; - break; + case SO_TYPE: + if (sk->prot == &tcp_prot) + val = SOCK_STREAM; + else + val = SOCK_DGRAM; + break; - case SO_OOBINLINE: - val = sk->urginline; - break; + case SO_ERROR: + val = sk->err; + sk->err = 0; + break; - case SO_NO_CHECK: - val = sk->no_check; - break; + case SO_OOBINLINE: + val = sk->urginline; + break; + + case SO_NO_CHECK: + val = sk->no_check; + break; - case SO_PRIORITY: - val = sk->priority; - break; + case SO_PRIORITY: + val = sk->priority; + break; - default: - return(-ENOPROTOOPT); - } - err=verify_area(VERIFY_WRITE, optlen, sizeof(int)); - if(err) - return err; - put_fs_long(sizeof(int),(unsigned long *) optlen); + default: + return(-ENOPROTOOPT); + } + err=verify_area(VERIFY_WRITE, optlen, sizeof(int)); + if(err) + return err; + put_fs_long(sizeof(int),(unsigned long *) optlen); - err=verify_area(VERIFY_WRITE, optval, sizeof(int)); - if(err) - return err; - put_fs_long(val,(unsigned long *)optval); + err=verify_area(VERIFY_WRITE, optval, sizeof(int)); + if(err) + return err; + put_fs_long(val,(unsigned long *)optval); - return(0); + return(0); } + + static int inet_listen(struct socket *sock, int backlog) { @@ -698,6 +732,23 @@ inet_listen(struct socket *sock, int backlog) return(0); } +/* + * Default callbacks for user INET sockets. These just wake up + * the user owning the socket. + */ + +static void def_callback1(struct sock *sk) +{ + if(!sk->dead) + wake_up(sk->sleep); +} + +static void def_callback2(struct sock *sk,int len) +{ + if(!sk->dead) + wake_up(sk->sleep); +} + static int inet_create(struct socket *sock, int protocol) @@ -773,6 +824,7 @@ inet_create(struct socket *sock, int protocol) return(-ESOCKTNOSUPPORT); } sk->socket = sock; + sk->type = sock->type; sk->protocol = protocol; sk->wmem_alloc = 0; sk->rmem_alloc = 0; @@ -853,6 +905,14 @@ inet_create(struct socket *sock, int protocol) sk->dummy_th.urg = 0; sk->dummy_th.dest = 0; + sk->ip_tos=0; + sk->ip_ttl=64; + + sk->state_change = def_callback1; + sk->data_ready = def_callback2; + sk->write_space = def_callback1; + sk->error_report = def_callback1; + if (sk->num) { /* * It assumes that any protocol which allows @@ -893,7 +953,7 @@ inet_release(struct socket *sock, struct socket *peer) if (sk == NULL) return(0); DPRINTF((DBG_INET, "inet_release(sock = %X, peer = %X)\n", sock, peer)); - wake_up(sk->sleep); + sk->state_change(sk); /* Start closing the connection. This may take a while. */ /* @@ -1579,7 +1639,7 @@ sock_wfree(struct sock *sk, void *mem, unsigned long size) sk->wmem_alloc -= size; /* In case it might be waiting for more memory. */ - if (!sk->dead) wake_up(sk->sleep); + if (!sk->dead) sk->write_space(sk); if (sk->destroy && sk->wmem_alloc == 0 && sk->rmem_alloc == 0) { DPRINTF((DBG_INET, "recovered lost memory, sock = %X\n", sk)); diff --git a/net/inet/sock.h b/net/inet/sock.h index cf6e2f3..cf8397a 100644 --- a/net/inet/sock.h +++ b/net/inet/sock.h @@ -136,7 +136,11 @@ struct sock { char ax25_retxqi; char ax25_rrtimer; char ax25_timer; + ax25_digi *ax25_digipeat; #endif +/* IP 'private area' or will be eventually */ + int ip_ttl; /* TTL setting */ + int ip_tos; /* TOS */ struct tcphdr dummy_th; /* This part is used for the timeout functions (timer.c). */ @@ -145,6 +149,13 @@ struct sock { /* identd */ struct socket *socket; + + /* Callbacks */ + void (*state_change)(struct sock *sk); + void (*data_ready)(struct sock *sk,int bytes); + void (*write_space)(struct sock *sk); + void (*error_report)(struct sock *sk); + }; struct proto { @@ -177,7 +188,7 @@ struct proto { unsigned long saddr, unsigned long daddr, struct device **dev, int type, - struct options *opt, int len); + struct options *opt, int len, int tos, int ttl); int (*connect)(struct sock *sk, struct sockaddr_in *usin, int addr_len); struct sock *(*accept) (struct sock *sk, int flags); @@ -197,6 +208,10 @@ struct proto { unsigned long arg); int (*init)(struct sock *sk); void (*shutdown)(struct sock *sk, int how); + int (*setsockopt)(struct sock *sk, int level, int optname, + char *optval, int optlen); + int (*getsockopt)(struct sock *sk, int level, int optname, + char *optval, int *option); unsigned short max_header; unsigned long retransmits; struct sock *sock_array[SOCK_ARRAY_SIZE]; @@ -238,6 +253,8 @@ extern void sock_rfree(struct sock *sk, void *mem, extern unsigned long sock_rspace(struct sock *sk); extern unsigned long sock_wspace(struct sock *sk); +extern int sock_setsockopt(struct sock *sk,int level,int op,char *optval,int optlen); +extern int sock_getsockopt(struct sock *sk,int level,int op,char *optval,int *optlen); /* declarations from timer.c */ extern struct sock *timer_base; diff --git a/net/inet/tcp.c b/net/inet/tcp.c index 83dfb41..9160821 100644 --- a/net/inet/tcp.c +++ b/net/inet/tcp.c @@ -47,6 +47,13 @@ * Michael O'Reilly : ack < copied bug fix. * Johannes Stille : Misc tcp fixes (not all in yet). * Alan Cox : FIN with no memory -> CRASH + * Alan Cox : Added socket option proto entries. Also added awareness of them to accept. + * Alan Cox : Added TCP options (SOL_TCP) + * Alan Cox : Switched wakeup calls to callbacks, so the kernel can layer network sockets. + * Alan Cox : Use ip_tos/ip_ttl settings. + * Alan Cox : Handle FIN (more) properly (we hope). + * Alan Cox : RST frames sent on unsynchronised state ack error/ + * Alan Cox : Put in missing check for SYN bit. * * * To Fix: @@ -155,7 +162,7 @@ static void tcp_time_wait(struct sock *sk) sk->state = TCP_TIME_WAIT; sk->shutdown = SHUTDOWN_MASK; if (!sk->dead) - wake_up(sk->sleep); + sk->state_change(sk); reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); } @@ -213,7 +220,7 @@ tcp_err(int err, unsigned char *header, unsigned long daddr, if(err<0) { sk->err = -err; - wake_up(sk->sleep); + sk->error_report(sk); return; } @@ -237,7 +244,7 @@ tcp_err(int err, unsigned char *header, unsigned long daddr, if (icmp_err_convert[err & 0xff].fatal) { if (sk->state == TCP_SYN_SENT) { sk->state = TCP_CLOSE; - wake_up(sk->sleep); /* Wake people up to see the error (see connect in sock.c) */ + sk->error_report(sk); /* Wake people up to see the error (see connect in sock.c) */ } } return; @@ -558,7 +565,20 @@ tcp_send_partial(struct sock *sk) if (sk == NULL || sk->send_tmp == NULL) return; skb = sk->send_tmp; - + + /* If we have queued a header size packet.. */ + if(skb->len-(unsigned long)skb->h.th + (unsigned long)(skb+1)==sizeof(struct tcphdr)) + { + /* If its got a syn or fin its notionally included in the size..*/ + if(!skb->h.th->syn && !skb->h.th->fin) + { + printk("tcp_send_partial: attempt to queue a bogon.\n"); + kfree_skb(skb,FREE_WRITE); + sk->send_tmp=NULL; + return; + } + } + /* We need to complete and send the packet. */ tcp_send_check(skb->h.th, sk->saddr, sk->daddr, skb->len-(unsigned long)skb->h.th + @@ -622,7 +642,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_ack: malloc failed\n"); /* Put in the IP header and routing stuff. */ tmp = sk->prot->build_header(buff, sk->saddr, daddr, &dev, - IPPROTO_TCP, sk->opt, MAX_ACK_SIZE); + IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl); if (tmp < 0) { buff->free=1; sk->prot->wfree(sk, buff->mem_addr, buff->mem_len); @@ -887,7 +907,7 @@ tcp_write(struct sock *sk, unsigned char *from, * Perhaps some hints here would be good. */ tmp = prot->build_header(skb, sk->saddr, sk->daddr, &dev, - IPPROTO_TCP, sk->opt, skb->mem_len); + IPPROTO_TCP, sk->opt, skb->mem_len,sk->ip_tos,sk->ip_ttl); if (tmp < 0 ) { prot->wfree(sk, skb->mem_addr, skb->mem_len); release_sock(sk); @@ -1008,7 +1028,7 @@ tcp_read_wakeup(struct sock *sk) /* Put in the IP header and routing stuff. */ tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev, - IPPROTO_TCP, sk->opt, MAX_ACK_SIZE); + IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl); if (tmp < 0) { buff->free=1; sk->prot->wfree(sk, buff->mem_addr, buff->mem_len); @@ -1484,7 +1504,7 @@ tcp_shutdown(struct sock *sk, int how) /* Put in the IP header and routing stuff. */ tmp = prot->build_header(buff,sk->saddr, sk->daddr, &dev, IPPROTO_TCP, sk->opt, - sizeof(struct tcphdr)); + sizeof(struct tcphdr),sk->ip_tos,sk->ip_ttl); if (tmp < 0) { buff->free=1; prot->wfree(sk,buff->mem_addr, buff->mem_len); @@ -1569,7 +1589,7 @@ tcp_recvfrom(struct sock *sk, unsigned char *to, /* This routine will send an RST to the other tcp. */ static void tcp_reset(unsigned long saddr, unsigned long daddr, struct tcphdr *th, - struct proto *prot, struct options *opt, struct device *dev) + struct proto *prot, struct options *opt, struct device *dev, int tos, int ttl) { struct sk_buff *buff; struct tcphdr *t1; @@ -1594,7 +1614,7 @@ tcp_reset(unsigned long saddr, unsigned long daddr, struct tcphdr *th, /* Put in the IP header and routing stuff. */ tmp = prot->build_header(buff, saddr, daddr, &dev, IPPROTO_TCP, opt, - sizeof(struct tcphdr)); + sizeof(struct tcphdr),tos,ttl); if (tmp < 0) { buff->free = 1; prot->wfree(NULL, buff->mem_addr, buff->mem_len); @@ -1714,10 +1734,10 @@ tcp_conn_request(struct sock *sk, struct sk_buff *skb, /* If the socket is dead, don't accept the connection. */ if (!sk->dead) { - wake_up(sk->sleep); + sk->data_ready(sk,0); } else { DPRINTF((DBG_TCP, "tcp_conn_request on dead socket\n")); - tcp_reset(daddr, saddr, th, sk->prot, opt, dev); + tcp_reset(daddr, saddr, th, sk->prot, opt, dev, sk->ip_tos,sk->ip_ttl); kfree_skb(skb, FREE_READ); return; } @@ -1802,21 +1822,12 @@ tcp_conn_request(struct sock *sk, struct sk_buff *skb, newsk->acked_seq = skb->h.th->seq + 1; newsk->copied_seq = skb->h.th->seq; -#ifdef OLDWAY - if (skb->h.th->doff == 5) { - newsk->mtu = dev->mtu - HEADER_SIZE; - } else { - ptr =(unsigned char *)(skb->h.th + 1); - if (ptr[0] != 2 || ptr[1] != 4) { - newsk->mtu = dev->mtu - HEADER_SIZE; - } else { - newsk->mtu = min(ptr[2] * 256 + ptr[3] - HEADER_SIZE, - dev->mtu - HEADER_SIZE); - } - } -#else + /* Grab the ttl and tos values and use them */ + newsk->ip_ttl=sk->ip_ttl; + newsk->ip_tos=skb->ip_hdr->tos; + tcp_options(newsk,skb->h.th); -#endif + buff = (struct sk_buff *) newsk->prot->wmalloc(newsk, MAX_SYN_SIZE, 1, GFP_ATOMIC); if (buff == NULL) { sk->err = -ENOMEM; @@ -1835,7 +1846,7 @@ tcp_conn_request(struct sock *sk, struct sk_buff *skb, /* Put in the IP header and routing stuff. */ tmp = sk->prot->build_header(buff, newsk->saddr, newsk->daddr, &dev, - IPPROTO_TCP, NULL, MAX_SYN_SIZE); + IPPROTO_TCP, NULL, MAX_SYN_SIZE,sk->ip_tos,sk->ip_ttl); /* Something went wrong. */ if (tmp < 0) { @@ -1912,25 +1923,13 @@ tcp_close(struct sock *sk, int timeout) sk->keepopen = 1; sk->shutdown = SHUTDOWN_MASK; - if (!sk->dead) wake_up(sk->sleep); + if (!sk->dead) + sk->state_change(sk); /* We need to flush the recv. buffs. */ if (skb_peek(&sk->rqueue) != NULL) { struct sk_buff *skb; -#ifdef OLD - struct sk_buff *skb2; - skb = skb_peek(&sk->rqueue); - do { - skb2 =(struct sk_buff *)skb->next; - /* if there is some real unread data, send a reset. */ - if (skb->len > 0 && - after(skb->h.th->seq + skb->len + 1, sk->copied_seq)) - need_reset = 1; - kfree_skb(skb, FREE_WRITE); - skb = skb2; - } while(skb != sk->rqueue); -#else if(sk->debug) printk("Clean rcv queue\n"); while((skb=skb_dequeue(&sk->rqueue))!=NULL) @@ -1941,7 +1940,6 @@ tcp_close(struct sock *sk, int timeout) } if(sk->debug) printk("Cleaned.\n"); -#endif } sk->rqueue = NULL; @@ -1999,7 +1997,7 @@ tcp_close(struct sock *sk, int timeout) /* Put in the IP header and routing stuff. */ tmp = prot->build_header(buff,sk->saddr, sk->daddr, &dev, IPPROTO_TCP, sk->opt, - sizeof(struct tcphdr)); + sizeof(struct tcphdr),sk->ip_tos,sk->ip_ttl); if (tmp < 0) { kfree_skb(buff,FREE_WRITE); DPRINTF((DBG_TCP, "Unable to build header for fin.\n")); @@ -2088,7 +2086,7 @@ tcp_write_xmit(struct sock *sk) if (before(skb->h.seq, sk->rcv_ack_seq +1)) { sk->retransmits = 0; kfree_skb(skb, FREE_WRITE); - if (!sk->dead) wake_up(sk->sleep); + if (!sk->dead) sk->write_space(sk); } else { sk->prot->queue_xmit(sk, skb->dev, skb, skb->free); } @@ -2185,30 +2183,7 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len) if (sk->packets_out > 0) sk->packets_out--; /* We may need to remove this from the dev send list. */ if (skb->next != NULL) { -#ifdef OLD_WAY - int i; - - if (skb->next != skb) { - skb->next->prev = skb->prev; - skb->prev->next = skb->next; - } - - for(i = 0; i < DEV_NUMBUFFS; i++) { - if (skb->dev->buffs[i] == skb) { - if (skb->next == skb) - skb->dev->buffs[i] = NULL; - else - skb->dev->buffs[i] = skb->next; - break; - } - } - if (arp_q == skb) { - if (skb->next == skb) arp_q = NULL; - else arp_q = skb->next; - } -#else skb_unlink(skb); -#endif } /* Now add it to the write_queue. */ skb->magic = TCP_WRITE_QUEUE_MAGIC; @@ -2272,7 +2247,7 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len) sk->send_head, sk->send_head->h.seq, ack)); /* Wake up the process, it can probably write more. */ - if (!sk->dead) wake_up(sk->sleep); + if (!sk->dead) sk->write_space(sk); oskb = sk->send_head; @@ -2310,7 +2285,7 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len) sti(); oskb->magic = 0; kfree_skb(oskb, FREE_WRITE); /* write. */ - if (!sk->dead) wake_up(sk->sleep); + if (!sk->dead) sk->write_space(sk); } else { break; } @@ -2330,7 +2305,7 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len) if (sk->send_head == NULL && sk->ack_backlog == 0 && sk->state != TCP_TIME_WAIT && !sk->keepopen) { DPRINTF((DBG_TCP, "Nothing to do, going to sleep.\n")); - if (!sk->dead) wake_up(sk->sleep); + if (!sk->dead) sk->write_space(sk); if (sk->keepopen) reset_timer(sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN); @@ -2355,7 +2330,8 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len) /* See if we are done. */ if (sk->state == TCP_TIME_WAIT) { - if (!sk->dead) wake_up(sk->sleep); + if (!sk->dead) + sk->state_change(sk); if (sk->rcv_ack_seq == sk->send_seq && sk->acked_seq == sk->fin_seq) { flag |= 1; sk->state = TCP_CLOSE; @@ -2364,7 +2340,7 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len) } if (sk->state == TCP_LAST_ACK || sk->state == TCP_FIN_WAIT2) { - if (!sk->dead) wake_up(sk->sleep); + if (!sk->dead) sk->state_change(sk); if (sk->rcv_ack_seq == sk->send_seq) { flag |= 1; if (sk->acked_seq != sk->fin_seq) { @@ -2421,13 +2397,13 @@ tcp_data(struct sk_buff *skb, struct sock *sk, if (sk->shutdown & RCV_SHUTDOWN) { sk->acked_seq = th->seq + skb->len + th->syn + th->fin; tcp_reset(sk->saddr, sk->daddr, skb->h.th, - sk->prot, NULL, skb->dev); + sk->prot, NULL, skb->dev, sk->ip_tos, sk->ip_ttl); sk->state = TCP_CLOSE; sk->err = EPIPE; sk->shutdown = SHUTDOWN_MASK; DPRINTF((DBG_TCP, "tcp_data: closing socket - %X\n", sk)); kfree_skb(skb, FREE_READ); - if (!sk->dead) wake_up(sk->sleep); + if (!sk->dead) sk->state_change(sk); return(0); } @@ -2523,7 +2499,7 @@ tcp_data(struct sk_buff *skb, struct sock *sk, /* When we ack the fin, we turn on the RCV_SHUTDOWN flag. */ if (skb->h.th->fin) { - if (!sk->dead) wake_up(sk->sleep); + if (!sk->dead) sk->state_change(sk); sk->shutdown |= RCV_SHUTDOWN; } @@ -2541,7 +2517,7 @@ tcp_data(struct sk_buff *skb, struct sock *sk, */ if (skb2->h.th->fin) { sk->shutdown |= RCV_SHUTDOWN; - if (!sk->dead) wake_up(sk->sleep); + if (!sk->dead) sk->state_change(sk); } /* Force an immediate ack. */ @@ -2614,7 +2590,7 @@ tcp_data(struct sk_buff *skb, struct sock *sk, if (!sk->dead) { if(sk->debug) printk("Data wakeup.\n"); - wake_up(sk->sleep); + sk->data_ready(sk,0); } else { DPRINTF((DBG_TCP, "data received on dead socket.\n")); } @@ -2626,7 +2602,7 @@ tcp_data(struct sk_buff *skb, struct sock *sk, /* tcp_send_ack(sk->send_seq, sk->acked_seq, sk, th, saddr); */ sk->shutdown = SHUTDOWN_MASK; sk->state = TCP_LAST_ACK; - if (!sk->dead) wake_up(sk->sleep); + if (!sk->dead) sk->state_change(sk); } return(0); @@ -2639,7 +2615,8 @@ tcp_urg(struct sock *sk, struct tcphdr *th, unsigned long saddr) extern int kill_pg(int pg, int sig, int priv); extern int kill_proc(int pid, int sig, int priv); - if (!sk->dead) wake_up(sk->sleep); + if (!sk->dead) + sk->data_ready(sk,0); if (sk->urginline) { th->urg = 0; @@ -2662,7 +2639,7 @@ tcp_urg(struct sock *sk, struct tcphdr *th, unsigned long saddr) } -/* This deals with incoming fins. */ +/* This deals with incoming fins. 'Linus at 9 O'clock' 8-) */ static int tcp_fin(struct sock *sk, struct tcphdr *th, unsigned long saddr, struct device *dev) @@ -2671,7 +2648,7 @@ tcp_fin(struct sock *sk, struct tcphdr *th, sk, th, saddr, dev)); if (!sk->dead) { - wake_up(sk->sleep); + sk->state_change(sk); } switch(sk->state) { @@ -2813,7 +2790,7 @@ tcp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len) /* Put in the IP header and routing stuff. */ /* We need to build the routing stuff fromt the things saved in skb. */ tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev, - IPPROTO_TCP, NULL, MAX_SYN_SIZE); + IPPROTO_TCP, NULL, MAX_SYN_SIZE,sk->ip_tos,sk->ip_ttl); if (tmp < 0) { sk->prot->wfree(sk, buff->mem_addr, buff->mem_len); release_sock(sk); @@ -2862,7 +2839,7 @@ tcp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len) /* This functions checks to see if the tcp header is actually acceptible. */ static int tcp_sequence(struct sock *sk, struct tcphdr *th, short len, - struct options *opt, unsigned long saddr) + struct options *opt, unsigned long saddr, struct device *dev) { /* * This isn't quite right. sk->acked_seq could be more recent @@ -2883,6 +2860,19 @@ tcp_sequence(struct sock *sk, struct tcphdr *th, short len, DPRINTF((DBG_TCP, "tcp_sequence: rejecting packet.\n")); /* + * Send a reset if we get something not ours and we are + * unsynchronized. Note: We don't do anything to our end. We + * are just killing the bogus remote connection then we will + * connect again and it will work (with luck). + */ + + if(sk->state==TCP_SYN_SENT||sk->state==TCP_SYN_RECV) + { + tcp_reset(sk->saddr,sk->daddr,th,sk->prot,NULL,dev, sk->ip_tos,sk->ip_ttl); + return(1); + } + + /* * If it's too far ahead, send an ack to let the * other end know what we expect. */ @@ -2969,7 +2959,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: bad checksum\n"); { th->seq = ntohl(th->seq); /* So reset is always called with th->seq in host order */ - tcp_reset(daddr, saddr, th, &tcp_prot, opt,dev); + tcp_reset(daddr, saddr, th, &tcp_prot, opt,dev,skb->ip_hdr->tos,255); } skb->sk = NULL; kfree_skb(skb, FREE_READ); @@ -3042,7 +3032,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: bad checksum\n"); sk->state = TCP_CLOSE; sk->shutdown = SHUTDOWN_MASK; if (!sk->dead) { - wake_up(sk->sleep); + sk->state_change(sk); } kfree_skb(skb, FREE_READ); release_sock(sk); @@ -3054,7 +3044,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: bad checksum\n"); case TCP_FIN_WAIT1: case TCP_FIN_WAIT2: case TCP_TIME_WAIT: - if (!tcp_sequence(sk, th, len, opt, saddr)) { + if (!tcp_sequence(sk, th, len, opt, saddr,dev)) { if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: not in seq\n"); if(!th->rst) tcp_send_ack(sk->send_seq, sk->acked_seq, @@ -3077,33 +3067,32 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: not in seq\n"); * A reset with a fin just means that * the data was not all read. */ -/* The comment above appears completely bogus --clh */ -/* if (!th->fin) { */ - sk->state = TCP_CLOSE; - sk->shutdown = SHUTDOWN_MASK; - if (!sk->dead) { - wake_up(sk->sleep); - } - kfree_skb(skb, FREE_READ); - release_sock(sk); - return(0); -/* } */ + sk->state = TCP_CLOSE; + sk->shutdown = SHUTDOWN_MASK; + if (!sk->dead) { + sk->state_change(sk); + } + kfree_skb(skb, FREE_READ); + release_sock(sk); + return(0); } + if ( #if 0 - if (opt && (opt->security != 0 || - opt->compartment != 0 || th->syn)) { + if ((opt && (opt->security != 0 || + opt->compartment != 0)) || +#endif + th->syn) { sk->err = ECONNRESET; sk->state = TCP_CLOSE; sk->shutdown = SHUTDOWN_MASK; - tcp_reset(daddr, saddr, th, sk->prot, opt,dev); + tcp_reset(daddr, saddr, th, sk->prot, opt,dev, sk->ip_tos,sk->ip_ttl); if (!sk->dead) { - wake_up(sk->sleep); + sk->state_change(sk); } kfree_skb(skb, FREE_READ); release_sock(sk); return(0); } -#endif if (th->ack) { if (!tcp_ack(sk, th, saddr, len)) { kfree_skb(skb, FREE_READ); @@ -3119,13 +3108,14 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: not in seq\n"); } } - if (th->fin && tcp_fin(sk, th, saddr, dev)) { + if (tcp_data(skb, sk, saddr, len)) { kfree_skb(skb, FREE_READ); release_sock(sk); return(0); } - if (tcp_data(skb, sk, saddr, len)) { + /* Moved: you must do data then fin bit */ + if (th->fin && tcp_fin(sk, th, saddr, dev)) { kfree_skb(skb, FREE_READ); release_sock(sk); return(0); @@ -3145,7 +3135,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: not in seq\n"); if (!th->rst) { if (!th->ack) th->ack_seq = 0; - tcp_reset(daddr, saddr, th, sk->prot, opt,dev); + tcp_reset(daddr, saddr, th, sk->prot, opt,dev,sk->ip_tos,sk->ip_ttl); } kfree_skb(skb, FREE_READ); release_sock(sk); @@ -3158,7 +3148,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: not in seq\n"); return(0); } if (th->ack) { - tcp_reset(daddr, saddr, th, sk->prot, opt,dev); + tcp_reset(daddr, saddr, th, sk->prot, opt,dev,sk->ip_tos,sk->ip_ttl); kfree_skb(skb, FREE_READ); release_sock(sk); return(0); @@ -3189,7 +3179,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: not in seq\n"); return(0); default: - if (!tcp_sequence(sk, th, len, opt, saddr)) { + if (!tcp_sequence(sk, th, len, opt, saddr,dev)) { kfree_skb(skb, FREE_READ); release_sock(sk); return(0); @@ -3202,7 +3192,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: not in seq\n"); sk->shutdown = SHUTDOWN_MASK; sk->zapped = 1; if (!sk->dead) { - wake_up(sk->sleep); + sk->state_change(sk); } kfree_skb(skb, FREE_READ); release_sock(sk); @@ -3236,7 +3226,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: not in seq\n"); case TCP_SYN_SENT: if (!tcp_ack(sk, th, saddr, len)) { tcp_reset(daddr, saddr, th, - sk->prot, opt,dev); + sk->prot, opt,dev,sk->ip_tos,sk->ip_ttl); kfree_skb(skb, FREE_READ); release_sock(sk); return(0); @@ -3261,7 +3251,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: not in seq\n"); case TCP_SYN_RECV: if (!tcp_ack(sk, th, saddr, len)) { tcp_reset(daddr, saddr, th, - sk->prot, opt, dev); + sk->prot, opt, dev,sk->ip_tos,sk->ip_ttl); kfree_skb(skb, FREE_READ); release_sock(sk); return(0); @@ -3277,7 +3267,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: not in seq\n"); sk->dummy_th.dest = th->source; sk->copied_seq = sk->acked_seq-1; if (!sk->dead) { - wake_up(sk->sleep); + sk->state_change(sk); } /* @@ -3354,7 +3344,7 @@ tcp_write_wakeup(struct sock *sk) /* Put in the IP header and routing stuff. */ tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev, - IPPROTO_TCP, sk->opt, MAX_ACK_SIZE); + IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl); if (tmp < 0) { sk->prot->wfree(sk, buff->mem_addr, buff->mem_len); return; @@ -3389,6 +3379,72 @@ tcp_write_wakeup(struct sock *sk) sk->prot->queue_xmit(sk, dev, buff, 1); } +/* + * Socket option code for TCP. + */ + +int tcp_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen) +{ + int val,err; + + if(level!=SOL_TCP) + return ip_setsockopt(sk,level,optname,optval,optlen); + + if (optval == NULL) + return(-EINVAL); + + err=verify_area(VERIFY_READ, optval, sizeof(int)); + if(err) + return err; + + val = get_fs_long((unsigned long *)optval); + + switch(optname) + { + case TCP_MSS: + if(val<200||val>2048) + return -EINVAL; + sk->mss=val; + return 0; + case TCP_NODELAY: + /* Ready for Johannes delayed ACK code */ + return 0; + default: + return(-ENOPROTOOPT); + } +} + +int tcp_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen) +{ + int val,err; + + if(level!=SOL_TCP) + return ip_getsockopt(sk,level,optname,optval,optlen); + + switch(optname) + { + case TCP_MSS: + val=sk->mss; + break; + case TCP_NODELAY: + val=1; /* Until Johannes stuff is in */ + break; + default: + return(-ENOPROTOOPT); + } + err=verify_area(VERIFY_WRITE, optlen, sizeof(int)); + if(err) + return err; + put_fs_long(sizeof(int),(unsigned long *) optlen); + + err=verify_area(VERIFY_WRITE, optval, sizeof(int)); + if(err) + return err; + put_fs_long(val,(unsigned long *)optval); + + return(0); +} + struct proto tcp_prot = { sock_wmalloc, @@ -3414,6 +3470,8 @@ struct proto tcp_prot = { tcp_ioctl, NULL, tcp_shutdown, + tcp_setsockopt, + tcp_getsockopt, 128, 0, {NULL,}, diff --git a/net/inet/timer.c b/net/inet/timer.c index f6540a1..b5d0d13 100644 --- a/net/inet/timer.c +++ b/net/inet/timer.c @@ -141,7 +141,7 @@ net_timer (unsigned long data) sk->state = TCP_CLOSE; delete_timer (sk); /* Kill the ARP entry in case the hardware has changed. */ - arp_destroy (sk->daddr); + arp_destroy_maybe (sk->daddr); if (!sk->dead) wake_up (sk->sleep); sk->shutdown = SHUTDOWN_MASK; @@ -167,7 +167,7 @@ net_timer (unsigned long data) if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7)) || (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) { DPRINTF ((DBG_TMR, "timer.c TIME_WRITE time-out 1\n")); - arp_destroy (sk->daddr); + arp_destroy_maybe (sk->daddr); ip_route_check (sk->daddr); } if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) { @@ -198,14 +198,14 @@ net_timer (unsigned long data) if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7)) || (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) { DPRINTF ((DBG_TMR, "timer.c TIME_KEEPOPEN time-out 1\n")); - arp_destroy (sk->daddr); + arp_destroy_maybe (sk->daddr); ip_route_check (sk->daddr); release_sock (sk); break; } if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) { DPRINTF ((DBG_TMR, "timer.c TIME_KEEPOPEN time-out 2\n")); - arp_destroy (sk->daddr); + arp_destroy_maybe (sk->daddr); sk->err = ETIMEDOUT; if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2) { sk->state = TCP_TIME_WAIT; diff --git a/net/inet/udp.c b/net/inet/udp.c index 78066c3..d6e8328 100644 --- a/net/inet/udp.c +++ b/net/inet/udp.c @@ -30,10 +30,11 @@ * bug no longer crashes it. * Fred Van Kempen : Net2e support for sk->broadcast. * Alan Cox : Uses skb_free_datagram + * Alan Cox : Added get/set sockopt support. + * Alan Cox : Broadcasting without option set returns EACCES. + * Alan Cox : No wakeup calls. Instead we now use the callbacks. + * Alan Cox : Use ip_tos and ip_ttl * - * To Do: - * Verify all the error codes from UDP operations match the - * BSD behaviour, since thats effectively the formal spec. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -114,7 +115,7 @@ sport=%d,dport=%d", err, header, daddr, saddr, protocol, (int)th->source,(int)th if (err < 0) /* As per the calling spec */ { sk->err = -err; - wake_up(sk->sleep); /* User process wakes to see error */ + sk->error_report(sk); /* User process wakes to see error */ return; } @@ -130,7 +131,7 @@ sport=%d,dport=%d", err, header, daddr, saddr, protocol, (int)th->source,(int)th if (icmp_err_convert[err & 0xff].fatal && sk->state == TCP_ESTABLISHED) { sk->err=ECONNREFUSED; } - wake_up(sk->sleep); + sk->error_report(sk); } @@ -249,7 +250,7 @@ udp_send(struct sock *sk, struct sockaddr_in *sin, DPRINTF((DBG_UDP, "UDP: >> IP_Header: %X -> %X dev=%X prot=%X len=%d\n", saddr, sin->sin_addr.s_addr, dev, IPPROTO_UDP, skb->mem_len)); tmp = sk->prot->build_header(skb, saddr, sin->sin_addr.s_addr, - &dev, IPPROTO_UDP, sk->opt, skb->mem_len); + &dev, IPPROTO_UDP, sk->opt, skb->mem_len,sk->ip_tos,sk->ip_ttl); skb->sk=sk; /* So memory is freed correctly */ if (tmp < 0 ) { @@ -335,7 +336,7 @@ udp_sendto(struct sock *sk, unsigned char *from, int len, int noblock, } if(!sk->broadcast && chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST) - return -ENETUNREACH; /* Must turn broadcast on first */ + return -EACCES; /* Must turn broadcast on first */ sk->inuse = 1; /* Send the packet. */ @@ -522,7 +523,7 @@ udp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len) return(-EAFNOSUPPORT); if(!sk->broadcast && chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST) - return -ENETUNREACH; /* Must turn broadcast on first */ + return -EACCES; /* Must turn broadcast on first */ sk->daddr = sin.sin_addr.s_addr; sk->dummy_th.dest = sin.sin_port; @@ -604,8 +605,9 @@ udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, skb->len = len - sizeof(*uh); - if (!sk->dead) wake_up(sk->sleep); - + if (!sk->dead) + sk->data_ready(sk,skb->len); + release_sock(sk); return(0); } @@ -635,6 +637,8 @@ struct proto udp_prot = { udp_ioctl, NULL, NULL, + ip_setsockopt, + ip_getsockopt, 128, 0, {NULL,}, diff --git a/net/unix/sock.c b/net/unix/sock.c index f4de8a8..dff2bbe 100644 --- a/net/unix/sock.c +++ b/net/unix/sock.c @@ -12,11 +12,12 @@ * * Fixes: * Alan Cox : Verify Area + * NET2E Team : Page fault locks * - * BUGS - * Page faults on read while another process reads could lose data. - * Page faults on write happen to interleave data (probably not allowed) - * with any other simultaneous writers on the socket but dont cause harm. + * To Do: + * + * Change to the NET2E3 code for Unix domain sockets in general. The + * read/write logic is much better and cleaner. * * * This program is free software; you can redistribute it and/or @@ -141,6 +142,29 @@ sockaddr_un_printk(struct sockaddr_un *sockun, int sockaddr_len) } +/* Support routines doing anti page fault locking + * FvK & Matt Dillon (borrowed From NET2E3) + */ + +/* + * Locking for unix-domain sockets. We don't use the socket structure's + * wait queue because it is allowed to 'go away' outside of our control, + * whereas unix_proto_data structures stick around. + */ +void unix_lock(struct unix_proto_data *upd) +{ + while (upd->lock_flag) + sleep_on(&upd->wait); + upd->lock_flag = 1; +} + + +void unix_unlock(struct unix_proto_data *upd) +{ + upd->lock_flag = 0; + wake_up(&upd->wait); +} + /* don't have to do anything. */ static int unix_proto_listen(struct socket *sock, int backlog) @@ -598,6 +622,8 @@ unix_proto_read(struct socket *sock, char *ubuf, int size, int nonblock) * Copy from the read buffer into the user's buffer, * watching for wraparound. Then we wake up the writer. */ + + unix_lock(upd); do { int part, cando; @@ -612,7 +638,10 @@ unix_proto_read(struct socket *sock, char *ubuf, int size, int nonblock) dprintf(1, "UNIX: read: avail=%d, todo=%d, cando=%d\n", avail, todo, cando); if((er=verify_area(VERIFY_WRITE,ubuf,cando))<0) + { + unix_unlock(upd); return er; + } memcpy_tofs(ubuf, upd->buf + upd->bp_tail, cando); upd->bp_tail =(upd->bp_tail + cando) &(BUF_SIZE-1); ubuf += cando; @@ -620,6 +649,7 @@ unix_proto_read(struct socket *sock, char *ubuf, int size, int nonblock) if (sock->state == SS_CONNECTED) wake_up(sock->conn->wait); avail = UN_BUF_AVAIL(upd); } while(todo && avail); + unix_unlock(upd); return(size - todo); } @@ -666,6 +696,9 @@ unix_proto_write(struct socket *sock, char *ubuf, int size, int nonblock) * Copy from the user's buffer to the write buffer, * watching for wraparound. Then we wake up the reader. */ + + unix_lock(pupd); + do { int part, cando; @@ -681,6 +714,7 @@ unix_proto_write(struct socket *sock, char *ubuf, int size, int nonblock) */ if (sock->state == SS_DISCONNECTING) { send_sig(SIGPIPE, current, 1); + unix_unlock(pupd); return(-EPIPE); } if ((cando = todo) > space) cando = space; @@ -689,7 +723,10 @@ unix_proto_write(struct socket *sock, char *ubuf, int size, int nonblock) space, todo, cando); er=verify_area(VERIFY_READ, ubuf, cando); if(er) + { + unix_unlock(pupd); return er; + } memcpy_fromfs(pupd->buf + pupd->bp_head, ubuf, cando); pupd->bp_head =(pupd->bp_head + cando) &(BUF_SIZE-1); ubuf += cando; @@ -697,6 +734,7 @@ unix_proto_write(struct socket *sock, char *ubuf, int size, int nonblock) if (sock->state == SS_CONNECTED) wake_up(sock->conn->wait); space = UN_BUF_SPACE(pupd); } while(todo && space); + unix_unlock(pupd); return(size - todo); } @@ -842,6 +880,8 @@ unix_ioctl(struct inode *inode, struct file *file, } + + static struct file_operations unix_fops = { NULL, /* LSEEK */ NULL, /* READ */ diff --git a/net/unix/unix.h b/net/unix/unix.h index 5b69801..74b1777 100644 --- a/net/unix/unix.h +++ b/net/unix/unix.h @@ -35,6 +35,8 @@ struct unix_proto_data { int bp_head, bp_tail; struct inode *inode; struct unix_proto_data *peerupd; + struct wait_queue *wait; /* Lock across page faults (FvK) */ + int lock_flag; }; extern struct unix_proto_data unix_datas[NSOCKETS]; |