--- /dev/null
+The Linux Digiboard Driver
+--------------------------
+
+The Digiboard Driver for Linux supports the following boards:
+
+ DigiBoard PC/Xe, PC/Xi, PC/Xeve
+
+Limitations:
+------------
+Currently the Driver does not do autoprobing. You have to configure
+the driver with the correct I/O address in drivers/char/pcxxconfig.h.
+
+The preconfigured I/O address is 0200h and the default memory address 0D0000h.
+Use them and you will not have to worry about configuring anything.
+
+Supporting Tools:
+-----------------
+Some tools and more detailed up to date information can be found at
+ftp://ftp.fuller.edu/Linux/digi
+
+The "ditty" tool described in the Digiboard Manuals for other Unixes
+is also available.
+
+Currently the Linux MAKEDEV command does not support generating the Digiboard
+Devices. Use the following script to generate the devices:
+
+------------------ mkdigidev begin
+#!/bin/sh
+#
+# Script to create Digiboard Devices
+# Christoph Lameter, April 4, 1996
+#
+# Usage:
+# mkdigidev [<number of devices>]
+#
+
+DIGIMAJOR=30
+DIGICUMAJOR=31
+
+BOARDS=$1
+
+if [ "$BOARDS" = "" ]; then
+BOARDS=1
+fi
+
+boardnum=0
+while [ $boardnum -lt $BOARDS ];
+do
+ for c in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15;
+ do
+ name=`expr $boardnum \* 16 + $c`
+ mknod /dev/ttyd$name c $DIGIMAJOR $name
+ mknod /dev/ttyD$name c $DIGICUMAJOR $name
+ done
+ boardnum=`expr $boardnum + 1`
+done
+------------------ mkdigidev end
+
+The ttyd devices behave like the /dev/cua?? devices
+and the ttyD devices are like the /dev/ttyS?? devices.
+
+Sources of Information
+----------------------
+
+Webpage: http://private.fuller.edu/clameter/digi.html
+
+Mailing List: digiboard@list.fuller.edu
+
+(Write e-mail to that address to subscribe. Common ListServ commands work.
+Archive of messages available)
+
+Christoph Lameter (clameter@fuller.edu) 4. April 1996.
VERSION = 1
PATCHLEVEL = 3
-SUBLEVEL = 83
+SUBLEVEL = 84
ARCH = i386
# CONFIG_SCSI_FUTURE_DOMAIN is not set
# CONFIG_SCSI_GENERIC_NCR5380 is not set
CONFIG_SCSI_NCR53C7xx=y
+CONFIG_SCSI_NCR53C7xx_sync=y
+CONFIG_SCSI_NCR53C7xx_FAST=y
# CONFIG_SCSI_IN2000 is not set
# CONFIG_SCSI_PAS16 is not set
# CONFIG_SCSI_QLOGIC is not set
# Character devices
#
CONFIG_SERIAL=y
+# CONFIG_DIGI is not set
# CONFIG_CYCLADES is not set
# CONFIG_STALDRV is not set
# CONFIG_PRINTER is not set
#if PCI_MODIFY
+#if 0
static unsigned int io_base = 64*KB; /* <64KB are (E)ISA ports */
+#else
+static unsigned int io_base = 0xb000;
+#endif
#if defined(CONFIG_ALPHA_XL)
/*
if (bus->self) {
struct pci_dev *bridge = bus->self;
/*
- * Set up the top and bottom of the I/O memory segment
+ * Set up the top and bottom of the PCI I/O segment
* for this bus.
*/
pcibios_read_config_dword(bridge->bus->number, bridge->devfn,
0x1c, &l);
- l = l | (bio >> 8) | ((tio - 1) & 0xf000);
+ l = (l & 0xffff0000) | (bio >> 8) | ((tio - 1) & 0xf000);
pcibios_write_config_dword(bridge->bus->number, bridge->devfn,
0x1c, l);
-
+ /*
+ * Set up the top and bottom of the PCI Memory segment
+ * for this bus.
+ */
l = ((bmem & 0xfff00000) >> 16) | ((tmem - 1) & 0xfff00000);
pcibios_write_config_dword(bridge->bus->number, bridge->devfn,
0x20, l);
outb(data | 0x40, ide_base+1); /* turn on IDE, really! */
}
+/*
+ * A small note about bridges and interrupts. The DECchip 21050 (and later chips)
+ * adheres to the PCI-PCI bridge specification. This says that the interrupts on
+ * the other side of a bridge are swizzled in the following manner:
+ *
+ * Dev Interrupt Interupt
+ * Pin on Pin on
+ * Device Connector
+ *
+ * 4 A A
+ * B B
+ * C C
+ * D D
+ *
+ * 5 A B
+ * B C
+ * C D
+ * D A
+ *
+ * 6 A C
+ * B D
+ * C A
+ * D B
+ *
+ * 7 A D
+ * B A
+ * C B
+ * D C
+ *
+ * Where A = pin 1, B = pin 2 and so on and pin=0 = default = A.
+ * Thus, each swizzle is ((pin-1) + (device#-4)) % 4
+ *
+ * The following code is somewhat simplistic as it assumes only one bridge.
+ * I will fix it later (david.rusling@reo.mts.dec.com).
+ */
+static inline unsigned char bridge_swizzle(unsigned char pin, unsigned int slot)
+{
+ /* swizzle */
+ return (((pin-1) + slot) % 4) + 1 ;
+}
+
/*
* Most evaluation boards share most of the fixup code, which is isolated here.
* This function is declared "inline" as only one platform will ever be selected
{
struct pci_dev *dev;
unsigned char pin;
+ unsigned char slot ;
/*
* Go through all devices, fixing up irqs as we see fit:
*/
for (dev = pci_devices; dev; dev = dev->next) {
- dev->irq = 0;
- /*
- * Ignore things not on the primary bus - I'll figure
- * this out one day - Dave Rusling
- */
- if (dev->bus->number != 0)
- continue;
-
- /* read the pin */
- pcibios_read_config_byte(dev->bus->number, dev->devfn,
- PCI_INTERRUPT_PIN, &pin);
- if (irq_tab[PCI_SLOT(dev->devfn) - min_idsel][pin] != -1)
- dev->irq = irq_tab[PCI_SLOT(dev->devfn) - min_idsel][pin];
+ if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) {
+ dev->irq = 0;
+ /*
+ * This device is not on the primary bus, we need to figure out which
+ * interrupt pin it will come in on. We know which slot it will come
+ * in on 'cos that slot is where the bridge is. Each time the interrupt
+ * line passes through a PCI-PCI bridge we must apply the swizzle function
+ * (see the inline static routine above).
+ */
+ if (dev->bus->number != 0) {
+ struct pci_dev *curr = dev ;
+ /* read the pin and do the PCI-PCI bridge interrupt pin swizzle */
+ pcibios_read_config_byte(dev->bus->number, dev->devfn,
+ PCI_INTERRUPT_PIN, &pin);
+ /* cope with 0 */
+ if (pin == 0) pin = 1 ;
+ /* follow the chain of bridges, swizzling as we go */
+ do {
+ /* swizzle */
+ pin = bridge_swizzle(pin, PCI_SLOT(curr->devfn)) ;
+ /* move up the chain of bridges */
+ curr = curr->bus->self ;
+ } while (curr->bus->self) ;
+ /* The slot is the slot of the last bridge. */
+ slot = PCI_SLOT(curr->devfn) ;
+ } else {
+ /* work out the slot */
+ slot = PCI_SLOT(dev->devfn) ;
+ /* read the pin */
+ pcibios_read_config_byte(dev->bus->number, dev->devfn,
+ PCI_INTERRUPT_PIN, &pin);
+ }
+ if (irq_tab[slot - min_idsel][pin] != -1)
+ dev->irq = irq_tab[slot - min_idsel][pin];
#if PCI_MODIFY
- /* tell the device: */
- pcibios_write_config_byte(dev->bus->number, dev->devfn,
- PCI_INTERRUPT_LINE, dev->irq);
+ /* tell the device: */
+ pcibios_write_config_byte(dev->bus->number, dev->devfn,
+ PCI_INTERRUPT_LINE, dev->irq);
#endif
- /*
- * if its a VGA, enable its BIOS ROM at C0000
- */
- if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) {
- pcibios_write_config_dword(dev->bus->number, dev->devfn,
- PCI_ROM_ADDRESS,
- 0x000c0000 | PCI_ROM_ADDRESS_ENABLE);
+ /*
+ * if its a VGA, enable its BIOS ROM at C0000
+ */
+ if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) {
+ pcibios_write_config_dword(dev->bus->number, dev->devfn,
+ PCI_ROM_ADDRESS,
+ 0x000c0000 | PCI_ROM_ADDRESS_ENABLE);
+ }
+ }
+
+ if (ide_base) {
+ enable_ide(ide_base);
}
- }
- if (ide_base) {
- enable_ide(ide_base);
}
}
* This driver does NOT support DigiBoard's fastcook FEP option and
* does not support the transparent print (i.e. digiprint) option.
*
- * Please email any suggestions or bug reports to troyd@skypoint.com
+ * This Driver is currently maintained by Christoph Lameter (clameter@fuller.edu)
+ * Please contact the mailing list for problems first.
*
+ * Sources of Information:
+ * 1. The Linux Digiboard Page at http://private.fuller.edu/clameter/digi.html
+ * 2. The Linux Digiboard Mailing list at digiboard@list.fuller.edu
+ * (Simply write a message to introduce yourself to subscribe)
*
- * January 1996 Bug fixes by an unknown author and released as 1.5.2
+ * 1.5.2 Fall 1995 Bug fixes by David Nugent
* 1.5.3 March 9, 1996 Christoph Lameter: Fixed 115.2K Support. Memory
* allocation harmonized with 1.3.X Series.
* 1.5.4 March 30, 1996 Christoph Lameter: Fixup for 1.3.81. Use init_bh
The driver supports the native 57.6K and 115K Baudrates under Linux, but
some distributions like Slackware 3.0 dont like these high baudrates.
*/
+
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/errno.h>
0, 0,
0, 0, 0, NULL, el3_probe };
+static int io = 0;
+static int irq = 0;
+
int
init_module(void)
{
+ dev_3c509.base_addr = io;
+ dev_3c509.irq = irq;
+ if (!EISA_bus && !io) {
+ printk("3c509: WARNING! Module load-time probing works reliably only for EISA bus!!\n");
+ }
if (register_netdev(&dev_3c509) != 0)
return -EIO;
return 0;
* process. Since locks still depend on the process id, locks are inherited
* after an exec() but not after a fork(). This agrees with POSIX, and both
* BSD and SVR4 practice.
- * Andy Walker (andy@keo.kvaerner.no), February 14, 1995
+ * Andy Walker (andy@lysaker.kvaerner.no), February 14, 1995
*
* Scrapped free list which is redundant now that we allocate locks
* dynamically with kmalloc()/kfree().
- * Andy Walker (andy@keo.kvaerner.no), February 21, 1995
+ * Andy Walker (andy@lysaker.kvaerner.no), February 21, 1995
*
* Implemented two lock personalities - F_FLOCK and F_POSIX.
*
* upgrading from shared to exclusive (or vice versa). When this happens
* any processes blocked by the current lock are woken up and allowed to
* run before the new lock is applied.
+ * Andy Walker (andy@lysaker.kvaerner.no), June 09, 1995
*
- * NOTE:
- * I do not intend to implement mandatory locks unless demand is *HUGE*.
- * They are not in BSD, and POSIX.1 does not require them. I have never
- * seen any public code that relied on them. As Kelly Carmichael suggests
- * above, mandatory locks requires lots of changes elsewhere and I am
- * reluctant to start something so drastic for so little gain.
- * Andy Walker (andy@keo.kvaerner.no), June 09, 1995
- *
* Removed some race conditions in flock_lock_file(), marked other possible
* races. Just grep for FIXME to see them.
* Dmitry Gorodchanin (begemot@bgm.rosprint.net), Feb 09, 1996.
+ *
+ * Addressed Dmitry's concerns. Deadlock checking no longer recursive.
+ * Lock allocation changed to GFP_ATOMIC as we can't afford to sleep
+ * once we've checked for blocking and deadlocking.
+ * Andy Walker (andy@lysaker.kvaerner.no), Apr 03, 1996.
+ *
+ * NOTE:
+ * Starting to look at mandatory locks - using SunOS as a model.
+ * Probably a configuration option because mandatory locking can cause
+ * all sorts of chaos with runaway processes.
*/
#include <asm/segment.h>
}
}
-/* flock() system call entry point. Apply a FLOCK style locks to
+/* flock() system call entry point. Apply a FLOCK style lock to
* an open file descriptor.
*/
asmlinkage int sys_flock(unsigned int fd, unsigned int cmd)
return (flock_lock_file(filp, &file_lock, cmd & LOCK_UN ? 0 : cmd & LOCK_NB ? 0 : 1));
}
-/* Report the first existing locks that would conflict with l. This implements
- * the F_GETLK command of fcntl().
+/* Report the first existing lock that would conflict with l.
+ * This implements the F_GETLK command of fcntl().
*/
int fcntl_getlk(unsigned int fd, struct flock *l)
{
return (0);
}
-/* Apply the lock described by l to an open file descriptor. This implements
- * both the F_SETLK and F_SETLKW commands of fcntl(). It also emulates flock()
- * in a pretty broken way for older C libraries.
+/* Apply the lock described by l to an open file descriptor.
+ * This implements both the F_SETLK and F_SETLKW commands of fcntl().
+ * It also emulates flock() in a pretty broken way for older C
+ * libraries.
*/
int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
{
return (1);
}
-/* Verify a call to flock() and fill in a file_lock structure with an appropriate
- * FLOCK lock.
+/* Verify a call to flock() and fill in a file_lock structure with
+ * an appropriate FLOCK lock.
*/
static int flock_make_lock(struct file *filp, struct file_lock *fl,
unsigned int cmd)
return (1);
}
-/* Determine if lock sys_fl blocks lock caller_fl. POSIX specific checking
- * before calling the locks_conflict().
+/* Determine if lock sys_fl blocks lock caller_fl. POSIX specific
+ * checking before calling the locks_conflict().
*/
static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
{
return (locks_conflict(caller_fl, sys_fl));
}
-/* Determine if lock sys_fl blocks lock caller_fl. FLOCK specific checking
- * before calling the locks_conflict().
+/* Determine if lock sys_fl blocks lock caller_fl. FLOCK specific
+ * checking before calling the locks_conflict().
*/
static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
{
(fl2->fl_end >= fl1->fl_start));
}
-/* This function tests for deadlock condition before putting a process to sleep.
- * The detection scheme is recursive... we may need a test to make it exit if the
- * function gets stuck due to bad lock data. 4.4 BSD uses a maximum depth of 50
- * for this.
- *
- * FIXME:
- * IMHO this function is dangerous, deep recursion may result in kernel stack
- * corruption. Perhaps we need to limit depth here.
- * Dmitry Gorodchanin 09/02/96
+/* This function tests for deadlock condition before putting a process to
+ * sleep. The detection scheme is no longer recursive. Recursive was neat,
+ * but dangerous - we risked stack corruption if the lock data was bad, or
+ * if the recursion was too deep for any other reason.
+ *
+ * We rely on the fact that a task can only be on one lock's wait queue
+ * at a time. When we find blocked_task on a wait queue we can re-search
+ * with blocked_task equal to that queue's owner, until either blocked_task
+ * isn't found, or blocked_task is found on a queue owned by my_task.
*/
static int posix_locks_deadlock(struct task_struct *my_task,
struct task_struct *blocked_task)
struct wait_queue *dlock_wait;
struct file_lock *fl;
+next_task:
for (fl = file_lock_table; fl != NULL; fl = fl->fl_nextlink) {
- if (fl->fl_owner == NULL)
- continue; /* Should never happen! */
- if (fl->fl_owner != my_task)
+ if (fl->fl_owner == NULL || fl->fl_wait == NULL)
continue;
- if (fl->fl_wait == NULL)
- continue; /* no queues */
dlock_wait = fl->fl_wait;
do {
- if (dlock_wait->task != NULL) {
- if (dlock_wait->task == blocked_task)
- return (-EDEADLOCK);
- if (posix_locks_deadlock(dlock_wait->task, blocked_task))
- return (-EDEADLOCK);
+ if (dlock_wait->task == blocked_task) {
+ if (fl->fl_owner == my_task) {
+ return(-EDEADLOCK);
+ }
+ blocked_task = fl->fl_owner;
+ goto next_task;
}
dlock_wait = dlock_wait->next;
} while (dlock_wait != fl->fl_wait);
return (0);
}
-/* Try to create a FLOCK lock on filp. We rely on FLOCK locks being sorting
+/* Try to create a FLOCK lock on filp. We rely on FLOCK locks being sorted
* first in an inode's lock list, and always insert new locks at the head
* of the list.
*/
}
caller = fl;
added = 1;
- goto next_lock;
}
- /* Processing for different lock types is a bit more complex.
- */
- if (fl->fl_end < caller->fl_start)
- goto next_lock;
- if (fl->fl_start > caller->fl_end)
- break;
- if (caller->fl_type == F_UNLCK)
- added = 1;
- if (fl->fl_start < caller->fl_start)
- left = fl;
- /* If the next lock in the list has a higher end address than
- * the new one, insert the new one here.
- */
- if (fl->fl_end > caller->fl_end) {
- right = fl;
- break;
- }
- if (fl->fl_start >= caller->fl_start) {
- /* The new lock completely replaces an old one (This may
- * happen several times).
+ else {
+ /* Processing for different lock types is a bit
+ * more complex.
*/
- if (added) {
- locks_delete_lock(before, 0);
- continue;
- }
- /* Replace the old lock with the new one. Wake up
- * anybody waiting for the old one, as the change in
- * lock type might satisfy his needs.
+ if (fl->fl_end < caller->fl_start)
+ goto next_lock;
+ if (fl->fl_start > caller->fl_end)
+ break;
+ if (caller->fl_type == F_UNLCK)
+ added = 1;
+ if (fl->fl_start < caller->fl_start)
+ left = fl;
+ /* If the next lock in the list has a higher end
+ * address than the new one, insert the new one here.
*/
- wake_up(&fl->fl_wait);
- fl->fl_start = caller->fl_start;
- fl->fl_end = caller->fl_end;
- fl->fl_type = caller->fl_type;
- caller = fl;
- added = 1;
+ if (fl->fl_end > caller->fl_end) {
+ right = fl;
+ break;
+ }
+ if (fl->fl_start >= caller->fl_start) {
+ /* The new lock completely replaces an old
+ * one (This may happen several times).
+ */
+ if (added) {
+ locks_delete_lock(before, 0);
+ continue;
+ }
+ /* Replace the old lock with the new one.
+ * Wake up anybody waiting for the old one,
+ * as the change in lock type might satisfy
+ * their needs.
+ */
+ wake_up(&fl->fl_wait);
+ fl->fl_start = caller->fl_start;
+ fl->fl_end = caller->fl_end;
+ fl->fl_type = caller->fl_type;
+ caller = fl;
+ added = 1;
+ }
}
/* Go on to next lock.
*/
before = &(*before)->fl_next;
}
- /* FIXME:
- * Note: We may sleep in locks_alloc_lock(), so
- * the 'before' pointer may be not valid any more.
- * This can cause random kernel memory corruption.
- * It seems the right way is to alloc two locks
- * at the begining of this func, and then free them
- * if they were not needed.
- * Another way is to change GFP_KERNEL to GFP_ATOMIC
- * in locks_alloc_lock() for this case.
- *
- * Dmitry Gorodchanin 09/02/96.
- */
if (!added) {
if (caller->fl_type == F_UNLCK)
return (0);
/* Okay, let's make a new file_lock structure... */
if ((tmp = (struct file_lock *)kmalloc(sizeof(struct file_lock),
- GFP_KERNEL)) == NULL)
+ GFP_ATOMIC)) == NULL)
return (tmp);
tmp->fl_nextlink = NULL;
}
/* Delete a lock and free it.
- * First remove our lock from the lock lists. Then remove all the blocked locks
- * from our blocked list, waking up the processes that own them. If told to wait,
- * then sleep on each of these lock's wait queues. Each blocked process will wake
- * up and immediately wake up its own wait queue allowing us to be scheduled again.
- * Lastly, wake up our own wait queue before freeing the file_lock structure.
+ * First remove our lock from the lock lists. Then remove all the blocked
+ * locks from our blocked list, waking up the processes that own them. If
+ * told to wait, then sleep on each of these lock's wait queues. Each
+ * blocked process will wake up and immediately wake up its own wait queue
+ * allowing us to be scheduled again. Lastly, wake up our own wait queue
+ * before freeing the file_lock structure.
*/
static void locks_delete_lock(struct file_lock **fl_p, unsigned int wait)
"m" (__atomic_fool_gcc(v)));
}
+/*
+ * Same as above, but return true if we counted down to zero
+ */
+extern __inline__ int atomic_sub_and_test(atomic_t i, atomic_t * v)
+{
+ unsigned long temp, result;
+ __asm__ __volatile__(
+ "\n1:\t"
+ "ldl_l %0,%1\n\t"
+ "subl %0,%3,%0\n\t"
+ "bis %0,%0,%2\n\t"
+ "stl_c %0,%1\n\t"
+ "beq %0,1b\n"
+ "2:"
+ :"=&r" (temp),
+ "=m" (__atomic_fool_gcc(v)),
+ "=&r" (result)
+ :"Ir" (i),
+ "m" (__atomic_fool_gcc(v)));
+ return result==0;
+}
+
#define atomic_inc(v) atomic_add(1,(v))
#define atomic_dec(v) atomic_sub(1,(v))
+#define atomic_dec_and_test(v) atomic_sub_and_test(1,(v))
#endif
#define LCA4_CPU 4 /* LCA4 (21066/21068) */
#define EV5_CPU 5 /* EV5 (21164) */
#define EV45_CPU 6 /* EV4.5 (21064/xxx) */
+#define EV56_CPU 7 /* EV5.6 (21164) */
+#define EV6_CPU 8 /* EV6 (21164) */
/*
* DEC system types for Alpha systems. Found in HWRPB.
typedef unsigned int __kernel_dev_t;
typedef unsigned int __kernel_ino_t;
typedef unsigned int __kernel_mode_t;
-typedef unsigned short __kernel_nlink_t;
+typedef unsigned int __kernel_nlink_t;
typedef long __kernel_off_t;
typedef int __kernel_pid_t;
typedef unsigned int __kernel_uid_t;
:"m" (__atomic_fool_gcc(v)));
}
+static __inline__ int atomic_dec_and_test(atomic_t *v)
+{
+ unsigned char c;
+
+ __asm__ __volatile__(
+ LOCK "decl %0; sete %1"
+ :"=m" (__atomic_fool_gcc(v)), "=qm" (c)
+ :"m" (__atomic_fool_gcc(v)));
+ return c != 0;
+}
+
#endif
#define cli() __asm__ __volatile__ ("cli": : :"memory")
#define save_flags(x) \
-__asm__ __volatile__("pushfl ; popl %0":"=r" (x): /* no input */ :"memory")
+__asm__ __volatile__("pushfl ; popl %0":"=g" (x): /* no input */ :"memory")
#define restore_flags(x) \
-__asm__ __volatile__("pushl %0 ; popfl": /* no output */ :"r" (x):"memory")
+__asm__ __volatile__("pushl %0 ; popfl": /* no output */ :"g" (x):"memory")
#define iret() __asm__ __volatile__ ("iret": : :"memory")
#include <linux/time.h>
#include <linux/config.h>
+#include <asm/atomic.h>
+
#define CONFIG_SKB_CHECK 0
#define HAVE_ALLOC_SKB /* For the drivers to know */
unsigned short protocol; /* Packet protocol from driver. */
unsigned short truesize; /* Buffer size */
- int count; /* reference count */
+ atomic_t count; /* reference count */
struct sk_buff *data_skb; /* Link to the actual data skb */
unsigned char *head; /* Head of buffer */
unsigned char *data; /* Data head pointer */
}
/*
- * Insert a packet before another one in a list.
+ * Insert a packet on a list.
*/
-extern __inline__ void __skb_insert(struct sk_buff *next, struct sk_buff *newsk,
+extern __inline__ void __skb_insert(struct sk_buff *newsk,
+ struct sk_buff * prev, struct sk_buff *next,
struct sk_buff_head * list)
{
- struct sk_buff * prev = next->prev;
-
newsk->next = next;
newsk->prev = prev;
next->prev = newsk;
list->qlen++;
}
+/*
+ * Place a packet before a given packet in a list
+ */
extern __inline__ void skb_insert(struct sk_buff *old, struct sk_buff *newsk)
{
unsigned long flags;
save_flags(flags);
cli();
- __skb_insert(old, newsk, old->list);
+ __skb_insert(newsk, old->prev, old, old->list);
restore_flags(flags);
}
* Place a packet after a given packet in a list.
*/
-extern __inline__ void __skb_append(struct sk_buff *prev, struct sk_buff *newsk,
- struct sk_buff_head * list)
-{
- struct sk_buff * next = prev->next;
-
- newsk->next = next;
- newsk->prev = prev;
- next->prev = newsk;
- prev->next = newsk;
- newsk->list = list;
- list->qlen++;
-}
-
extern __inline__ void skb_append(struct sk_buff *old, struct sk_buff *newsk)
{
unsigned long flags;
save_flags(flags);
cli();
- __skb_append(old, newsk, old->list);
+ __skb_insert(newsk, old, old->next, old->list);
restore_flags(flags);
}
struct task_struct *next = p->next_run;
struct task_struct *prev = p->prev_run;
+ /* remove from list */
next->prev_run = prev;
prev->next_run = next;
- (p->prev_run = init_task.prev_run)->next_run = p;
+ /* add back to list */
p->next_run = &init_task;
+ prev = init_task.prev_run;
init_task.prev_run = p;
+ p->prev_run = prev;
+ prev->next_run = p;
}
/*
struct size_descriptor {
struct page_descriptor *firstfree;
struct page_descriptor *dmafree; /* DMA-able memory */
- int size;
int nblocks;
int nmallocs;
/*
* For now it is unsafe to allocate bucket sizes between n and
* n-sizeof(page_descriptor) where n is PAGE_SIZE * any power of two
+ *
+ * The blocksize and sizes arrays _must_ match!
*/
#if PAGE_SIZE == 4096
-struct size_descriptor sizes[] =
+static const unsigned int blocksize[] = {
+ 32,
+ 64,
+ 128,
+ 252,
+ 508,
+ 1020,
+ 2040,
+ 4096 - 16,
+ 8192 - 16,
+ 16384 - 16,
+ 32768 - 16,
+ 65536 - 16,
+ 131072 - 16,
+ 0
+};
+
+static struct size_descriptor sizes[] =
{
- {NULL, NULL, 32, 127, 0, 0, 0, 0, 0},
- {NULL, NULL, 64, 63, 0, 0, 0, 0, 0},
- {NULL, NULL, 128, 31, 0, 0, 0, 0, 0},
- {NULL, NULL, 252, 16, 0, 0, 0, 0, 0},
- {NULL, NULL, 508, 8, 0, 0, 0, 0, 0},
- {NULL, NULL, 1020, 4, 0, 0, 0, 0, 0},
- {NULL, NULL, 2040, 2, 0, 0, 0, 0, 0},
- {NULL, NULL, 4096 - 16, 1, 0, 0, 0, 0, 0},
- {NULL, NULL, 8192 - 16, 1, 0, 0, 0, 0, 1},
- {NULL, NULL, 16384 - 16, 1, 0, 0, 0, 0, 2},
- {NULL, NULL, 32768 - 16, 1, 0, 0, 0, 0, 3},
- {NULL, NULL, 65536 - 16, 1, 0, 0, 0, 0, 4},
- {NULL, NULL, 131072 - 16, 1, 0, 0, 0, 0, 5},
- {NULL, NULL, 0, 0, 0, 0, 0, 0, 0}
+ {NULL, NULL, 127, 0, 0, 0, 0, 0},
+ {NULL, NULL, 63, 0, 0, 0, 0, 0},
+ {NULL, NULL, 31, 0, 0, 0, 0, 0},
+ {NULL, NULL, 16, 0, 0, 0, 0, 0},
+ {NULL, NULL, 8, 0, 0, 0, 0, 0},
+ {NULL, NULL, 4, 0, 0, 0, 0, 0},
+ {NULL, NULL, 2, 0, 0, 0, 0, 0},
+ {NULL, NULL, 1, 0, 0, 0, 0, 0},
+ {NULL, NULL, 1, 0, 0, 0, 0, 1},
+ {NULL, NULL, 1, 0, 0, 0, 0, 2},
+ {NULL, NULL, 1, 0, 0, 0, 0, 3},
+ {NULL, NULL, 1, 0, 0, 0, 0, 4},
+ {NULL, NULL, 1, 0, 0, 0, 0, 5},
+ {NULL, NULL, 0, 0, 0, 0, 0, 0}
};
#elif PAGE_SIZE == 8192
+static const unsigned int blocksize[] = {
+ 64,
+ 128,
+ 248,
+ 504,
+ 1016,
+ 2040,
+ 4080,
+ 8192 - 32,
+ 16384 - 32,
+ 32768 - 32,
+ 65536 - 32,
+ 131072 - 32,
+ 262144 - 32,
+ 0
+};
+
struct size_descriptor sizes[] =
{
- {NULL, NULL, 64, 127, 0, 0, 0, 0, 0},
- {NULL, NULL, 128, 63, 0, 0, 0, 0, 0},
- {NULL, NULL, 248, 31, 0, 0, 0, 0, 0},
- {NULL, NULL, 504, 16, 0, 0, 0, 0, 0},
- {NULL, NULL, 1016, 8, 0, 0, 0, 0, 0},
- {NULL, NULL, 2040, 4, 0, 0, 0, 0, 0},
- {NULL, NULL, 4080, 2, 0, 0, 0, 0, 0},
- {NULL, NULL, 8192 - 32, 1, 0, 0, 0, 0, 0},
- {NULL, NULL, 16384 - 32, 1, 0, 0, 0, 0, 1},
- {NULL, NULL, 32768 - 32, 1, 0, 0, 0, 0, 2},
- {NULL, NULL, 65536 - 32, 1, 0, 0, 0, 0, 3},
- {NULL, NULL, 131072 - 32, 1, 0, 0, 0, 0, 4},
- {NULL, NULL, 262144 - 32, 1, 0, 0, 0, 0, 5},
- {NULL, NULL, 0, 0, 0, 0, 0, 0, 0}
+ {NULL, NULL, 127, 0, 0, 0, 0, 0},
+ {NULL, NULL, 63, 0, 0, 0, 0, 0},
+ {NULL, NULL, 31, 0, 0, 0, 0, 0},
+ {NULL, NULL, 16, 0, 0, 0, 0, 0},
+ {NULL, NULL, 8, 0, 0, 0, 0, 0},
+ {NULL, NULL, 4, 0, 0, 0, 0, 0},
+ {NULL, NULL, 2, 0, 0, 0, 0, 0},
+ {NULL, NULL, 1, 0, 0, 0, 0, 0},
+ {NULL, NULL, 1, 0, 0, 0, 0, 1},
+ {NULL, NULL, 1, 0, 0, 0, 0, 2},
+ {NULL, NULL, 1, 0, 0, 0, 0, 3},
+ {NULL, NULL, 1, 0, 0, 0, 0, 4},
+ {NULL, NULL, 1, 0, 0, 0, 0, 5},
+ {NULL, NULL, 0, 0, 0, 0, 0, 0}
};
#else
#error you need to make a version for your pagesize
#endif
#define NBLOCKS(order) (sizes[order].nblocks)
-#define BLOCKSIZE(order) (sizes[order].size)
+#define BLOCKSIZE(order) (blocksize[order])
#define AREASIZE(order) (PAGE_SIZE<<(sizes[order].gfporder))
}
-
-int get_order(int size)
-{
- int order;
-
- /* Add the size of the header */
- size += sizeof(struct block_header);
- for (order = 0; BLOCKSIZE(order); order++)
- if (size <= BLOCKSIZE(order))
- return order;
- return -1;
-}
-
void *kmalloc(size_t size, int priority)
{
unsigned long flags;
unsigned long type;
- int order, i, sz, dma;
+ int order, dma;
struct block_header *p;
struct page_descriptor *page, **pg;
- order = get_order(size);
- if (order < 0) {
- printk("kmalloc of too large a block (%d bytes).\n", (int) size);
- return (NULL);
+ /* Get order */
+ order = 0;
+ {
+ unsigned int realsize = size + sizeof(struct block_header);
+ for (;;) {
+ int ordersize = BLOCKSIZE(order);
+ if (realsize <= ordersize)
+ break;
+ order++;
+ if (ordersize)
+ continue;
+ printk("kmalloc of too large a block (%d bytes).\n", (int) size);
+ return NULL;
+ }
}
dma = 0;
page = *pg;
if (page) {
p = page->firstfree;
- if (p->bh_flags != MF_FREE) {
- restore_flags(flags);
- printk("Problem: block on freelist at %08lx isn't free.\n", (long) p);
- return NULL;
- }
+ if (p->bh_flags != MF_FREE)
+ goto not_free_on_freelist;
goto found_it;
}
/* This can be done with ints on: This is private to this invocation */
restore_flags(flags);
- /* sz is the size of the blocks we're dealing with */
- sz = BLOCKSIZE(order);
+ {
+ int i, sz;
+
+ /* sz is the size of the blocks we're dealing with */
+ sz = BLOCKSIZE(order);
- page = (struct page_descriptor *) __get_free_pages(priority,
- sizes[order].gfporder, dma);
+ page = (struct page_descriptor *) __get_free_pages(priority,
+ sizes[order].gfporder, dma);
- if (!page) {
- static unsigned long last = 0;
- if (priority != GFP_BUFFER && (last + 10 * HZ < jiffies)) {
- last = jiffies;
- printk("Couldn't get a free page.....\n");
- }
- return NULL;
- }
- sizes[order].npages++;
+ if (!page)
+ goto no_free_page;
+ sizes[order].npages++;
- /* Loop for all but last block: */
- for (i = NBLOCKS(order), p = BH(page + 1); i > 1; i--, p = p->bh_next) {
+ /* Loop for all but last block: */
+ for (i = NBLOCKS(order), p = BH(page + 1); i > 1; i--, p = p->bh_next) {
+ p->bh_flags = MF_FREE;
+ p->bh_next = BH(((long) p) + sz);
+ }
+ /* Last block: */
p->bh_flags = MF_FREE;
- p->bh_next = BH(((long) p) + sz);
- }
- /* Last block: */
- p->bh_flags = MF_FREE;
- p->bh_next = NULL;
+ p->bh_next = NULL;
- page->order = order;
- page->nfree = NBLOCKS(order);
- p = BH(page+1);
+ page->order = order;
+ page->nfree = NBLOCKS(order);
+ p = BH(page+1);
+ }
/*
* Now we're going to muck with the "global" freelist
memset(p+1, 0xf0, size);
#endif
return p + 1; /* Pointer arithmetic: increments past header */
+
+no_free_page:
+ {
+ static unsigned long last = 0;
+ if (priority != GFP_BUFFER && (last + 10 * HZ < jiffies)) {
+ last = jiffies;
+ printk("Couldn't get a free page.....\n");
+ }
+ return NULL;
+ }
+
+not_free_on_freelist:
+ restore_flags(flags);
+ printk("Problem: block on freelist at %08lx isn't free.\n", (long) p);
+ return NULL;
}
void kfree(void *ptr)
pte = pte_wrprotect(mk_pte(ZERO_PAGE, vma->vm_page_prot));
if (write_access) {
unsigned long page = get_free_page(GFP_KERNEL);
- pte = pte_mkwrite(mk_pte(page, vma->vm_page_prot));
+ pte = pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
vma->vm_mm->rss++;
tsk->min_flt++;
if (!page) {
* Insert a packet before another one in a list.
*/
-void __skb_insert(struct sk_buff *old, struct sk_buff *newsk)
+void __skb_insert(struct sk_buff *newsk,
+ struct sk_buff * prev, struct sk_buff *next,
+ struct sk_buff_head * list)
{
- IS_SKB(old);
+ IS_SKB(prev);
IS_SKB(newsk);
+ IS_SKB(next);
- if(!old->next || !old->prev)
+ if(!prev->next || !prev->prev)
+ printk("insert after unlisted item!\n");
+ if(!next->next || !next->prev)
printk("insert before unlisted item!\n");
if(newsk->next || newsk->prev)
printk("inserted item is already on a list.\n");
- newsk->next = old;
- newsk->prev = old->prev;
- old->prev = newsk;
- newsk->prev->next = newsk;
- newsk->list = old->list;
- newsk->list->qlen++;
+ newsk->next = next;
+ newsk->prev = prev;
+ next->prev = newsk;
+ prev->next = newsk;
+ newsk->list = list;
+ list->qlen++;
}
restore_flags(flags);
}
-void __skb_append(struct sk_buff *old, struct sk_buff *newsk)
-{
- IS_SKB(old);
- IS_SKB(newsk);
-
- if(!old->next || !old->prev)
- printk("append before unlisted item!\n");
- if(newsk->next || newsk->prev)
- printk("append item is already on a list.\n");
-
- newsk->prev = old;
- newsk->next = old->next;
- newsk->next->prev = newsk;
- old->next = newsk;
- newsk->list = old->list;
- newsk->list->qlen++;
-
-}
-
/*
* Remove an sk_buff from its list. Works even without knowing the list it
* is sitting on, which can be handy at times. It also means that THE LIST
static inline void __kfree_skbmem(struct sk_buff *skb)
{
/* don't do anything if somebody still uses us */
- if (--skb->count <= 0) {
+ if (atomic_dec_and_test(&skb->count)) {
kfree(skb->head);
- net_skbcount--;
+ atomic_dec(&net_skbcount);
}
}
void kfree_skbmem(struct sk_buff *skb)
{
- unsigned long flags;
void * addr = skb->head;
- save_flags(flags);
- cli();
/* don't do anything if somebody still uses us */
- if (--skb->count <= 0) {
+ if (atomic_dec_and_test(&skb->count)) {
/* free the skb that contains the actual data if we've clone()'d */
if (skb->data_skb) {
addr = skb;
__kfree_skbmem(skb->data_skb);
}
kfree(addr);
- net_skbcount--;
+ atomic_dec(&net_skbcount);
}
- restore_flags(flags);
}
/*
return(0);
}
+/*
+ * Add a sk_buff to the TCP receive queue, calculating
+ * the ACK sequence as we go..
+ */
+static inline void tcp_insert_skb(struct sk_buff * skb, struct sk_buff_head * list)
+{
+ struct sk_buff * prev, * next;
+ u32 seq;
+
+ /*
+ * Find where the new skb goes.. (This goes backwards,
+ * on the assumption that we get the packets in order)
+ */
+ seq = skb->seq;
+ prev = list->prev;
+ next = (struct sk_buff *) list;
+ for (;;) {
+ if (prev == (struct sk_buff *) list || !after(prev->seq, seq))
+ break;
+ next = prev;
+ prev = prev->prev;
+ }
+ __skb_insert(skb, prev, next, list);
+}
+
/*
* Called for each packet when we find a new ACK endpoint sequence in it
*/
if (skb->h.th->fin)
tcp_fin(skb,sk,skb->h.th);
return skb->end_seq;
-}
-
+}
-/*
- * Add a sk_buff to the TCP receive queue, calculating
- * the ACK sequence as we go..
- */
static void tcp_queue(struct sk_buff * skb, struct sock * sk,
struct tcphdr *th, unsigned long saddr)
{
- struct sk_buff_head * list = &sk->receive_queue;
- struct sk_buff * next;
u32 ack_seq;
- /*
- * Find where the new skb goes.. (This goes backwards,
- * on the assumption that we get the packets in order)
- */
- next = list->prev;
- while (next != (struct sk_buff *) list) {
- if (!after(next->seq, skb->seq))
- break;
- next = next->prev;
- }
- /*
- * put it after the packet we found (which
- * may be the list-head, but that's fine).
- */
- __skb_append(next, skb, list);
- next = skb->next;
-
+ tcp_insert_skb(skb, &sk->receive_queue);
/*
* Did we get anything new to ack?
*/
ack_seq = sk->acked_seq;
if (!after(skb->seq, ack_seq) && after(skb->end_seq, ack_seq)) {
+ struct sk_buff_head * list = &sk->receive_queue;
+ struct sk_buff * next;
ack_seq = tcp_queue_ack(skb, sk);
/*
* Do we have any old packets to ack that the above
* made visible? (Go forward from skb)
*/
+ next = skb->next;
while (next != (struct sk_buff *) list) {
if (after(next->seq, ack_seq))
break;
}
}
-/*
- * Throw out all unnecessary packets: we've gone over the
- * receive queue limit. This shouldn't happen in a normal
- * TCP connection, but we might have gotten duplicates etc.
- */
-static inline void tcp_forget_unacked(struct sk_buff_head * list)
-{
- for (;;) {
- struct sk_buff * skb = list->prev;
-
- /* gone through it all? */
- if (skb == (struct sk_buff *) list)
- break;
- if (skb->acked)
- break;
- __skb_unlink(skb, list);
- }
-}
-
/*
* This should be a bit smarter and remove partially
* overlapping stuff too, but this should be good
* enough for any even remotely normal case (and the
* worst that can happen is that we have a few
* unnecessary packets in the receive queue).
+ *
+ * This function is never called with an empty list..
*/
static inline void tcp_remove_dups(struct sk_buff_head * list)
{
- struct sk_buff * skb = list->next;
+ struct sk_buff * next = list->next;
for (;;) {
- struct sk_buff * next;
-
- if (skb == (struct sk_buff *) list)
+ struct sk_buff * skb = next;
+ next = next->next;
+ if (next == (struct sk_buff *) list)
break;
- next = skb->next;
- if (next->seq == skb->seq) {
- if (before(next->end_seq, skb->end_seq)) {
- __skb_unlink(next, list);
- continue;
- }
- __skb_unlink(skb, list);
+ if (before(next->end_seq, skb->end_seq)) {
+ __skb_unlink(next, list);
+ kfree_skb(next, FREE_READ);
+ next = skb;
+ continue;
}
- skb = next;
+ if (next->seq != skb->seq)
+ continue;
+ __skb_unlink(skb, list);
+ kfree_skb(skb, FREE_READ);
}
}
+/*
+ * Throw out all unnecessary packets: we've gone over the
+ * receive queue limit. This shouldn't happen in a normal
+ * TCP connection, but we might have gotten duplicates etc.
+ */
static void prune_queue(struct sk_buff_head * list)
{
- /*
- * Throw out things we haven't acked.
- */
- tcp_forget_unacked(list);
+ for (;;) {
+ struct sk_buff * skb = list->prev;
- /*
- * Throw out duplicates
- */
- tcp_remove_dups(list);
+ /* gone through it all? */
+ if (skb == (struct sk_buff *) list)
+ break;
+ if (!skb->acked) {
+ __skb_unlink(skb, list);
+ kfree_skb(skb, FREE_READ);
+ continue;
+ }
+ tcp_remove_dups(list);
+ break;
+ }
}
-
/*
* A TCP packet has arrived.
* skb->h.raw is the TCP header.
X(memcpy_fromiovec),
X(sock_setsockopt),
X(sock_getsockopt),
+ X(sk_alloc),
+ X(sk_free),
X(sock_wake_async),
X(sock_alloc_send_skb),
X(skb_recv_datagram),