- TUN/TAP driver: use proper device number (misc device, minor=200).
- teach st.c about some SCSI tapes that really aren't SCSI tapes (OnStream)
- samba 2.2 needs leases for efficient file sharing. They are kind
of like file locks with async IO notification.
- broadcast I/O APIC interrupt MP-tables are legal..
- alpha RTC year magic again..
- careful memory ordering by Andrea..
- make the scsi-generic module work properly again.
- file locking fixes
- update atp ISA net driver
- VIA IDE driver bugfixes
- more linux-2.2 driver sync-ups
- new PCI ids
- emu10k stereo sound fix.
- makefile documentation update
- USB uhci updates
- networking updates
- codafs fixups
- VM UP deadlock fix
- Add Camino chipset ID to eepro100 driver.
module, say M here and read Documentation/modules.txt as well as
Documentation/networking/net-modules.txt.
-RealTek 8129 (not 8019/8029!) support (EXPERIMENTAL)
+RealTek 8129 (not 8019/8029/8139!) support (EXPERIMENTAL)
CONFIG_RTL8129
+ This is NOT for RTL-8139 cards. Instead, select the 8139too driver
+ (CONFIG_8139TOO).
This is a driver for the Fast Ethernet PCI network cards based on
the RTL8129 chip. If you have one of those, say Y and
read the Ethernet-HOWTO, available from
Documentation/networking/net-modules.txt. The module will be called
eexpress.o.
+Packet Engines Hamachi GNIC-II support
+CONFIG_HAMACHI
+ If you have a Gigabit Ethernet card of this type, say Y and read
+ the Ethernet-HOWTO, available from
+ http://www.linuxdoc.org/docs.html#howto .
+
+ If you want to compile this as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want),
+ say M here and read Documentation/modules.txt as well as
+ Documentation/networking/net-modules.txt. The module will be called
+ hamachi.o.
+
HP PCLAN+ (27247B and 27252A) support
CONFIG_HPLAN_PLUS
If you have a network (Ethernet) card of this type, say Y and read
module, say M here and read Documentation/modules.txt as well as
Documentation/networking/net-modules.txt.
-SMC EtherPower II (EXPERIMENTAL)
+SMC EtherPower II
CONFIG_EPIC100
- If you have an SMC EtherPower II 9432 PCI Ethernet network card
- which is based on the SMC83c170, say Y and read the Ethernet-HOWTO,
- available from http://www.linuxdoc.org/docs.html#howto .
-
- This driver is also available as a module ( = code which can be
- inserted in and removed from the running kernel whenever you want).
- The module will be called epic100.o. If you want to compile it as a
- module, say M here and read Documentation/modules.txt as well as
- Documentation/networking/net-modules.txt.
+ This driver is for the SMC EtherPower II 9432 PCI Ethernet NIC,
+ which is based on the SMC83c17x (EPIC/100).
+ More specific information and updates are available from
+ http://www.scyld.com/network/epic100.html
SGI Seeq ethernet controller support
CONFIG_SGISEEQ
Say Y here if you have an Seeq based Ethernet network card. This is
used in many Silicon Graphics machines.
+Sundance "Alta" PCI Ethernet support
+CONFIG_SUNDANCE
+ This driver is for the Sundance "Alta" chip.
+ More specific information and updates are available from
+ http://www.scyld.com/network/sundance.html
+
+Winbond W89c840 PCI Ethernet support
+CONFIG_WINBOND_840
+ This driver is for the Winbond W89c840 chip. It also works with
+ the TX9882 chip on the Compex RL100-ATX board.
+ More specific information and updates are available from
+ http://www.scyld.com/network/drivers.html
+
Zenith Z-Note support (EXPERIMENTAL)
CONFIG_ZNET
The Zenith Z-Note notebook computer has a built-in network
$(TOPDIR)/drivers/sound/sound_firmware.c \
$(TOPDIR)/drivers/net/wan/syncppp.c \
$(TOPDIR)/drivers/net/wan/z85230.c \
+ $(TOPDIR)/fs/locks.c \
$(TOPDIR)/fs/devfs/base.c \
$(TOPDIR)/kernel/pm.c \
$(TOPDIR)/kernel/ksyms.c \
<sect1><title>Registration and Superblocks</title>
!Efs/super.c
</sect1>
+ <sect1><title>File Locks</title>
+!Efs/locks.c
+!Ifs/locks.c
+ </sect1>
</chapter>
<chapter id="netcore">
Linux Kernel Makefiles
-16 August 2000
-Michael Elizabeth Chastain, <chastain@redhat.com>
+2000-September-14
+Michael Elizabeth Chastain, <mec@shout.net>
-=== Introduction
+=== Table of Contents
This document describes the Linux kernel Makefiles.
+ 1 Overview
+ 2 Who does what
+ 3 Makefile language
+ 4 Variables passed down from the top
+ 5 The structure of an arch Makefile
+ 5.1 Architecture-specific variables
+ 5.2 Vmlinux build variables
+ 5.3 Post-vmlinux goals
+ 5.4 Mandatory arch-specific goals
+ 6 The structure of a subdirectory Makefile
+ 6.1 Comments
+ 6.2 Goal definitions
+ 6.3 Adapter section
+ 6.4 Rules.make section
+ 6.5 Special rules
+ 7 Rules.make variables
+ 7.1 Subdirectories
+ 7.2 Object file goals
+ 7.3 Library file goals
+ 7.4 Loadable module goals
+ 7.5 Multi-part modules
+ 7.6 Compilation flags
+ 7.7 Miscellaneous variables
+ 8 New-style variables
+ 9 Compatibility with Linux Kernel 2.2
+ 10 Credits
+
+
+=== 1 Overview
+
The Makefiles have five parts:
Makefile: the top Makefile.
The top Makefile is responsible for building two major products: vmlinux
(the resident kernel image) and modules (any module files). It builds
-these targets by recursively descending into the subdirectories of the
+these goals by recursively descending into the subdirectories of the
kernel source tree. The list of subdirectories which are visited depends
upon the kernel configuration.
-=== Who does what
+=== 2 Who does what
People have four different relationships with the kernel Makefiles.
-=== Makefile language
+=== 3 Makefile language
The kernel Makefiles are designed to run with Gnu Make. The Makefiles
use only the documented features of Gnu Make, but they do use many
-=== Variables passed down from the top
+=== 4 Variables passed down from the top
The top Makefile exports the following variables:
GENKSYMS
These variables specify the commands and flags that Rules.make
- uses to build target files from source files.
+ uses to build goal files from source files.
$(CFLAGS_KERNEL) contains extra C compiler flags used to compile
resident kernel code.
-=== The structure of an arch Makefile
+=== 5 The structure of an arch Makefile
+
+
+
+--- 5.1 Architecture-specific variables
The top Makefile includes one arch Makefile file, arch/$(ARCH)/Makefile.
This section describes the functions of the arch Makefile.
LD=$(CROSS_COMPILE)ld -m elf_s390
OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S
+
+
+--- 5.2 Vmlinux build variables
+
An arch Makefile co-operates with the top Makefile to define variables
which specify how to build the vmlinux file. Note that there is no
corresponding arch-specific section for modules; the module-building
CORE_FILES := arch/m68k/kernel/kernel.o arch/m68k/mm/mm.o $(CORE_FILES)
LIBS += arch/m68k/lib/lib.a
-An arch Makefile specifies targets that take the vmlinux file, compress
+
+
+--- 5.3 Post-vmlinux goals
+
+An arch Makefile specifies goals that take the vmlinux file, compress
it, wrap it in bootstrapping code, and copy the resulting files somewhere.
This includes various kinds of installation commands.
-These post-vmlinux targets are not standardized across different
-architectures. Here is a list of these targets and the architectures
+These post-vmlinux goals are not standardized across different
+architectures. Here is a list of these goals and the architectures
that support each of them (as of kernel version 2.4.0-test6-pre5):
balo mips
zlilo i386
znetboot.initrd ppc
-An arch Makefile must define the following auxiliary targets. These
-targets provide arch-specific actions for the corresponding targets
+
+
+--- 5.4 Mandatory arch-specific goals
+
+An arch Makefile must define the following arch-specific goals.
+These goals provide arch-specific actions for the corresponding goals
in the top Makefile:
archclean clean
-=== The structure of a subdirectory Makefile
+=== 6 The structure of a subdirectory Makefile
A subdirectory Makefile has five sections.
+
+
+--- 6.1 Comments
+
The first section is a comment header. Just write what you would
write if you were editing a C source file, but use "# ..." instead of
"/* ... */". Historically, many anonymous people have edited kernel
Makefiles without leaving any change histories in the header; comments
from them would have been valuable.
+
+
+--- 6.2 Goal definitions
+
The second section is a bunch of definitions that are the heart of the
subdirectory Makefile. These lines define the files to be built, any
special compilation options, and any subdirectories to be recursively
for certain features (such as CONFIG_* options that enable more than
one file). If you have a choice, please write a new-style Makefile.
+
+
+--- 6.3 Adapter section
+
The third section is an adapter section. In old-style Makefiles, this
third section is not present. In new-style Makefiles, the third section
contains boilerplate code which converts from new-style variables to
old-style variables. This is because Rules.make processes only the
old-style variables.
+
+
+--- 6.4 Rules.make section
+
The fourth section is the single line:
include $(TOPDIR)/Rules.make
+
+
+--- 6.5 Special rules
+
The fifth section contains any special Makefile rules needed that are
not available through the common rules in Rules.make.
-=== Rules.make variables
+=== 7 Rules.make variables
The public interface of Rules.make consists of the following variables:
+
+
+--- 7.1 Subdirectories
+
ALL_SUB_DIRS, SUB_DIRS, MOD_IN_SUB_DIRS, MOD_SUB_DIRS
$(ALL_SUB_DIRS) is an unconditional list of *all* the
endif
endif
+
+
+--- 7.2 Object file goals
+
O_TARGET, O_OBJS, OX_OBJS
The subdirectory Makefile specifies object files for vmlinux in
+--- 7.3 Library file goals
+
L_TARGET, L_OBJS, LX_OBJS
These names are similar to the O_* names. Once again, $(L_OBJS)
during development activity, but $(L_TARGET) gives better error
messages for unresolved symbols.
+
+
+--- 7.4 Loadable module goals
+
M_OBJS, MX_OBJS
$(M_OBJS) and $(MX_OBJS) specify object files which are built
will build $(M_OBJS) instead. (Yes, this is a little delicate
and a little confusing).
+
+
+--- 7.5 Multi-part modules
+
MI_OBJS, MIX_OBJS
Some kernel modules are composed of several object files
sb.o: $(sb-objs)
$(LD) -r -o $@ $(sb-objs)
- IGNORE_FLAGS_OBJS
- $(IGNORE_FLAGS_OBJS) is a list of object files which will not have
- their flag dependencies automatically tracked. This is a hackish
- feature, used to kludge around a problem in the implementation
- of flag dependencies. (The problem is that flag dependencies
- assume that a %.o file is built from a matching %.S or %.c file.
- This is sometimes not true).
+
+--- 7.6 Compilation flags
EXTRA_CFLAGS, EXTRA_AFLAGS, EXTRA_LDFLAGS, EXTRA_ARFLAGS
for such a file, you will have to remove the object file in order
to re-build from source.
+ LD_RFLAG
+
+ This variable is used, but never defined. It appears to be a
+ vestige of some abandoned experiment.
+
+
+
+--- 7.7 Miscellaneous variables
+
+ IGNORE_FLAGS_OBJS
+
+ $(IGNORE_FLAGS_OBJS) is a list of object files which will not have
+ their flag dependencies automatically tracked. This is a hackish
+ feature, used to kludge around a problem in the implementation
+ of flag dependencies. (The problem is that flag dependencies
+ assume that a %.o file is built from a matching %.S or %.c file.
+ This is sometimes not true).
+
USE_STANDARD_AS_RULE
This is a transition variable. If $(USE_STANDARD_AS_RULE)
-=== New-style variables
+=== 8 New-style variables
The "new-style variables" are simpler and more powerful than the
"old-style variables". As a result, many subdirectory Makefiles shrank
-=== Compatibility with Linux Kernel 2.2
+=== 9 Compatibility with Linux Kernel 2.2
Most of the information in this document also applies to 2.2, although
there is no indication of which things have changed when. Here are some
-=== Credits
+=== 10 Credits
Thanks to the members of the linux-kbuild mailing list for reviewing
-drafts of this document, with particular thanks to Peter Samuelson.
+drafts of this document, with particular thanks to Peter Samuelson
+and Thomas Molina.
br_sigio.c - bridge based on async io and SIGIO signal.
However the best example is VTun http://vtun.sourceforge.net :))
-2. Installation
- Run './configure' to configure the driver.
-
- Run 'make install' to compile and install driver module and to create
- /dev/net/tun device node.
-
-3. Loading driver module
- Linux
- To load TUN/TAP driver module run:
- modprobe tun
- To configure automatic loading of the 'tun' module you have to add:
- alias char-major-195 tun
- to the /etc/conf.modules, and run:
+2. Configuration
+ Create device node:
+ mknod /dev/net/tun c 10 200
+
+ Driver module autoloading
+ Make sure that "Kernel module loader" - module auto-loading support is enabled
+ in your kernel.
+
+ Add following line to the /etc/modules.conf:
+ alias char-major-10-200 tun
+
+ Run:
modprobe -a
- TUN/TAP driver will be automatically loaded when application access
- /dev/net/tun.
- If "Kernel module loader" - module auto-loading support is not enabled
- in your kernel then you can add
- modprobe tun
- to one of the startup rc files.
-4. Program interface
- 4.1 Network device allocation:
+ Driver will be automatically loaded when application access /dev/net/tun.
+
+3. Program interface
+ 3.1 Network device allocation:
int tun_alloc(char *dev)
{
return fd;
}
- 4.2 Frame format:
+ 3.2 Frame format:
If flag IFF_NO_PI is not set each frame format is:
Flags [2 bytes]
Proto [2 bytes]
4. What is TUN/TAP driver used for?
As mentioned above, main purpose of TUN/TAP driver is tunneling.
-It used by VTun (http://vtun.netpedia.net).
+It is used by VTun (http://vtun.sourceforge.net).
5. How does Virtual network device actually work ?
Virtual network device can be viewed as a simple Point-to-Point or
8. Does TAP driver support kernel Ethernet bridging?
Yes. Linux and FreeBSD drivers support Ethernet bridging.
-
-
P: Michael Elizabeth Chastain
M: mec@shout.net
L: linux-kbuild@torque.net
+W: http://www.kernel.org/pub/linux/kernel/projects/kbuild/
S: Maintained
CONFIGURE.HELP
W: http://www.nt.tuwien.ac.at/~kkudielk/Linux/
S: Maintained
+KERNEL BUILD (Makefile, Rules.make, scripts/*)
+P: Keith Owens
+M: kaos@ocs.com.au
+P: Michael Elizabeth Chastain
+M: mec@shout.net
+L: linux-kbuild@torque.net
+W: http://www.kernel.org/pub/linux/kernel/projects/kbuild/
+S: Maintained
+
LOGICAL VOLUME MANAGER
P: Heinz Mauelshagen
M: linux-LVM@EZ-Darmstadt.Telekom.de
DRIVERS-$(CONFIG_NUBUS) += drivers/nubus/nubus.a
DRIVERS-$(CONFIG_ISDN) += drivers/isdn/isdn.a
DRIVERS-$(CONFIG_NET_FC) += drivers/net/fc/fc.o
-DRIVERS-$(CONFIG_APPLETALK) += drivers/net/appletalk/appletalk.a
+DRIVERS-$(CONFIG_APPLETALK) += drivers/net/appletalk/appletalk.o
DRIVERS-$(CONFIG_TR) += drivers/net/tokenring/tr.a
DRIVERS-$(CONFIG_WAN) += drivers/net/wan/wan.o
DRIVERS-$(CONFIG_ARCNET) += drivers/net/arcnet/arcnet.a
archdep:
@$(MAKEBOOT) dep
+
+vmlinux: arch/alpha/vmlinux.lds
+
+arch/alpha/vmlinux.lds: arch/alpha/vmlinux.lds.in
$(CPP) $(CPPFLAGS) -xc -P arch/alpha/vmlinux.lds.in -o arch/alpha/vmlinux.lds
bootpfile:
ptes = &arena->ptes[dma_ofs];
sg = leader;
do {
+#if DEBUG_ALLOC > 0
struct scatterlist *last_sg = sg;
+#endif
size = sg->length;
paddr = virt_to_phys(sg->address);
" blbs %0,2b\n"
" br 1b\n"
".previous"
- : "=r" (tmp), "=m" (__dummy_lock(lock)), "=r" (stuck)
- : "1" (__dummy_lock(lock)), "2" (stuck));
+ : "=r" (tmp), "=m" (lock->lock), "=r" (stuck)
+ : "1" (lock->lock), "2" (stuck) : "memory");
if (stuck < 0) {
printk(KERN_WARNING
" blt %1,8b\n"
" br 1b\n"
".previous"
- : "=m" (__dummy_lock(lock)), "=&r" (regx), "=&r" (regy),
+ : "=m" (*(volatile int *)lock), "=&r" (regx), "=&r" (regy),
"=&r" (stuck_lock), "=&r" (stuck_reader)
- : "0" (__dummy_lock(lock)), "3" (stuck_lock), "4" (stuck_reader));
+ : "0" (*(volatile int *)lock), "3" (stuck_lock), "4" (stuck_reader) : "memory");
if (stuck_lock < 0) {
printk(KERN_WARNING "write_lock stuck at %p\n", inline_pc);
" blbs %1,6b;"
" br 1b\n"
".previous"
- : "=m" (__dummy_lock(lock)), "=&r" (regx), "=&r" (stuck_lock)
- : "0" (__dummy_lock(lock)), "2" (stuck_lock));
+ : "=m" (*(volatile int *)lock), "=&r" (regx), "=&r" (stuck_lock)
+ : "0" (*(volatile int *)lock), "2" (stuck_lock) : "memory");
if (stuck_lock < 0) {
printk(KERN_WARNING "read_lock stuck at %p\n", inline_pc);
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
+#include <linux/init.h>
#include <asm/uaccess.h>
#include <asm/io.h>
init_rtc_irq();
}
-void
+void __init
time_init(void)
{
unsigned int year, mon, day, hour, min, sec, cc1, cc2, epoch;
BCD_TO_BIN(year);
}
- /* PC-like is standard; used for year <= 20 || year >= 100 */
+ /* PC-like is standard; used for year < 20 || year >= 70 */
epoch = 1900;
- if (year > 20 && year < 48)
- /* ARC console, used on some not so old boards */
+ if (year >= 20 && year < 48)
+ /* NT epoch */
epoch = 1980;
else if (year >= 48 && year < 70)
- /* Digital UNIX, used on older boards (eg. AXPpxi33) */
+ /* Digital UNIX epoch */
epoch = 1952;
- else if (year >= 70 && year < 100)
- /* Digital DECstations, very old... */
- epoch = 1928;
printk(KERN_INFO "Using epoch = %d\n", epoch);
# Note 2! The CFLAGS definition is now in the main makefile...
O_TARGET := math-emu.o
-O_OBJS := math.o
+O_OBJS := math.o qrnnd.o
CFLAGS += -I. -I$(TOPDIR)/include/math-emu -w
ifeq ($(CONFIG_MATHEMU),m)
#endif /* MODULE */
-/* For 128-bit division. */
-
-void
-udiv128(unsigned long divisor_f0, unsigned long divisor_f1,
- unsigned long dividend_f0, unsigned long dividend_f1,
- unsigned long *quot, unsigned long *remd)
-{
- _FP_FRAC_DECL_2(quo);
- _FP_FRAC_DECL_2(rem);
- _FP_FRAC_DECL_2(tmp);
- unsigned long i, num_bits, bit;
-
- _FP_FRAC_SET_2(rem, _FP_ZEROFRAC_2);
- _FP_FRAC_SET_2(quo, _FP_ZEROFRAC_2);
-
- if (_FP_FRAC_ZEROP_2(divisor))
- goto out;
-
- if (_FP_FRAC_GT_2(divisor, dividend)) {
- _FP_FRAC_COPY_2(rem, dividend);
- goto out;
- }
-
- if (_FP_FRAC_EQ_2(divisor, dividend)) {
- __FP_FRAC_SET_2(quo, 0, 1);
- goto out;
- }
-
- num_bits = 128;
- while (1) {
- bit = _FP_FRAC_NEGP_2(dividend);
- _FP_FRAC_COPY_2(tmp, rem);
- _FP_FRAC_SLL_2(tmp, 1);
- _FP_FRAC_LOW_2(tmp) |= bit;
- if (! _FP_FRAC_GE_2(tmp, divisor))
- break;
- _FP_FRAC_COPY_2(rem, tmp);
- _FP_FRAC_SLL_2(dividend, 1);
- num_bits--;
- }
-
- for (i = 0; i < num_bits; i++) {
- bit = _FP_FRAC_NEGP_2(dividend);
- _FP_FRAC_SLL_2(rem, 1);
- _FP_FRAC_LOW_2(rem) |= bit;
- _FP_FRAC_SUB_2(tmp, rem, divisor);
- bit = _FP_FRAC_NEGP_2(tmp);
- _FP_FRAC_SLL_2(dividend, 1);
- _FP_FRAC_SLL_2(quo, 1);
- if (!bit) {
- _FP_FRAC_LOW_2(quo) |= 1;
- _FP_FRAC_COPY_2(rem, tmp);
- }
- }
-
-out:
- *quot = quo_f1;
- *remd = rem_f1;
- return;
-}
/*
* Emulate the floating point instruction at address PC. Returns 0 if
--- /dev/null
+ # Alpha 21064 __udiv_qrnnd
+ # Copyright (C) 1992, 1994, 1995, 2000 Free Software Foundation, Inc.
+
+ # This file is part of GCC.
+
+ # The GNU MP Library is free software; you can redistribute it and/or modify
+ # it under the terms of the GNU General Public License as published by
+ # the Free Software Foundation; either version 2 of the License, or (at your
+ # option) any later version.
+
+ # In addition to the permissions in the GNU General Public License, the
+ # Free Software Foundation gives you unlimited permission to link the
+ # compiled version of this file with other programs, and to distribute
+ # those programs without any restriction coming from the use of this
+ # file. (The General Public License restrictions do apply in other
+ # respects; for example, they cover modification of the file, and
+ # distribution when not linked into another program.)
+
+ # This file is distributed in the hope that it will be useful, but
+ # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
+ # License for more details.
+
+ # You should have received a copy of the GNU General Public License
+ # along with GCC; see the file COPYING. If not, write to the
+ # Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ # MA 02111-1307, USA.
+
+ .set noreorder
+ .set noat
+
+ .text
+
+ .globl __udiv_qrnnd
+ .ent __udiv_qrnnd
+__udiv_qrnnd:
+ .frame $30,0,$26,0
+ .prologue 0
+
+#define cnt $2
+#define tmp $3
+#define rem_ptr $16
+#define n1 $17
+#define n0 $18
+#define d $19
+#define qb $20
+#define AT $at
+
+ ldiq cnt,16
+ blt d,$largedivisor
+
+$loop1: cmplt n0,0,tmp
+ addq n1,n1,n1
+ bis n1,tmp,n1
+ addq n0,n0,n0
+ cmpule d,n1,qb
+ subq n1,d,tmp
+ cmovne qb,tmp,n1
+ bis n0,qb,n0
+ cmplt n0,0,tmp
+ addq n1,n1,n1
+ bis n1,tmp,n1
+ addq n0,n0,n0
+ cmpule d,n1,qb
+ subq n1,d,tmp
+ cmovne qb,tmp,n1
+ bis n0,qb,n0
+ cmplt n0,0,tmp
+ addq n1,n1,n1
+ bis n1,tmp,n1
+ addq n0,n0,n0
+ cmpule d,n1,qb
+ subq n1,d,tmp
+ cmovne qb,tmp,n1
+ bis n0,qb,n0
+ cmplt n0,0,tmp
+ addq n1,n1,n1
+ bis n1,tmp,n1
+ addq n0,n0,n0
+ cmpule d,n1,qb
+ subq n1,d,tmp
+ cmovne qb,tmp,n1
+ bis n0,qb,n0
+ subq cnt,1,cnt
+ bgt cnt,$loop1
+ stq n1,0(rem_ptr)
+ bis $31,n0,$0
+ ret $31,($26),1
+
+$largedivisor:
+ and n0,1,$4
+
+ srl n0,1,n0
+ sll n1,63,tmp
+ or tmp,n0,n0
+ srl n1,1,n1
+
+ and d,1,$6
+ srl d,1,$5
+ addq $5,$6,$5
+
+$loop2: cmplt n0,0,tmp
+ addq n1,n1,n1
+ bis n1,tmp,n1
+ addq n0,n0,n0
+ cmpule $5,n1,qb
+ subq n1,$5,tmp
+ cmovne qb,tmp,n1
+ bis n0,qb,n0
+ cmplt n0,0,tmp
+ addq n1,n1,n1
+ bis n1,tmp,n1
+ addq n0,n0,n0
+ cmpule $5,n1,qb
+ subq n1,$5,tmp
+ cmovne qb,tmp,n1
+ bis n0,qb,n0
+ cmplt n0,0,tmp
+ addq n1,n1,n1
+ bis n1,tmp,n1
+ addq n0,n0,n0
+ cmpule $5,n1,qb
+ subq n1,$5,tmp
+ cmovne qb,tmp,n1
+ bis n0,qb,n0
+ cmplt n0,0,tmp
+ addq n1,n1,n1
+ bis n1,tmp,n1
+ addq n0,n0,n0
+ cmpule $5,n1,qb
+ subq n1,$5,tmp
+ cmovne qb,tmp,n1
+ bis n0,qb,n0
+ subq cnt,1,cnt
+ bgt cnt,$loop2
+
+ addq n1,n1,n1
+ addq $4,n1,n1
+ bne $6,$Odd
+ stq n1,0(rem_ptr)
+ bis $31,n0,$0
+ ret $31,($26),1
+
+$Odd:
+ /* q' in n0. r' in n1 */
+ addq n1,n0,n1
+
+ cmpult n1,n0,tmp # tmp := carry from addq
+ subq n1,d,AT
+ addq n0,tmp,n0
+ cmovne tmp,AT,n1
+
+ cmpult n1,d,tmp
+ addq n0,1,AT
+ cmoveq tmp,AT,n0
+ subq n1,d,AT
+ cmoveq tmp,AT,n1
+
+ stq n1,0(rem_ptr)
+ bis $31,n0,$0
+ ret $31,($26),1
+
+ .end __udiv_qrnnd
: "r" ((UDItype)(u)), \
"r" ((UDItype)(v)))
-extern void udiv128(unsigned long, unsigned long,
- unsigned long, unsigned long,
- unsigned long *,
- unsigned long *);
-
-#define udiv_qrnnd(q, r, n1, n0, d) \
- do { \
- unsigned long xr, xi; \
- udiv128((n0), (n1), 0, (d), &xr, &xi); \
- (q) = xr; \
- (r) = xi; \
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ do { unsigned long __r; \
+ (q) = __udiv_qrnnd (&__r, (n1), (n0), (d)); \
+ (r) = __r; \
} while (0)
+extern unsigned long __udiv_qrnnd (unsigned long *, unsigned long,
+ unsigned long , unsigned long);
#define UDIV_NEEDS_NORMALIZATION 1
*/
ret = search_exception_table_without_gp(addr);
if (ret) {
- printk(KERN_ALERT, "%s: [%lx] EX_TABLE search fail with"
+ printk(KERN_ALERT "%s: [%lx] EX_TABLE search fail with"
"exc frame GP, success with raw GP\n",
current->comm, addr);
return ret;
# CONFIG_MD_RAID1 is not set
# CONFIG_MD_RAID5 is not set
# CONFIG_BLK_DEV_LVM is not set
+# CONFIG_LVM_PROC_FS is not set
#
# Networking options
# CONFIG_NE2K_PCI is not set
# CONFIG_8139TOO is not set
# CONFIG_SIS900 is not set
+# CONFIG_EPIC100 is not set
+# CONFIG_SUNDANCE is not set
# CONFIG_TLAN is not set
# CONFIG_VIA_RHINE is not set
+# CONFIG_WINBOND_840 is not set
# CONFIG_NET_POCKET is not set
#
# Ethernet (1000 Mbit)
#
# CONFIG_ACENIC is not set
+# CONFIG_HAMACHI is not set
# CONFIG_SK98LIN is not set
# CONFIG_FDDI is not set
# CONFIG_PPP is not set
#
# Sound
#
-# CONFIG_SOUND is not set
+CONFIG_SOUND=y
+# CONFIG_SOUND_CMPCI is not set
+# CONFIG_SOUND_EMU10K1 is not set
+# CONFIG_SOUND_FUSION is not set
+# CONFIG_SOUND_ES1370 is not set
+CONFIG_SOUND_ES1371=y
+# CONFIG_SOUND_ESSSOLO1 is not set
+# CONFIG_SOUND_MAESTRO is not set
+# CONFIG_SOUND_SONICVIBES is not set
+# CONFIG_SOUND_TRIDENT is not set
+# CONFIG_SOUND_MSNDCLAS is not set
+# CONFIG_SOUND_MSNDPIN is not set
+# CONFIG_SOUND_VIA82CXXX is not set
+# CONFIG_SOUND_OSS is not set
+# CONFIG_SOUND_TVMIXER is not set
#
# USB support
#
# USB Controllers
#
-# CONFIG_USB_UHCI is not set
CONFIG_USB_UHCI_ALT=y
# CONFIG_USB_OHCI is not set
int i;
for (i = 0; i < mp_irq_entries; i++)
- if ( (mp_irqs[i].mpc_irqtype == type) &&
- (mp_irqs[i].mpc_dstapic == mp_ioapics[apic].mpc_apicid) &&
- (mp_irqs[i].mpc_dstirq == pin))
-
+ if (mp_irqs[i].mpc_irqtype == type &&
+ (mp_irqs[i].mpc_dstapic == mp_ioapics[apic].mpc_apicid ||
+ mp_irqs[i].mpc_dstapic == MP_APIC_ALL) &&
+ mp_irqs[i].mpc_dstirq == pin)
return i;
return -1;
int lbus = mp_irqs[i].mpc_srcbus;
for (apic = 0; apic < nr_ioapics; apic++)
- if (mp_ioapics[apic].mpc_apicid == mp_irqs[i].mpc_dstapic)
+ if (mp_ioapics[apic].mpc_apicid == mp_irqs[i].mpc_dstapic ||
+ mp_irqs[i].mpc_dstapic == MP_APIC_ALL)
break;
if ((mp_bus_id_to_type[lbus] == MP_BUS_PCI) &&
value &= FLAG_MASK;
value |= get_stack_long(child, EFL_OFFSET) & ~FLAG_MASK;
break;
- case EIP:
- /* Mark us as not being in a system call, so that no restart issues happen */
- put_stack_long(child, 4*ORIG_EAX - sizeof(struct pt_regs), -1);
- break;
}
if (regno > GS*4)
regno -= 2*4;
*/
static spinlock_t call_lock = SPIN_LOCK_UNLOCKED;
-static volatile struct call_data_struct {
+struct call_data_struct {
void (*func) (void *info);
void *info;
atomic_t started;
atomic_t finished;
int wait;
-} *call_data = NULL;
+};
+
+static struct call_data_struct * call_data = NULL;
/*
* this function sends a 'generic call function' IPI to all other CPUs
if (regs->eflags & VM_MASK)
goto debug_vm86;
+ /* Save debug status register where ptrace can see it */
+ tsk->thread.debugreg[6] = condition;
+
/* Mask out spurious TF errors due to lazy TF clearing */
if (condition & DR_STEP) {
/*
#include <linux/mm.h>
#include <linux/pci.h>
#include <linux/acpi.h>
+#include <linux/delay.h>
#include <asm/io.h>
-#include <asm/delay.h>
#include "acpi.h"
#include "driver.h"
cciss_pci_info_struct pciinfo;
if (!arg) return -EINVAL;
- pciinfo.bus = hba[ctlr]->pci_bus;
- pciinfo.dev_fn = hba[ctlr]->pci_dev_fn;
+ pciinfo.bus = hba[ctlr]->pci_dev->bus->number;
+ pciinfo.dev_fn = hba[ctlr]->pci_dev->devfn;
pciinfo.board_id = hba[ctlr]->board_id;
if(copy_to_user((void *) arg, &pciinfo, sizeof( cciss_pci_info_struct)))
return -EFAULT;
/*
* Map (physical) PCI mem into (virtual) kernel space
*/
-static ulong remap_pci_mem(ulong base, ulong size)
+static void *remap_pci_mem(ulong base, ulong size)
{
ulong page_base = ((ulong) base) & PAGE_MASK;
ulong page_offs = ((ulong) base) - page_base;
- ulong page_remapped = (ulong) ioremap(page_base, page_offs+size);
+ void *page_remapped = ioremap(page_base, page_offs+size);
- return (ulong) (page_remapped ? (page_remapped + page_offs) : 0UL);
+ return (page_remapped ? (page_remapped + page_offs) : NULL);
}
/*
}
#endif /* CCISS_DEBUG */
-static int cciss_pci_init(ctlr_info_t *c, unchar bus, unchar device_fn)
+static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
{
ushort vendor_id, device_id, command;
unchar cache_line_size, latency_timer;
unchar irq, revision;
- uint addr[6];
+ unsigned long addr[6];
__u32 board_id;
- struct pci_dev *pdev;
int i;
- pdev = pci_find_slot(bus, device_fn);
vendor_id = pdev->vendor;
device_id = pdev->device;
irq = pdev->irq;
for(i=0; i<6; i++)
- addr[i] = pdev->resource[i].start;
+ addr[i] = pci_resource_start(pdev, i);
(void) pci_read_config_word(pdev, PCI_COMMAND,&command);
(void) pci_read_config_byte(pdev, PCI_CLASS_REVISION,&revision);
(void) pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache_line_size);
*/
static int cciss_pci_detect(void)
{
+ struct pci_dev *pdev;
- int index;
- unchar bus=0, dev_fn=0;
-
- for(index=0; ; index++) {
- if (pcibios_find_device(PCI_VENDOR_ID_COMPAQ,
- PCI_DEVICE_ID_COMPAQ_CISS,
- index, &bus, &dev_fn))
- break;
+ pdev = pci_find_device(PCI_VENDOR_ID_COMPAQ,
+ PCI_DEVICE_ID_COMPAQ_CISS, NULL);
+ while (pdev) {
printk(KERN_DEBUG "cciss: Device %x has been found at %x %x\n",
- PCI_DEVICE_ID_COMPAQ_CISS, bus, dev_fn);
- if (index == 1000000) break;
+ PCI_DEVICE_ID_COMPAQ_CISS,
+ pdev->bus->number, pdev->devfn);
if (nr_ctlr == 8) {
printk(KERN_WARNING "cciss: This driver"
" supports a maximum of 8 controllers.\n");
break;
}
+ if (pci_enable_device(pdev))
+ continue;
hba[nr_ctlr] = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL);
if(hba[nr_ctlr]==NULL)
{
continue;
}
memset(hba[nr_ctlr], 0, sizeof(ctlr_info_t));
- if (cciss_pci_init(hba[nr_ctlr], bus, dev_fn) != 0)
+ if (cciss_pci_init(hba[nr_ctlr], pdev) != 0)
{
kfree(hba[nr_ctlr]);
continue;
}
sprintf(hba[nr_ctlr]->devname, "cciss%d", nr_ctlr);
hba[nr_ctlr]->ctlr = nr_ctlr;
- hba[nr_ctlr]->pci_bus = bus;
- hba[nr_ctlr]->pci_dev_fn = dev_fn;
+ hba[nr_ctlr]->pci_dev = pdev;
nr_ctlr++;
- }
+ pdev = pci_find_device(PCI_VENDOR_ID_COMPAQ,
+ PCI_DEVICE_ID_COMPAQ_CISS, pdev);
+ }
return nr_ctlr;
}
/* Turn board interrupts off */
hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_OFF);
free_irq(hba[i]->intr, hba[i]);
- iounmap((void*)hba[i]->vaddr);
+ iounmap(hba[i]->vaddr);
unregister_blkdev(MAJOR_NR+i, hba[i]->devname);
blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR + i));
remove_proc_entry(hba[i]->devname, proc_cciss);
char devname[8];
char *product_name;
char firm_ver[4]; // Firmware version
- unchar pci_bus;
- unchar pci_dev_fn;
+ struct pci_dev *pci_dev;
__u32 board_id;
- __u32 vaddr;
- __u32 paddr;
+ void *vaddr;
+ unsigned long paddr;
CfgTable_struct *cfgtable;
int intr;
int cpqarray_init(void);
static int cpqarray_pci_detect(void);
-static int cpqarray_pci_init(ctlr_info_t *c, unchar bus, unchar device_fn);
-static ulong remap_pci_mem(ulong base, ulong size);
+static int cpqarray_pci_init(ctlr_info_t *c, struct pci_dev *pdev);
+static void *remap_pci_mem(ulong base, ulong size);
static int cpqarray_eisa_detect(void);
static int pollcomplete(int ctlr);
static void getgeometry(int ctlr);
for(i=0; i<nr_ctlr; i++) {
hba[i]->access.set_intr_mask(hba[i], 0);
free_irq(hba[i]->intr, hba[i]);
- iounmap((void*)hba[i]->vaddr);
+ iounmap(hba[i]->vaddr);
unregister_blkdev(MAJOR_NR+i, hba[i]->devname);
del_timer(&hba[i]->timer);
blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR + i));
*/
static int cpqarray_pci_detect(void)
{
- int index;
- unchar bus=0, dev_fn=0;
+ struct pci_dev *pdev;
#define IDA_BOARD_TYPES 3
static int ida_vendor_id[IDA_BOARD_TYPES] = { PCI_VENDOR_ID_DEC,
/* search for all PCI board types that could be for this driver */
for(brdtype=0; brdtype<IDA_BOARD_TYPES; brdtype++)
{
- for(index=0; ; index++) {
- if (pcibios_find_device(ida_vendor_id[brdtype],
- ida_device_id[brdtype], index, &bus, &dev_fn))
- break;
+ pdev = pci_find_device(ida_vendor_id[brdtype],
+ ida_device_id[brdtype], NULL);
+ while (pdev) {
printk(KERN_DEBUG "cpqarray: Device %x has been found at %x %x\n",
- ida_vendor_id[brdtype], bus, dev_fn);
- if (index == 1000000) break;
+ ida_vendor_id[brdtype],
+ pdev->bus->number, pdev->devfn);
if (nr_ctlr == 8) {
printk(KERN_WARNING "cpqarray: This driver"
" supports a maximum of 8 controllers.\n");
break;
}
-
+
/* if it is a PCI_DEVICE_ID_NCR_53C1510, make sure it's the Compaq version of the chip */
if (ida_device_id[brdtype] == PCI_DEVICE_ID_NCR_53C1510) {
- unsigned short subvendor=0;
- if(pcibios_read_config_word(bus, dev_fn,
- PCI_SUBSYSTEM_VENDOR_ID, &subvendor))
- {
- printk(KERN_DEBUG "cpqarray: failed to read subvendor\n");
- continue;
- }
+ unsigned short subvendor=pdev->subsystem_vendor;
if(subvendor != PCI_VENDOR_ID_COMPAQ)
{
printk(KERN_DEBUG
continue;
}
memset(hba[nr_ctlr], 0, sizeof(ctlr_info_t));
- if (cpqarray_pci_init(hba[nr_ctlr], bus, dev_fn) != 0)
+ if (cpqarray_pci_init(hba[nr_ctlr], pdev) != 0)
{
kfree(hba[nr_ctlr]);
continue;
hba[nr_ctlr]->ctlr = nr_ctlr;
nr_ctlr++;
+ pdev = pci_find_device(ida_vendor_id[brdtype],
+ ida_device_id[brdtype], pdev);
}
}
* Find the IO address of the controller, its IRQ and so forth. Fill
* in some basic stuff into the ctlr_info_t structure.
*/
-static int cpqarray_pci_init(ctlr_info_t *c, unchar bus, unchar device_fn)
+static int cpqarray_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
{
ushort vendor_id, device_id, command;
unchar cache_line_size, latency_timer;
unchar irq, revision;
- uint addr[6];
+ unsigned long addr[6];
__u32 board_id;
- struct pci_dev *pdev;
int i;
- c->pci_bus = bus;
- c->pci_dev_fn = device_fn;
- pdev = pci_find_slot(bus, device_fn);
+ c->pci_dev = pdev;
vendor_id = pdev->vendor;
device_id = pdev->device;
irq = pdev->irq;
for(i=0; i<6; i++)
- addr[i] = pdev->resource[i].flags;
+ addr[i] = pci_resource_start(pdev, i);
if (pci_enable_device(pdev))
return -1;
printk("device_id = %x\n", device_id);
printk("command = %x\n", command);
for(i=0; i<6; i++)
- printk("addr[%d] = %x\n", i, addr[i]);
+ printk("addr[%d] = %lx\n", i, addr[i]);
printk("revision = %x\n", revision);
printk("irq = %x\n", irq);
printk("cache_line_size = %x\n", cache_line_size);
);
c->intr = irq;
- c->ioaddr = addr[0] & ~0x1;
+ c->ioaddr = addr[0];
- /*
- * Memory base addr is first addr with the first bit _not_ set
- */
+ c->paddr = 0;
for(i=0; i<6; i++)
- if (!(addr[i] & 0x1)) {
+ if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) {
c->paddr = pci_resource_start (pdev, i);
break;
}
+ if (!c->paddr)
+ return -1;
c->vaddr = remap_pci_mem(c->paddr, 128);
+ if (!c->vaddr)
+ return -1;
c->board_id = board_id;
for(i=0; i<NR_PRODUCTS; i++) {
/*
* Map (physical) PCI mem into (virtual) kernel space
*/
-static ulong remap_pci_mem(ulong base, ulong size)
+static void *remap_pci_mem(ulong base, ulong size)
{
ulong page_base = ((ulong) base) & PAGE_MASK;
ulong page_offs = ((ulong) base) - page_base;
- ulong page_remapped = (ulong) ioremap(page_base, page_offs+size);
+ void *page_remapped = ioremap(page_base, page_offs+size);
- return (ulong) (page_remapped ? (page_remapped + page_offs) : 0UL);
+ return (page_remapped ? (page_remapped + page_offs) : NULL);
}
#ifndef MODULE
hba[nr_ctlr]->access = *(products[j].access);
hba[nr_ctlr]->ctlr = nr_ctlr;
hba[nr_ctlr]->board_id = board_id;
- hba[nr_ctlr]->pci_bus = 0; /* not PCI */
- hba[nr_ctlr]->pci_dev_fn = 0; /* not PCI */
+ hba[nr_ctlr]->pci_dev = NULL; /* not PCI */
DBGINFO(
printk("i = %d, j = %d\n", i, j);
ida_pci_info_struct pciinfo;
if (!arg) return -EINVAL;
- pciinfo.bus = hba[ctlr]->pci_bus;
- pciinfo.dev_fn = hba[ctlr]->pci_dev_fn;
+ pciinfo.bus = hba[ctlr]->pci_dev->bus->number;
+ pciinfo.dev_fn = hba[ctlr]->pci_dev->devfn;
pciinfo.board_id = hba[ctlr]->board_id;
if(copy_to_user((void *) arg, &pciinfo,
sizeof( ida_pci_info_struct)))
int log_drives;
int phys_drives;
- unsigned char pci_bus; /* 0 if EISA */
- unsigned char pci_dev_fn; /* 0 if EISA */
+ struct pci_dev *pci_dev; /* NULL if EISA */
__u32 board_id;
char *product_name;
- __u32 vaddr;
- __u32 paddr;
- __u32 ioaddr;
+ void *vaddr;
+ unsigned long paddr;
+ unsigned long ioaddr;
int intr;
int usage_count;
drv_info_t drv[NWD];
#ifdef CONFIG_SJCD
sjcd_init();
#endif CONFIG_SJCD
-#ifdef CONFIG_BLK_DEV_MD
- md_init();
-#endif CONFIG_BLK_DEV_MD
#ifdef CONFIG_APBLOCK
ap_init();
#endif
+++ /dev/null
-/*
- * kernel/lvm-snap.c
- *
- * Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE
- *
- * LVM snapshot driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * LVM driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/vmalloc.h>
-#include <linux/blkdev.h>
-#include <linux/smp_lock.h>
-#include <linux/types.h>
-#include <linux/iobuf.h>
-#include <linux/lvm.h>
-
-
-static char *lvm_snap_version __attribute__ ((unused)) = "LVM 0.8final (15/02/2000)\n";
-
-extern const char *const lvm_name;
-extern int lvm_blocksizes[];
-
-void lvm_snapshot_release(lv_t *);
-
-#define hashfn(dev,block,mask,chunk_size) \
- ((HASHDEV(dev)^((block)/(chunk_size))) & (mask))
-
-static inline lv_block_exception_t *
-lvm_find_exception_table(kdev_t org_dev, unsigned long org_start, lv_t * lv)
-{
- struct list_head * hash_table = lv->lv_snapshot_hash_table, * next;
- unsigned long mask = lv->lv_snapshot_hash_mask;
- int chunk_size = lv->lv_chunk_size;
- lv_block_exception_t * ret;
- int i = 0;
-
- hash_table = &hash_table[hashfn(org_dev, org_start, mask, chunk_size)];
- ret = NULL;
- for (next = hash_table->next; next != hash_table; next = next->next)
- {
- lv_block_exception_t * exception;
-
- exception = list_entry(next, lv_block_exception_t, hash);
- if (exception->rsector_org == org_start &&
- exception->rdev_org == org_dev)
- {
- if (i)
- {
- /* fun, isn't it? :) */
- list_del(next);
- list_add(next, hash_table);
- }
- ret = exception;
- break;
- }
- i++;
- }
- return ret;
-}
-
-static inline void lvm_hash_link(lv_block_exception_t * exception,
- kdev_t org_dev, unsigned long org_start,
- lv_t * lv)
-{
- struct list_head * hash_table = lv->lv_snapshot_hash_table;
- unsigned long mask = lv->lv_snapshot_hash_mask;
- int chunk_size = lv->lv_chunk_size;
-
- hash_table = &hash_table[hashfn(org_dev, org_start, mask, chunk_size)];
- list_add(&exception->hash, hash_table);
-}
-
-int lvm_snapshot_remap_block(kdev_t * org_dev, unsigned long * org_sector,
- unsigned long pe_start, lv_t * lv)
-{
- int ret;
- unsigned long pe_off, pe_adjustment, __org_start;
- kdev_t __org_dev;
- int chunk_size = lv->lv_chunk_size;
- lv_block_exception_t * exception;
-
- pe_off = pe_start % chunk_size;
- pe_adjustment = (*org_sector-pe_off) % chunk_size;
- __org_start = *org_sector - pe_adjustment;
- __org_dev = *org_dev;
-
- ret = 0;
- exception = lvm_find_exception_table(__org_dev, __org_start, lv);
- if (exception)
- {
- *org_dev = exception->rdev_new;
- *org_sector = exception->rsector_new + pe_adjustment;
- ret = 1;
- }
- return ret;
-}
-
-static void lvm_drop_snapshot(lv_t * lv_snap, const char * reason)
-{
- kdev_t last_dev;
- int i;
-
- /* no exception storage space available for this snapshot
- or error on this snapshot --> release it */
- invalidate_buffers(lv_snap->lv_dev);
-
- last_dev = 0;
- for (i = 0; i < lv_snap->lv_remap_ptr; i++) {
- if ( lv_snap->lv_block_exception[i].rdev_new != last_dev) {
- last_dev = lv_snap->lv_block_exception[i].rdev_new;
- invalidate_buffers(last_dev);
- }
- }
-
- lvm_snapshot_release(lv_snap);
-
- printk(KERN_INFO
- "%s -- giving up to snapshot %s on %s due %s\n",
- lvm_name, lv_snap->lv_snapshot_org->lv_name, lv_snap->lv_name,
- reason);
-}
-
-static inline void lvm_snapshot_prepare_blocks(unsigned long * blocks,
- unsigned long start,
- int nr_sectors,
- int blocksize)
-{
- int i, sectors_per_block, nr_blocks;
-
- sectors_per_block = blocksize >> 9;
- nr_blocks = nr_sectors / sectors_per_block;
- start /= sectors_per_block;
-
- for (i = 0; i < nr_blocks; i++)
- blocks[i] = start++;
-}
-
-static inline int get_blksize(kdev_t dev)
-{
- int correct_size = BLOCK_SIZE, i, major;
-
- major = MAJOR(dev);
- if (blksize_size[major])
- {
- i = blksize_size[major][MINOR(dev)];
- if (i)
- correct_size = i;
- }
- return correct_size;
-}
-
-#ifdef DEBUG_SNAPSHOT
-static inline void invalidate_snap_cache(unsigned long start, unsigned long nr,
- kdev_t dev)
-{
- struct buffer_head * bh;
- int sectors_per_block, i, blksize, minor;
-
- minor = MINOR(dev);
- blksize = lvm_blocksizes[minor];
- sectors_per_block = blksize >> 9;
- nr /= sectors_per_block;
- start /= sectors_per_block;
-
- for (i = 0; i < nr; i++)
- {
- bh = get_hash_table(dev, start++, blksize);
- if (bh)
- bforget(bh);
- }
-}
-#endif
-
-/*
- * copy on write handler for one snapshot logical volume
- *
- * read the original blocks and store it/them on the new one(s).
- * if there is no exception storage space free any longer --> release snapshot.
- *
- * this routine gets called for each _first_ write to a physical chunk.
- */
-int lvm_snapshot_COW(kdev_t org_phys_dev,
- unsigned long org_phys_sector,
- unsigned long org_pe_start,
- unsigned long org_virt_sector,
- lv_t * lv_snap)
-{
- const char * reason;
- unsigned long org_start, snap_start, virt_start, pe_off;
- int idx = lv_snap->lv_remap_ptr, chunk_size = lv_snap->lv_chunk_size;
- kdev_t snap_phys_dev;
- struct kiobuf * iobuf;
- unsigned long blocks[KIO_MAX_SECTORS];
- int blksize_snap, blksize_org, min_blksize, max_blksize;
- int max_sectors, nr_sectors;
-
- /* check if we are out of snapshot space */
- if (idx >= lv_snap->lv_remap_end)
- goto fail_out_of_space;
-
- /* calculate physical boundaries of source chunk */
- pe_off = org_pe_start % chunk_size;
- org_start = org_phys_sector - ((org_phys_sector-pe_off) % chunk_size);
- virt_start = org_virt_sector - (org_phys_sector - org_start);
-
- /* calculate physical boundaries of destination chunk */
- snap_phys_dev = lv_snap->lv_block_exception[idx].rdev_new;
- snap_start = lv_snap->lv_block_exception[idx].rsector_new;
-
-#ifdef DEBUG_SNAPSHOT
- printk(KERN_INFO
- "%s -- COW: "
- "org %02d:%02d faulting %lu start %lu, "
- "snap %02d:%02d start %lu, "
- "size %d, pe_start %lu pe_off %lu, virt_sec %lu\n",
- lvm_name,
- MAJOR(org_phys_dev), MINOR(org_phys_dev), org_phys_sector,
- org_start,
- MAJOR(snap_phys_dev), MINOR(snap_phys_dev), snap_start,
- chunk_size,
- org_pe_start, pe_off,
- org_virt_sector);
-#endif
-
- iobuf = lv_snap->lv_iobuf;
-
- blksize_org = get_blksize(org_phys_dev);
- blksize_snap = get_blksize(snap_phys_dev);
- max_blksize = max(blksize_org, blksize_snap);
- min_blksize = min(blksize_org, blksize_snap);
- max_sectors = KIO_MAX_SECTORS * (min_blksize>>9);
-
- if (chunk_size % (max_blksize>>9))
- goto fail_blksize;
-
- while (chunk_size)
- {
- nr_sectors = min(chunk_size, max_sectors);
- chunk_size -= nr_sectors;
-
- iobuf->length = nr_sectors << 9;
-
- lvm_snapshot_prepare_blocks(blocks, org_start,
- nr_sectors, blksize_org);
- if (brw_kiovec(READ, 1, &iobuf, org_phys_dev,
- blocks, blksize_org) != (nr_sectors<<9))
- goto fail_raw_read;
-
- lvm_snapshot_prepare_blocks(blocks, snap_start,
- nr_sectors, blksize_snap);
- if (brw_kiovec(WRITE, 1, &iobuf, snap_phys_dev,
- blocks, blksize_snap) != (nr_sectors<<9))
- goto fail_raw_write;
- }
-
-#ifdef DEBUG_SNAPSHOT
- /* invalidate the logcial snapshot buffer cache */
- invalidate_snap_cache(virt_start, lv_snap->lv_chunk_size,
- lv_snap->lv_dev);
-#endif
-
- /* the original chunk is now stored on the snapshot volume
- so update the execption table */
- lv_snap->lv_block_exception[idx].rdev_org = org_phys_dev;
- lv_snap->lv_block_exception[idx].rsector_org = org_start;
- lvm_hash_link(lv_snap->lv_block_exception + idx,
- org_phys_dev, org_start, lv_snap);
- lv_snap->lv_remap_ptr = idx + 1;
- return 1;
-
- /* slow path */
- out:
- lvm_drop_snapshot(lv_snap, reason);
- return -1;
-
- fail_out_of_space:
- reason = "out of space";
- goto out;
- fail_raw_read:
- reason = "read error";
- goto out;
- fail_raw_write:
- reason = "write error";
- goto out;
- fail_blksize:
- reason = "blocksize error";
- goto out;
-}
-
-static int lvm_snapshot_alloc_iobuf_pages(struct kiobuf * iobuf, int sectors)
-{
- int bytes, nr_pages, err, i;
-
- bytes = sectors << 9;
- nr_pages = (bytes + ~PAGE_MASK) >> PAGE_SHIFT;
- err = expand_kiobuf(iobuf, nr_pages);
- if (err)
- goto out;
-
- err = -ENOMEM;
- iobuf->locked = 1;
- iobuf->nr_pages = 0;
- for (i = 0; i < nr_pages; i++)
- {
- struct page * page;
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27)
- page = alloc_page(GFP_KERNEL);
- if (!page)
- goto out;
-#else
- {
- unsigned long addr = __get_free_page(GFP_USER);
- if (!addr)
- goto out;
- iobuf->pagelist[i] = addr;
- page = virt_to_page(addr);
- }
-#endif
-
- iobuf->maplist[i] = page;
- /* the only point to lock the page here is to be allowed
- to share unmap_kiobuf() in the fail-path */
-#ifndef LockPage
-#define LockPage(map) set_bit(PG_locked, &(map)->flags)
-#endif
- LockPage(page);
- iobuf->nr_pages++;
- }
- iobuf->offset = 0;
-
- err = 0;
- out:
- return err;
-}
-
-static int calc_max_buckets(void)
-{
- unsigned long mem;
-
- mem = num_physpages << PAGE_SHIFT;
- mem /= 100;
- mem *= 2;
- mem /= sizeof(struct list_head);
-
- return mem;
-}
-
-static int lvm_snapshot_alloc_hash_table(lv_t * lv)
-{
- int err;
- unsigned long buckets, max_buckets, size;
- struct list_head * hash;
-
- buckets = lv->lv_remap_end;
- max_buckets = calc_max_buckets();
- buckets = min(buckets, max_buckets);
- while (buckets & (buckets-1))
- buckets &= (buckets-1);
-
- size = buckets * sizeof(struct list_head);
-
- err = -ENOMEM;
- hash = vmalloc(size);
- lv->lv_snapshot_hash_table = hash;
-
- if (!hash)
- goto out;
-
- lv->lv_snapshot_hash_mask = buckets-1;
- while (buckets--)
- INIT_LIST_HEAD(hash+buckets);
- err = 0;
- out:
- return err;
-}
-
-int lvm_snapshot_alloc(lv_t * lv_snap)
-{
- int err, blocksize, max_sectors;
-
- err = alloc_kiovec(1, &lv_snap->lv_iobuf);
- if (err)
- goto out;
-
- blocksize = lvm_blocksizes[MINOR(lv_snap->lv_dev)];
- max_sectors = KIO_MAX_SECTORS << (PAGE_SHIFT-9);
-
- err = lvm_snapshot_alloc_iobuf_pages(lv_snap->lv_iobuf, max_sectors);
- if (err)
- goto out_free_kiovec;
-
- err = lvm_snapshot_alloc_hash_table(lv_snap);
- if (err)
- goto out_free_kiovec;
- out:
- return err;
-
- out_free_kiovec:
- unmap_kiobuf(lv_snap->lv_iobuf);
- free_kiovec(1, &lv_snap->lv_iobuf);
- goto out;
-}
-
-void lvm_snapshot_release(lv_t * lv)
-{
- if (lv->lv_block_exception)
- {
- vfree(lv->lv_block_exception);
- lv->lv_block_exception = NULL;
- }
- if (lv->lv_snapshot_hash_table)
- {
- vfree(lv->lv_snapshot_hash_table);
- lv->lv_snapshot_hash_table = NULL;
- }
- if (lv->lv_iobuf)
- {
- free_kiovec(1, &lv->lv_iobuf);
- lv->lv_iobuf = NULL;
- }
-}
+++ /dev/null
-/*
- * kernel/lvm.c
- *
- * Copyright (C) 1997 - 2000 Heinz Mauelshagen, Germany
- *
- * February-November 1997
- * April-May,July-August,November 1998
- * January-March,May,July,September,October 1999
- * January,February 2000
- *
- *
- * LVM driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * LVM driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- */
-
-/*
- * Changelog
- *
- * 09/11/1997 - added chr ioctls VG_STATUS_GET_COUNT
- * and VG_STATUS_GET_NAMELIST
- * 18/01/1998 - change lvm_chr_open/close lock handling
- * 30/04/1998 - changed LV_STATUS ioctl to LV_STATUS_BYNAME and
- * - added LV_STATUS_BYINDEX ioctl
- * - used lvm_status_byname_req_t and
- * lvm_status_byindex_req_t vars
- * 04/05/1998 - added multiple device support
- * 08/05/1998 - added support to set/clear extendable flag in volume group
- * 09/05/1998 - changed output of lvm_proc_get_info() because of
- * support for free (eg. longer) logical volume names
- * 12/05/1998 - added spin_locks (thanks to Pascal van Dam
- * <pascal@ramoth.xs4all.nl>)
- * 25/05/1998 - fixed handling of locked PEs in lvm_map() and lvm_chr_ioctl()
- * 26/05/1998 - reactivated verify_area by access_ok
- * 07/06/1998 - used vmalloc/vfree instead of kmalloc/kfree to go
- * beyond 128/256 KB max allocation limit per call
- * - #ifdef blocked spin_lock calls to avoid compile errors
- * with 2.0.x
- * 11/06/1998 - another enhancement to spinlock code in lvm_chr_open()
- * and use of LVM_VERSION_CODE instead of my own macros
- * (thanks to Michael Marxmeier <mike@msede.com>)
- * 07/07/1998 - added statistics in lvm_map()
- * 08/07/1998 - saved statistics in lvm_do_lv_extend_reduce()
- * 25/07/1998 - used __initfunc macro
- * 02/08/1998 - changes for official char/block major numbers
- * 07/08/1998 - avoided init_module() and cleanup_module() to be static
- * 30/08/1998 - changed VG lv_open counter from sum of LV lv_open counters
- * to sum of LVs open (no matter how often each is)
- * 01/09/1998 - fixed lvm_gendisk.part[] index error
- * 07/09/1998 - added copying of lv_current_pe-array
- * in LV_STATUS_BYINDEX ioctl
- * 17/11/1998 - added KERN_* levels to printk
- * 13/01/1999 - fixed LV index bug in lvm_do_lv_create() which hit lvrename
- * 07/02/1999 - fixed spinlock handling bug in case of LVM_RESET
- * by moving spinlock code from lvm_chr_open()
- * to lvm_chr_ioctl()
- * - added LVM_LOCK_LVM ioctl to lvm_chr_ioctl()
- * - allowed LVM_RESET and retrieval commands to go ahead;
- * only other update ioctls are blocked now
- * - fixed pv->pe to NULL for pv_status
- * - using lv_req structure in lvm_chr_ioctl() now
- * - fixed NULL ptr reference bug in lvm_do_lv_extend_reduce()
- * caused by uncontiguous PV array in lvm_chr_ioctl(VG_REDUCE)
- * 09/02/1999 - changed BLKRASET and BLKRAGET in lvm_chr_ioctl() to
- * handle lgoical volume private read ahead sector
- * - implemented LV read_ahead handling with lvm_blk_read()
- * and lvm_blk_write()
- * 10/02/1999 - implemented 2.[12].* support function lvm_hd_name()
- * to be used in drivers/block/genhd.c by disk_name()
- * 12/02/1999 - fixed index bug in lvm_blk_ioctl(), HDIO_GETGEO
- * - enhanced gendisk insert/remove handling
- * 16/02/1999 - changed to dynamic block minor number allocation to
- * have as much as 99 volume groups with 256 logical volumes
- * as the grand total; this allows having 1 volume group with
- * up to 256 logical volumes in it
- * 21/02/1999 - added LV open count information to proc filesystem
- * - substituted redundant LVM_RESET code by calls
- * to lvm_do_vg_remove()
- * 22/02/1999 - used schedule_timeout() to be more responsive
- * in case of lvm_do_vg_remove() with lots of logical volumes
- * 19/03/1999 - fixed NULL pointer bug in module_init/lvm_init
- * 17/05/1999 - used DECLARE_WAIT_QUEUE_HEAD macro (>2.3.0)
- * - enhanced lvm_hd_name support
- * 03/07/1999 - avoided use of KERNEL_VERSION macro based ifdefs and
- * memcpy_tofs/memcpy_fromfs macro redefinitions
- * 06/07/1999 - corrected reads/writes statistic counter copy in case
- * of striped logical volume
- * 28/07/1999 - implemented snapshot logical volumes
- * - lvm_chr_ioctl
- * - LV_STATUS_BYINDEX
- * - LV_STATUS_BYNAME
- * - lvm_do_lv_create
- * - lvm_do_lv_remove
- * - lvm_map
- * - new lvm_snapshot_remap_block
- * - new lvm_snapshot_remap_new_block
- * 08/10/1999 - implemented support for multiple snapshots per
- * original logical volume
- * 12/10/1999 - support for 2.3.19
- * 11/11/1999 - support for 2.3.28
- * 21/11/1999 - changed lvm_map() interface to buffer_head based
- * 19/12/1999 - support for 2.3.33
- * 01/01/2000 - changed locking concept in lvm_map(),
- * lvm_do_vg_create() and lvm_do_lv_remove()
- * 15/01/2000 - fixed PV_FLUSH bug in lvm_chr_ioctl()
- * 24/01/2000 - ported to 2.3.40 including Alan Cox's pointer changes etc.
- * 29/01/2000 - used kmalloc/kfree again for all small structures
- * 20/01/2000 - cleaned up lvm_chr_ioctl by moving code
- * to seperated functions
- * - avoided "/dev/" in proc filesystem output
- * - avoided inline strings functions lvm_strlen etc.
- * 14/02/2000 - support for 2.3.43
- * - integrated Andrea Arcangeli's snapshot code
- *
- */
-
-
-static char *lvm_version = "LVM version 0.8final by Heinz Mauelshagen (15/02/2000)\n";
-static char *lvm_short_version = "version 0.8final (15/02/2000)";
-
-#define MAJOR_NR LVM_BLK_MAJOR
-#define DEVICE_OFF(device)
-
-#include <linux/config.h>
-#include <linux/version.h>
-
-#ifdef MODVERSIONS
-#undef MODULE
-#define MODULE
-#include <linux/modversions.h>
-#endif
-
-#include <linux/module.h>
-
-#include <linux/kernel.h>
-#include <linux/vmalloc.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-
-#include <linux/hdreg.h>
-#include <linux/stat.h>
-#include <linux/fs.h>
-#include <linux/proc_fs.h>
-#include <linux/blkdev.h>
-#include <linux/genhd.h>
-#include <linux/locks.h>
-#include <linux/smp_lock.h>
-#include <asm/ioctl.h>
-#include <asm/segment.h>
-#include <asm/uaccess.h>
-
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
-#endif
-
-#define LOCAL_END_REQUEST
-
-#include <linux/blk.h>
-#include <linux/blkpg.h>
-
-#include <linux/errno.h>
-#include <linux/lvm.h>
-
-#define LVM_CORRECT_READ_AHEAD(a) \
- (((a) < LVM_MIN_READ_AHEAD || (a) > LVM_MAX_READ_AHEAD) \
- ? LVM_MAX_READ_AHEAD : (a))
-
-#ifndef WRITEA
-# define WRITEA WRITE
-#endif
-
-/*
- * External function prototypes
- */
-#ifdef MODULE
-int init_module(void);
-void cleanup_module(void);
-#else
-extern int lvm_init(void);
-#endif
-
-static void lvm_dummy_device_request(request_queue_t *);
-#define DEVICE_REQUEST lvm_dummy_device_request
-
-static int lvm_make_request_fn(request_queue_t *, int, struct buffer_head*);
-static void lvm_plug_device_noop(request_queue_t *, kdev_t);
-
-static int lvm_blk_ioctl(struct inode *, struct file *, uint, ulong);
-static int lvm_blk_open(struct inode *, struct file *);
-
-static int lvm_chr_open(struct inode *, struct file *);
-
-static int lvm_chr_close(struct inode *, struct file *);
-static int lvm_blk_close(struct inode *, struct file *);
-
-static int lvm_chr_ioctl(struct inode *, struct file *, uint, ulong);
-
-#if defined CONFIG_LVM_PROC_FS && defined CONFIG_PROC_FS
-static int lvm_proc_get_info(char *, char **, off_t, int);
-static int (*lvm_proc_get_info_ptr) (char *, char **, off_t, int) =
-&lvm_proc_get_info;
-#endif
-
-#ifdef LVM_HD_NAME
-void lvm_hd_name(char *, int);
-#endif
-/* End external function prototypes */
-
-
-/*
- * Internal function prototypes
- */
-static void lvm_init_vars(void);
-
-/* external snapshot calls */
-int lvm_snapshot_remap_block(kdev_t *, ulong *, ulong, lv_t *);
-int lvm_snapshot_COW(kdev_t, ulong, ulong, ulong, lv_t *);
-int lvm_snapshot_alloc(lv_t *);
-void lvm_snapshot_release(lv_t *);
-
-#ifdef LVM_HD_NAME
-extern void (*lvm_hd_name_ptr) (char *, int);
-#endif
-static int lvm_map(struct buffer_head *, int);
-static int lvm_do_lock_lvm(void);
-static int lvm_do_le_remap(vg_t *, void *);
-static int lvm_do_pe_lock_unlock(vg_t *r, void *);
-static int lvm_do_vg_create(int, void *);
-static int lvm_do_vg_extend(vg_t *, void *);
-static int lvm_do_vg_reduce(vg_t *, void *);
-static int lvm_do_vg_remove(int);
-static int lvm_do_lv_create(int, char *, lv_t *);
-static int lvm_do_lv_remove(int, char *, int);
-static int lvm_do_lv_extend_reduce(int, char *, lv_t *);
-static int lvm_do_lv_status_byname(vg_t *r, void *);
-static int lvm_do_lv_status_byindex(vg_t *, void *arg);
-static int lvm_do_pv_change(vg_t*, void*);
-static int lvm_do_pv_status(vg_t *, void *);
-static void lvm_geninit(struct gendisk *);
-#ifdef LVM_GET_INODE
-static struct inode *lvm_get_inode(kdev_t);
-void lvm_clear_inode(struct inode *);
-#endif
-/* END Internal function prototypes */
-
-
-/* volume group descriptor area pointers */
-static vg_t *vg[ABS_MAX_VG];
-static pv_t *pvp = NULL;
-static lv_t *lvp = NULL;
-static pe_t *pep = NULL;
-static pe_t *pep1 = NULL;
-
-
-/* map from block minor number to VG and LV numbers */
-typedef struct {
- int vg_number;
- int lv_number;
-} vg_lv_map_t;
-static vg_lv_map_t vg_lv_map[ABS_MAX_LV];
-
-
-/* Request structures (lvm_chr_ioctl()) */
-static pv_change_req_t pv_change_req;
-static pv_flush_req_t pv_flush_req;
-static pv_status_req_t pv_status_req;
-static pe_lock_req_t pe_lock_req;
-static le_remap_req_t le_remap_req;
-static lv_req_t lv_req;
-
-#ifdef LVM_TOTAL_RESET
-static int lvm_reset_spindown = 0;
-#endif
-
-static char pv_name[NAME_LEN];
-/* static char rootvg[NAME_LEN] = { 0, }; */
-static uint lv_open = 0;
-const char *const lvm_name = LVM_NAME;
-static int lock = 0;
-static int loadtime = 0;
-static uint vg_count = 0;
-static long lvm_chr_open_count = 0;
-static ushort lvm_iop_version = LVM_DRIVER_IOP_VERSION;
-static DECLARE_WAIT_QUEUE_HEAD(lvm_snapshot_wait);
-static DECLARE_WAIT_QUEUE_HEAD(lvm_wait);
-static DECLARE_WAIT_QUEUE_HEAD(lvm_map_wait);
-
-static spinlock_t lvm_lock = SPIN_LOCK_UNLOCKED;
-
-static devfs_handle_t lvm_devfs_handle;
-static devfs_handle_t vg_devfs_handle[MAX_VG];
-static devfs_handle_t ch_devfs_handle[MAX_VG];
-static devfs_handle_t lv_devfs_handle[MAX_LV];
-
-static struct file_operations lvm_chr_fops =
-{
- owner: THIS_MODULE,
- open: lvm_chr_open,
- release: lvm_chr_close,
- ioctl: lvm_chr_ioctl,
-};
-
-static struct block_device_operations lvm_blk_dops =
-{
- open: lvm_blk_open,
- release: lvm_blk_close,
- ioctl: lvm_blk_ioctl
-};
-
-/* gendisk structures */
-static struct hd_struct lvm_hd_struct[MAX_LV];
-static int lvm_blocksizes[MAX_LV] =
-{0,};
-static int lvm_size[MAX_LV] =
-{0,};
-static struct gendisk lvm_gendisk =
-{
- MAJOR_NR, /* major # */
- LVM_NAME, /* name of major */
- 0, /* number of times minor is shifted
- to get real minor */
- 1, /* maximum partitions per device */
- lvm_hd_struct, /* partition table */
- lvm_size, /* device size in blocks, copied
- to block_size[] */
- MAX_LV, /* number or real devices */
- NULL, /* internal */
- NULL, /* pointer to next gendisk struct (internal) */
-};
-
-
-#ifdef MODULE
-/*
- * Module initialization...
- */
-int init_module(void)
-#else
-/*
- * Driver initialization...
- */
-#ifdef __initfunc
-__initfunc(int lvm_init(void))
-#else
-int __init lvm_init(void)
-#endif
-#endif /* #ifdef MODULE */
-{
- struct gendisk *gendisk_ptr = NULL;
-
- if (register_chrdev(LVM_CHAR_MAJOR, lvm_name, &lvm_chr_fops) < 0) {
- printk(KERN_ERR "%s -- register_chrdev failed\n", lvm_name);
- return -EIO;
- }
- if (register_blkdev(MAJOR_NR, lvm_name, &lvm_blk_dops) < 0) {
- printk("%s -- register_blkdev failed\n", lvm_name);
- if (unregister_chrdev(LVM_CHAR_MAJOR, lvm_name) < 0)
- printk(KERN_ERR "%s -- unregister_chrdev failed\n", lvm_name);
- return -EIO;
- }
-
- lvm_devfs_handle = devfs_register(
- 0 , "lvm", 0, 0, LVM_CHAR_MAJOR,
- S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,
- &lvm_chr_fops, NULL);
-
-#if defined CONFIG_LVM_PROC_FS && defined CONFIG_PROC_FS
- create_proc_info_entry(LVM_NAME, S_IFREG | S_IRUGO,
- &proc_root, lvm_proc_get_info_ptr);
-#endif
-
- lvm_init_vars();
- lvm_geninit(&lvm_gendisk);
-
- /* insert our gendisk at the corresponding major */
- if (gendisk_head != NULL) {
- gendisk_ptr = gendisk_head;
- while (gendisk_ptr->next != NULL &&
- gendisk_ptr->major > lvm_gendisk.major) {
- gendisk_ptr = gendisk_ptr->next;
- }
- lvm_gendisk.next = gendisk_ptr->next;
- gendisk_ptr->next = &lvm_gendisk;
- } else {
- gendisk_head = &lvm_gendisk;
- lvm_gendisk.next = NULL;
- }
-
-#ifdef LVM_HD_NAME
- /* reference from drivers/block/genhd.c */
- lvm_hd_name_ptr = lvm_hd_name;
-#endif
-
- blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
- blk_queue_make_request(BLK_DEFAULT_QUEUE(MAJOR_NR), lvm_make_request_fn);
- blk_queue_pluggable(BLK_DEFAULT_QUEUE(MAJOR_NR), lvm_plug_device_noop);
- /* optional read root VGDA */
-/*
- if ( *rootvg != 0) vg_read_with_pv_and_lv ( rootvg, &vg);
-*/
-
- printk(KERN_INFO
- "%s%s -- "
-#ifdef MODULE
- "Module"
-#else
- "Driver"
-#endif
- " successfully initialized\n",
- lvm_version, lvm_name);
-
- return 0;
-} /* init_module() / lvm_init() */
-
-
-#ifdef MODULE
-/*
- * Module cleanup...
- */
-void cleanup_module(void)
-{
- struct gendisk *gendisk_ptr = NULL, *gendisk_ptr_prev = NULL;
-
- devfs_unregister (lvm_devfs_handle);
-
- if (unregister_chrdev(LVM_CHAR_MAJOR, lvm_name) < 0) {
- printk(KERN_ERR "%s -- unregister_chrdev failed\n", lvm_name);
- }
- if (unregister_blkdev(MAJOR_NR, lvm_name) < 0) {
- printk(KERN_ERR "%s -- unregister_blkdev failed\n", lvm_name);
- }
- blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
-
- gendisk_ptr = gendisk_ptr_prev = gendisk_head;
- while (gendisk_ptr != NULL) {
- if (gendisk_ptr == &lvm_gendisk)
- break;
- gendisk_ptr_prev = gendisk_ptr;
- gendisk_ptr = gendisk_ptr->next;
- }
- /* delete our gendisk from chain */
- if (gendisk_ptr == &lvm_gendisk)
- gendisk_ptr_prev->next = gendisk_ptr->next;
-
- blk_size[MAJOR_NR] = NULL;
- blksize_size[MAJOR_NR] = NULL;
-
-#if defined CONFIG_LVM_PROC_FS && defined CONFIG_PROC_FS
- remove_proc_entry(LVM_NAME, &proc_root);
-#endif
-
-#ifdef LVM_HD_NAME
- /* reference from linux/drivers/block/genhd.c */
- lvm_hd_name_ptr = NULL;
-#endif
-
- printk(KERN_INFO "%s -- Module successfully deactivated\n", lvm_name);
-
- return;
-} /* void cleanup_module() */
-#endif /* #ifdef MODULE */
-
-
-/*
- * support function to initialize lvm variables
- */
-#ifdef __initfunc
-__initfunc(void lvm_init_vars(void))
-#else
-void __init lvm_init_vars(void)
-#endif
-{
- int v;
-
- loadtime = CURRENT_TIME;
-
- pe_lock_req.lock = UNLOCK_PE;
- pe_lock_req.data.lv_dev = pe_lock_req.data.pv_dev = 0;
- pe_lock_req.data.pv_offset = 0;
-
- /* Initialize VG pointers */
- for (v = 0; v < ABS_MAX_VG; v++) vg[v] = NULL;
-
- /* Initialize LV -> VG association */
- for (v = 0; v < ABS_MAX_LV; v++) {
- /* index ABS_MAX_VG never used for real VG */
- vg_lv_map[v].vg_number = ABS_MAX_VG;
- vg_lv_map[v].lv_number = -1;
- }
-
- return;
-} /* lvm_init_vars() */
-
-
-/********************************************************************
- *
- * Character device functions
- *
- ********************************************************************/
-
-/*
- * character device open routine
- */
-static int lvm_chr_open(struct inode *inode,
- struct file *file)
-{
- int minor = MINOR(inode->i_rdev);
-
-#ifdef DEBUG
- printk(KERN_DEBUG
- "%s -- lvm_chr_open MINOR: %d VG#: %d mode: 0x%X lock: %d\n",
- lvm_name, minor, VG_CHR(minor), file->f_mode, lock);
-#endif
-
- /* super user validation */
- if (!capable(CAP_SYS_ADMIN)) return -EACCES;
-
- /* Group special file open */
- if (VG_CHR(minor) > MAX_VG) return -ENXIO;
-
- lvm_chr_open_count++;
- return 0;
-} /* lvm_chr_open() */
-
-
-/*
- * character device i/o-control routine
- *
- * Only one changing process can do changing ioctl at one time,
- * others will block.
- *
- */
-static int lvm_chr_ioctl(struct inode *inode, struct file *file,
- uint command, ulong a)
-{
- int minor = MINOR(inode->i_rdev);
- uint extendable, l, v;
- void *arg = (void *) a;
- lv_t lv;
- vg_t* vg_ptr = vg[VG_CHR(minor)];
-
- /* otherwise cc will complain about unused variables */
- (void) lvm_lock;
-
-
-#ifdef DEBUG_IOCTL
- printk(KERN_DEBUG
- "%s -- lvm_chr_ioctl: command: 0x%X MINOR: %d "
- "VG#: %d mode: 0x%X\n",
- lvm_name, command, minor, VG_CHR(minor), file->f_mode);
-#endif
-
-#ifdef LVM_TOTAL_RESET
- if (lvm_reset_spindown > 0) return -EACCES;
-#endif
-
- /* Main command switch */
- switch (command) {
- case LVM_LOCK_LVM:
- /* lock the LVM */
- return lvm_do_lock_lvm();
-
- case LVM_GET_IOP_VERSION:
- /* check lvm version to ensure driver/tools+lib
- interoperability */
- if (copy_to_user(arg, &lvm_iop_version, sizeof(ushort)) != 0)
- return -EFAULT;
- return 0;
-
-#ifdef LVM_TOTAL_RESET
- case LVM_RESET:
- /* lock reset function */
- lvm_reset_spindown = 1;
- for (v = 0; v < ABS_MAX_VG; v++) {
- if (vg[v] != NULL) lvm_do_vg_remove(v);
- }
-
-#ifdef MODULE
- while (GET_USE_COUNT(&__this_module) < 1)
- MOD_INC_USE_COUNT;
- while (GET_USE_COUNT(&__this_module) > 1)
- MOD_DEC_USE_COUNT;
-#endif /* MODULE */
- lock = 0; /* release lock */
- wake_up_interruptible(&lvm_wait);
- return 0;
-#endif /* LVM_TOTAL_RESET */
-
-
- case LE_REMAP:
- /* remap a logical extent (after moving the physical extent) */
- return lvm_do_le_remap(vg_ptr,arg);
-
- case PE_LOCK_UNLOCK:
- /* lock/unlock i/o to a physical extent to move it to another
- physical volume (move's done in user space's pvmove) */
- return lvm_do_pe_lock_unlock(vg_ptr,arg);
-
- case VG_CREATE:
- /* create a VGDA */
- return lvm_do_vg_create(minor, arg);
-
- case VG_REMOVE:
- /* remove an inactive VGDA */
- return lvm_do_vg_remove(minor);
-
- case VG_EXTEND:
- /* extend a volume group */
- return lvm_do_vg_extend(vg_ptr,arg);
-
- case VG_REDUCE:
- /* reduce a volume group */
- return lvm_do_vg_reduce(vg_ptr,arg);
-
-
- case VG_SET_EXTENDABLE:
- /* set/clear extendability flag of volume group */
- if (vg_ptr == NULL) return -ENXIO;
- if (copy_from_user(&extendable, arg, sizeof(extendable)) != 0)
- return -EFAULT;
-
- if (extendable == VG_EXTENDABLE ||
- extendable == ~VG_EXTENDABLE) {
- if (extendable == VG_EXTENDABLE)
- vg_ptr->vg_status |= VG_EXTENDABLE;
- else
- vg_ptr->vg_status &= ~VG_EXTENDABLE;
- } else return -EINVAL;
- return 0;
-
-
- case VG_STATUS:
- /* get volume group data (only the vg_t struct) */
- if (vg_ptr == NULL) return -ENXIO;
- if (copy_to_user(arg, vg_ptr, sizeof(vg_t)) != 0)
- return -EFAULT;
- return 0;
-
-
- case VG_STATUS_GET_COUNT:
- /* get volume group count */
- if (copy_to_user(arg, &vg_count, sizeof(vg_count)) != 0)
- return -EFAULT;
- return 0;
-
-
- case VG_STATUS_GET_NAMELIST:
- /* get volume group count */
- for (l = v = 0; v < ABS_MAX_VG; v++) {
- if (vg[v] != NULL) {
- if (copy_to_user(arg + l++ * NAME_LEN,
- vg[v]->vg_name,
- NAME_LEN) != 0)
- return -EFAULT;
- }
- }
- return 0;
-
-
- case LV_CREATE:
- case LV_REMOVE:
- case LV_EXTEND:
- case LV_REDUCE:
- /* create, remove, extend or reduce a logical volume */
- if (vg_ptr == NULL) return -ENXIO;
- if (copy_from_user(&lv_req, arg, sizeof(lv_req)) != 0)
- return -EFAULT;
-
- if (command != LV_REMOVE) {
- if (copy_from_user(&lv, lv_req.lv, sizeof(lv_t)) != 0)
- return -EFAULT;
- }
- switch (command) {
- case LV_CREATE:
- return lvm_do_lv_create(minor, lv_req.lv_name, &lv);
-
- case LV_REMOVE:
- return lvm_do_lv_remove(minor, lv_req.lv_name, -1);
-
- case LV_EXTEND:
- case LV_REDUCE:
- return lvm_do_lv_extend_reduce(minor, lv_req.lv_name, &lv);
- }
-
-
- case LV_STATUS_BYNAME:
- /* get status of a logical volume by name */
- return lvm_do_lv_status_byname(vg_ptr,arg);
-
- case LV_STATUS_BYINDEX:
- /* get status of a logical volume by index */
- return lvm_do_lv_status_byindex(vg_ptr,arg);
-
- case PV_CHANGE:
- /* change a physical volume */
- return lvm_do_pv_change(vg_ptr,arg);
-
- case PV_STATUS:
- /* get physical volume data (pv_t structure only) */
- return lvm_do_pv_status(vg_ptr,arg);
-
- case PV_FLUSH:
- /* physical volume buffer flush/invalidate */
- if (copy_from_user(&pv_flush_req, arg,
- sizeof(pv_flush_req)) != 0)
- return -EFAULT;
-
- for ( v = 0; v < ABS_MAX_VG; v++) {
- unsigned int p;
- if ( vg[v] == NULL) continue;
- for ( p = 0; p < vg[v]->pv_max; p++) {
- if ( vg[v]->pv[p] != NULL &&
- strcmp ( vg[v]->pv[p]->pv_name,
- pv_flush_req.pv_name) == 0) {
- fsync_dev ( vg[v]->pv[p]->pv_dev);
- invalidate_buffers ( vg[v]->pv[p]->pv_dev);
- return 0;
- }
- }
- }
- return 0;
-
- default:
- printk(KERN_WARNING
- "%s -- lvm_chr_ioctl: unknown command %x\n",
- lvm_name, command);
- return -EINVAL;
- }
-
- return 0;
-} /* lvm_chr_ioctl */
-
-
-/*
- * character device close routine
- */
-static int lvm_chr_close(struct inode *inode, struct file *file)
-{
-#ifdef DEBUG
- int minor = MINOR(inode->i_rdev);
- printk(KERN_DEBUG
- "%s -- lvm_chr_close VG#: %d\n", lvm_name, VG_CHR(minor));
-#endif
-
- lock_kernel();
-#ifdef LVM_TOTAL_RESET
- if (lvm_reset_spindown > 0) {
- lvm_reset_spindown = 0;
- lvm_chr_open_count = 1;
- }
-#endif
-
- if (lvm_chr_open_count > 0) lvm_chr_open_count--;
- if (lock == current->pid) {
- lock = 0; /* release lock */
- wake_up_interruptible(&lvm_wait);
- }
- unlock_kernel();
-
- return 0;
-} /* lvm_chr_close() */
-
-
-
-/********************************************************************
- *
- * Block device functions
- *
- ********************************************************************/
-
-/*
- * block device open routine
- */
-static int lvm_blk_open(struct inode *inode, struct file *file)
-{
- int minor = MINOR(inode->i_rdev);
- lv_t *lv_ptr;
- vg_t *vg_ptr = vg[VG_BLK(minor)];
-
-#ifdef DEBUG_LVM_BLK_OPEN
- printk(KERN_DEBUG
- "%s -- lvm_blk_open MINOR: %d VG#: %d LV#: %d mode: 0x%X\n",
- lvm_name, minor, VG_BLK(minor), LV_BLK(minor), file->f_mode);
-#endif
-
-#ifdef LVM_TOTAL_RESET
- if (lvm_reset_spindown > 0)
- return -EPERM;
-#endif
-
- if (vg_ptr != NULL &&
- (vg_ptr->vg_status & VG_ACTIVE) &&
- (lv_ptr = vg_ptr->lv[LV_BLK(minor)]) != NULL &&
- LV_BLK(minor) >= 0 &&
- LV_BLK(minor) < vg_ptr->lv_max) {
-
- /* Check parallel LV spindown (LV remove) */
- if (lv_ptr->lv_status & LV_SPINDOWN) return -EPERM;
-
- /* Check inactive LV and open for read/write */
- if (file->f_mode & O_RDWR) {
- if (!(lv_ptr->lv_status & LV_ACTIVE)) return -EPERM;
- if (!(lv_ptr->lv_access & LV_WRITE)) return -EACCES;
- }
-
- /* be sure to increment VG counter */
- if (lv_ptr->lv_open == 0) vg_ptr->lv_open++;
- lv_ptr->lv_open++;
-
- MOD_INC_USE_COUNT;
-
-#ifdef DEBUG_LVM_BLK_OPEN
- printk(KERN_DEBUG
- "%s -- lvm_blk_open MINOR: %d VG#: %d LV#: %d size: %d\n",
- lvm_name, minor, VG_BLK(minor), LV_BLK(minor),
- lv_ptr->lv_size);
-#endif
-
- return 0;
- }
- return -ENXIO;
-} /* lvm_blk_open() */
-
-
-/*
- * block device i/o-control routine
- */
-static int lvm_blk_ioctl(struct inode *inode, struct file *file,
- uint command, ulong a)
-{
- int minor = MINOR(inode->i_rdev);
- vg_t *vg_ptr = vg[VG_BLK(minor)];
- lv_t *lv_ptr = vg_ptr->lv[LV_BLK(minor)];
- void *arg = (void *) a;
- struct hd_geometry *hd = (struct hd_geometry *) a;
-
-#ifdef DEBUG_IOCTL
- printk(KERN_DEBUG
- "%s -- lvm_blk_ioctl MINOR: %d command: 0x%X arg: %X "
- "VG#: %dl LV#: %d\n",
- lvm_name, minor, command, (ulong) arg,
- VG_BLK(minor), LV_BLK(minor));
-#endif
-
- switch (command) {
- case BLKGETSIZE:
- /* return device size */
-#ifdef DEBUG_IOCTL
- printk(KERN_DEBUG
- "%s -- lvm_blk_ioctl -- BLKGETSIZE: %u\n",
- lvm_name, lv_ptr->lv_size);
-#endif
- if (put_user(lv_ptr->lv_size, (long *)arg))
- return -EFAULT;
- break;
-
-
- case BLKFLSBUF:
- /* flush buffer cache */
- if (!capable(CAP_SYS_ADMIN)) return -EACCES;
-
-#ifdef DEBUG_IOCTL
- printk(KERN_DEBUG
- "%s -- lvm_blk_ioctl -- BLKFLSBUF\n", lvm_name);
-#endif
- fsync_dev(inode->i_rdev);
- invalidate_buffers(inode->i_rdev);
- break;
-
-
- case BLKRASET:
- /* set read ahead for block device */
- if (!capable(CAP_SYS_ADMIN)) return -EACCES;
-
-#ifdef DEBUG_IOCTL
- printk(KERN_DEBUG
- "%s -- lvm_blk_ioctl -- BLKRASET: %d sectors for %02X:%02X\n",
- lvm_name, (long) arg, MAJOR(inode->i_rdev), minor);
-#endif
- if ((long) arg < LVM_MIN_READ_AHEAD ||
- (long) arg > LVM_MAX_READ_AHEAD)
- return -EINVAL;
- read_ahead[MAJOR_NR] = lv_ptr->lv_read_ahead = (long) arg;
- break;
-
-
- case BLKRAGET:
- /* get current read ahead setting */
-#ifdef DEBUG_IOCTL
- printk(KERN_DEBUG
- "%s -- lvm_blk_ioctl -- BLKRAGET\n", lvm_name);
-#endif
- if (put_user(lv_ptr->lv_read_ahead, (long *)arg))
- return -EFAULT;
- break;
-
-
- case HDIO_GETGEO:
- /* get disk geometry */
-#ifdef DEBUG_IOCTL
- printk(KERN_DEBUG
- "%s -- lvm_blk_ioctl -- HDIO_GETGEO\n", lvm_name);
-#endif
- if (hd == NULL)
- return -EINVAL;
- {
- unsigned char heads = 64;
- unsigned char sectors = 32;
- long start = 0;
- short cylinders = lv_ptr->lv_size / heads / sectors;
-
- if (copy_to_user((char *) &hd->heads, &heads,
- sizeof(heads)) != 0 ||
- copy_to_user((char *) &hd->sectors, §ors,
- sizeof(sectors)) != 0 ||
- copy_to_user((short *) &hd->cylinders,
- &cylinders, sizeof(cylinders)) != 0 ||
- copy_to_user((long *) &hd->start, &start,
- sizeof(start)) != 0)
- return -EFAULT;
- }
-
-#ifdef DEBUG_IOCTL
- printk(KERN_DEBUG
- "%s -- lvm_blk_ioctl -- cylinders: %d\n",
- lvm_name, lv_ptr->lv_size / heads / sectors);
-#endif
- break;
-
-
- case LV_SET_ACCESS:
- /* set access flags of a logical volume */
- if (!capable(CAP_SYS_ADMIN)) return -EACCES;
- lv_ptr->lv_access = (ulong) arg;
- break;
-
-
- case LV_SET_STATUS:
- /* set status flags of a logical volume */
- if (!capable(CAP_SYS_ADMIN)) return -EACCES;
- if (!((ulong) arg & LV_ACTIVE) && lv_ptr->lv_open > 1)
- return -EPERM;
- lv_ptr->lv_status = (ulong) arg;
- break;
-
-
- case LV_SET_ALLOCATION:
- /* set allocation flags of a logical volume */
- if (!capable(CAP_SYS_ADMIN)) return -EACCES;
- lv_ptr->lv_allocation = (ulong) arg;
- break;
-
-
- default:
- printk(KERN_WARNING
- "%s -- lvm_blk_ioctl: unknown command %d\n",
- lvm_name, command);
- return -EINVAL;
- }
-
- return 0;
-} /* lvm_blk_ioctl() */
-
-
-/*
- * block device close routine
- */
-static int lvm_blk_close(struct inode *inode, struct file *file)
-{
- int minor = MINOR(inode->i_rdev);
- vg_t *vg_ptr = vg[VG_BLK(minor)];
- lv_t *lv_ptr = vg_ptr->lv[LV_BLK(minor)];
-
-#ifdef DEBUG
- printk(KERN_DEBUG
- "%s -- lvm_blk_close MINOR: %d VG#: %d LV#: %d\n",
- lvm_name, minor, VG_BLK(minor), LV_BLK(minor));
-#endif
-
- sync_dev(inode->i_rdev);
- if (lv_ptr->lv_open == 1) vg_ptr->lv_open--;
- lv_ptr->lv_open--;
-
- MOD_DEC_USE_COUNT;
-
- return 0;
-} /* lvm_blk_close() */
-
-
-#if defined CONFIG_LVM_PROC_FS && defined CONFIG_PROC_FS
-/*
- * Support function /proc-Filesystem
- */
-#define LVM_PROC_BUF ( i == 0 ? dummy_buf : &buf[sz])
-
-static int lvm_proc_get_info(char *page, char **start, off_t pos, int count)
-{
- int c, i, l, p, v, vg_counter, pv_counter, lv_counter, lv_open_counter,
- lv_open_total, pe_t_bytes, lv_block_exception_t_bytes, seconds;
- static off_t sz;
- off_t sz_last;
- char allocation_flag, inactive_flag, rw_flag, stripes_flag;
- char *lv_name, *pv_name;
- static char *buf = NULL;
- static char dummy_buf[160]; /* sized for 2 lines */
- vg_t *vg_ptr;
- lv_t *lv_ptr;
- pv_t *pv_ptr;
-
-
-#ifdef DEBUG_LVM_PROC_GET_INFO
- printk(KERN_DEBUG
- "%s - lvm_proc_get_info CALLED pos: %lu count: %d whence: %d\n",
- lvm_name, pos, count, whence);
-#endif
-
- if (pos == 0 || buf == NULL) {
- sz_last = vg_counter = pv_counter = lv_counter = lv_open_counter = \
- lv_open_total = pe_t_bytes = lv_block_exception_t_bytes = 0;
-
- /* search for activity */
- for (v = 0; v < ABS_MAX_VG; v++) {
- if ((vg_ptr = vg[v]) != NULL) {
- vg_counter++;
- pv_counter += vg_ptr->pv_cur;
- lv_counter += vg_ptr->lv_cur;
- if (vg_ptr->lv_cur > 0) {
- for (l = 0; l < vg[v]->lv_max; l++) {
- if ((lv_ptr = vg_ptr->lv[l]) != NULL) {
- pe_t_bytes += lv_ptr->lv_allocated_le;
- if (lv_ptr->lv_block_exception != NULL)
- lv_block_exception_t_bytes += lv_ptr->lv_remap_end;
- if (lv_ptr->lv_open > 0) {
- lv_open_counter++;
- lv_open_total += lv_ptr->lv_open;
- }
- }
- }
- }
- }
- }
- pe_t_bytes *= sizeof(pe_t);
- lv_block_exception_t_bytes *= sizeof(lv_block_exception_t);
-
- if (buf != NULL) {
-#ifdef DEBUG_KFREE
- printk(KERN_DEBUG
- "%s -- kfree %d\n", lvm_name, __LINE__);
-#endif
- kfree(buf);
- buf = NULL;
- }
- /* 2 times: first to get size to allocate buffer,
- 2nd to fill the malloced buffer */
- for (i = 0; i < 2; i++) {
- sz = 0;
- sz += sprintf(LVM_PROC_BUF,
- "LVM "
-#ifdef MODULE
- "module"
-#else
- "driver"
-#endif
- " %s\n\n"
- "Total: %d VG%s %d PV%s %d LV%s ",
- lvm_short_version,
- vg_counter, vg_counter == 1 ? "" : "s",
- pv_counter, pv_counter == 1 ? "" : "s",
- lv_counter, lv_counter == 1 ? "" : "s");
- sz += sprintf(LVM_PROC_BUF,
- "(%d LV%s open",
- lv_open_counter,
- lv_open_counter == 1 ? "" : "s");
- if (lv_open_total > 0)
- sz += sprintf(LVM_PROC_BUF,
- " %d times)\n",
- lv_open_total);
- else
- sz += sprintf(LVM_PROC_BUF, ")");
- sz += sprintf(LVM_PROC_BUF,
- "\nGlobal: %lu bytes malloced IOP version: %d ",
- vg_counter * sizeof(vg_t) +
- pv_counter * sizeof(pv_t) +
- lv_counter * sizeof(lv_t) +
- pe_t_bytes + lv_block_exception_t_bytes + sz_last,
- lvm_iop_version);
-
- seconds = CURRENT_TIME - loadtime;
- if (seconds < 0)
- loadtime = CURRENT_TIME + seconds;
- if (seconds / 86400 > 0) {
- sz += sprintf(LVM_PROC_BUF, "%d day%s ",
- seconds / 86400,
- seconds / 86400 == 0 ||
- seconds / 86400 > 1 ? "s" : "");
- }
- sz += sprintf(LVM_PROC_BUF, "%d:%02d:%02d active\n",
- (seconds % 86400) / 3600,
- (seconds % 3600) / 60,
- seconds % 60);
-
- if (vg_counter > 0) {
- for (v = 0; v < ABS_MAX_VG; v++) {
- /* volume group */
- if ((vg_ptr = vg[v]) != NULL) {
- inactive_flag = ' ';
- if (!(vg_ptr->vg_status & VG_ACTIVE)) inactive_flag = 'I';
- sz += sprintf(LVM_PROC_BUF,
- "\nVG: %c%s [%d PV, %d LV/%d open] "
- " PE Size: %d KB\n"
- " Usage [KB/PE]: %d /%d total "
- "%d /%d used %d /%d free",
- inactive_flag,
- vg_ptr->vg_name,
- vg_ptr->pv_cur,
- vg_ptr->lv_cur,
- vg_ptr->lv_open,
- vg_ptr->pe_size >> 1,
- vg_ptr->pe_size * vg_ptr->pe_total >> 1,
- vg_ptr->pe_total,
- vg_ptr->pe_allocated * vg_ptr->pe_size >> 1,
- vg_ptr->pe_allocated,
- (vg_ptr->pe_total - vg_ptr->pe_allocated) *
- vg_ptr->pe_size >> 1,
- vg_ptr->pe_total - vg_ptr->pe_allocated);
-
- /* physical volumes */
- sz += sprintf(LVM_PROC_BUF,
- "\n PV%s ",
- vg_ptr->pv_cur == 1 ? ": " : "s:");
- c = 0;
- for (p = 0; p < vg_ptr->pv_max; p++) {
- if ((pv_ptr = vg_ptr->pv[p]) != NULL) {
- inactive_flag = 'A';
- if (!(pv_ptr->pv_status & PV_ACTIVE))
- inactive_flag = 'I';
- allocation_flag = 'A';
- if (!(pv_ptr->pv_allocatable & PV_ALLOCATABLE))
- allocation_flag = 'N';
- pv_name = strchr(pv_ptr->pv_name+1,'/');
- if ( pv_name == 0) pv_name = pv_ptr->pv_name;
- else pv_name++;
- sz += sprintf(LVM_PROC_BUF,
- "[%c%c] %-21s %8d /%-6d "
- "%8d /%-6d %8d /%-6d",
- inactive_flag,
- allocation_flag,
- pv_name,
- pv_ptr->pe_total *
- pv_ptr->pe_size >> 1,
- pv_ptr->pe_total,
- pv_ptr->pe_allocated *
- pv_ptr->pe_size >> 1,
- pv_ptr->pe_allocated,
- (pv_ptr->pe_total -
- pv_ptr->pe_allocated) *
- pv_ptr->pe_size >> 1,
- pv_ptr->pe_total -
- pv_ptr->pe_allocated);
- c++;
- if (c < vg_ptr->pv_cur)
- sz += sprintf(LVM_PROC_BUF,
- "\n ");
- }
- }
-
- /* logical volumes */
- sz += sprintf(LVM_PROC_BUF,
- "\n LV%s ",
- vg_ptr->lv_cur == 1 ? ": " : "s:");
- c = 0;
- for (l = 0; l < vg[v]->lv_max; l++) {
- if ((lv_ptr = vg_ptr->lv[l]) != NULL) {
- inactive_flag = 'A';
- if (!(lv_ptr->lv_status & LV_ACTIVE))
- inactive_flag = 'I';
- rw_flag = 'R';
- if (lv_ptr->lv_access & LV_WRITE)
- rw_flag = 'W';
- allocation_flag = 'D';
- if (lv_ptr->lv_allocation & LV_CONTIGUOUS)
- allocation_flag = 'C';
- stripes_flag = 'L';
- if (lv_ptr->lv_stripes > 1)
- stripes_flag = 'S';
- sz += sprintf(LVM_PROC_BUF,
- "[%c%c%c%c",
- inactive_flag,
- rw_flag,
- allocation_flag,
- stripes_flag);
- if (lv_ptr->lv_stripes > 1)
- sz += sprintf(LVM_PROC_BUF, "%-2d",
- lv_ptr->lv_stripes);
- else
- sz += sprintf(LVM_PROC_BUF, " ");
- lv_name = strrchr(lv_ptr->lv_name, '/');
- if ( lv_name == 0) lv_name = lv_ptr->lv_name;
- else lv_name++;
- sz += sprintf(LVM_PROC_BUF, "] %-25s", lv_name);
- if (strlen(lv_name) > 25)
- sz += sprintf(LVM_PROC_BUF,
- "\n ");
- sz += sprintf(LVM_PROC_BUF, "%9d /%-6d ",
- lv_ptr->lv_size >> 1,
- lv_ptr->lv_size / vg[v]->pe_size);
-
- if (lv_ptr->lv_open == 0)
- sz += sprintf(LVM_PROC_BUF, "close");
- else
- sz += sprintf(LVM_PROC_BUF, "%dx open",
- lv_ptr->lv_open);
- c++;
- if (c < vg_ptr->lv_cur)
- sz += sprintf(LVM_PROC_BUF,
- "\n ");
- }
- }
- if (vg_ptr->lv_cur == 0) sz += sprintf(LVM_PROC_BUF, "none");
- sz += sprintf(LVM_PROC_BUF, "\n");
- }
- }
- }
- if (buf == NULL) {
- if ((buf = vmalloc(sz)) == NULL) {
- sz = 0;
- return sprintf(page, "%s - vmalloc error at line %d\n",
- lvm_name, __LINE__);
- }
- }
- sz_last = sz;
- }
- }
- if (pos > sz - 1) {
- vfree(buf);
- buf = NULL;
- return 0;
- }
- *start = &buf[pos];
- if (sz - pos < count)
- return sz - pos;
- else
- return count;
-} /* lvm_proc_get_info() */
-#endif /* #if defined CONFIG_LVM_PROC_FS && defined CONFIG_PROC_FS */
-
-
-/*
- * block device support function for /usr/src/linux/drivers/block/ll_rw_blk.c
- * (see init_module/lvm_init)
- */
-static int lvm_map(struct buffer_head *bh, int rw)
-{
- int minor = MINOR(bh->b_rdev);
- ulong index;
- ulong pe_start;
- ulong size = bh->b_size >> 9;
- ulong rsector_tmp = bh->b_rsector;
- ulong rsector_sav;
- kdev_t rdev_tmp = bh->b_rdev;
- kdev_t rdev_sav;
- lv_t *lv = vg[VG_BLK(minor)]->lv[LV_BLK(minor)];
-
-
- if (!(lv->lv_status & LV_ACTIVE)) {
- printk(KERN_ALERT
- "%s - lvm_map: ll_rw_blk for inactive LV %s\n",
- lvm_name, lv->lv_name);
- goto error;
- }
-/*
- if ( lv->lv_access & LV_SNAPSHOT)
- printk ( "%s -- %02d:%02d block: %lu rw: %d\n", lvm_name, MAJOR ( bh->b_dev), MINOR ( bh->b_dev), bh->b_blocknr, rw);
- */
-
- /* take care of snapshot chunk writes before
- check for writable logical volume */
- if ((lv->lv_access & LV_SNAPSHOT) &&
- MAJOR(bh->b_rdev) != 0 &&
- MAJOR(bh->b_rdev) != MAJOR_NR &&
- (rw == WRITEA || rw == WRITE))
- {
- printk ( "%s -- doing snapshot write for %02d:%02d[%02d:%02d] b_blocknr: %lu b_rsector: %lu\n", lvm_name, MAJOR ( bh->b_dev), MINOR ( bh->b_dev), MAJOR ( bh->b_rdev), MINOR ( bh->b_rdev), bh->b_blocknr, bh->b_rsector);
- goto error;
- }
-
- if ((rw == WRITE || rw == WRITEA) &&
- !(lv->lv_access & LV_WRITE)) {
- printk(KERN_CRIT
- "%s - lvm_map: ll_rw_blk write for readonly LV %s\n",
- lvm_name, lv->lv_name);
- goto error;
- }
-#ifdef DEBUG_MAP
- printk(KERN_DEBUG
- "%s - lvm_map minor:%d *rdev: %02d:%02d *rsector: %lu "
- "size:%lu\n",
- lvm_name, minor,
- MAJOR(rdev_tmp),
- MINOR(rdev_tmp),
- rsector_tmp, size);
-#endif
-
- if (rsector_tmp + size > lv->lv_size) {
- printk(KERN_ALERT
- "%s - lvm_map *rsector: %lu or size: %lu wrong for"
- " minor: %2d\n", lvm_name, rsector_tmp, size, minor);
- goto error;
- }
- rsector_sav = rsector_tmp;
- rdev_sav = rdev_tmp;
-
-lvm_second_remap:
- /* linear mapping */
- if (lv->lv_stripes < 2) {
- /* get the index */
- index = rsector_tmp / vg[VG_BLK(minor)]->pe_size;
- pe_start = lv->lv_current_pe[index].pe;
- rsector_tmp = lv->lv_current_pe[index].pe +
- (rsector_tmp % vg[VG_BLK(minor)]->pe_size);
- rdev_tmp = lv->lv_current_pe[index].dev;
-
-#ifdef DEBUG_MAP
- printk(KERN_DEBUG
- "lv_current_pe[%ld].pe: %ld rdev: %02d:%02d rsector:%ld\n",
- index,
- lv->lv_current_pe[index].pe,
- MAJOR(rdev_tmp),
- MINOR(rdev_tmp),
- rsector_tmp);
-#endif
-
- /* striped mapping */
- } else {
- ulong stripe_index;
- ulong stripe_length;
-
- stripe_length = vg[VG_BLK(minor)]->pe_size * lv->lv_stripes;
- stripe_index = (rsector_tmp % stripe_length) / lv->lv_stripesize;
- index = rsector_tmp / stripe_length +
- (stripe_index % lv->lv_stripes) *
- (lv->lv_allocated_le / lv->lv_stripes);
- pe_start = lv->lv_current_pe[index].pe;
- rsector_tmp = lv->lv_current_pe[index].pe +
- (rsector_tmp % stripe_length) -
- (stripe_index % lv->lv_stripes) * lv->lv_stripesize -
- stripe_index / lv->lv_stripes *
- (lv->lv_stripes - 1) * lv->lv_stripesize;
- rdev_tmp = lv->lv_current_pe[index].dev;
- }
-
-#ifdef DEBUG_MAP
- printk(KERN_DEBUG
- "lv_current_pe[%ld].pe: %ld rdev: %02d:%02d rsector:%ld\n"
- "stripe_length: %ld stripe_index: %ld\n",
- index,
- lv->lv_current_pe[index].pe,
- MAJOR(rdev_tmp),
- MINOR(rdev_tmp),
- rsector_tmp,
- stripe_length,
- stripe_index);
-#endif
-
- /* handle physical extents on the move */
- if (pe_lock_req.lock == LOCK_PE) {
- if (rdev_tmp == pe_lock_req.data.pv_dev &&
- rsector_tmp >= pe_lock_req.data.pv_offset &&
- rsector_tmp < (pe_lock_req.data.pv_offset +
- vg[VG_BLK(minor)]->pe_size)) {
- sleep_on(&lvm_map_wait);
- rsector_tmp = rsector_sav;
- rdev_tmp = rdev_sav;
- goto lvm_second_remap;
- }
- }
- /* statistic */
- if (rw == WRITE || rw == WRITEA)
- lv->lv_current_pe[index].writes++;
- else
- lv->lv_current_pe[index].reads++;
-
- /* snapshot volume exception handling on physical device address base */
- if (lv->lv_access & (LV_SNAPSHOT | LV_SNAPSHOT_ORG)) {
- /* original logical volume */
- if (lv->lv_access & LV_SNAPSHOT_ORG) {
- if (rw == WRITE || rw == WRITEA)
- {
- lv_t *lv_ptr;
-
- /* start with first snapshot and loop thrugh all of them */
- for (lv_ptr = lv->lv_snapshot_next;
- lv_ptr != NULL;
- lv_ptr = lv_ptr->lv_snapshot_next) {
- down(&lv->lv_snapshot_org->lv_snapshot_sem);
- /* do we still have exception storage for this snapshot free? */
- if (lv_ptr->lv_block_exception != NULL) {
- rdev_sav = rdev_tmp;
- rsector_sav = rsector_tmp;
- if (!lvm_snapshot_remap_block(&rdev_tmp,
- &rsector_tmp,
- pe_start,
- lv_ptr)) {
- /* create a new mapping */
- lvm_snapshot_COW(rdev_tmp,
- rsector_tmp,
- pe_start,
- rsector_sav,
- lv_ptr);
- }
- rdev_tmp = rdev_sav;
- rsector_tmp = rsector_sav;
- }
- up(&lv->lv_snapshot_org->lv_snapshot_sem);
- }
- }
- } else {
- /* remap snapshot logical volume */
- down(&lv->lv_snapshot_sem);
- if (lv->lv_block_exception != NULL)
- lvm_snapshot_remap_block(&rdev_tmp, &rsector_tmp, pe_start, lv);
- up(&lv->lv_snapshot_sem);
- }
- }
- bh->b_rdev = rdev_tmp;
- bh->b_rsector = rsector_tmp;
-
- return 1;
-
- error:
- buffer_IO_error(bh);
- return -1;
-} /* lvm_map() */
-
-
-/*
- * internal support functions
- */
-
-#ifdef LVM_HD_NAME
-/*
- * generate "hard disk" name
- */
-void lvm_hd_name(char *buf, int minor)
-{
- int len = 0;
- lv_t *lv_ptr;
-
- if (vg[VG_BLK(minor)] == NULL ||
- (lv_ptr = vg[VG_BLK(minor)]->lv[LV_BLK(minor)]) == NULL)
- return;
- len = strlen(lv_ptr->lv_name) - 5;
- memcpy(buf, &lv_ptr->lv_name[5], len);
- buf[len] = 0;
- return;
-}
-#endif
-
-
-/*
- * this one never should be called...
- */
-static void lvm_dummy_device_request(request_queue_t * t)
-{
- printk(KERN_EMERG
- "%s -- oops, got lvm request for %02d:%02d [sector: %lu]\n",
- lvm_name,
- MAJOR(CURRENT->rq_dev),
- MINOR(CURRENT->rq_dev),
- CURRENT->sector);
- return;
-}
-
-
-/*
- * make request function
- */
-static int lvm_make_request_fn(request_queue_t *q, int rw, struct buffer_head *bh)
-{
- lvm_map(bh, rw);
- return 1;
-}
-
-/*
- * plug device function is a noop because plugging has to happen
- * in the queue of the physical blockdevice to allow the
- * elevator to do a better job.
- */
-static void lvm_plug_device_noop(request_queue_t *q, kdev_t dev) { }
-
-/********************************************************************
- *
- * Character device support functions
- *
- ********************************************************************/
-/*
- * character device support function logical volume manager lock
- */
-static int lvm_do_lock_lvm(void)
-{
-lock_try_again:
- spin_lock(&lvm_lock);
- if (lock != 0 && lock != current->pid) {
-#ifdef DEBUG_IOCTL
- printk(KERN_INFO "lvm_do_lock_lvm: %s is locked by pid %d ...\n",
- lvm_name, lock);
-#endif
- spin_unlock(&lvm_lock);
- interruptible_sleep_on(&lvm_wait);
- if (current->sigpending != 0)
- return -EINTR;
-#ifdef LVM_TOTAL_RESET
- if (lvm_reset_spindown > 0)
- return -EACCES;
-#endif
- goto lock_try_again;
- }
- lock = current->pid;
- spin_unlock(&lvm_lock);
- return 0;
-} /* lvm_do_lock_lvm */
-
-
-/*
- * character device support function lock/unlock physical extend
- */
-static int lvm_do_pe_lock_unlock(vg_t *vg_ptr, void *arg)
-{
- uint p;
-
- if (vg_ptr == NULL) return -ENXIO;
- if (copy_from_user(&pe_lock_req, arg,
- sizeof(pe_lock_req_t)) != 0) return -EFAULT;
-
- switch (pe_lock_req.lock) {
- case LOCK_PE:
- for (p = 0; p < vg_ptr->pv_max; p++) {
- if (vg_ptr->pv[p] != NULL &&
- pe_lock_req.data.pv_dev ==
- vg_ptr->pv[p]->pv_dev)
- break;
- }
- if (p == vg_ptr->pv_max) return -ENXIO;
-
- pe_lock_req.lock = UNLOCK_PE;
- fsync_dev(pe_lock_req.data.lv_dev);
- pe_lock_req.lock = LOCK_PE;
- break;
-
- case UNLOCK_PE:
- pe_lock_req.lock = UNLOCK_PE;
- pe_lock_req.data.lv_dev = pe_lock_req.data.pv_dev = 0;
- pe_lock_req.data.pv_offset = 0;
- wake_up(&lvm_map_wait);
- break;
-
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-
-/*
- * character device support function logical extend remap
- */
-static int lvm_do_le_remap(vg_t *vg_ptr, void *arg)
-{
- uint l, le;
- lv_t *lv_ptr;
-
- if (vg_ptr == NULL) return -ENXIO;
- if (copy_from_user(&le_remap_req, arg,
- sizeof(le_remap_req_t)) != 0)
- return -EFAULT;
-
- for (l = 0; l < vg_ptr->lv_max; l++) {
- lv_ptr = vg_ptr->lv[l];
- if (lv_ptr != NULL &&
- strcmp(lv_ptr->lv_name,
- le_remap_req.lv_name) == 0) {
- for (le = 0; le < lv_ptr->lv_allocated_le;
- le++) {
- if (lv_ptr->lv_current_pe[le].dev ==
- le_remap_req.old_dev &&
- lv_ptr->lv_current_pe[le].pe ==
- le_remap_req.old_pe) {
- lv_ptr->lv_current_pe[le].dev =
- le_remap_req.new_dev;
- lv_ptr->lv_current_pe[le].pe =
- le_remap_req.new_pe;
- return 0;
- }
- }
- return -EINVAL;
- }
- }
- return -ENXIO;
-} /* lvm_do_le_remap() */
-
-
-/*
- * character device support function VGDA create
- */
-int lvm_do_vg_create(int minor, void *arg)
-{
- int snaporg_minor = 0;
- ulong l, p;
- lv_t lv;
- vg_t *vg_ptr;
- pv_t *pv_ptr;
- lv_t *lv_ptr;
-
- if (vg[VG_CHR(minor)] != NULL) return -EPERM;
-
- if ((vg_ptr = kmalloc(sizeof(vg_t),GFP_KERNEL)) == NULL) {
- printk(KERN_CRIT
- "%s -- VG_CREATE: kmalloc error VG at line %d\n",
- lvm_name, __LINE__);
- return -ENOMEM;
- }
- /* get the volume group structure */
- if (copy_from_user(vg_ptr, arg, sizeof(vg_t)) != 0) {
- kfree(vg_ptr);
- return -EFAULT;
- }
-
- vg_devfs_handle[vg_ptr->vg_number] = devfs_mk_dir(0, vg_ptr->vg_name, NULL);
- ch_devfs_handle[vg_ptr->vg_number] = devfs_register(
- vg_devfs_handle[vg_ptr->vg_number] , "group",
- DEVFS_FL_DEFAULT, LVM_CHAR_MAJOR, vg_ptr->vg_number,
- S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,
- &lvm_chr_fops, NULL);
-
- /* we are not that active so far... */
- vg_ptr->vg_status &= ~VG_ACTIVE;
- vg[VG_CHR(minor)] = vg_ptr;
-
- vg[VG_CHR(minor)]->pe_allocated = 0;
- if (vg_ptr->pv_max > ABS_MAX_PV) {
- printk(KERN_WARNING
- "%s -- Can't activate VG: ABS_MAX_PV too small\n",
- lvm_name);
- kfree(vg_ptr);
- vg[VG_CHR(minor)] = NULL;
- return -EPERM;
- }
- if (vg_ptr->lv_max > ABS_MAX_LV) {
- printk(KERN_WARNING
- "%s -- Can't activate VG: ABS_MAX_LV too small for %u\n",
- lvm_name, vg_ptr->lv_max);
- kfree(vg_ptr);
- vg_ptr = NULL;
- return -EPERM;
- }
- /* get the physical volume structures */
- vg_ptr->pv_act = vg_ptr->pv_cur = 0;
- for (p = 0; p < vg_ptr->pv_max; p++) {
- /* user space address */
- if ((pvp = vg_ptr->pv[p]) != NULL) {
- pv_ptr = vg_ptr->pv[p] = kmalloc(sizeof(pv_t),GFP_KERNEL);
- if (pv_ptr == NULL) {
- printk(KERN_CRIT
- "%s -- VG_CREATE: kmalloc error PV at line %d\n",
- lvm_name, __LINE__);
- lvm_do_vg_remove(minor);
- return -ENOMEM;
- }
- if (copy_from_user(pv_ptr, pvp, sizeof(pv_t)) != 0) {
- lvm_do_vg_remove(minor);
- return -EFAULT;
- }
- /* We don't need the PE list
- in kernel space as with LVs pe_t list (see below) */
- pv_ptr->pe = NULL;
- pv_ptr->pe_allocated = 0;
- pv_ptr->pv_status = PV_ACTIVE;
- vg_ptr->pv_act++;
- vg_ptr->pv_cur++;
-
-#ifdef LVM_GET_INODE
- /* insert a dummy inode for fs_may_mount */
- pv_ptr->inode = lvm_get_inode(pv_ptr->pv_dev);
-#endif
- }
- }
-
- /* get the logical volume structures */
- vg_ptr->lv_cur = 0;
- for (l = 0; l < vg_ptr->lv_max; l++) {
- /* user space address */
- if ((lvp = vg_ptr->lv[l]) != NULL) {
- if (copy_from_user(&lv, lvp, sizeof(lv_t)) != 0) {
- lvm_do_vg_remove(minor);
- return -EFAULT;
- }
- vg_ptr->lv[l] = NULL;
- if (lvm_do_lv_create(minor, lv.lv_name, &lv) != 0) {
- lvm_do_vg_remove(minor);
- return -EFAULT;
- }
- }
- }
-
- /* Second path to correct snapshot logical volumes which are not
- in place during first path above */
- for (l = 0; l < vg_ptr->lv_max; l++) {
- if ((lv_ptr = vg_ptr->lv[l]) != NULL &&
- vg_ptr->lv[l]->lv_access & LV_SNAPSHOT) {
- snaporg_minor = lv_ptr->lv_snapshot_minor;
- if (vg_ptr->lv[LV_BLK(snaporg_minor)] != NULL) {
- /* get pointer to original logical volume */
- lv_ptr = vg_ptr->lv[l]->lv_snapshot_org =
- vg_ptr->lv[LV_BLK(snaporg_minor)];
-
- /* set necessary fields of original logical volume */
- lv_ptr->lv_access |= LV_SNAPSHOT_ORG;
- lv_ptr->lv_snapshot_minor = 0;
- lv_ptr->lv_snapshot_org = lv_ptr;
- lv_ptr->lv_snapshot_prev = NULL;
-
- /* find last snapshot logical volume in the chain */
- while (lv_ptr->lv_snapshot_next != NULL)
- lv_ptr = lv_ptr->lv_snapshot_next;
-
- /* set back pointer to this last one in our new logical volume */
- vg_ptr->lv[l]->lv_snapshot_prev = lv_ptr;
-
- /* last logical volume now points to our new snapshot volume */
- lv_ptr->lv_snapshot_next = vg_ptr->lv[l];
-
- /* now point to the new one */
- lv_ptr = lv_ptr->lv_snapshot_next;
-
- /* set necessary fields of new snapshot logical volume */
- lv_ptr->lv_snapshot_next = NULL;
- lv_ptr->lv_current_pe =
- vg_ptr->lv[LV_BLK(snaporg_minor)]->lv_current_pe;
- lv_ptr->lv_allocated_le =
- vg_ptr->lv[LV_BLK(snaporg_minor)]->lv_allocated_le;
- lv_ptr->lv_current_le =
- vg_ptr->lv[LV_BLK(snaporg_minor)]->lv_current_le;
- lv_ptr->lv_size =
- vg_ptr->lv[LV_BLK(snaporg_minor)]->lv_size;
- }
- }
- }
-
- vg_count++;
-
- /* let's go active */
- vg_ptr->vg_status |= VG_ACTIVE;
-
- MOD_INC_USE_COUNT;
-
- return 0;
-} /* lvm_do_vg_create() */
-
-
-/*
- * character device support function VGDA extend
- */
-static int lvm_do_vg_extend(vg_t *vg_ptr, void *arg)
-{
- uint p;
- pv_t *pv_ptr;
-
- if (vg_ptr == NULL) return -ENXIO;
- if (vg_ptr->pv_cur < vg_ptr->pv_max) {
- for (p = 0; p < vg_ptr->pv_max; p++) {
- if (vg_ptr->pv[p] == NULL) {
- if ((pv_ptr = vg_ptr->pv[p] = kmalloc(sizeof(pv_t),GFP_KERNEL)) == NULL) {
- printk(KERN_CRIT
- "%s -- VG_EXTEND: kmalloc error PV at line %d\n",
- lvm_name, __LINE__);
- return -ENOMEM;
- }
- if (copy_from_user(pv_ptr, arg, sizeof(pv_t)) != 0) {
- kfree(pv_ptr);
- vg_ptr->pv[p] = NULL;
- return -EFAULT;
- }
-
- pv_ptr->pv_status = PV_ACTIVE;
- /* We don't need the PE list
- in kernel space like LVs pe_t list */
- pv_ptr->pe = NULL;
- vg_ptr->pv_cur++;
- vg_ptr->pv_act++;
- vg_ptr->pe_total +=
- pv_ptr->pe_total;
-#ifdef LVM_GET_INODE
- /* insert a dummy inode for fs_may_mount */
- pv_ptr->inode = lvm_get_inode(pv_ptr->pv_dev);
-#endif
- return 0;
- }
- }
- }
-return -EPERM;
-} /* lvm_do_vg_extend() */
-
-
-/*
- * character device support function VGDA reduce
- */
-static int lvm_do_vg_reduce(vg_t *vg_ptr, void *arg)
-{
- uint p;
- pv_t *pv_ptr;
-
- if (vg_ptr == NULL) return -ENXIO;
- if (copy_from_user(pv_name, arg, sizeof(pv_name)) != 0)
- return -EFAULT;
-
- for (p = 0; p < vg_ptr->pv_max; p++) {
- pv_ptr = vg_ptr->pv[p];
- if (pv_ptr != NULL &&
- strcmp(pv_ptr->pv_name,
- pv_name) == 0) {
- if (pv_ptr->lv_cur > 0) return -EPERM;
- vg_ptr->pe_total -=
- pv_ptr->pe_total;
- vg_ptr->pv_cur--;
- vg_ptr->pv_act--;
-#ifdef LVM_GET_INODE
- lvm_clear_inode(pv_ptr->inode);
-#endif
- kfree(pv_ptr);
- /* Make PV pointer array contiguous */
- for (; p < vg_ptr->pv_max - 1; p++)
- vg_ptr->pv[p] = vg_ptr->pv[p + 1];
- vg_ptr->pv[p + 1] = NULL;
- return 0;
- }
- }
- return -ENXIO;
-} /* lvm_do_vg_reduce */
-
-
-/*
- * character device support function VGDA remove
- */
-static int lvm_do_vg_remove(int minor)
-{
- int i;
- vg_t *vg_ptr = vg[VG_CHR(minor)];
- pv_t *pv_ptr;
-
- if (vg_ptr == NULL) return -ENXIO;
-
-#ifdef LVM_TOTAL_RESET
- if (vg_ptr->lv_open > 0 && lvm_reset_spindown == 0)
-#else
- if (vg_ptr->lv_open > 0)
-#endif
- return -EPERM;
-
- /* let's go inactive */
- vg_ptr->vg_status &= ~VG_ACTIVE;
-
- devfs_unregister (ch_devfs_handle[vg_ptr->vg_number]);
- devfs_unregister (vg_devfs_handle[vg_ptr->vg_number]);
-
- /* free LVs */
- /* first free snapshot logical volumes */
- for (i = 0; i < vg_ptr->lv_max; i++) {
- if (vg_ptr->lv[i] != NULL &&
- vg_ptr->lv[i]->lv_access & LV_SNAPSHOT) {
- lvm_do_lv_remove(minor, NULL, i);
- current->state = TASK_UNINTERRUPTIBLE;
- schedule_timeout(1);
- }
- }
- /* then free the rest of the LVs */
- for (i = 0; i < vg_ptr->lv_max; i++) {
- if (vg_ptr->lv[i] != NULL) {
- lvm_do_lv_remove(minor, NULL, i);
- current->state = TASK_UNINTERRUPTIBLE;
- schedule_timeout(1);
- }
- }
-
- /* free PVs */
- for (i = 0; i < vg_ptr->pv_max; i++) {
- if ((pv_ptr = vg_ptr->pv[i]) != NULL) {
-#ifdef DEBUG_KFREE
- printk(KERN_DEBUG
- "%s -- kfree %d\n", lvm_name, __LINE__);
-#endif
-#ifdef LVM_GET_INODE
- lvm_clear_inode(pv_ptr->inode);
-#endif
- kfree(pv_ptr);
- vg[VG_CHR(minor)]->pv[i] = NULL;
- }
- }
-
-#ifdef DEBUG_KFREE
- printk(KERN_DEBUG "%s -- kfree %d\n", lvm_name, __LINE__);
-#endif
- kfree(vg_ptr);
- vg[VG_CHR(minor)] = NULL;
-
- vg_count--;
-
- MOD_DEC_USE_COUNT;
-
- return 0;
-} /* lvm_do_vg_remove() */
-
-
-/*
- * character device support function logical volume create
- */
-static int lvm_do_lv_create(int minor, char *lv_name, lv_t *lv)
-{
- int l, le, l_new, p, size;
- ulong lv_status_save;
- char *lv_tmp, *lv_buf;
- lv_block_exception_t *lvbe = lv->lv_block_exception;
- vg_t *vg_ptr = vg[VG_CHR(minor)];
- lv_t *lv_ptr = NULL;
-
- if ((pep = lv->lv_current_pe) == NULL) return -EINVAL;
- if (lv->lv_chunk_size > LVM_SNAPSHOT_MAX_CHUNK)
- return -EINVAL;
-
- for (l = 0; l < vg_ptr->lv_max; l++) {
- if (vg_ptr->lv[l] != NULL &&
- strcmp(vg_ptr->lv[l]->lv_name, lv_name) == 0)
- return -EEXIST;
- }
-
- /* in case of lv_remove(), lv_create() pair; for eg. lvrename does this */
- l_new = -1;
- if (vg_ptr->lv[lv->lv_number] == NULL)
- l_new = lv->lv_number;
- else {
- for (l = 0; l < vg_ptr->lv_max; l++) {
- if (vg_ptr->lv[l] == NULL)
- if (l_new == -1) l_new = l;
- }
- }
- if (l_new == -1) return -EPERM;
- else l = l_new;
-
- if ((lv_ptr = kmalloc(sizeof(lv_t),GFP_KERNEL)) == NULL) {;
- printk(KERN_CRIT "%s -- LV_CREATE: kmalloc error LV at line %d\n",
- lvm_name, __LINE__);
- return -ENOMEM;
- }
- /* copy preloaded LV */
- memcpy((char *) lv_ptr, (char *) lv, sizeof(lv_t));
-
- lv_status_save = lv_ptr->lv_status;
- lv_ptr->lv_status &= ~LV_ACTIVE;
- lv_ptr->lv_snapshot_org = \
- lv_ptr->lv_snapshot_prev = \
- lv_ptr->lv_snapshot_next = NULL;
- lv_ptr->lv_block_exception = NULL;
- init_MUTEX(&lv_ptr->lv_snapshot_sem);
- vg_ptr->lv[l] = lv_ptr;
-
- /* get the PE structures from user space if this
- is no snapshot logical volume */
- if (!(lv_ptr->lv_access & LV_SNAPSHOT)) {
- size = lv_ptr->lv_allocated_le * sizeof(pe_t);
- if ((lv_ptr->lv_current_pe = vmalloc(size)) == NULL) {
- printk(KERN_CRIT
- "%s -- LV_CREATE: vmalloc error LV_CURRENT_PE of %d Byte "
- "at line %d\n",
- lvm_name, size, __LINE__);
-#ifdef DEBUG_KFREE
- printk(KERN_DEBUG "%s -- kfree %d\n", lvm_name, __LINE__);
-#endif
- kfree(lv_ptr);
- vg[VG_CHR(minor)]->lv[l] = NULL;
- return -ENOMEM;
- }
- if (copy_from_user(lv_ptr->lv_current_pe, pep, size)) {
- vfree(lv_ptr->lv_current_pe);
- kfree(lv_ptr);
- vg_ptr->lv[l] = NULL;
- return -EFAULT;
- }
- /* correct the PE count in PVs */
- for (le = 0; le < lv_ptr->lv_allocated_le; le++) {
- vg_ptr->pe_allocated++;
- for (p = 0; p < vg_ptr->pv_cur; p++) {
- if (vg_ptr->pv[p]->pv_dev ==
- lv_ptr->lv_current_pe[le].dev)
- vg_ptr->pv[p]->pe_allocated++;
- }
- }
- } else {
- /* Get snapshot exception data and block list */
- if (lvbe != NULL) {
- lv_ptr->lv_snapshot_org =
- vg_ptr->lv[LV_BLK(lv_ptr->lv_snapshot_minor)];
- if (lv_ptr->lv_snapshot_org != NULL) {
- size = lv_ptr->lv_remap_end * sizeof(lv_block_exception_t);
- if ((lv_ptr->lv_block_exception = vmalloc(size)) == NULL) {
- printk(KERN_CRIT
- "%s -- lvm_do_lv_create: vmalloc error LV_BLOCK_EXCEPTION "
- "of %d byte at line %d\n",
- lvm_name, size, __LINE__);
-#ifdef DEBUG_KFREE
- printk(KERN_DEBUG "%s -- kfree %d\n", lvm_name, __LINE__);
-#endif
- kfree(lv_ptr);
- vg_ptr->lv[l] = NULL;
- return -ENOMEM;
- }
- if (copy_from_user(lv_ptr->lv_block_exception, lvbe, size)) {
- vfree(lv_ptr->lv_block_exception);
- kfree(lv_ptr);
- vg[VG_CHR(minor)]->lv[l] = NULL;
- return -EFAULT;
- }
- /* get pointer to original logical volume */
- lv_ptr = lv_ptr->lv_snapshot_org;
-
- lv_ptr->lv_snapshot_minor = 0;
- lv_ptr->lv_snapshot_org = lv_ptr;
- lv_ptr->lv_snapshot_prev = NULL;
- /* walk thrugh the snapshot list */
- while (lv_ptr->lv_snapshot_next != NULL)
- lv_ptr = lv_ptr->lv_snapshot_next;
- /* now lv_ptr points to the last existing snapshot in the chain */
- vg_ptr->lv[l]->lv_snapshot_prev = lv_ptr;
- /* our new one now back points to the previous last in the chain */
- lv_ptr = vg_ptr->lv[l];
- /* now lv_ptr points to our new last snapshot logical volume */
- lv_ptr->lv_snapshot_org = lv_ptr->lv_snapshot_prev->lv_snapshot_org;
- lv_ptr->lv_snapshot_next = NULL;
- lv_ptr->lv_current_pe = lv_ptr->lv_snapshot_org->lv_current_pe;
- lv_ptr->lv_allocated_le = lv_ptr->lv_snapshot_org->lv_allocated_le;
- lv_ptr->lv_current_le = lv_ptr->lv_snapshot_org->lv_current_le;
- lv_ptr->lv_size = lv_ptr->lv_snapshot_org->lv_size;
- lv_ptr->lv_stripes = lv_ptr->lv_snapshot_org->lv_stripes;
- lv_ptr->lv_stripesize = lv_ptr->lv_snapshot_org->lv_stripesize;
- {
- int err = lvm_snapshot_alloc(lv_ptr);
- if (err)
- {
- vfree(lv_ptr->lv_block_exception);
- kfree(lv_ptr);
- vg[VG_CHR(minor)]->lv[l] = NULL;
- return err;
- }
- }
- } else {
- vfree(lv_ptr->lv_block_exception);
- kfree(lv_ptr);
- vg_ptr->lv[l] = NULL;
- return -EFAULT;
- }
- } else {
- kfree(vg_ptr->lv[l]);
- vg_ptr->lv[l] = NULL;
- return -EINVAL;
- }
- } /* if ( vg[VG_CHR(minor)]->lv[l]->lv_access & LV_SNAPSHOT) */
-
- lv_ptr = vg_ptr->lv[l];
- lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].start_sect = 0;
- lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].nr_sects = lv_ptr->lv_size;
- lvm_size[MINOR(lv_ptr->lv_dev)] = lv_ptr->lv_size >> 1;
- vg_lv_map[MINOR(lv_ptr->lv_dev)].vg_number = vg_ptr->vg_number;
- vg_lv_map[MINOR(lv_ptr->lv_dev)].lv_number = lv_ptr->lv_number;
- read_ahead[MAJOR_NR] = lv_ptr->lv_read_ahead = LVM_CORRECT_READ_AHEAD(lv_ptr->lv_read_ahead);
- vg_ptr->lv_cur++;
- lv_ptr->lv_status = lv_status_save;
-
- strtok(lv->lv_name, "/"); /* /dev */
-
- while((lv_tmp = strtok(NULL, "/")) != NULL)
- lv_buf = lv_tmp;
-
- lv_devfs_handle[lv->lv_number] = devfs_register(
- vg_devfs_handle[vg_ptr->vg_number], lv_buf,
- DEVFS_FL_DEFAULT, LVM_BLK_MAJOR, lv->lv_number,
- S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP,
- &lvm_blk_dops, NULL);
-
- /* optionally add our new snapshot LV */
- if (lv_ptr->lv_access & LV_SNAPSHOT) {
- /* sync the original logical volume */
- fsync_dev(lv_ptr->lv_snapshot_org->lv_dev);
- /* put ourselve into the chain */
- lv_ptr->lv_snapshot_prev->lv_snapshot_next = lv_ptr;
- lv_ptr->lv_snapshot_org->lv_access |= LV_SNAPSHOT_ORG;
- }
- return 0;
-} /* lvm_do_lv_create() */
-
-
-/*
- * character device support function logical volume remove
- */
-static int lvm_do_lv_remove(int minor, char *lv_name, int l)
-{
- uint le, p;
- vg_t *vg_ptr = vg[VG_CHR(minor)];
- lv_t *lv_ptr;
-
- if (l == -1) {
- for (l = 0; l < vg_ptr->lv_max; l++) {
- if (vg_ptr->lv[l] != NULL &&
- strcmp(vg_ptr->lv[l]->lv_name, lv_name) == 0) {
- break;
- }
- }
- }
- if (l == vg_ptr->lv_max) return -ENXIO;
-
- lv_ptr = vg_ptr->lv[l];
-#ifdef LVM_TOTAL_RESET
- if (lv_ptr->lv_open > 0 && lvm_reset_spindown == 0)
-#else
- if (lv_ptr->lv_open > 0)
-#endif
- return -EBUSY;
-
- /* check for deletion of snapshot source while
- snapshot volume still exists */
- if ((lv_ptr->lv_access & LV_SNAPSHOT_ORG) &&
- lv_ptr->lv_snapshot_next != NULL)
- return -EPERM;
-
- lv_ptr->lv_status |= LV_SPINDOWN;
-
- /* sync the buffers */
- fsync_dev(lv_ptr->lv_dev);
-
- lv_ptr->lv_status &= ~LV_ACTIVE;
-
- /* invalidate the buffers */
- invalidate_buffers(lv_ptr->lv_dev);
-
- /* reset generic hd */
- lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].start_sect = -1;
- lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].nr_sects = 0;
- lvm_size[MINOR(lv_ptr->lv_dev)] = 0;
-
- /* reset VG/LV mapping */
- vg_lv_map[MINOR(lv_ptr->lv_dev)].vg_number = ABS_MAX_VG;
- vg_lv_map[MINOR(lv_ptr->lv_dev)].lv_number = -1;
-
- /* correct the PE count in PVs if this is no snapshot logical volume */
- if (!(lv_ptr->lv_access & LV_SNAPSHOT)) {
- /* only if this is no snapshot logical volume because
- we share the lv_current_pe[] structs with the
- original logical volume */
- for (le = 0; le < lv_ptr->lv_allocated_le; le++) {
- vg_ptr->pe_allocated--;
- for (p = 0; p < vg_ptr->pv_cur; p++) {
- if (vg_ptr->pv[p]->pv_dev ==
- lv_ptr->lv_current_pe[le].dev)
- vg_ptr->pv[p]->pe_allocated--;
- }
- }
- vfree(lv_ptr->lv_current_pe);
- /* LV_SNAPSHOT */
- } else {
- /* remove this snapshot logical volume from the chain */
- lv_ptr->lv_snapshot_prev->lv_snapshot_next = lv_ptr->lv_snapshot_next;
- if (lv_ptr->lv_snapshot_next != NULL) {
- lv_ptr->lv_snapshot_next->lv_snapshot_prev =
- lv_ptr->lv_snapshot_prev;
- }
- /* no more snapshots? */
- if (lv_ptr->lv_snapshot_org->lv_snapshot_next == NULL)
- lv_ptr->lv_snapshot_org->lv_access &= ~LV_SNAPSHOT_ORG;
- lvm_snapshot_release(lv_ptr);
- }
-
- devfs_unregister(lv_devfs_handle[lv_ptr->lv_number]);
-
-#ifdef DEBUG_KFREE
- printk(KERN_DEBUG "%s -- kfree %d\n", lvm_name, __LINE__);
-#endif
- kfree(lv_ptr);
- vg_ptr->lv[l] = NULL;
- vg_ptr->lv_cur--;
- return 0;
-} /* lvm_do_lv_remove() */
-
-
-/*
- * character device support function logical volume extend / reduce
- */
-static int lvm_do_lv_extend_reduce(int minor, char *lv_name, lv_t *lv)
-{
- int l, le, p, size, old_allocated_le;
- uint32_t end, lv_status_save;
- vg_t *vg_ptr = vg[VG_CHR(minor)];
- lv_t *lv_ptr;
- pe_t *pe;
-
- if ((pep = lv->lv_current_pe) == NULL) return -EINVAL;
-
- for (l = 0; l < vg_ptr->lv_max; l++) {
- if (vg_ptr->lv[l] != NULL &&
- strcmp(vg_ptr->lv[l]->lv_name, lv_name) == 0)
- break;
- }
- if (l == vg_ptr->lv_max) return -ENXIO;
- lv_ptr = vg_ptr->lv[l];
-
- /* check for active snapshot */
- if (lv->lv_access & (LV_SNAPSHOT | LV_SNAPSHOT_ORG)) return -EPERM;
-
- if ((pe = vmalloc(size = lv->lv_current_le * sizeof(pe_t))) == NULL) {
- printk(KERN_CRIT
- "%s -- lvm_do_lv_extend_reduce: vmalloc error LV_CURRENT_PE "
- "of %d Byte at line %d\n",
- lvm_name, size, __LINE__);
- return -ENOMEM;
- }
- /* get the PE structures from user space */
- if (copy_from_user(pe, pep, size)) {
- vfree(pe);
- return -EFAULT;
- }
-
-#ifdef DEBUG
- printk(KERN_DEBUG
- "%s -- fsync_dev and "
- "invalidate_buffers for %s [%s] in %s\n",
- lvm_name, lv_ptr->lv_name,
- kdevname(lv_ptr->lv_dev),
- vg_ptr->vg_name);
-#endif
-
- lv_ptr->lv_status |= LV_SPINDOWN;
- fsync_dev(lv_ptr->lv_dev);
- lv_ptr->lv_status &= ~LV_ACTIVE;
- invalidate_buffers(lv_ptr->lv_dev);
-
- /* reduce allocation counters on PV(s) */
- for (le = 0; le < lv_ptr->lv_allocated_le; le++) {
- vg_ptr->pe_allocated--;
- for (p = 0; p < vg_ptr->pv_cur; p++) {
- if (vg_ptr->pv[p]->pv_dev ==
- lv_ptr->lv_current_pe[le].dev) {
- vg_ptr->pv[p]->pe_allocated--;
- break;
- }
- }
- }
-
-
- /* save pointer to "old" lv/pe pointer array */
- pep1 = lv_ptr->lv_current_pe;
- end = lv_ptr->lv_current_le;
-
- /* save open counter */
- lv_open = lv_ptr->lv_open;
-
- /* save # of old allocated logical extents */
- old_allocated_le = lv_ptr->lv_allocated_le;
-
- /* copy preloaded LV */
- lv_status_save = lv->lv_status;
- lv->lv_status |= LV_SPINDOWN;
- lv->lv_status &= ~LV_ACTIVE;
- memcpy((char *) lv_ptr, (char *) lv, sizeof(lv_t));
- lv_ptr->lv_current_pe = pe;
- lv_ptr->lv_open = lv_open;
-
- /* save availiable i/o statistic data */
- /* linear logical volume */
- if (lv_ptr->lv_stripes < 2) {
- /* Check what last LE shall be used */
- if (end > lv_ptr->lv_current_le) end = lv_ptr->lv_current_le;
- for (le = 0; le < end; le++) {
- lv_ptr->lv_current_pe[le].reads = pep1[le].reads;
- lv_ptr->lv_current_pe[le].writes = pep1[le].writes;
- }
- /* striped logical volume */
- } else {
- uint i, j, source, dest, end, old_stripe_size, new_stripe_size;
-
- old_stripe_size = old_allocated_le / lv_ptr->lv_stripes;
- new_stripe_size = lv_ptr->lv_allocated_le / lv_ptr->lv_stripes;
- end = old_stripe_size;
- if (end > new_stripe_size) end = new_stripe_size;
- for (i = source = dest = 0;
- i < lv_ptr->lv_stripes; i++) {
- for (j = 0; j < end; j++) {
- lv_ptr->lv_current_pe[dest + j].reads =
- pep1[source + j].reads;
- lv_ptr->lv_current_pe[dest + j].writes =
- pep1[source + j].writes;
- }
- source += old_stripe_size;
- dest += new_stripe_size;
- }
- }
- vfree(pep1);
- pep1 = NULL;
-
-
- /* extend the PE count in PVs */
- for (le = 0; le < lv_ptr->lv_allocated_le; le++) {
- vg_ptr->pe_allocated++;
- for (p = 0; p < vg_ptr->pv_cur; p++) {
- if (vg_ptr->pv[p]->pv_dev ==
- vg_ptr->lv[l]->lv_current_pe[le].dev) {
- vg_ptr->pv[p]->pe_allocated++;
- break;
- }
- }
- }
-
- lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].start_sect = 0;
- lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].nr_sects = lv_ptr->lv_size;
- lvm_size[MINOR(lv_ptr->lv_dev)] = lv_ptr->lv_size >> 1;
- /* vg_lv_map array doesn't have to be changed here */
-
- read_ahead[MAJOR_NR] = lv_ptr->lv_read_ahead = LVM_CORRECT_READ_AHEAD(lv_ptr->lv_read_ahead);
- lv_ptr->lv_status = lv_status_save;
-
- return 0;
-} /* lvm_do_lv_extend_reduce() */
-
-
-/*
- * character device support function logical volume status by name
- */
-static int lvm_do_lv_status_byname(vg_t *vg_ptr, void *arg)
-{
- uint l;
- ulong size;
- lv_t lv;
- lv_t *lv_ptr;
- lv_status_byname_req_t lv_status_byname_req;
-
- if (vg_ptr == NULL) return -ENXIO;
- if (copy_from_user(&lv_status_byname_req, arg,
- sizeof(lv_status_byname_req_t)) != 0)
- return -EFAULT;
-
- if (lv_status_byname_req.lv == NULL) return -EINVAL;
- if (copy_from_user(&lv, lv_status_byname_req.lv,
- sizeof(lv_t)) != 0)
- return -EFAULT;
-
- for (l = 0; l < vg_ptr->lv_max; l++) {
- lv_ptr = vg_ptr->lv[l];
- if (lv_ptr != NULL &&
- strcmp(lv_ptr->lv_name,
- lv_status_byname_req.lv_name) == 0) {
- if (copy_to_user(lv_status_byname_req.lv,
- lv_ptr,
- sizeof(lv_t)) != 0)
- return -EFAULT;
-
- if (lv.lv_current_pe != NULL) {
- size = lv_ptr->lv_allocated_le *
- sizeof(pe_t);
- if (copy_to_user(lv.lv_current_pe,
- lv_ptr->lv_current_pe,
- size) != 0)
- return -EFAULT;
- }
- return 0;
- }
- }
- return -ENXIO;
-} /* lvm_do_lv_status_byname() */
-
-
-/*
- * character device support function logical volume status by index
- */
-static int lvm_do_lv_status_byindex(vg_t *vg_ptr,void *arg)
-{
- ulong size;
- lv_t lv;
- lv_t *lv_ptr;
- lv_status_byindex_req_t lv_status_byindex_req;
-
- if (vg_ptr == NULL) return -ENXIO;
- if (copy_from_user(&lv_status_byindex_req, arg,
- sizeof(lv_status_byindex_req)) != 0)
- return -EFAULT;
-
- if ((lvp = lv_status_byindex_req.lv) == NULL)
- return -EINVAL;
- if ( ( lv_ptr = vg_ptr->lv[lv_status_byindex_req.lv_index]) == NULL)
- return -ENXIO;
-
- if (copy_from_user(&lv, lvp, sizeof(lv_t)) != 0)
- return -EFAULT;
-
- if (copy_to_user(lvp, lv_ptr, sizeof(lv_t)) != 0)
- return -EFAULT;
-
- if (lv.lv_current_pe != NULL) {
- size = lv_ptr->lv_allocated_le * sizeof(pe_t);
- if (copy_to_user(lv.lv_current_pe,
- lv_ptr->lv_current_pe,
- size) != 0)
- return -EFAULT;
- }
- return 0;
-} /* lvm_do_lv_status_byindex() */
-
-
-/*
- * character device support function physical volume change
- */
-static int lvm_do_pv_change(vg_t *vg_ptr, void *arg)
-{
- uint p;
- pv_t *pv_ptr;
-#ifdef LVM_GET_INODE
- struct inode *inode_sav;
-#endif
-
- if (vg_ptr == NULL) return -ENXIO;
- if (copy_from_user(&pv_change_req, arg,
- sizeof(pv_change_req)) != 0)
- return -EFAULT;
-
- for (p = 0; p < vg_ptr->pv_max; p++) {
- pv_ptr = vg_ptr->pv[p];
- if (pv_ptr != NULL &&
- strcmp(pv_ptr->pv_name,
- pv_change_req.pv_name) == 0) {
-#ifdef LVM_GET_INODE
- inode_sav = pv_ptr->inode;
-#endif
- if (copy_from_user(pv_ptr,
- pv_change_req.pv,
- sizeof(pv_t)) != 0)
- return -EFAULT;
-
- /* We don't need the PE list
- in kernel space as with LVs pe_t list */
- pv_ptr->pe = NULL;
-#ifdef LVM_GET_INODE
- pv_ptr->inode = inode_sav;
-#endif
- return 0;
- }
- }
- return -ENXIO;
-} /* lvm_do_pv_change() */
-
-/*
- * character device support function get physical volume status
- */
-static int lvm_do_pv_status(vg_t *vg_ptr, void *arg)
-{
- uint p;
- pv_t *pv_ptr;
-
- if (vg_ptr == NULL) return -ENXIO;
- if (copy_from_user(&pv_status_req, arg,
- sizeof(pv_status_req)) != 0)
- return -EFAULT;
-
- for (p = 0; p < vg_ptr->pv_max; p++) {
- pv_ptr = vg_ptr->pv[p];
- if (pv_ptr != NULL &&
- strcmp(pv_ptr->pv_name,
- pv_status_req.pv_name) == 0) {
- if (copy_to_user(pv_status_req.pv,
- pv_ptr,
- sizeof(pv_t)) != 0)
- return -EFAULT;
- return 0;
- }
- }
- return -ENXIO;
-} /* lvm_do_pv_status() */
-
-
-/*
- * support function initialize gendisk variables
- */
-#ifdef __initfunc
-__initfunc(void lvm_geninit(struct gendisk *lvm_gdisk))
-#else
-void __init
- lvm_geninit(struct gendisk *lvm_gdisk)
-#endif
-{
- int i = 0;
-
-#ifdef DEBUG_GENDISK
- printk(KERN_DEBUG "%s -- lvm_gendisk\n", lvm_name);
-#endif
-
- for (i = 0; i < MAX_LV; i++) {
- lvm_gendisk.part[i].start_sect = -1; /* avoid partition check */
- lvm_size[i] = lvm_gendisk.part[i].nr_sects = 0;
- lvm_blocksizes[i] = BLOCK_SIZE;
- }
-
- blksize_size[MAJOR_NR] = lvm_blocksizes;
- blk_size[MAJOR_NR] = lvm_size;
-
- return;
-} /* lvm_gen_init() */
-
-
-#ifdef LVM_GET_INODE
-/*
- * support function to get an empty inode
- *
- * Gets an empty inode to be inserted into the inode hash,
- * so that a physical volume can't be mounted.
- * This is analog to drivers/block/md.c
- *
- * Is this the real thing?
- *
- * No, it's bollocks. md.c tries to do a bit different thing that might
- * _somewhat_ work eons ago. Neither does any good these days. mount() couldn't
- * care less for icache (it cares only for ->s_root->d_count and if we want
- * loopback mounts even that will stop). BTW, with the form used here mount()
- * would have to scan the _whole_ icache to detect the attempt - how on the
- * Earth could it guess the i_ino of your dummy inode? Official line on the
- * exclusion between mount()/swapon()/open()/etc. is Just Don't Do It(tm).
- * If you can convince Linus that it's worth changing - fine, then you'll need
- * to do blkdev_get()/blkdev_put(). Until then...
- */
-struct inode *lvm_get_inode(kdev_t dev)
-{
- struct inode *inode_this = NULL;
-
- /* Lock the device by inserting a dummy inode. */
- inode_this = get_empty_inode();
- inode_this->i_dev = dev;
- insert_inode_hash(inode_this);
- return inode_this;
-}
-
-
-/*
- * support function to clear an inode
- *
- */
-void lvm_clear_inode(struct inode *inode)
-{
-#ifdef I_FREEING
- inode->i_state |= I_FREEING;
-#endif
- clear_inode(inode);
- return;
-}
-#endif /* #ifdef LVM_GET_INODE */
+#ifndef __DRIVERS_PARIDE_H__
+#define __DRIVERS_PARIDE_H__
+
/*
paride.h (c) 1997-8 Grant R. Guenther <grant@torque.net>
Under the terms of the GPL.
extern int pi_register( PIP * );
extern void pi_unregister ( PIP * );
+#endif /* __DRIVERS_PARIDE_H__ */
/* end of paride.h */
cdinfo(CD_DVD, "entering DVD_LU_SEND_RPC_STATE\n");
setup_report_key(&cgc, 0, 8);
memset(&rpc_state, 0, sizeof(rpc_state_t));
+ cgc.buffer = (char *) &rpc_state;
if ((ret = cdo->generic_packet(cdi, &cgc)))
return ret;
}
#elif defined(__alpha__)
#if CONFIG_FT_ALPHA_CLOCK == 0
-#error You must define and set CONFIG_FT_ALPHA_CLOCK in the Makefile !
+#error You must define and set CONFIG_FT_ALPHA_CLOCK in 'make config' !
#endif
extern struct hwrpb_struct *hwrpb;
TRACE_FUN(ft_t_any);
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/init.h>
-#include <asm/delay.h>
+#include <linux/delay.h>
#include <asm/io.h>
#include "scan_keyb.h"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
-#include <linux/pci_ids.h>
#include <linux/init.h>
+#include <linux/slab.h>
#include <linux/gameport.h>
#define PCI_VENDOR_ID_AUREAL 0x12eb
if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
BCD_TO_BIN(year); /* This should never happen... */
- if (year > 20 && year < 48) {
+ if (year >= 20 && year < 48) {
epoch = 1980;
guess = "ARC console";
} else if (year >= 48 && year < 70) {
#define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \
IORESOURCE_IO)
#endif
+#ifndef IS_PCI_REGION_IOMEM
+#define IS_PCI_REGION_IOMEM(dev, r) (pci_resource_flags((dev), (r)) & \
+ IORESOURCE_MEM)
+#endif
#ifndef PCI_IRQ_RESOURCE
#define PCI_IRQ_RESOURCE(dev, r) ((dev)->irq_resource[r].start)
#endif
* This is the configuration table for all of the PCI serial boards
* which we support.
*/
-static struct pci_board pci_boards[] __initdata = {
+static struct pci_board pci_boards[] __devinitdata = {
/*
* Vendor ID, Device ID,
* Subvendor ID, Subdevice ID,
num_port++;
if (first_port == -1)
first_port = i;
- } else {
- num_iomem++;
}
+ if (IS_PCI_REGION_IOMEM(dev, i))
+ num_iomem++;
}
/*
/*
- * $Id: via82cxxx.c,v 2.1 2000/08/29 01:34:60 vojtech Exp $
+ * $Id: via82cxxx.c,v 2.1b 2000/09/20 23:19:60 vojtech Exp $
*
* Copyright (c) 2000 Vojtech Pavlik
*
{ "vt82c586b", PCI_DEVICE_ID_VIA_82C586_0, XFER_UDMA_2 },
{ "vt82c586a", PCI_DEVICE_ID_VIA_82C586_0, XFER_UDMA_2 },
{ "vt82c586", PCI_DEVICE_ID_VIA_82C586_0, XFER_MW_DMA_2 },
- { "Unknown SouthBridge", 0, XFER_UDMA_4 },
{ "Unknown SouthBridge", 0, XFER_UDMA_2 },
};
{ XFER_MW_DMA_1, "MDMA1", 45, 80, 50, 150, 0 },
{ XFER_MW_DMA_0, "MDMA0", 60, 215, 215, 480, 0 },
- { XFER_SW_DMA_0, "SDMA0", 60, 120, 120, 240, 0 },
- { XFER_SW_DMA_0, "SDMA0", 90, 240, 240, 480, 0 },
+ { XFER_SW_DMA_2, "SDMA2", 60, 120, 120, 240, 0 },
+ { XFER_SW_DMA_1, "SDMA1", 90, 240, 240, 480, 0 },
{ XFER_SW_DMA_0, "SDMA0",120, 480, 480, 960, 0 },
{ XFER_PIO_5, "PIO5", 20, 50, 30, 100, 0 },
via_print("----------VIA BusMastering IDE Configuration----------------");
- via_print("Driver Version: 2.1");
+ via_print("Driver Version: 2.1b");
pci_read_config_byte(isa_dev, PCI_REVISION_ID, &t);
via_print("South Bridge: VIA %s rev %#x", via_isa_bridges[via_config].name, t);
via_print("FIFO Output Data 1/2 Clock Advance: %s", (t & 16) ? "on" : "off" );
via_print("BM IDE Status Register Read Retry: %s", (t & 8) ? "on" : "off" );
- pci_read_config_byte(dev, VIA_MISC_2, &t);
- sprintf(p, "Interrupt Steering Swap: %s", (t & 64) ? "on" : "off");
-
pci_read_config_byte(dev, VIA_MISC_3, &t);
via_print("Max DRDY Pulse Width: %s%s", via_control3[(t & 0x03)], (t & 0x03) ? "PCI clocks" : "");
* UDMA cycle
*/
- if (via_timing[i].udma) {
- t = 0xe8;
- if (via_isa_bridges[via_config].speed >= XFER_UDMA_4)
- t |= FIT(ENOUGH(via_timing[i].udma, T >> 1) - 2, 0, 7);
- else
- t |= FIT(ENOUGH(via_timing[i].udma, T ) - 2, 0, 3);
- } else t = 0x0b;
+ switch(via_isa_bridges[via_config].speed) {
+ case XFER_UDMA_2: t = via_timing[i].udma ? (0x60 | (FIT(via_timing[i].udma, 2, 5) - 2)) : 0x03; break;
+ case XFER_UDMA_4: t = via_timing[i].udma ? (0xe8 | (FIT(via_timing[i].udma, 2, 9) - 2)) : 0x0f; break;
+ }
- via_write_config_byte(dev, VIA_UDMA_TIMING + (3 - drive->dn), t);
+ if (via_isa_bridges[via_config].speed != XFER_MW_DMA_2)
+ via_write_config_byte(dev, VIA_UDMA_TIMING + (3 - drive->dn), t);
/*
* Drive init
if (t < 0x20) via_config++; /* vt82c586 */
}
+ if (via_isa_bridges[via_config].id == PCI_DEVICE_ID_VIA_82C596) {
+ pci_read_config_byte(isa, PCI_REVISION_ID, &t);
+ if (t < 0x10) via_config++; /* vt82c596a */
+ }
+
/*
* Check UDMA66 mode set by BIOS.
*/
/*
* Set UDMA66 double clock bits.
*/
-
- pci_write_config_dword(dev, VIA_UDMA_TIMING, u | 0x80008);
- pci_read_config_dword(dev, VIA_UDMA_TIMING, &u);
-
- if ((via_isa_bridges[via_config].id == PCI_DEVICE_ID_VIA_82C596 || !isa)
- && (u & 0x80008) != 0x80008)
- via_config++; /* vt82c596a / Unknown UDMA33 */
+ if (via_isa_bridges[via_config].speed == XFER_UDMA_4)
+ pci_write_config_dword(dev, VIA_UDMA_TIMING, u | 0x80008);
/*
* Set up FIFO, flush, prefetch and post-writes.
mainmenu_option next_comment
comment 'Multi-device support (RAID and LVM)'
-tristate 'Multiple devices driver support' CONFIG_BLK_DEV_MD
+tristate 'Multiple devices driver support (RAID and LVM)' CONFIG_BLK_DEV_MD
dep_tristate ' Linear (append) mode' CONFIG_MD_LINEAR $CONFIG_BLK_DEV_MD
dep_tristate ' RAID-0 (striping) mode' CONFIG_MD_RAID0 $CONFIG_BLK_DEV_MD
dep_tristate ' RAID-1 (mirroring) mode' CONFIG_MD_RAID1 $CONFIG_BLK_DEV_MD
bool ' Auto Detect support' CONFIG_AUTODETECT_RAID
fi
-tristate 'Logical volume manager (LVM) support' CONFIG_BLK_DEV_LVM N
-if [ "$CONFIG_BLK_DEV_LVM" != "n" ]; then
- bool ' LVM information in proc filesystem' CONFIG_LVM_PROC_FS Y
-fi
+dep_tristate 'Logical volume manager (LVM) support' CONFIG_BLK_DEV_LVM $CONFIG_BLK_DEV_MD
+dep_mbool ' LVM information in proc filesystem' CONFIG_LVM_PROC_FS $CONFIG_BLK_DEV_LVM
endmenu
--- /dev/null
+/*
+ * kernel/lvm-snap.c
+ *
+ * Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE
+ *
+ * LVM snapshot driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * LVM driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/blkdev.h>
+#include <linux/smp_lock.h>
+#include <linux/types.h>
+#include <linux/iobuf.h>
+#include <linux/lvm.h>
+
+
+static char *lvm_snap_version __attribute__ ((unused)) = "LVM 0.8final (15/02/2000)\n";
+
+extern const char *const lvm_name;
+extern int lvm_blocksizes[];
+
+void lvm_snapshot_release(lv_t *);
+
+#define hashfn(dev,block,mask,chunk_size) \
+ ((HASHDEV(dev)^((block)/(chunk_size))) & (mask))
+
+static inline lv_block_exception_t *
+lvm_find_exception_table(kdev_t org_dev, unsigned long org_start, lv_t * lv)
+{
+ struct list_head * hash_table = lv->lv_snapshot_hash_table, * next;
+ unsigned long mask = lv->lv_snapshot_hash_mask;
+ int chunk_size = lv->lv_chunk_size;
+ lv_block_exception_t * ret;
+ int i = 0;
+
+ hash_table = &hash_table[hashfn(org_dev, org_start, mask, chunk_size)];
+ ret = NULL;
+ for (next = hash_table->next; next != hash_table; next = next->next)
+ {
+ lv_block_exception_t * exception;
+
+ exception = list_entry(next, lv_block_exception_t, hash);
+ if (exception->rsector_org == org_start &&
+ exception->rdev_org == org_dev)
+ {
+ if (i)
+ {
+ /* fun, isn't it? :) */
+ list_del(next);
+ list_add(next, hash_table);
+ }
+ ret = exception;
+ break;
+ }
+ i++;
+ }
+ return ret;
+}
+
+static inline void lvm_hash_link(lv_block_exception_t * exception,
+ kdev_t org_dev, unsigned long org_start,
+ lv_t * lv)
+{
+ struct list_head * hash_table = lv->lv_snapshot_hash_table;
+ unsigned long mask = lv->lv_snapshot_hash_mask;
+ int chunk_size = lv->lv_chunk_size;
+
+ hash_table = &hash_table[hashfn(org_dev, org_start, mask, chunk_size)];
+ list_add(&exception->hash, hash_table);
+}
+
+int lvm_snapshot_remap_block(kdev_t * org_dev, unsigned long * org_sector,
+ unsigned long pe_start, lv_t * lv)
+{
+ int ret;
+ unsigned long pe_off, pe_adjustment, __org_start;
+ kdev_t __org_dev;
+ int chunk_size = lv->lv_chunk_size;
+ lv_block_exception_t * exception;
+
+ pe_off = pe_start % chunk_size;
+ pe_adjustment = (*org_sector-pe_off) % chunk_size;
+ __org_start = *org_sector - pe_adjustment;
+ __org_dev = *org_dev;
+
+ ret = 0;
+ exception = lvm_find_exception_table(__org_dev, __org_start, lv);
+ if (exception)
+ {
+ *org_dev = exception->rdev_new;
+ *org_sector = exception->rsector_new + pe_adjustment;
+ ret = 1;
+ }
+ return ret;
+}
+
+static void lvm_drop_snapshot(lv_t * lv_snap, const char * reason)
+{
+ kdev_t last_dev;
+ int i;
+
+ /* no exception storage space available for this snapshot
+ or error on this snapshot --> release it */
+ invalidate_buffers(lv_snap->lv_dev);
+
+ last_dev = 0;
+ for (i = 0; i < lv_snap->lv_remap_ptr; i++) {
+ if ( lv_snap->lv_block_exception[i].rdev_new != last_dev) {
+ last_dev = lv_snap->lv_block_exception[i].rdev_new;
+ invalidate_buffers(last_dev);
+ }
+ }
+
+ lvm_snapshot_release(lv_snap);
+
+ printk(KERN_INFO
+ "%s -- giving up to snapshot %s on %s due %s\n",
+ lvm_name, lv_snap->lv_snapshot_org->lv_name, lv_snap->lv_name,
+ reason);
+}
+
+static inline void lvm_snapshot_prepare_blocks(unsigned long * blocks,
+ unsigned long start,
+ int nr_sectors,
+ int blocksize)
+{
+ int i, sectors_per_block, nr_blocks;
+
+ sectors_per_block = blocksize >> 9;
+ nr_blocks = nr_sectors / sectors_per_block;
+ start /= sectors_per_block;
+
+ for (i = 0; i < nr_blocks; i++)
+ blocks[i] = start++;
+}
+
+static inline int get_blksize(kdev_t dev)
+{
+ int correct_size = BLOCK_SIZE, i, major;
+
+ major = MAJOR(dev);
+ if (blksize_size[major])
+ {
+ i = blksize_size[major][MINOR(dev)];
+ if (i)
+ correct_size = i;
+ }
+ return correct_size;
+}
+
+#ifdef DEBUG_SNAPSHOT
+static inline void invalidate_snap_cache(unsigned long start, unsigned long nr,
+ kdev_t dev)
+{
+ struct buffer_head * bh;
+ int sectors_per_block, i, blksize, minor;
+
+ minor = MINOR(dev);
+ blksize = lvm_blocksizes[minor];
+ sectors_per_block = blksize >> 9;
+ nr /= sectors_per_block;
+ start /= sectors_per_block;
+
+ for (i = 0; i < nr; i++)
+ {
+ bh = get_hash_table(dev, start++, blksize);
+ if (bh)
+ bforget(bh);
+ }
+}
+#endif
+
+/*
+ * copy on write handler for one snapshot logical volume
+ *
+ * read the original blocks and store it/them on the new one(s).
+ * if there is no exception storage space free any longer --> release snapshot.
+ *
+ * this routine gets called for each _first_ write to a physical chunk.
+ */
+int lvm_snapshot_COW(kdev_t org_phys_dev,
+ unsigned long org_phys_sector,
+ unsigned long org_pe_start,
+ unsigned long org_virt_sector,
+ lv_t * lv_snap)
+{
+ const char * reason;
+ unsigned long org_start, snap_start, virt_start, pe_off;
+ int idx = lv_snap->lv_remap_ptr, chunk_size = lv_snap->lv_chunk_size;
+ kdev_t snap_phys_dev;
+ struct kiobuf * iobuf;
+ unsigned long blocks[KIO_MAX_SECTORS];
+ int blksize_snap, blksize_org, min_blksize, max_blksize;
+ int max_sectors, nr_sectors;
+
+ /* check if we are out of snapshot space */
+ if (idx >= lv_snap->lv_remap_end)
+ goto fail_out_of_space;
+
+ /* calculate physical boundaries of source chunk */
+ pe_off = org_pe_start % chunk_size;
+ org_start = org_phys_sector - ((org_phys_sector-pe_off) % chunk_size);
+ virt_start = org_virt_sector - (org_phys_sector - org_start);
+
+ /* calculate physical boundaries of destination chunk */
+ snap_phys_dev = lv_snap->lv_block_exception[idx].rdev_new;
+ snap_start = lv_snap->lv_block_exception[idx].rsector_new;
+
+#ifdef DEBUG_SNAPSHOT
+ printk(KERN_INFO
+ "%s -- COW: "
+ "org %02d:%02d faulting %lu start %lu, "
+ "snap %02d:%02d start %lu, "
+ "size %d, pe_start %lu pe_off %lu, virt_sec %lu\n",
+ lvm_name,
+ MAJOR(org_phys_dev), MINOR(org_phys_dev), org_phys_sector,
+ org_start,
+ MAJOR(snap_phys_dev), MINOR(snap_phys_dev), snap_start,
+ chunk_size,
+ org_pe_start, pe_off,
+ org_virt_sector);
+#endif
+
+ iobuf = lv_snap->lv_iobuf;
+
+ blksize_org = get_blksize(org_phys_dev);
+ blksize_snap = get_blksize(snap_phys_dev);
+ max_blksize = max(blksize_org, blksize_snap);
+ min_blksize = min(blksize_org, blksize_snap);
+ max_sectors = KIO_MAX_SECTORS * (min_blksize>>9);
+
+ if (chunk_size % (max_blksize>>9))
+ goto fail_blksize;
+
+ while (chunk_size)
+ {
+ nr_sectors = min(chunk_size, max_sectors);
+ chunk_size -= nr_sectors;
+
+ iobuf->length = nr_sectors << 9;
+
+ lvm_snapshot_prepare_blocks(blocks, org_start,
+ nr_sectors, blksize_org);
+ if (brw_kiovec(READ, 1, &iobuf, org_phys_dev,
+ blocks, blksize_org) != (nr_sectors<<9))
+ goto fail_raw_read;
+
+ lvm_snapshot_prepare_blocks(blocks, snap_start,
+ nr_sectors, blksize_snap);
+ if (brw_kiovec(WRITE, 1, &iobuf, snap_phys_dev,
+ blocks, blksize_snap) != (nr_sectors<<9))
+ goto fail_raw_write;
+ }
+
+#ifdef DEBUG_SNAPSHOT
+ /* invalidate the logcial snapshot buffer cache */
+ invalidate_snap_cache(virt_start, lv_snap->lv_chunk_size,
+ lv_snap->lv_dev);
+#endif
+
+ /* the original chunk is now stored on the snapshot volume
+ so update the execption table */
+ lv_snap->lv_block_exception[idx].rdev_org = org_phys_dev;
+ lv_snap->lv_block_exception[idx].rsector_org = org_start;
+ lvm_hash_link(lv_snap->lv_block_exception + idx,
+ org_phys_dev, org_start, lv_snap);
+ lv_snap->lv_remap_ptr = idx + 1;
+ return 1;
+
+ /* slow path */
+ out:
+ lvm_drop_snapshot(lv_snap, reason);
+ return -1;
+
+ fail_out_of_space:
+ reason = "out of space";
+ goto out;
+ fail_raw_read:
+ reason = "read error";
+ goto out;
+ fail_raw_write:
+ reason = "write error";
+ goto out;
+ fail_blksize:
+ reason = "blocksize error";
+ goto out;
+}
+
+static int lvm_snapshot_alloc_iobuf_pages(struct kiobuf * iobuf, int sectors)
+{
+ int bytes, nr_pages, err, i;
+
+ bytes = sectors << 9;
+ nr_pages = (bytes + ~PAGE_MASK) >> PAGE_SHIFT;
+ err = expand_kiobuf(iobuf, nr_pages);
+ if (err)
+ goto out;
+
+ err = -ENOMEM;
+ iobuf->locked = 1;
+ iobuf->nr_pages = 0;
+ for (i = 0; i < nr_pages; i++)
+ {
+ struct page * page;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27)
+ page = alloc_page(GFP_KERNEL);
+ if (!page)
+ goto out;
+#else
+ {
+ unsigned long addr = __get_free_page(GFP_USER);
+ if (!addr)
+ goto out;
+ iobuf->pagelist[i] = addr;
+ page = virt_to_page(addr);
+ }
+#endif
+
+ iobuf->maplist[i] = page;
+ /* the only point to lock the page here is to be allowed
+ to share unmap_kiobuf() in the fail-path */
+#ifndef LockPage
+#define LockPage(map) set_bit(PG_locked, &(map)->flags)
+#endif
+ LockPage(page);
+ iobuf->nr_pages++;
+ }
+ iobuf->offset = 0;
+
+ err = 0;
+ out:
+ return err;
+}
+
+static int calc_max_buckets(void)
+{
+ unsigned long mem;
+
+ mem = num_physpages << PAGE_SHIFT;
+ mem /= 100;
+ mem *= 2;
+ mem /= sizeof(struct list_head);
+
+ return mem;
+}
+
+static int lvm_snapshot_alloc_hash_table(lv_t * lv)
+{
+ int err;
+ unsigned long buckets, max_buckets, size;
+ struct list_head * hash;
+
+ buckets = lv->lv_remap_end;
+ max_buckets = calc_max_buckets();
+ buckets = min(buckets, max_buckets);
+ while (buckets & (buckets-1))
+ buckets &= (buckets-1);
+
+ size = buckets * sizeof(struct list_head);
+
+ err = -ENOMEM;
+ hash = vmalloc(size);
+ lv->lv_snapshot_hash_table = hash;
+
+ if (!hash)
+ goto out;
+
+ lv->lv_snapshot_hash_mask = buckets-1;
+ while (buckets--)
+ INIT_LIST_HEAD(hash+buckets);
+ err = 0;
+ out:
+ return err;
+}
+
+int lvm_snapshot_alloc(lv_t * lv_snap)
+{
+ int err, blocksize, max_sectors;
+
+ err = alloc_kiovec(1, &lv_snap->lv_iobuf);
+ if (err)
+ goto out;
+
+ blocksize = lvm_blocksizes[MINOR(lv_snap->lv_dev)];
+ max_sectors = KIO_MAX_SECTORS << (PAGE_SHIFT-9);
+
+ err = lvm_snapshot_alloc_iobuf_pages(lv_snap->lv_iobuf, max_sectors);
+ if (err)
+ goto out_free_kiovec;
+
+ err = lvm_snapshot_alloc_hash_table(lv_snap);
+ if (err)
+ goto out_free_kiovec;
+ out:
+ return err;
+
+ out_free_kiovec:
+ unmap_kiobuf(lv_snap->lv_iobuf);
+ free_kiovec(1, &lv_snap->lv_iobuf);
+ goto out;
+}
+
+void lvm_snapshot_release(lv_t * lv)
+{
+ if (lv->lv_block_exception)
+ {
+ vfree(lv->lv_block_exception);
+ lv->lv_block_exception = NULL;
+ }
+ if (lv->lv_snapshot_hash_table)
+ {
+ vfree(lv->lv_snapshot_hash_table);
+ lv->lv_snapshot_hash_table = NULL;
+ }
+ if (lv->lv_iobuf)
+ {
+ free_kiovec(1, &lv->lv_iobuf);
+ lv->lv_iobuf = NULL;
+ }
+}
--- /dev/null
+/*
+ * kernel/lvm.c
+ *
+ * Copyright (C) 1997 - 2000 Heinz Mauelshagen, Germany
+ *
+ * February-November 1997
+ * April-May,July-August,November 1998
+ * January-March,May,July,September,October 1999
+ * January,February 2000
+ *
+ *
+ * LVM driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * LVM driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU CC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+/*
+ * Changelog
+ *
+ * 09/11/1997 - added chr ioctls VG_STATUS_GET_COUNT
+ * and VG_STATUS_GET_NAMELIST
+ * 18/01/1998 - change lvm_chr_open/close lock handling
+ * 30/04/1998 - changed LV_STATUS ioctl to LV_STATUS_BYNAME and
+ * - added LV_STATUS_BYINDEX ioctl
+ * - used lvm_status_byname_req_t and
+ * lvm_status_byindex_req_t vars
+ * 04/05/1998 - added multiple device support
+ * 08/05/1998 - added support to set/clear extendable flag in volume group
+ * 09/05/1998 - changed output of lvm_proc_get_info() because of
+ * support for free (eg. longer) logical volume names
+ * 12/05/1998 - added spin_locks (thanks to Pascal van Dam
+ * <pascal@ramoth.xs4all.nl>)
+ * 25/05/1998 - fixed handling of locked PEs in lvm_map() and lvm_chr_ioctl()
+ * 26/05/1998 - reactivated verify_area by access_ok
+ * 07/06/1998 - used vmalloc/vfree instead of kmalloc/kfree to go
+ * beyond 128/256 KB max allocation limit per call
+ * - #ifdef blocked spin_lock calls to avoid compile errors
+ * with 2.0.x
+ * 11/06/1998 - another enhancement to spinlock code in lvm_chr_open()
+ * and use of LVM_VERSION_CODE instead of my own macros
+ * (thanks to Michael Marxmeier <mike@msede.com>)
+ * 07/07/1998 - added statistics in lvm_map()
+ * 08/07/1998 - saved statistics in lvm_do_lv_extend_reduce()
+ * 25/07/1998 - used __initfunc macro
+ * 02/08/1998 - changes for official char/block major numbers
+ * 07/08/1998 - avoided init_module() and cleanup_module() to be static
+ * 30/08/1998 - changed VG lv_open counter from sum of LV lv_open counters
+ * to sum of LVs open (no matter how often each is)
+ * 01/09/1998 - fixed lvm_gendisk.part[] index error
+ * 07/09/1998 - added copying of lv_current_pe-array
+ * in LV_STATUS_BYINDEX ioctl
+ * 17/11/1998 - added KERN_* levels to printk
+ * 13/01/1999 - fixed LV index bug in lvm_do_lv_create() which hit lvrename
+ * 07/02/1999 - fixed spinlock handling bug in case of LVM_RESET
+ * by moving spinlock code from lvm_chr_open()
+ * to lvm_chr_ioctl()
+ * - added LVM_LOCK_LVM ioctl to lvm_chr_ioctl()
+ * - allowed LVM_RESET and retrieval commands to go ahead;
+ * only other update ioctls are blocked now
+ * - fixed pv->pe to NULL for pv_status
+ * - using lv_req structure in lvm_chr_ioctl() now
+ * - fixed NULL ptr reference bug in lvm_do_lv_extend_reduce()
+ * caused by uncontiguous PV array in lvm_chr_ioctl(VG_REDUCE)
+ * 09/02/1999 - changed BLKRASET and BLKRAGET in lvm_chr_ioctl() to
+ * handle lgoical volume private read ahead sector
+ * - implemented LV read_ahead handling with lvm_blk_read()
+ * and lvm_blk_write()
+ * 10/02/1999 - implemented 2.[12].* support function lvm_hd_name()
+ * to be used in drivers/block/genhd.c by disk_name()
+ * 12/02/1999 - fixed index bug in lvm_blk_ioctl(), HDIO_GETGEO
+ * - enhanced gendisk insert/remove handling
+ * 16/02/1999 - changed to dynamic block minor number allocation to
+ * have as much as 99 volume groups with 256 logical volumes
+ * as the grand total; this allows having 1 volume group with
+ * up to 256 logical volumes in it
+ * 21/02/1999 - added LV open count information to proc filesystem
+ * - substituted redundant LVM_RESET code by calls
+ * to lvm_do_vg_remove()
+ * 22/02/1999 - used schedule_timeout() to be more responsive
+ * in case of lvm_do_vg_remove() with lots of logical volumes
+ * 19/03/1999 - fixed NULL pointer bug in module_init/lvm_init
+ * 17/05/1999 - used DECLARE_WAIT_QUEUE_HEAD macro (>2.3.0)
+ * - enhanced lvm_hd_name support
+ * 03/07/1999 - avoided use of KERNEL_VERSION macro based ifdefs and
+ * memcpy_tofs/memcpy_fromfs macro redefinitions
+ * 06/07/1999 - corrected reads/writes statistic counter copy in case
+ * of striped logical volume
+ * 28/07/1999 - implemented snapshot logical volumes
+ * - lvm_chr_ioctl
+ * - LV_STATUS_BYINDEX
+ * - LV_STATUS_BYNAME
+ * - lvm_do_lv_create
+ * - lvm_do_lv_remove
+ * - lvm_map
+ * - new lvm_snapshot_remap_block
+ * - new lvm_snapshot_remap_new_block
+ * 08/10/1999 - implemented support for multiple snapshots per
+ * original logical volume
+ * 12/10/1999 - support for 2.3.19
+ * 11/11/1999 - support for 2.3.28
+ * 21/11/1999 - changed lvm_map() interface to buffer_head based
+ * 19/12/1999 - support for 2.3.33
+ * 01/01/2000 - changed locking concept in lvm_map(),
+ * lvm_do_vg_create() and lvm_do_lv_remove()
+ * 15/01/2000 - fixed PV_FLUSH bug in lvm_chr_ioctl()
+ * 24/01/2000 - ported to 2.3.40 including Alan Cox's pointer changes etc.
+ * 29/01/2000 - used kmalloc/kfree again for all small structures
+ * 20/01/2000 - cleaned up lvm_chr_ioctl by moving code
+ * to seperated functions
+ * - avoided "/dev/" in proc filesystem output
+ * - avoided inline strings functions lvm_strlen etc.
+ * 14/02/2000 - support for 2.3.43
+ * - integrated Andrea Arcangeli's snapshot code
+ *
+ */
+
+
+static char *lvm_version = "LVM version 0.8final by Heinz Mauelshagen (15/02/2000)\n";
+static char *lvm_short_version = "version 0.8final (15/02/2000)";
+
+#define MAJOR_NR LVM_BLK_MAJOR
+#define DEVICE_OFF(device)
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#ifdef MODVERSIONS
+#undef MODULE
+#define MODULE
+#include <linux/modversions.h>
+#endif
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+#include <linux/hdreg.h>
+#include <linux/stat.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/blkdev.h>
+#include <linux/genhd.h>
+#include <linux/locks.h>
+#include <linux/smp_lock.h>
+#include <asm/ioctl.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+
+#define LOCAL_END_REQUEST
+
+#include <linux/blk.h>
+#include <linux/blkpg.h>
+
+#include <linux/errno.h>
+#include <linux/lvm.h>
+
+#define LVM_CORRECT_READ_AHEAD(a) \
+ (((a) < LVM_MIN_READ_AHEAD || (a) > LVM_MAX_READ_AHEAD) \
+ ? LVM_MAX_READ_AHEAD : (a))
+
+#ifndef WRITEA
+# define WRITEA WRITE
+#endif
+
+/*
+ * External function prototypes
+ */
+#ifdef MODULE
+int init_module(void);
+void cleanup_module(void);
+#else
+extern int lvm_init(void);
+#endif
+
+static void lvm_dummy_device_request(request_queue_t *);
+#define DEVICE_REQUEST lvm_dummy_device_request
+
+static int lvm_make_request_fn(request_queue_t *, int, struct buffer_head*);
+static void lvm_plug_device_noop(request_queue_t *, kdev_t);
+
+static int lvm_blk_ioctl(struct inode *, struct file *, uint, ulong);
+static int lvm_blk_open(struct inode *, struct file *);
+
+static int lvm_chr_open(struct inode *, struct file *);
+
+static int lvm_chr_close(struct inode *, struct file *);
+static int lvm_blk_close(struct inode *, struct file *);
+
+static int lvm_chr_ioctl(struct inode *, struct file *, uint, ulong);
+
+#if defined CONFIG_LVM_PROC_FS && defined CONFIG_PROC_FS
+static int lvm_proc_get_info(char *, char **, off_t, int);
+static int (*lvm_proc_get_info_ptr) (char *, char **, off_t, int) =
+&lvm_proc_get_info;
+#endif
+
+#ifdef LVM_HD_NAME
+void lvm_hd_name(char *, int);
+#endif
+/* End external function prototypes */
+
+
+/*
+ * Internal function prototypes
+ */
+static void lvm_init_vars(void);
+
+/* external snapshot calls */
+int lvm_snapshot_remap_block(kdev_t *, ulong *, ulong, lv_t *);
+int lvm_snapshot_COW(kdev_t, ulong, ulong, ulong, lv_t *);
+int lvm_snapshot_alloc(lv_t *);
+void lvm_snapshot_release(lv_t *);
+
+#ifdef LVM_HD_NAME
+extern void (*lvm_hd_name_ptr) (char *, int);
+#endif
+static int lvm_map(struct buffer_head *, int);
+static int lvm_do_lock_lvm(void);
+static int lvm_do_le_remap(vg_t *, void *);
+static int lvm_do_pe_lock_unlock(vg_t *r, void *);
+static int lvm_do_vg_create(int, void *);
+static int lvm_do_vg_extend(vg_t *, void *);
+static int lvm_do_vg_reduce(vg_t *, void *);
+static int lvm_do_vg_remove(int);
+static int lvm_do_lv_create(int, char *, lv_t *);
+static int lvm_do_lv_remove(int, char *, int);
+static int lvm_do_lv_extend_reduce(int, char *, lv_t *);
+static int lvm_do_lv_status_byname(vg_t *r, void *);
+static int lvm_do_lv_status_byindex(vg_t *, void *arg);
+static int lvm_do_pv_change(vg_t*, void*);
+static int lvm_do_pv_status(vg_t *, void *);
+static void lvm_geninit(struct gendisk *);
+#ifdef LVM_GET_INODE
+static struct inode *lvm_get_inode(kdev_t);
+void lvm_clear_inode(struct inode *);
+#endif
+/* END Internal function prototypes */
+
+
+/* volume group descriptor area pointers */
+static vg_t *vg[ABS_MAX_VG];
+static pv_t *pvp = NULL;
+static lv_t *lvp = NULL;
+static pe_t *pep = NULL;
+static pe_t *pep1 = NULL;
+
+
+/* map from block minor number to VG and LV numbers */
+typedef struct {
+ int vg_number;
+ int lv_number;
+} vg_lv_map_t;
+static vg_lv_map_t vg_lv_map[ABS_MAX_LV];
+
+
+/* Request structures (lvm_chr_ioctl()) */
+static pv_change_req_t pv_change_req;
+static pv_flush_req_t pv_flush_req;
+static pv_status_req_t pv_status_req;
+static pe_lock_req_t pe_lock_req;
+static le_remap_req_t le_remap_req;
+static lv_req_t lv_req;
+
+#ifdef LVM_TOTAL_RESET
+static int lvm_reset_spindown = 0;
+#endif
+
+static char pv_name[NAME_LEN];
+/* static char rootvg[NAME_LEN] = { 0, }; */
+static uint lv_open = 0;
+const char *const lvm_name = LVM_NAME;
+static int lock = 0;
+static int loadtime = 0;
+static uint vg_count = 0;
+static long lvm_chr_open_count = 0;
+static ushort lvm_iop_version = LVM_DRIVER_IOP_VERSION;
+static DECLARE_WAIT_QUEUE_HEAD(lvm_snapshot_wait);
+static DECLARE_WAIT_QUEUE_HEAD(lvm_wait);
+static DECLARE_WAIT_QUEUE_HEAD(lvm_map_wait);
+
+static spinlock_t lvm_lock = SPIN_LOCK_UNLOCKED;
+
+static devfs_handle_t lvm_devfs_handle;
+static devfs_handle_t vg_devfs_handle[MAX_VG];
+static devfs_handle_t ch_devfs_handle[MAX_VG];
+static devfs_handle_t lv_devfs_handle[MAX_LV];
+
+static struct file_operations lvm_chr_fops =
+{
+ owner: THIS_MODULE,
+ open: lvm_chr_open,
+ release: lvm_chr_close,
+ ioctl: lvm_chr_ioctl,
+};
+
+static struct block_device_operations lvm_blk_dops =
+{
+ open: lvm_blk_open,
+ release: lvm_blk_close,
+ ioctl: lvm_blk_ioctl
+};
+
+/* gendisk structures */
+static struct hd_struct lvm_hd_struct[MAX_LV];
+static int lvm_blocksizes[MAX_LV] =
+{0,};
+static int lvm_size[MAX_LV] =
+{0,};
+static struct gendisk lvm_gendisk =
+{
+ MAJOR_NR, /* major # */
+ LVM_NAME, /* name of major */
+ 0, /* number of times minor is shifted
+ to get real minor */
+ 1, /* maximum partitions per device */
+ lvm_hd_struct, /* partition table */
+ lvm_size, /* device size in blocks, copied
+ to block_size[] */
+ MAX_LV, /* number or real devices */
+ NULL, /* internal */
+ NULL, /* pointer to next gendisk struct (internal) */
+};
+
+
+#ifdef MODULE
+/*
+ * Module initialization...
+ */
+int init_module(void)
+#else
+/*
+ * Driver initialization...
+ */
+#ifdef __initfunc
+__initfunc(int lvm_init(void))
+#else
+int __init lvm_init(void)
+#endif
+#endif /* #ifdef MODULE */
+{
+ struct gendisk *gendisk_ptr = NULL;
+
+ if (register_chrdev(LVM_CHAR_MAJOR, lvm_name, &lvm_chr_fops) < 0) {
+ printk(KERN_ERR "%s -- register_chrdev failed\n", lvm_name);
+ return -EIO;
+ }
+ if (register_blkdev(MAJOR_NR, lvm_name, &lvm_blk_dops) < 0) {
+ printk("%s -- register_blkdev failed\n", lvm_name);
+ if (unregister_chrdev(LVM_CHAR_MAJOR, lvm_name) < 0)
+ printk(KERN_ERR "%s -- unregister_chrdev failed\n", lvm_name);
+ return -EIO;
+ }
+
+ lvm_devfs_handle = devfs_register(
+ 0 , "lvm", 0, 0, LVM_CHAR_MAJOR,
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,
+ &lvm_chr_fops, NULL);
+
+#if defined CONFIG_LVM_PROC_FS && defined CONFIG_PROC_FS
+ create_proc_info_entry(LVM_NAME, S_IFREG | S_IRUGO,
+ &proc_root, lvm_proc_get_info_ptr);
+#endif
+
+ lvm_init_vars();
+ lvm_geninit(&lvm_gendisk);
+
+ /* insert our gendisk at the corresponding major */
+ if (gendisk_head != NULL) {
+ gendisk_ptr = gendisk_head;
+ while (gendisk_ptr->next != NULL &&
+ gendisk_ptr->major > lvm_gendisk.major) {
+ gendisk_ptr = gendisk_ptr->next;
+ }
+ lvm_gendisk.next = gendisk_ptr->next;
+ gendisk_ptr->next = &lvm_gendisk;
+ } else {
+ gendisk_head = &lvm_gendisk;
+ lvm_gendisk.next = NULL;
+ }
+
+#ifdef LVM_HD_NAME
+ /* reference from drivers/block/genhd.c */
+ lvm_hd_name_ptr = lvm_hd_name;
+#endif
+
+ blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
+ blk_queue_make_request(BLK_DEFAULT_QUEUE(MAJOR_NR), lvm_make_request_fn);
+ blk_queue_pluggable(BLK_DEFAULT_QUEUE(MAJOR_NR), lvm_plug_device_noop);
+ /* optional read root VGDA */
+/*
+ if ( *rootvg != 0) vg_read_with_pv_and_lv ( rootvg, &vg);
+*/
+
+ printk(KERN_INFO
+ "%s%s -- "
+#ifdef MODULE
+ "Module"
+#else
+ "Driver"
+#endif
+ " successfully initialized\n",
+ lvm_version, lvm_name);
+
+ return 0;
+} /* init_module() / lvm_init() */
+
+
+#ifdef MODULE
+/*
+ * Module cleanup...
+ */
+void cleanup_module(void)
+{
+ struct gendisk *gendisk_ptr = NULL, *gendisk_ptr_prev = NULL;
+
+ devfs_unregister (lvm_devfs_handle);
+
+ if (unregister_chrdev(LVM_CHAR_MAJOR, lvm_name) < 0) {
+ printk(KERN_ERR "%s -- unregister_chrdev failed\n", lvm_name);
+ }
+ if (unregister_blkdev(MAJOR_NR, lvm_name) < 0) {
+ printk(KERN_ERR "%s -- unregister_blkdev failed\n", lvm_name);
+ }
+ blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+
+ gendisk_ptr = gendisk_ptr_prev = gendisk_head;
+ while (gendisk_ptr != NULL) {
+ if (gendisk_ptr == &lvm_gendisk)
+ break;
+ gendisk_ptr_prev = gendisk_ptr;
+ gendisk_ptr = gendisk_ptr->next;
+ }
+ /* delete our gendisk from chain */
+ if (gendisk_ptr == &lvm_gendisk)
+ gendisk_ptr_prev->next = gendisk_ptr->next;
+
+ blk_size[MAJOR_NR] = NULL;
+ blksize_size[MAJOR_NR] = NULL;
+
+#if defined CONFIG_LVM_PROC_FS && defined CONFIG_PROC_FS
+ remove_proc_entry(LVM_NAME, &proc_root);
+#endif
+
+#ifdef LVM_HD_NAME
+ /* reference from linux/drivers/block/genhd.c */
+ lvm_hd_name_ptr = NULL;
+#endif
+
+ printk(KERN_INFO "%s -- Module successfully deactivated\n", lvm_name);
+
+ return;
+} /* void cleanup_module() */
+#endif /* #ifdef MODULE */
+
+
+/*
+ * support function to initialize lvm variables
+ */
+#ifdef __initfunc
+__initfunc(void lvm_init_vars(void))
+#else
+void __init lvm_init_vars(void)
+#endif
+{
+ int v;
+
+ loadtime = CURRENT_TIME;
+
+ pe_lock_req.lock = UNLOCK_PE;
+ pe_lock_req.data.lv_dev = pe_lock_req.data.pv_dev = 0;
+ pe_lock_req.data.pv_offset = 0;
+
+ /* Initialize VG pointers */
+ for (v = 0; v < ABS_MAX_VG; v++) vg[v] = NULL;
+
+ /* Initialize LV -> VG association */
+ for (v = 0; v < ABS_MAX_LV; v++) {
+ /* index ABS_MAX_VG never used for real VG */
+ vg_lv_map[v].vg_number = ABS_MAX_VG;
+ vg_lv_map[v].lv_number = -1;
+ }
+
+ return;
+} /* lvm_init_vars() */
+
+
+/********************************************************************
+ *
+ * Character device functions
+ *
+ ********************************************************************/
+
+/*
+ * character device open routine
+ */
+static int lvm_chr_open(struct inode *inode,
+ struct file *file)
+{
+ int minor = MINOR(inode->i_rdev);
+
+#ifdef DEBUG
+ printk(KERN_DEBUG
+ "%s -- lvm_chr_open MINOR: %d VG#: %d mode: 0x%X lock: %d\n",
+ lvm_name, minor, VG_CHR(minor), file->f_mode, lock);
+#endif
+
+ /* super user validation */
+ if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+
+ /* Group special file open */
+ if (VG_CHR(minor) > MAX_VG) return -ENXIO;
+
+ lvm_chr_open_count++;
+ return 0;
+} /* lvm_chr_open() */
+
+
+/*
+ * character device i/o-control routine
+ *
+ * Only one changing process can do changing ioctl at one time,
+ * others will block.
+ *
+ */
+static int lvm_chr_ioctl(struct inode *inode, struct file *file,
+ uint command, ulong a)
+{
+ int minor = MINOR(inode->i_rdev);
+ uint extendable, l, v;
+ void *arg = (void *) a;
+ lv_t lv;
+ vg_t* vg_ptr = vg[VG_CHR(minor)];
+
+ /* otherwise cc will complain about unused variables */
+ (void) lvm_lock;
+
+
+#ifdef DEBUG_IOCTL
+ printk(KERN_DEBUG
+ "%s -- lvm_chr_ioctl: command: 0x%X MINOR: %d "
+ "VG#: %d mode: 0x%X\n",
+ lvm_name, command, minor, VG_CHR(minor), file->f_mode);
+#endif
+
+#ifdef LVM_TOTAL_RESET
+ if (lvm_reset_spindown > 0) return -EACCES;
+#endif
+
+ /* Main command switch */
+ switch (command) {
+ case LVM_LOCK_LVM:
+ /* lock the LVM */
+ return lvm_do_lock_lvm();
+
+ case LVM_GET_IOP_VERSION:
+ /* check lvm version to ensure driver/tools+lib
+ interoperability */
+ if (copy_to_user(arg, &lvm_iop_version, sizeof(ushort)) != 0)
+ return -EFAULT;
+ return 0;
+
+#ifdef LVM_TOTAL_RESET
+ case LVM_RESET:
+ /* lock reset function */
+ lvm_reset_spindown = 1;
+ for (v = 0; v < ABS_MAX_VG; v++) {
+ if (vg[v] != NULL) lvm_do_vg_remove(v);
+ }
+
+#ifdef MODULE
+ while (GET_USE_COUNT(&__this_module) < 1)
+ MOD_INC_USE_COUNT;
+ while (GET_USE_COUNT(&__this_module) > 1)
+ MOD_DEC_USE_COUNT;
+#endif /* MODULE */
+ lock = 0; /* release lock */
+ wake_up_interruptible(&lvm_wait);
+ return 0;
+#endif /* LVM_TOTAL_RESET */
+
+
+ case LE_REMAP:
+ /* remap a logical extent (after moving the physical extent) */
+ return lvm_do_le_remap(vg_ptr,arg);
+
+ case PE_LOCK_UNLOCK:
+ /* lock/unlock i/o to a physical extent to move it to another
+ physical volume (move's done in user space's pvmove) */
+ return lvm_do_pe_lock_unlock(vg_ptr,arg);
+
+ case VG_CREATE:
+ /* create a VGDA */
+ return lvm_do_vg_create(minor, arg);
+
+ case VG_REMOVE:
+ /* remove an inactive VGDA */
+ return lvm_do_vg_remove(minor);
+
+ case VG_EXTEND:
+ /* extend a volume group */
+ return lvm_do_vg_extend(vg_ptr,arg);
+
+ case VG_REDUCE:
+ /* reduce a volume group */
+ return lvm_do_vg_reduce(vg_ptr,arg);
+
+
+ case VG_SET_EXTENDABLE:
+ /* set/clear extendability flag of volume group */
+ if (vg_ptr == NULL) return -ENXIO;
+ if (copy_from_user(&extendable, arg, sizeof(extendable)) != 0)
+ return -EFAULT;
+
+ if (extendable == VG_EXTENDABLE ||
+ extendable == ~VG_EXTENDABLE) {
+ if (extendable == VG_EXTENDABLE)
+ vg_ptr->vg_status |= VG_EXTENDABLE;
+ else
+ vg_ptr->vg_status &= ~VG_EXTENDABLE;
+ } else return -EINVAL;
+ return 0;
+
+
+ case VG_STATUS:
+ /* get volume group data (only the vg_t struct) */
+ if (vg_ptr == NULL) return -ENXIO;
+ if (copy_to_user(arg, vg_ptr, sizeof(vg_t)) != 0)
+ return -EFAULT;
+ return 0;
+
+
+ case VG_STATUS_GET_COUNT:
+ /* get volume group count */
+ if (copy_to_user(arg, &vg_count, sizeof(vg_count)) != 0)
+ return -EFAULT;
+ return 0;
+
+
+ case VG_STATUS_GET_NAMELIST:
+ /* get volume group count */
+ for (l = v = 0; v < ABS_MAX_VG; v++) {
+ if (vg[v] != NULL) {
+ if (copy_to_user(arg + l++ * NAME_LEN,
+ vg[v]->vg_name,
+ NAME_LEN) != 0)
+ return -EFAULT;
+ }
+ }
+ return 0;
+
+
+ case LV_CREATE:
+ case LV_REMOVE:
+ case LV_EXTEND:
+ case LV_REDUCE:
+ /* create, remove, extend or reduce a logical volume */
+ if (vg_ptr == NULL) return -ENXIO;
+ if (copy_from_user(&lv_req, arg, sizeof(lv_req)) != 0)
+ return -EFAULT;
+
+ if (command != LV_REMOVE) {
+ if (copy_from_user(&lv, lv_req.lv, sizeof(lv_t)) != 0)
+ return -EFAULT;
+ }
+ switch (command) {
+ case LV_CREATE:
+ return lvm_do_lv_create(minor, lv_req.lv_name, &lv);
+
+ case LV_REMOVE:
+ return lvm_do_lv_remove(minor, lv_req.lv_name, -1);
+
+ case LV_EXTEND:
+ case LV_REDUCE:
+ return lvm_do_lv_extend_reduce(minor, lv_req.lv_name, &lv);
+ }
+
+
+ case LV_STATUS_BYNAME:
+ /* get status of a logical volume by name */
+ return lvm_do_lv_status_byname(vg_ptr,arg);
+
+ case LV_STATUS_BYINDEX:
+ /* get status of a logical volume by index */
+ return lvm_do_lv_status_byindex(vg_ptr,arg);
+
+ case PV_CHANGE:
+ /* change a physical volume */
+ return lvm_do_pv_change(vg_ptr,arg);
+
+ case PV_STATUS:
+ /* get physical volume data (pv_t structure only) */
+ return lvm_do_pv_status(vg_ptr,arg);
+
+ case PV_FLUSH:
+ /* physical volume buffer flush/invalidate */
+ if (copy_from_user(&pv_flush_req, arg,
+ sizeof(pv_flush_req)) != 0)
+ return -EFAULT;
+
+ for ( v = 0; v < ABS_MAX_VG; v++) {
+ unsigned int p;
+ if ( vg[v] == NULL) continue;
+ for ( p = 0; p < vg[v]->pv_max; p++) {
+ if ( vg[v]->pv[p] != NULL &&
+ strcmp ( vg[v]->pv[p]->pv_name,
+ pv_flush_req.pv_name) == 0) {
+ fsync_dev ( vg[v]->pv[p]->pv_dev);
+ invalidate_buffers ( vg[v]->pv[p]->pv_dev);
+ return 0;
+ }
+ }
+ }
+ return 0;
+
+ default:
+ printk(KERN_WARNING
+ "%s -- lvm_chr_ioctl: unknown command %x\n",
+ lvm_name, command);
+ return -EINVAL;
+ }
+
+ return 0;
+} /* lvm_chr_ioctl */
+
+
+/*
+ * character device close routine
+ */
+static int lvm_chr_close(struct inode *inode, struct file *file)
+{
+#ifdef DEBUG
+ int minor = MINOR(inode->i_rdev);
+ printk(KERN_DEBUG
+ "%s -- lvm_chr_close VG#: %d\n", lvm_name, VG_CHR(minor));
+#endif
+
+ lock_kernel();
+#ifdef LVM_TOTAL_RESET
+ if (lvm_reset_spindown > 0) {
+ lvm_reset_spindown = 0;
+ lvm_chr_open_count = 1;
+ }
+#endif
+
+ if (lvm_chr_open_count > 0) lvm_chr_open_count--;
+ if (lock == current->pid) {
+ lock = 0; /* release lock */
+ wake_up_interruptible(&lvm_wait);
+ }
+ unlock_kernel();
+
+ return 0;
+} /* lvm_chr_close() */
+
+
+
+/********************************************************************
+ *
+ * Block device functions
+ *
+ ********************************************************************/
+
+/*
+ * block device open routine
+ */
+static int lvm_blk_open(struct inode *inode, struct file *file)
+{
+ int minor = MINOR(inode->i_rdev);
+ lv_t *lv_ptr;
+ vg_t *vg_ptr = vg[VG_BLK(minor)];
+
+#ifdef DEBUG_LVM_BLK_OPEN
+ printk(KERN_DEBUG
+ "%s -- lvm_blk_open MINOR: %d VG#: %d LV#: %d mode: 0x%X\n",
+ lvm_name, minor, VG_BLK(minor), LV_BLK(minor), file->f_mode);
+#endif
+
+#ifdef LVM_TOTAL_RESET
+ if (lvm_reset_spindown > 0)
+ return -EPERM;
+#endif
+
+ if (vg_ptr != NULL &&
+ (vg_ptr->vg_status & VG_ACTIVE) &&
+ (lv_ptr = vg_ptr->lv[LV_BLK(minor)]) != NULL &&
+ LV_BLK(minor) >= 0 &&
+ LV_BLK(minor) < vg_ptr->lv_max) {
+
+ /* Check parallel LV spindown (LV remove) */
+ if (lv_ptr->lv_status & LV_SPINDOWN) return -EPERM;
+
+ /* Check inactive LV and open for read/write */
+ if (file->f_mode & O_RDWR) {
+ if (!(lv_ptr->lv_status & LV_ACTIVE)) return -EPERM;
+ if (!(lv_ptr->lv_access & LV_WRITE)) return -EACCES;
+ }
+
+ /* be sure to increment VG counter */
+ if (lv_ptr->lv_open == 0) vg_ptr->lv_open++;
+ lv_ptr->lv_open++;
+
+ MOD_INC_USE_COUNT;
+
+#ifdef DEBUG_LVM_BLK_OPEN
+ printk(KERN_DEBUG
+ "%s -- lvm_blk_open MINOR: %d VG#: %d LV#: %d size: %d\n",
+ lvm_name, minor, VG_BLK(minor), LV_BLK(minor),
+ lv_ptr->lv_size);
+#endif
+
+ return 0;
+ }
+ return -ENXIO;
+} /* lvm_blk_open() */
+
+
+/*
+ * block device i/o-control routine
+ */
+static int lvm_blk_ioctl(struct inode *inode, struct file *file,
+ uint command, ulong a)
+{
+ int minor = MINOR(inode->i_rdev);
+ vg_t *vg_ptr = vg[VG_BLK(minor)];
+ lv_t *lv_ptr = vg_ptr->lv[LV_BLK(minor)];
+ void *arg = (void *) a;
+ struct hd_geometry *hd = (struct hd_geometry *) a;
+
+#ifdef DEBUG_IOCTL
+ printk(KERN_DEBUG
+ "%s -- lvm_blk_ioctl MINOR: %d command: 0x%X arg: %X "
+ "VG#: %dl LV#: %d\n",
+ lvm_name, minor, command, (ulong) arg,
+ VG_BLK(minor), LV_BLK(minor));
+#endif
+
+ switch (command) {
+ case BLKGETSIZE:
+ /* return device size */
+#ifdef DEBUG_IOCTL
+ printk(KERN_DEBUG
+ "%s -- lvm_blk_ioctl -- BLKGETSIZE: %u\n",
+ lvm_name, lv_ptr->lv_size);
+#endif
+ if (put_user(lv_ptr->lv_size, (long *)arg))
+ return -EFAULT;
+ break;
+
+
+ case BLKFLSBUF:
+ /* flush buffer cache */
+ if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+
+#ifdef DEBUG_IOCTL
+ printk(KERN_DEBUG
+ "%s -- lvm_blk_ioctl -- BLKFLSBUF\n", lvm_name);
+#endif
+ fsync_dev(inode->i_rdev);
+ invalidate_buffers(inode->i_rdev);
+ break;
+
+
+ case BLKRASET:
+ /* set read ahead for block device */
+ if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+
+#ifdef DEBUG_IOCTL
+ printk(KERN_DEBUG
+ "%s -- lvm_blk_ioctl -- BLKRASET: %d sectors for %02X:%02X\n",
+ lvm_name, (long) arg, MAJOR(inode->i_rdev), minor);
+#endif
+ if ((long) arg < LVM_MIN_READ_AHEAD ||
+ (long) arg > LVM_MAX_READ_AHEAD)
+ return -EINVAL;
+ read_ahead[MAJOR_NR] = lv_ptr->lv_read_ahead = (long) arg;
+ break;
+
+
+ case BLKRAGET:
+ /* get current read ahead setting */
+#ifdef DEBUG_IOCTL
+ printk(KERN_DEBUG
+ "%s -- lvm_blk_ioctl -- BLKRAGET\n", lvm_name);
+#endif
+ if (put_user(lv_ptr->lv_read_ahead, (long *)arg))
+ return -EFAULT;
+ break;
+
+
+ case HDIO_GETGEO:
+ /* get disk geometry */
+#ifdef DEBUG_IOCTL
+ printk(KERN_DEBUG
+ "%s -- lvm_blk_ioctl -- HDIO_GETGEO\n", lvm_name);
+#endif
+ if (hd == NULL)
+ return -EINVAL;
+ {
+ unsigned char heads = 64;
+ unsigned char sectors = 32;
+ long start = 0;
+ short cylinders = lv_ptr->lv_size / heads / sectors;
+
+ if (copy_to_user((char *) &hd->heads, &heads,
+ sizeof(heads)) != 0 ||
+ copy_to_user((char *) &hd->sectors, §ors,
+ sizeof(sectors)) != 0 ||
+ copy_to_user((short *) &hd->cylinders,
+ &cylinders, sizeof(cylinders)) != 0 ||
+ copy_to_user((long *) &hd->start, &start,
+ sizeof(start)) != 0)
+ return -EFAULT;
+ }
+
+#ifdef DEBUG_IOCTL
+ printk(KERN_DEBUG
+ "%s -- lvm_blk_ioctl -- cylinders: %d\n",
+ lvm_name, lv_ptr->lv_size / heads / sectors);
+#endif
+ break;
+
+
+ case LV_SET_ACCESS:
+ /* set access flags of a logical volume */
+ if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+ lv_ptr->lv_access = (ulong) arg;
+ break;
+
+
+ case LV_SET_STATUS:
+ /* set status flags of a logical volume */
+ if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+ if (!((ulong) arg & LV_ACTIVE) && lv_ptr->lv_open > 1)
+ return -EPERM;
+ lv_ptr->lv_status = (ulong) arg;
+ break;
+
+
+ case LV_SET_ALLOCATION:
+ /* set allocation flags of a logical volume */
+ if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+ lv_ptr->lv_allocation = (ulong) arg;
+ break;
+
+
+ default:
+ printk(KERN_WARNING
+ "%s -- lvm_blk_ioctl: unknown command %d\n",
+ lvm_name, command);
+ return -EINVAL;
+ }
+
+ return 0;
+} /* lvm_blk_ioctl() */
+
+
+/*
+ * block device close routine
+ */
+static int lvm_blk_close(struct inode *inode, struct file *file)
+{
+ int minor = MINOR(inode->i_rdev);
+ vg_t *vg_ptr = vg[VG_BLK(minor)];
+ lv_t *lv_ptr = vg_ptr->lv[LV_BLK(minor)];
+
+#ifdef DEBUG
+ printk(KERN_DEBUG
+ "%s -- lvm_blk_close MINOR: %d VG#: %d LV#: %d\n",
+ lvm_name, minor, VG_BLK(minor), LV_BLK(minor));
+#endif
+
+ sync_dev(inode->i_rdev);
+ if (lv_ptr->lv_open == 1) vg_ptr->lv_open--;
+ lv_ptr->lv_open--;
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+} /* lvm_blk_close() */
+
+
+#if defined CONFIG_LVM_PROC_FS && defined CONFIG_PROC_FS
+/*
+ * Support function /proc-Filesystem
+ */
+#define LVM_PROC_BUF ( i == 0 ? dummy_buf : &buf[sz])
+
+static int lvm_proc_get_info(char *page, char **start, off_t pos, int count)
+{
+ int c, i, l, p, v, vg_counter, pv_counter, lv_counter, lv_open_counter,
+ lv_open_total, pe_t_bytes, lv_block_exception_t_bytes, seconds;
+ static off_t sz;
+ off_t sz_last;
+ char allocation_flag, inactive_flag, rw_flag, stripes_flag;
+ char *lv_name, *pv_name;
+ static char *buf = NULL;
+ static char dummy_buf[160]; /* sized for 2 lines */
+ vg_t *vg_ptr;
+ lv_t *lv_ptr;
+ pv_t *pv_ptr;
+
+
+#ifdef DEBUG_LVM_PROC_GET_INFO
+ printk(KERN_DEBUG
+ "%s - lvm_proc_get_info CALLED pos: %lu count: %d whence: %d\n",
+ lvm_name, pos, count, whence);
+#endif
+
+ if (pos == 0 || buf == NULL) {
+ sz_last = vg_counter = pv_counter = lv_counter = lv_open_counter = \
+ lv_open_total = pe_t_bytes = lv_block_exception_t_bytes = 0;
+
+ /* search for activity */
+ for (v = 0; v < ABS_MAX_VG; v++) {
+ if ((vg_ptr = vg[v]) != NULL) {
+ vg_counter++;
+ pv_counter += vg_ptr->pv_cur;
+ lv_counter += vg_ptr->lv_cur;
+ if (vg_ptr->lv_cur > 0) {
+ for (l = 0; l < vg[v]->lv_max; l++) {
+ if ((lv_ptr = vg_ptr->lv[l]) != NULL) {
+ pe_t_bytes += lv_ptr->lv_allocated_le;
+ if (lv_ptr->lv_block_exception != NULL)
+ lv_block_exception_t_bytes += lv_ptr->lv_remap_end;
+ if (lv_ptr->lv_open > 0) {
+ lv_open_counter++;
+ lv_open_total += lv_ptr->lv_open;
+ }
+ }
+ }
+ }
+ }
+ }
+ pe_t_bytes *= sizeof(pe_t);
+ lv_block_exception_t_bytes *= sizeof(lv_block_exception_t);
+
+ if (buf != NULL) {
+#ifdef DEBUG_KFREE
+ printk(KERN_DEBUG
+ "%s -- kfree %d\n", lvm_name, __LINE__);
+#endif
+ kfree(buf);
+ buf = NULL;
+ }
+ /* 2 times: first to get size to allocate buffer,
+ 2nd to fill the malloced buffer */
+ for (i = 0; i < 2; i++) {
+ sz = 0;
+ sz += sprintf(LVM_PROC_BUF,
+ "LVM "
+#ifdef MODULE
+ "module"
+#else
+ "driver"
+#endif
+ " %s\n\n"
+ "Total: %d VG%s %d PV%s %d LV%s ",
+ lvm_short_version,
+ vg_counter, vg_counter == 1 ? "" : "s",
+ pv_counter, pv_counter == 1 ? "" : "s",
+ lv_counter, lv_counter == 1 ? "" : "s");
+ sz += sprintf(LVM_PROC_BUF,
+ "(%d LV%s open",
+ lv_open_counter,
+ lv_open_counter == 1 ? "" : "s");
+ if (lv_open_total > 0)
+ sz += sprintf(LVM_PROC_BUF,
+ " %d times)\n",
+ lv_open_total);
+ else
+ sz += sprintf(LVM_PROC_BUF, ")");
+ sz += sprintf(LVM_PROC_BUF,
+ "\nGlobal: %lu bytes malloced IOP version: %d ",
+ vg_counter * sizeof(vg_t) +
+ pv_counter * sizeof(pv_t) +
+ lv_counter * sizeof(lv_t) +
+ pe_t_bytes + lv_block_exception_t_bytes + sz_last,
+ lvm_iop_version);
+
+ seconds = CURRENT_TIME - loadtime;
+ if (seconds < 0)
+ loadtime = CURRENT_TIME + seconds;
+ if (seconds / 86400 > 0) {
+ sz += sprintf(LVM_PROC_BUF, "%d day%s ",
+ seconds / 86400,
+ seconds / 86400 == 0 ||
+ seconds / 86400 > 1 ? "s" : "");
+ }
+ sz += sprintf(LVM_PROC_BUF, "%d:%02d:%02d active\n",
+ (seconds % 86400) / 3600,
+ (seconds % 3600) / 60,
+ seconds % 60);
+
+ if (vg_counter > 0) {
+ for (v = 0; v < ABS_MAX_VG; v++) {
+ /* volume group */
+ if ((vg_ptr = vg[v]) != NULL) {
+ inactive_flag = ' ';
+ if (!(vg_ptr->vg_status & VG_ACTIVE)) inactive_flag = 'I';
+ sz += sprintf(LVM_PROC_BUF,
+ "\nVG: %c%s [%d PV, %d LV/%d open] "
+ " PE Size: %d KB\n"
+ " Usage [KB/PE]: %d /%d total "
+ "%d /%d used %d /%d free",
+ inactive_flag,
+ vg_ptr->vg_name,
+ vg_ptr->pv_cur,
+ vg_ptr->lv_cur,
+ vg_ptr->lv_open,
+ vg_ptr->pe_size >> 1,
+ vg_ptr->pe_size * vg_ptr->pe_total >> 1,
+ vg_ptr->pe_total,
+ vg_ptr->pe_allocated * vg_ptr->pe_size >> 1,
+ vg_ptr->pe_allocated,
+ (vg_ptr->pe_total - vg_ptr->pe_allocated) *
+ vg_ptr->pe_size >> 1,
+ vg_ptr->pe_total - vg_ptr->pe_allocated);
+
+ /* physical volumes */
+ sz += sprintf(LVM_PROC_BUF,
+ "\n PV%s ",
+ vg_ptr->pv_cur == 1 ? ": " : "s:");
+ c = 0;
+ for (p = 0; p < vg_ptr->pv_max; p++) {
+ if ((pv_ptr = vg_ptr->pv[p]) != NULL) {
+ inactive_flag = 'A';
+ if (!(pv_ptr->pv_status & PV_ACTIVE))
+ inactive_flag = 'I';
+ allocation_flag = 'A';
+ if (!(pv_ptr->pv_allocatable & PV_ALLOCATABLE))
+ allocation_flag = 'N';
+ pv_name = strchr(pv_ptr->pv_name+1,'/');
+ if ( pv_name == 0) pv_name = pv_ptr->pv_name;
+ else pv_name++;
+ sz += sprintf(LVM_PROC_BUF,
+ "[%c%c] %-21s %8d /%-6d "
+ "%8d /%-6d %8d /%-6d",
+ inactive_flag,
+ allocation_flag,
+ pv_name,
+ pv_ptr->pe_total *
+ pv_ptr->pe_size >> 1,
+ pv_ptr->pe_total,
+ pv_ptr->pe_allocated *
+ pv_ptr->pe_size >> 1,
+ pv_ptr->pe_allocated,
+ (pv_ptr->pe_total -
+ pv_ptr->pe_allocated) *
+ pv_ptr->pe_size >> 1,
+ pv_ptr->pe_total -
+ pv_ptr->pe_allocated);
+ c++;
+ if (c < vg_ptr->pv_cur)
+ sz += sprintf(LVM_PROC_BUF,
+ "\n ");
+ }
+ }
+
+ /* logical volumes */
+ sz += sprintf(LVM_PROC_BUF,
+ "\n LV%s ",
+ vg_ptr->lv_cur == 1 ? ": " : "s:");
+ c = 0;
+ for (l = 0; l < vg[v]->lv_max; l++) {
+ if ((lv_ptr = vg_ptr->lv[l]) != NULL) {
+ inactive_flag = 'A';
+ if (!(lv_ptr->lv_status & LV_ACTIVE))
+ inactive_flag = 'I';
+ rw_flag = 'R';
+ if (lv_ptr->lv_access & LV_WRITE)
+ rw_flag = 'W';
+ allocation_flag = 'D';
+ if (lv_ptr->lv_allocation & LV_CONTIGUOUS)
+ allocation_flag = 'C';
+ stripes_flag = 'L';
+ if (lv_ptr->lv_stripes > 1)
+ stripes_flag = 'S';
+ sz += sprintf(LVM_PROC_BUF,
+ "[%c%c%c%c",
+ inactive_flag,
+ rw_flag,
+ allocation_flag,
+ stripes_flag);
+ if (lv_ptr->lv_stripes > 1)
+ sz += sprintf(LVM_PROC_BUF, "%-2d",
+ lv_ptr->lv_stripes);
+ else
+ sz += sprintf(LVM_PROC_BUF, " ");
+ lv_name = strrchr(lv_ptr->lv_name, '/');
+ if ( lv_name == 0) lv_name = lv_ptr->lv_name;
+ else lv_name++;
+ sz += sprintf(LVM_PROC_BUF, "] %-25s", lv_name);
+ if (strlen(lv_name) > 25)
+ sz += sprintf(LVM_PROC_BUF,
+ "\n ");
+ sz += sprintf(LVM_PROC_BUF, "%9d /%-6d ",
+ lv_ptr->lv_size >> 1,
+ lv_ptr->lv_size / vg[v]->pe_size);
+
+ if (lv_ptr->lv_open == 0)
+ sz += sprintf(LVM_PROC_BUF, "close");
+ else
+ sz += sprintf(LVM_PROC_BUF, "%dx open",
+ lv_ptr->lv_open);
+ c++;
+ if (c < vg_ptr->lv_cur)
+ sz += sprintf(LVM_PROC_BUF,
+ "\n ");
+ }
+ }
+ if (vg_ptr->lv_cur == 0) sz += sprintf(LVM_PROC_BUF, "none");
+ sz += sprintf(LVM_PROC_BUF, "\n");
+ }
+ }
+ }
+ if (buf == NULL) {
+ if ((buf = vmalloc(sz)) == NULL) {
+ sz = 0;
+ return sprintf(page, "%s - vmalloc error at line %d\n",
+ lvm_name, __LINE__);
+ }
+ }
+ sz_last = sz;
+ }
+ }
+ if (pos > sz - 1) {
+ vfree(buf);
+ buf = NULL;
+ return 0;
+ }
+ *start = &buf[pos];
+ if (sz - pos < count)
+ return sz - pos;
+ else
+ return count;
+} /* lvm_proc_get_info() */
+#endif /* #if defined CONFIG_LVM_PROC_FS && defined CONFIG_PROC_FS */
+
+
+/*
+ * block device support function for /usr/src/linux/drivers/block/ll_rw_blk.c
+ * (see init_module/lvm_init)
+ */
+static int lvm_map(struct buffer_head *bh, int rw)
+{
+ int minor = MINOR(bh->b_rdev);
+ ulong index;
+ ulong pe_start;
+ ulong size = bh->b_size >> 9;
+ ulong rsector_tmp = bh->b_rsector;
+ ulong rsector_sav;
+ kdev_t rdev_tmp = bh->b_rdev;
+ kdev_t rdev_sav;
+ lv_t *lv = vg[VG_BLK(minor)]->lv[LV_BLK(minor)];
+
+
+ if (!(lv->lv_status & LV_ACTIVE)) {
+ printk(KERN_ALERT
+ "%s - lvm_map: ll_rw_blk for inactive LV %s\n",
+ lvm_name, lv->lv_name);
+ goto error;
+ }
+/*
+ if ( lv->lv_access & LV_SNAPSHOT)
+ printk ( "%s -- %02d:%02d block: %lu rw: %d\n", lvm_name, MAJOR ( bh->b_dev), MINOR ( bh->b_dev), bh->b_blocknr, rw);
+ */
+
+ /* take care of snapshot chunk writes before
+ check for writable logical volume */
+ if ((lv->lv_access & LV_SNAPSHOT) &&
+ MAJOR(bh->b_rdev) != 0 &&
+ MAJOR(bh->b_rdev) != MAJOR_NR &&
+ (rw == WRITEA || rw == WRITE))
+ {
+ printk ( "%s -- doing snapshot write for %02d:%02d[%02d:%02d] b_blocknr: %lu b_rsector: %lu\n", lvm_name, MAJOR ( bh->b_dev), MINOR ( bh->b_dev), MAJOR ( bh->b_rdev), MINOR ( bh->b_rdev), bh->b_blocknr, bh->b_rsector);
+ goto error;
+ }
+
+ if ((rw == WRITE || rw == WRITEA) &&
+ !(lv->lv_access & LV_WRITE)) {
+ printk(KERN_CRIT
+ "%s - lvm_map: ll_rw_blk write for readonly LV %s\n",
+ lvm_name, lv->lv_name);
+ goto error;
+ }
+#ifdef DEBUG_MAP
+ printk(KERN_DEBUG
+ "%s - lvm_map minor:%d *rdev: %02d:%02d *rsector: %lu "
+ "size:%lu\n",
+ lvm_name, minor,
+ MAJOR(rdev_tmp),
+ MINOR(rdev_tmp),
+ rsector_tmp, size);
+#endif
+
+ if (rsector_tmp + size > lv->lv_size) {
+ printk(KERN_ALERT
+ "%s - lvm_map *rsector: %lu or size: %lu wrong for"
+ " minor: %2d\n", lvm_name, rsector_tmp, size, minor);
+ goto error;
+ }
+ rsector_sav = rsector_tmp;
+ rdev_sav = rdev_tmp;
+
+lvm_second_remap:
+ /* linear mapping */
+ if (lv->lv_stripes < 2) {
+ /* get the index */
+ index = rsector_tmp / vg[VG_BLK(minor)]->pe_size;
+ pe_start = lv->lv_current_pe[index].pe;
+ rsector_tmp = lv->lv_current_pe[index].pe +
+ (rsector_tmp % vg[VG_BLK(minor)]->pe_size);
+ rdev_tmp = lv->lv_current_pe[index].dev;
+
+#ifdef DEBUG_MAP
+ printk(KERN_DEBUG
+ "lv_current_pe[%ld].pe: %ld rdev: %02d:%02d rsector:%ld\n",
+ index,
+ lv->lv_current_pe[index].pe,
+ MAJOR(rdev_tmp),
+ MINOR(rdev_tmp),
+ rsector_tmp);
+#endif
+
+ /* striped mapping */
+ } else {
+ ulong stripe_index;
+ ulong stripe_length;
+
+ stripe_length = vg[VG_BLK(minor)]->pe_size * lv->lv_stripes;
+ stripe_index = (rsector_tmp % stripe_length) / lv->lv_stripesize;
+ index = rsector_tmp / stripe_length +
+ (stripe_index % lv->lv_stripes) *
+ (lv->lv_allocated_le / lv->lv_stripes);
+ pe_start = lv->lv_current_pe[index].pe;
+ rsector_tmp = lv->lv_current_pe[index].pe +
+ (rsector_tmp % stripe_length) -
+ (stripe_index % lv->lv_stripes) * lv->lv_stripesize -
+ stripe_index / lv->lv_stripes *
+ (lv->lv_stripes - 1) * lv->lv_stripesize;
+ rdev_tmp = lv->lv_current_pe[index].dev;
+ }
+
+#ifdef DEBUG_MAP
+ printk(KERN_DEBUG
+ "lv_current_pe[%ld].pe: %ld rdev: %02d:%02d rsector:%ld\n"
+ "stripe_length: %ld stripe_index: %ld\n",
+ index,
+ lv->lv_current_pe[index].pe,
+ MAJOR(rdev_tmp),
+ MINOR(rdev_tmp),
+ rsector_tmp,
+ stripe_length,
+ stripe_index);
+#endif
+
+ /* handle physical extents on the move */
+ if (pe_lock_req.lock == LOCK_PE) {
+ if (rdev_tmp == pe_lock_req.data.pv_dev &&
+ rsector_tmp >= pe_lock_req.data.pv_offset &&
+ rsector_tmp < (pe_lock_req.data.pv_offset +
+ vg[VG_BLK(minor)]->pe_size)) {
+ sleep_on(&lvm_map_wait);
+ rsector_tmp = rsector_sav;
+ rdev_tmp = rdev_sav;
+ goto lvm_second_remap;
+ }
+ }
+ /* statistic */
+ if (rw == WRITE || rw == WRITEA)
+ lv->lv_current_pe[index].writes++;
+ else
+ lv->lv_current_pe[index].reads++;
+
+ /* snapshot volume exception handling on physical device address base */
+ if (lv->lv_access & (LV_SNAPSHOT | LV_SNAPSHOT_ORG)) {
+ /* original logical volume */
+ if (lv->lv_access & LV_SNAPSHOT_ORG) {
+ if (rw == WRITE || rw == WRITEA)
+ {
+ lv_t *lv_ptr;
+
+ /* start with first snapshot and loop thrugh all of them */
+ for (lv_ptr = lv->lv_snapshot_next;
+ lv_ptr != NULL;
+ lv_ptr = lv_ptr->lv_snapshot_next) {
+ down(&lv->lv_snapshot_org->lv_snapshot_sem);
+ /* do we still have exception storage for this snapshot free? */
+ if (lv_ptr->lv_block_exception != NULL) {
+ rdev_sav = rdev_tmp;
+ rsector_sav = rsector_tmp;
+ if (!lvm_snapshot_remap_block(&rdev_tmp,
+ &rsector_tmp,
+ pe_start,
+ lv_ptr)) {
+ /* create a new mapping */
+ lvm_snapshot_COW(rdev_tmp,
+ rsector_tmp,
+ pe_start,
+ rsector_sav,
+ lv_ptr);
+ }
+ rdev_tmp = rdev_sav;
+ rsector_tmp = rsector_sav;
+ }
+ up(&lv->lv_snapshot_org->lv_snapshot_sem);
+ }
+ }
+ } else {
+ /* remap snapshot logical volume */
+ down(&lv->lv_snapshot_sem);
+ if (lv->lv_block_exception != NULL)
+ lvm_snapshot_remap_block(&rdev_tmp, &rsector_tmp, pe_start, lv);
+ up(&lv->lv_snapshot_sem);
+ }
+ }
+ bh->b_rdev = rdev_tmp;
+ bh->b_rsector = rsector_tmp;
+
+ return 1;
+
+ error:
+ buffer_IO_error(bh);
+ return -1;
+} /* lvm_map() */
+
+
+/*
+ * internal support functions
+ */
+
+#ifdef LVM_HD_NAME
+/*
+ * generate "hard disk" name
+ */
+void lvm_hd_name(char *buf, int minor)
+{
+ int len = 0;
+ lv_t *lv_ptr;
+
+ if (vg[VG_BLK(minor)] == NULL ||
+ (lv_ptr = vg[VG_BLK(minor)]->lv[LV_BLK(minor)]) == NULL)
+ return;
+ len = strlen(lv_ptr->lv_name) - 5;
+ memcpy(buf, &lv_ptr->lv_name[5], len);
+ buf[len] = 0;
+ return;
+}
+#endif
+
+
+/*
+ * this one never should be called...
+ */
+static void lvm_dummy_device_request(request_queue_t * t)
+{
+ printk(KERN_EMERG
+ "%s -- oops, got lvm request for %02d:%02d [sector: %lu]\n",
+ lvm_name,
+ MAJOR(CURRENT->rq_dev),
+ MINOR(CURRENT->rq_dev),
+ CURRENT->sector);
+ return;
+}
+
+
+/*
+ * make request function
+ */
+static int lvm_make_request_fn(request_queue_t *q, int rw, struct buffer_head *bh)
+{
+ lvm_map(bh, rw);
+ return 1;
+}
+
+/*
+ * plug device function is a noop because plugging has to happen
+ * in the queue of the physical blockdevice to allow the
+ * elevator to do a better job.
+ */
+static void lvm_plug_device_noop(request_queue_t *q, kdev_t dev) { }
+
+/********************************************************************
+ *
+ * Character device support functions
+ *
+ ********************************************************************/
+/*
+ * character device support function logical volume manager lock
+ */
+static int lvm_do_lock_lvm(void)
+{
+lock_try_again:
+ spin_lock(&lvm_lock);
+ if (lock != 0 && lock != current->pid) {
+#ifdef DEBUG_IOCTL
+ printk(KERN_INFO "lvm_do_lock_lvm: %s is locked by pid %d ...\n",
+ lvm_name, lock);
+#endif
+ spin_unlock(&lvm_lock);
+ interruptible_sleep_on(&lvm_wait);
+ if (current->sigpending != 0)
+ return -EINTR;
+#ifdef LVM_TOTAL_RESET
+ if (lvm_reset_spindown > 0)
+ return -EACCES;
+#endif
+ goto lock_try_again;
+ }
+ lock = current->pid;
+ spin_unlock(&lvm_lock);
+ return 0;
+} /* lvm_do_lock_lvm */
+
+
+/*
+ * character device support function lock/unlock physical extend
+ */
+static int lvm_do_pe_lock_unlock(vg_t *vg_ptr, void *arg)
+{
+ uint p;
+
+ if (vg_ptr == NULL) return -ENXIO;
+ if (copy_from_user(&pe_lock_req, arg,
+ sizeof(pe_lock_req_t)) != 0) return -EFAULT;
+
+ switch (pe_lock_req.lock) {
+ case LOCK_PE:
+ for (p = 0; p < vg_ptr->pv_max; p++) {
+ if (vg_ptr->pv[p] != NULL &&
+ pe_lock_req.data.pv_dev ==
+ vg_ptr->pv[p]->pv_dev)
+ break;
+ }
+ if (p == vg_ptr->pv_max) return -ENXIO;
+
+ pe_lock_req.lock = UNLOCK_PE;
+ fsync_dev(pe_lock_req.data.lv_dev);
+ pe_lock_req.lock = LOCK_PE;
+ break;
+
+ case UNLOCK_PE:
+ pe_lock_req.lock = UNLOCK_PE;
+ pe_lock_req.data.lv_dev = pe_lock_req.data.pv_dev = 0;
+ pe_lock_req.data.pv_offset = 0;
+ wake_up(&lvm_map_wait);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+/*
+ * character device support function logical extend remap
+ */
+static int lvm_do_le_remap(vg_t *vg_ptr, void *arg)
+{
+ uint l, le;
+ lv_t *lv_ptr;
+
+ if (vg_ptr == NULL) return -ENXIO;
+ if (copy_from_user(&le_remap_req, arg,
+ sizeof(le_remap_req_t)) != 0)
+ return -EFAULT;
+
+ for (l = 0; l < vg_ptr->lv_max; l++) {
+ lv_ptr = vg_ptr->lv[l];
+ if (lv_ptr != NULL &&
+ strcmp(lv_ptr->lv_name,
+ le_remap_req.lv_name) == 0) {
+ for (le = 0; le < lv_ptr->lv_allocated_le;
+ le++) {
+ if (lv_ptr->lv_current_pe[le].dev ==
+ le_remap_req.old_dev &&
+ lv_ptr->lv_current_pe[le].pe ==
+ le_remap_req.old_pe) {
+ lv_ptr->lv_current_pe[le].dev =
+ le_remap_req.new_dev;
+ lv_ptr->lv_current_pe[le].pe =
+ le_remap_req.new_pe;
+ return 0;
+ }
+ }
+ return -EINVAL;
+ }
+ }
+ return -ENXIO;
+} /* lvm_do_le_remap() */
+
+
+/*
+ * character device support function VGDA create
+ */
+int lvm_do_vg_create(int minor, void *arg)
+{
+ int snaporg_minor = 0;
+ ulong l, p;
+ lv_t lv;
+ vg_t *vg_ptr;
+ pv_t *pv_ptr;
+ lv_t *lv_ptr;
+
+ if (vg[VG_CHR(minor)] != NULL) return -EPERM;
+
+ if ((vg_ptr = kmalloc(sizeof(vg_t),GFP_KERNEL)) == NULL) {
+ printk(KERN_CRIT
+ "%s -- VG_CREATE: kmalloc error VG at line %d\n",
+ lvm_name, __LINE__);
+ return -ENOMEM;
+ }
+ /* get the volume group structure */
+ if (copy_from_user(vg_ptr, arg, sizeof(vg_t)) != 0) {
+ kfree(vg_ptr);
+ return -EFAULT;
+ }
+
+ vg_devfs_handle[vg_ptr->vg_number] = devfs_mk_dir(0, vg_ptr->vg_name, NULL);
+ ch_devfs_handle[vg_ptr->vg_number] = devfs_register(
+ vg_devfs_handle[vg_ptr->vg_number] , "group",
+ DEVFS_FL_DEFAULT, LVM_CHAR_MAJOR, vg_ptr->vg_number,
+ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,
+ &lvm_chr_fops, NULL);
+
+ /* we are not that active so far... */
+ vg_ptr->vg_status &= ~VG_ACTIVE;
+ vg[VG_CHR(minor)] = vg_ptr;
+
+ vg[VG_CHR(minor)]->pe_allocated = 0;
+ if (vg_ptr->pv_max > ABS_MAX_PV) {
+ printk(KERN_WARNING
+ "%s -- Can't activate VG: ABS_MAX_PV too small\n",
+ lvm_name);
+ kfree(vg_ptr);
+ vg[VG_CHR(minor)] = NULL;
+ return -EPERM;
+ }
+ if (vg_ptr->lv_max > ABS_MAX_LV) {
+ printk(KERN_WARNING
+ "%s -- Can't activate VG: ABS_MAX_LV too small for %u\n",
+ lvm_name, vg_ptr->lv_max);
+ kfree(vg_ptr);
+ vg_ptr = NULL;
+ return -EPERM;
+ }
+ /* get the physical volume structures */
+ vg_ptr->pv_act = vg_ptr->pv_cur = 0;
+ for (p = 0; p < vg_ptr->pv_max; p++) {
+ /* user space address */
+ if ((pvp = vg_ptr->pv[p]) != NULL) {
+ pv_ptr = vg_ptr->pv[p] = kmalloc(sizeof(pv_t),GFP_KERNEL);
+ if (pv_ptr == NULL) {
+ printk(KERN_CRIT
+ "%s -- VG_CREATE: kmalloc error PV at line %d\n",
+ lvm_name, __LINE__);
+ lvm_do_vg_remove(minor);
+ return -ENOMEM;
+ }
+ if (copy_from_user(pv_ptr, pvp, sizeof(pv_t)) != 0) {
+ lvm_do_vg_remove(minor);
+ return -EFAULT;
+ }
+ /* We don't need the PE list
+ in kernel space as with LVs pe_t list (see below) */
+ pv_ptr->pe = NULL;
+ pv_ptr->pe_allocated = 0;
+ pv_ptr->pv_status = PV_ACTIVE;
+ vg_ptr->pv_act++;
+ vg_ptr->pv_cur++;
+
+#ifdef LVM_GET_INODE
+ /* insert a dummy inode for fs_may_mount */
+ pv_ptr->inode = lvm_get_inode(pv_ptr->pv_dev);
+#endif
+ }
+ }
+
+ /* get the logical volume structures */
+ vg_ptr->lv_cur = 0;
+ for (l = 0; l < vg_ptr->lv_max; l++) {
+ /* user space address */
+ if ((lvp = vg_ptr->lv[l]) != NULL) {
+ if (copy_from_user(&lv, lvp, sizeof(lv_t)) != 0) {
+ lvm_do_vg_remove(minor);
+ return -EFAULT;
+ }
+ vg_ptr->lv[l] = NULL;
+ if (lvm_do_lv_create(minor, lv.lv_name, &lv) != 0) {
+ lvm_do_vg_remove(minor);
+ return -EFAULT;
+ }
+ }
+ }
+
+ /* Second path to correct snapshot logical volumes which are not
+ in place during first path above */
+ for (l = 0; l < vg_ptr->lv_max; l++) {
+ if ((lv_ptr = vg_ptr->lv[l]) != NULL &&
+ vg_ptr->lv[l]->lv_access & LV_SNAPSHOT) {
+ snaporg_minor = lv_ptr->lv_snapshot_minor;
+ if (vg_ptr->lv[LV_BLK(snaporg_minor)] != NULL) {
+ /* get pointer to original logical volume */
+ lv_ptr = vg_ptr->lv[l]->lv_snapshot_org =
+ vg_ptr->lv[LV_BLK(snaporg_minor)];
+
+ /* set necessary fields of original logical volume */
+ lv_ptr->lv_access |= LV_SNAPSHOT_ORG;
+ lv_ptr->lv_snapshot_minor = 0;
+ lv_ptr->lv_snapshot_org = lv_ptr;
+ lv_ptr->lv_snapshot_prev = NULL;
+
+ /* find last snapshot logical volume in the chain */
+ while (lv_ptr->lv_snapshot_next != NULL)
+ lv_ptr = lv_ptr->lv_snapshot_next;
+
+ /* set back pointer to this last one in our new logical volume */
+ vg_ptr->lv[l]->lv_snapshot_prev = lv_ptr;
+
+ /* last logical volume now points to our new snapshot volume */
+ lv_ptr->lv_snapshot_next = vg_ptr->lv[l];
+
+ /* now point to the new one */
+ lv_ptr = lv_ptr->lv_snapshot_next;
+
+ /* set necessary fields of new snapshot logical volume */
+ lv_ptr->lv_snapshot_next = NULL;
+ lv_ptr->lv_current_pe =
+ vg_ptr->lv[LV_BLK(snaporg_minor)]->lv_current_pe;
+ lv_ptr->lv_allocated_le =
+ vg_ptr->lv[LV_BLK(snaporg_minor)]->lv_allocated_le;
+ lv_ptr->lv_current_le =
+ vg_ptr->lv[LV_BLK(snaporg_minor)]->lv_current_le;
+ lv_ptr->lv_size =
+ vg_ptr->lv[LV_BLK(snaporg_minor)]->lv_size;
+ }
+ }
+ }
+
+ vg_count++;
+
+ /* let's go active */
+ vg_ptr->vg_status |= VG_ACTIVE;
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+} /* lvm_do_vg_create() */
+
+
+/*
+ * character device support function VGDA extend
+ */
+static int lvm_do_vg_extend(vg_t *vg_ptr, void *arg)
+{
+ uint p;
+ pv_t *pv_ptr;
+
+ if (vg_ptr == NULL) return -ENXIO;
+ if (vg_ptr->pv_cur < vg_ptr->pv_max) {
+ for (p = 0; p < vg_ptr->pv_max; p++) {
+ if (vg_ptr->pv[p] == NULL) {
+ if ((pv_ptr = vg_ptr->pv[p] = kmalloc(sizeof(pv_t),GFP_KERNEL)) == NULL) {
+ printk(KERN_CRIT
+ "%s -- VG_EXTEND: kmalloc error PV at line %d\n",
+ lvm_name, __LINE__);
+ return -ENOMEM;
+ }
+ if (copy_from_user(pv_ptr, arg, sizeof(pv_t)) != 0) {
+ kfree(pv_ptr);
+ vg_ptr->pv[p] = NULL;
+ return -EFAULT;
+ }
+
+ pv_ptr->pv_status = PV_ACTIVE;
+ /* We don't need the PE list
+ in kernel space like LVs pe_t list */
+ pv_ptr->pe = NULL;
+ vg_ptr->pv_cur++;
+ vg_ptr->pv_act++;
+ vg_ptr->pe_total +=
+ pv_ptr->pe_total;
+#ifdef LVM_GET_INODE
+ /* insert a dummy inode for fs_may_mount */
+ pv_ptr->inode = lvm_get_inode(pv_ptr->pv_dev);
+#endif
+ return 0;
+ }
+ }
+ }
+return -EPERM;
+} /* lvm_do_vg_extend() */
+
+
+/*
+ * character device support function VGDA reduce
+ */
+static int lvm_do_vg_reduce(vg_t *vg_ptr, void *arg)
+{
+ uint p;
+ pv_t *pv_ptr;
+
+ if (vg_ptr == NULL) return -ENXIO;
+ if (copy_from_user(pv_name, arg, sizeof(pv_name)) != 0)
+ return -EFAULT;
+
+ for (p = 0; p < vg_ptr->pv_max; p++) {
+ pv_ptr = vg_ptr->pv[p];
+ if (pv_ptr != NULL &&
+ strcmp(pv_ptr->pv_name,
+ pv_name) == 0) {
+ if (pv_ptr->lv_cur > 0) return -EPERM;
+ vg_ptr->pe_total -=
+ pv_ptr->pe_total;
+ vg_ptr->pv_cur--;
+ vg_ptr->pv_act--;
+#ifdef LVM_GET_INODE
+ lvm_clear_inode(pv_ptr->inode);
+#endif
+ kfree(pv_ptr);
+ /* Make PV pointer array contiguous */
+ for (; p < vg_ptr->pv_max - 1; p++)
+ vg_ptr->pv[p] = vg_ptr->pv[p + 1];
+ vg_ptr->pv[p + 1] = NULL;
+ return 0;
+ }
+ }
+ return -ENXIO;
+} /* lvm_do_vg_reduce */
+
+
+/*
+ * character device support function VGDA remove
+ */
+static int lvm_do_vg_remove(int minor)
+{
+ int i;
+ vg_t *vg_ptr = vg[VG_CHR(minor)];
+ pv_t *pv_ptr;
+
+ if (vg_ptr == NULL) return -ENXIO;
+
+#ifdef LVM_TOTAL_RESET
+ if (vg_ptr->lv_open > 0 && lvm_reset_spindown == 0)
+#else
+ if (vg_ptr->lv_open > 0)
+#endif
+ return -EPERM;
+
+ /* let's go inactive */
+ vg_ptr->vg_status &= ~VG_ACTIVE;
+
+ devfs_unregister (ch_devfs_handle[vg_ptr->vg_number]);
+ devfs_unregister (vg_devfs_handle[vg_ptr->vg_number]);
+
+ /* free LVs */
+ /* first free snapshot logical volumes */
+ for (i = 0; i < vg_ptr->lv_max; i++) {
+ if (vg_ptr->lv[i] != NULL &&
+ vg_ptr->lv[i]->lv_access & LV_SNAPSHOT) {
+ lvm_do_lv_remove(minor, NULL, i);
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout(1);
+ }
+ }
+ /* then free the rest of the LVs */
+ for (i = 0; i < vg_ptr->lv_max; i++) {
+ if (vg_ptr->lv[i] != NULL) {
+ lvm_do_lv_remove(minor, NULL, i);
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout(1);
+ }
+ }
+
+ /* free PVs */
+ for (i = 0; i < vg_ptr->pv_max; i++) {
+ if ((pv_ptr = vg_ptr->pv[i]) != NULL) {
+#ifdef DEBUG_KFREE
+ printk(KERN_DEBUG
+ "%s -- kfree %d\n", lvm_name, __LINE__);
+#endif
+#ifdef LVM_GET_INODE
+ lvm_clear_inode(pv_ptr->inode);
+#endif
+ kfree(pv_ptr);
+ vg[VG_CHR(minor)]->pv[i] = NULL;
+ }
+ }
+
+#ifdef DEBUG_KFREE
+ printk(KERN_DEBUG "%s -- kfree %d\n", lvm_name, __LINE__);
+#endif
+ kfree(vg_ptr);
+ vg[VG_CHR(minor)] = NULL;
+
+ vg_count--;
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+} /* lvm_do_vg_remove() */
+
+
+/*
+ * character device support function logical volume create
+ */
+static int lvm_do_lv_create(int minor, char *lv_name, lv_t *lv)
+{
+ int l, le, l_new, p, size;
+ ulong lv_status_save;
+ char *lv_tmp, *lv_buf;
+ lv_block_exception_t *lvbe = lv->lv_block_exception;
+ vg_t *vg_ptr = vg[VG_CHR(minor)];
+ lv_t *lv_ptr = NULL;
+
+ if ((pep = lv->lv_current_pe) == NULL) return -EINVAL;
+ if (lv->lv_chunk_size > LVM_SNAPSHOT_MAX_CHUNK)
+ return -EINVAL;
+
+ for (l = 0; l < vg_ptr->lv_max; l++) {
+ if (vg_ptr->lv[l] != NULL &&
+ strcmp(vg_ptr->lv[l]->lv_name, lv_name) == 0)
+ return -EEXIST;
+ }
+
+ /* in case of lv_remove(), lv_create() pair; for eg. lvrename does this */
+ l_new = -1;
+ if (vg_ptr->lv[lv->lv_number] == NULL)
+ l_new = lv->lv_number;
+ else {
+ for (l = 0; l < vg_ptr->lv_max; l++) {
+ if (vg_ptr->lv[l] == NULL)
+ if (l_new == -1) l_new = l;
+ }
+ }
+ if (l_new == -1) return -EPERM;
+ else l = l_new;
+
+ if ((lv_ptr = kmalloc(sizeof(lv_t),GFP_KERNEL)) == NULL) {;
+ printk(KERN_CRIT "%s -- LV_CREATE: kmalloc error LV at line %d\n",
+ lvm_name, __LINE__);
+ return -ENOMEM;
+ }
+ /* copy preloaded LV */
+ memcpy((char *) lv_ptr, (char *) lv, sizeof(lv_t));
+
+ lv_status_save = lv_ptr->lv_status;
+ lv_ptr->lv_status &= ~LV_ACTIVE;
+ lv_ptr->lv_snapshot_org = \
+ lv_ptr->lv_snapshot_prev = \
+ lv_ptr->lv_snapshot_next = NULL;
+ lv_ptr->lv_block_exception = NULL;
+ init_MUTEX(&lv_ptr->lv_snapshot_sem);
+ vg_ptr->lv[l] = lv_ptr;
+
+ /* get the PE structures from user space if this
+ is no snapshot logical volume */
+ if (!(lv_ptr->lv_access & LV_SNAPSHOT)) {
+ size = lv_ptr->lv_allocated_le * sizeof(pe_t);
+ if ((lv_ptr->lv_current_pe = vmalloc(size)) == NULL) {
+ printk(KERN_CRIT
+ "%s -- LV_CREATE: vmalloc error LV_CURRENT_PE of %d Byte "
+ "at line %d\n",
+ lvm_name, size, __LINE__);
+#ifdef DEBUG_KFREE
+ printk(KERN_DEBUG "%s -- kfree %d\n", lvm_name, __LINE__);
+#endif
+ kfree(lv_ptr);
+ vg[VG_CHR(minor)]->lv[l] = NULL;
+ return -ENOMEM;
+ }
+ if (copy_from_user(lv_ptr->lv_current_pe, pep, size)) {
+ vfree(lv_ptr->lv_current_pe);
+ kfree(lv_ptr);
+ vg_ptr->lv[l] = NULL;
+ return -EFAULT;
+ }
+ /* correct the PE count in PVs */
+ for (le = 0; le < lv_ptr->lv_allocated_le; le++) {
+ vg_ptr->pe_allocated++;
+ for (p = 0; p < vg_ptr->pv_cur; p++) {
+ if (vg_ptr->pv[p]->pv_dev ==
+ lv_ptr->lv_current_pe[le].dev)
+ vg_ptr->pv[p]->pe_allocated++;
+ }
+ }
+ } else {
+ /* Get snapshot exception data and block list */
+ if (lvbe != NULL) {
+ lv_ptr->lv_snapshot_org =
+ vg_ptr->lv[LV_BLK(lv_ptr->lv_snapshot_minor)];
+ if (lv_ptr->lv_snapshot_org != NULL) {
+ size = lv_ptr->lv_remap_end * sizeof(lv_block_exception_t);
+ if ((lv_ptr->lv_block_exception = vmalloc(size)) == NULL) {
+ printk(KERN_CRIT
+ "%s -- lvm_do_lv_create: vmalloc error LV_BLOCK_EXCEPTION "
+ "of %d byte at line %d\n",
+ lvm_name, size, __LINE__);
+#ifdef DEBUG_KFREE
+ printk(KERN_DEBUG "%s -- kfree %d\n", lvm_name, __LINE__);
+#endif
+ kfree(lv_ptr);
+ vg_ptr->lv[l] = NULL;
+ return -ENOMEM;
+ }
+ if (copy_from_user(lv_ptr->lv_block_exception, lvbe, size)) {
+ vfree(lv_ptr->lv_block_exception);
+ kfree(lv_ptr);
+ vg[VG_CHR(minor)]->lv[l] = NULL;
+ return -EFAULT;
+ }
+ /* get pointer to original logical volume */
+ lv_ptr = lv_ptr->lv_snapshot_org;
+
+ lv_ptr->lv_snapshot_minor = 0;
+ lv_ptr->lv_snapshot_org = lv_ptr;
+ lv_ptr->lv_snapshot_prev = NULL;
+ /* walk thrugh the snapshot list */
+ while (lv_ptr->lv_snapshot_next != NULL)
+ lv_ptr = lv_ptr->lv_snapshot_next;
+ /* now lv_ptr points to the last existing snapshot in the chain */
+ vg_ptr->lv[l]->lv_snapshot_prev = lv_ptr;
+ /* our new one now back points to the previous last in the chain */
+ lv_ptr = vg_ptr->lv[l];
+ /* now lv_ptr points to our new last snapshot logical volume */
+ lv_ptr->lv_snapshot_org = lv_ptr->lv_snapshot_prev->lv_snapshot_org;
+ lv_ptr->lv_snapshot_next = NULL;
+ lv_ptr->lv_current_pe = lv_ptr->lv_snapshot_org->lv_current_pe;
+ lv_ptr->lv_allocated_le = lv_ptr->lv_snapshot_org->lv_allocated_le;
+ lv_ptr->lv_current_le = lv_ptr->lv_snapshot_org->lv_current_le;
+ lv_ptr->lv_size = lv_ptr->lv_snapshot_org->lv_size;
+ lv_ptr->lv_stripes = lv_ptr->lv_snapshot_org->lv_stripes;
+ lv_ptr->lv_stripesize = lv_ptr->lv_snapshot_org->lv_stripesize;
+ {
+ int err = lvm_snapshot_alloc(lv_ptr);
+ if (err)
+ {
+ vfree(lv_ptr->lv_block_exception);
+ kfree(lv_ptr);
+ vg[VG_CHR(minor)]->lv[l] = NULL;
+ return err;
+ }
+ }
+ } else {
+ vfree(lv_ptr->lv_block_exception);
+ kfree(lv_ptr);
+ vg_ptr->lv[l] = NULL;
+ return -EFAULT;
+ }
+ } else {
+ kfree(vg_ptr->lv[l]);
+ vg_ptr->lv[l] = NULL;
+ return -EINVAL;
+ }
+ } /* if ( vg[VG_CHR(minor)]->lv[l]->lv_access & LV_SNAPSHOT) */
+
+ lv_ptr = vg_ptr->lv[l];
+ lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].start_sect = 0;
+ lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].nr_sects = lv_ptr->lv_size;
+ lvm_size[MINOR(lv_ptr->lv_dev)] = lv_ptr->lv_size >> 1;
+ vg_lv_map[MINOR(lv_ptr->lv_dev)].vg_number = vg_ptr->vg_number;
+ vg_lv_map[MINOR(lv_ptr->lv_dev)].lv_number = lv_ptr->lv_number;
+ read_ahead[MAJOR_NR] = lv_ptr->lv_read_ahead = LVM_CORRECT_READ_AHEAD(lv_ptr->lv_read_ahead);
+ vg_ptr->lv_cur++;
+ lv_ptr->lv_status = lv_status_save;
+
+ strtok(lv->lv_name, "/"); /* /dev */
+
+ while((lv_tmp = strtok(NULL, "/")) != NULL)
+ lv_buf = lv_tmp;
+
+ lv_devfs_handle[lv->lv_number] = devfs_register(
+ vg_devfs_handle[vg_ptr->vg_number], lv_buf,
+ DEVFS_FL_DEFAULT, LVM_BLK_MAJOR, lv->lv_number,
+ S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP,
+ &lvm_blk_dops, NULL);
+
+ /* optionally add our new snapshot LV */
+ if (lv_ptr->lv_access & LV_SNAPSHOT) {
+ /* sync the original logical volume */
+ fsync_dev(lv_ptr->lv_snapshot_org->lv_dev);
+ /* put ourselve into the chain */
+ lv_ptr->lv_snapshot_prev->lv_snapshot_next = lv_ptr;
+ lv_ptr->lv_snapshot_org->lv_access |= LV_SNAPSHOT_ORG;
+ }
+ return 0;
+} /* lvm_do_lv_create() */
+
+
+/*
+ * character device support function logical volume remove
+ */
+static int lvm_do_lv_remove(int minor, char *lv_name, int l)
+{
+ uint le, p;
+ vg_t *vg_ptr = vg[VG_CHR(minor)];
+ lv_t *lv_ptr;
+
+ if (l == -1) {
+ for (l = 0; l < vg_ptr->lv_max; l++) {
+ if (vg_ptr->lv[l] != NULL &&
+ strcmp(vg_ptr->lv[l]->lv_name, lv_name) == 0) {
+ break;
+ }
+ }
+ }
+ if (l == vg_ptr->lv_max) return -ENXIO;
+
+ lv_ptr = vg_ptr->lv[l];
+#ifdef LVM_TOTAL_RESET
+ if (lv_ptr->lv_open > 0 && lvm_reset_spindown == 0)
+#else
+ if (lv_ptr->lv_open > 0)
+#endif
+ return -EBUSY;
+
+ /* check for deletion of snapshot source while
+ snapshot volume still exists */
+ if ((lv_ptr->lv_access & LV_SNAPSHOT_ORG) &&
+ lv_ptr->lv_snapshot_next != NULL)
+ return -EPERM;
+
+ lv_ptr->lv_status |= LV_SPINDOWN;
+
+ /* sync the buffers */
+ fsync_dev(lv_ptr->lv_dev);
+
+ lv_ptr->lv_status &= ~LV_ACTIVE;
+
+ /* invalidate the buffers */
+ invalidate_buffers(lv_ptr->lv_dev);
+
+ /* reset generic hd */
+ lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].start_sect = -1;
+ lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].nr_sects = 0;
+ lvm_size[MINOR(lv_ptr->lv_dev)] = 0;
+
+ /* reset VG/LV mapping */
+ vg_lv_map[MINOR(lv_ptr->lv_dev)].vg_number = ABS_MAX_VG;
+ vg_lv_map[MINOR(lv_ptr->lv_dev)].lv_number = -1;
+
+ /* correct the PE count in PVs if this is no snapshot logical volume */
+ if (!(lv_ptr->lv_access & LV_SNAPSHOT)) {
+ /* only if this is no snapshot logical volume because
+ we share the lv_current_pe[] structs with the
+ original logical volume */
+ for (le = 0; le < lv_ptr->lv_allocated_le; le++) {
+ vg_ptr->pe_allocated--;
+ for (p = 0; p < vg_ptr->pv_cur; p++) {
+ if (vg_ptr->pv[p]->pv_dev ==
+ lv_ptr->lv_current_pe[le].dev)
+ vg_ptr->pv[p]->pe_allocated--;
+ }
+ }
+ vfree(lv_ptr->lv_current_pe);
+ /* LV_SNAPSHOT */
+ } else {
+ /* remove this snapshot logical volume from the chain */
+ lv_ptr->lv_snapshot_prev->lv_snapshot_next = lv_ptr->lv_snapshot_next;
+ if (lv_ptr->lv_snapshot_next != NULL) {
+ lv_ptr->lv_snapshot_next->lv_snapshot_prev =
+ lv_ptr->lv_snapshot_prev;
+ }
+ /* no more snapshots? */
+ if (lv_ptr->lv_snapshot_org->lv_snapshot_next == NULL)
+ lv_ptr->lv_snapshot_org->lv_access &= ~LV_SNAPSHOT_ORG;
+ lvm_snapshot_release(lv_ptr);
+ }
+
+ devfs_unregister(lv_devfs_handle[lv_ptr->lv_number]);
+
+#ifdef DEBUG_KFREE
+ printk(KERN_DEBUG "%s -- kfree %d\n", lvm_name, __LINE__);
+#endif
+ kfree(lv_ptr);
+ vg_ptr->lv[l] = NULL;
+ vg_ptr->lv_cur--;
+ return 0;
+} /* lvm_do_lv_remove() */
+
+
+/*
+ * character device support function logical volume extend / reduce
+ */
+static int lvm_do_lv_extend_reduce(int minor, char *lv_name, lv_t *lv)
+{
+ int l, le, p, size, old_allocated_le;
+ uint32_t end, lv_status_save;
+ vg_t *vg_ptr = vg[VG_CHR(minor)];
+ lv_t *lv_ptr;
+ pe_t *pe;
+
+ if ((pep = lv->lv_current_pe) == NULL) return -EINVAL;
+
+ for (l = 0; l < vg_ptr->lv_max; l++) {
+ if (vg_ptr->lv[l] != NULL &&
+ strcmp(vg_ptr->lv[l]->lv_name, lv_name) == 0)
+ break;
+ }
+ if (l == vg_ptr->lv_max) return -ENXIO;
+ lv_ptr = vg_ptr->lv[l];
+
+ /* check for active snapshot */
+ if (lv->lv_access & (LV_SNAPSHOT | LV_SNAPSHOT_ORG)) return -EPERM;
+
+ if ((pe = vmalloc(size = lv->lv_current_le * sizeof(pe_t))) == NULL) {
+ printk(KERN_CRIT
+ "%s -- lvm_do_lv_extend_reduce: vmalloc error LV_CURRENT_PE "
+ "of %d Byte at line %d\n",
+ lvm_name, size, __LINE__);
+ return -ENOMEM;
+ }
+ /* get the PE structures from user space */
+ if (copy_from_user(pe, pep, size)) {
+ vfree(pe);
+ return -EFAULT;
+ }
+
+#ifdef DEBUG
+ printk(KERN_DEBUG
+ "%s -- fsync_dev and "
+ "invalidate_buffers for %s [%s] in %s\n",
+ lvm_name, lv_ptr->lv_name,
+ kdevname(lv_ptr->lv_dev),
+ vg_ptr->vg_name);
+#endif
+
+ lv_ptr->lv_status |= LV_SPINDOWN;
+ fsync_dev(lv_ptr->lv_dev);
+ lv_ptr->lv_status &= ~LV_ACTIVE;
+ invalidate_buffers(lv_ptr->lv_dev);
+
+ /* reduce allocation counters on PV(s) */
+ for (le = 0; le < lv_ptr->lv_allocated_le; le++) {
+ vg_ptr->pe_allocated--;
+ for (p = 0; p < vg_ptr->pv_cur; p++) {
+ if (vg_ptr->pv[p]->pv_dev ==
+ lv_ptr->lv_current_pe[le].dev) {
+ vg_ptr->pv[p]->pe_allocated--;
+ break;
+ }
+ }
+ }
+
+
+ /* save pointer to "old" lv/pe pointer array */
+ pep1 = lv_ptr->lv_current_pe;
+ end = lv_ptr->lv_current_le;
+
+ /* save open counter */
+ lv_open = lv_ptr->lv_open;
+
+ /* save # of old allocated logical extents */
+ old_allocated_le = lv_ptr->lv_allocated_le;
+
+ /* copy preloaded LV */
+ lv_status_save = lv->lv_status;
+ lv->lv_status |= LV_SPINDOWN;
+ lv->lv_status &= ~LV_ACTIVE;
+ memcpy((char *) lv_ptr, (char *) lv, sizeof(lv_t));
+ lv_ptr->lv_current_pe = pe;
+ lv_ptr->lv_open = lv_open;
+
+ /* save availiable i/o statistic data */
+ /* linear logical volume */
+ if (lv_ptr->lv_stripes < 2) {
+ /* Check what last LE shall be used */
+ if (end > lv_ptr->lv_current_le) end = lv_ptr->lv_current_le;
+ for (le = 0; le < end; le++) {
+ lv_ptr->lv_current_pe[le].reads = pep1[le].reads;
+ lv_ptr->lv_current_pe[le].writes = pep1[le].writes;
+ }
+ /* striped logical volume */
+ } else {
+ uint i, j, source, dest, end, old_stripe_size, new_stripe_size;
+
+ old_stripe_size = old_allocated_le / lv_ptr->lv_stripes;
+ new_stripe_size = lv_ptr->lv_allocated_le / lv_ptr->lv_stripes;
+ end = old_stripe_size;
+ if (end > new_stripe_size) end = new_stripe_size;
+ for (i = source = dest = 0;
+ i < lv_ptr->lv_stripes; i++) {
+ for (j = 0; j < end; j++) {
+ lv_ptr->lv_current_pe[dest + j].reads =
+ pep1[source + j].reads;
+ lv_ptr->lv_current_pe[dest + j].writes =
+ pep1[source + j].writes;
+ }
+ source += old_stripe_size;
+ dest += new_stripe_size;
+ }
+ }
+ vfree(pep1);
+ pep1 = NULL;
+
+
+ /* extend the PE count in PVs */
+ for (le = 0; le < lv_ptr->lv_allocated_le; le++) {
+ vg_ptr->pe_allocated++;
+ for (p = 0; p < vg_ptr->pv_cur; p++) {
+ if (vg_ptr->pv[p]->pv_dev ==
+ vg_ptr->lv[l]->lv_current_pe[le].dev) {
+ vg_ptr->pv[p]->pe_allocated++;
+ break;
+ }
+ }
+ }
+
+ lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].start_sect = 0;
+ lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].nr_sects = lv_ptr->lv_size;
+ lvm_size[MINOR(lv_ptr->lv_dev)] = lv_ptr->lv_size >> 1;
+ /* vg_lv_map array doesn't have to be changed here */
+
+ read_ahead[MAJOR_NR] = lv_ptr->lv_read_ahead = LVM_CORRECT_READ_AHEAD(lv_ptr->lv_read_ahead);
+ lv_ptr->lv_status = lv_status_save;
+
+ return 0;
+} /* lvm_do_lv_extend_reduce() */
+
+
+/*
+ * character device support function logical volume status by name
+ */
+static int lvm_do_lv_status_byname(vg_t *vg_ptr, void *arg)
+{
+ uint l;
+ ulong size;
+ lv_t lv;
+ lv_t *lv_ptr;
+ lv_status_byname_req_t lv_status_byname_req;
+
+ if (vg_ptr == NULL) return -ENXIO;
+ if (copy_from_user(&lv_status_byname_req, arg,
+ sizeof(lv_status_byname_req_t)) != 0)
+ return -EFAULT;
+
+ if (lv_status_byname_req.lv == NULL) return -EINVAL;
+ if (copy_from_user(&lv, lv_status_byname_req.lv,
+ sizeof(lv_t)) != 0)
+ return -EFAULT;
+
+ for (l = 0; l < vg_ptr->lv_max; l++) {
+ lv_ptr = vg_ptr->lv[l];
+ if (lv_ptr != NULL &&
+ strcmp(lv_ptr->lv_name,
+ lv_status_byname_req.lv_name) == 0) {
+ if (copy_to_user(lv_status_byname_req.lv,
+ lv_ptr,
+ sizeof(lv_t)) != 0)
+ return -EFAULT;
+
+ if (lv.lv_current_pe != NULL) {
+ size = lv_ptr->lv_allocated_le *
+ sizeof(pe_t);
+ if (copy_to_user(lv.lv_current_pe,
+ lv_ptr->lv_current_pe,
+ size) != 0)
+ return -EFAULT;
+ }
+ return 0;
+ }
+ }
+ return -ENXIO;
+} /* lvm_do_lv_status_byname() */
+
+
+/*
+ * character device support function logical volume status by index
+ */
+static int lvm_do_lv_status_byindex(vg_t *vg_ptr,void *arg)
+{
+ ulong size;
+ lv_t lv;
+ lv_t *lv_ptr;
+ lv_status_byindex_req_t lv_status_byindex_req;
+
+ if (vg_ptr == NULL) return -ENXIO;
+ if (copy_from_user(&lv_status_byindex_req, arg,
+ sizeof(lv_status_byindex_req)) != 0)
+ return -EFAULT;
+
+ if ((lvp = lv_status_byindex_req.lv) == NULL)
+ return -EINVAL;
+ if ( ( lv_ptr = vg_ptr->lv[lv_status_byindex_req.lv_index]) == NULL)
+ return -ENXIO;
+
+ if (copy_from_user(&lv, lvp, sizeof(lv_t)) != 0)
+ return -EFAULT;
+
+ if (copy_to_user(lvp, lv_ptr, sizeof(lv_t)) != 0)
+ return -EFAULT;
+
+ if (lv.lv_current_pe != NULL) {
+ size = lv_ptr->lv_allocated_le * sizeof(pe_t);
+ if (copy_to_user(lv.lv_current_pe,
+ lv_ptr->lv_current_pe,
+ size) != 0)
+ return -EFAULT;
+ }
+ return 0;
+} /* lvm_do_lv_status_byindex() */
+
+
+/*
+ * character device support function physical volume change
+ */
+static int lvm_do_pv_change(vg_t *vg_ptr, void *arg)
+{
+ uint p;
+ pv_t *pv_ptr;
+#ifdef LVM_GET_INODE
+ struct inode *inode_sav;
+#endif
+
+ if (vg_ptr == NULL) return -ENXIO;
+ if (copy_from_user(&pv_change_req, arg,
+ sizeof(pv_change_req)) != 0)
+ return -EFAULT;
+
+ for (p = 0; p < vg_ptr->pv_max; p++) {
+ pv_ptr = vg_ptr->pv[p];
+ if (pv_ptr != NULL &&
+ strcmp(pv_ptr->pv_name,
+ pv_change_req.pv_name) == 0) {
+#ifdef LVM_GET_INODE
+ inode_sav = pv_ptr->inode;
+#endif
+ if (copy_from_user(pv_ptr,
+ pv_change_req.pv,
+ sizeof(pv_t)) != 0)
+ return -EFAULT;
+
+ /* We don't need the PE list
+ in kernel space as with LVs pe_t list */
+ pv_ptr->pe = NULL;
+#ifdef LVM_GET_INODE
+ pv_ptr->inode = inode_sav;
+#endif
+ return 0;
+ }
+ }
+ return -ENXIO;
+} /* lvm_do_pv_change() */
+
+/*
+ * character device support function get physical volume status
+ */
+static int lvm_do_pv_status(vg_t *vg_ptr, void *arg)
+{
+ uint p;
+ pv_t *pv_ptr;
+
+ if (vg_ptr == NULL) return -ENXIO;
+ if (copy_from_user(&pv_status_req, arg,
+ sizeof(pv_status_req)) != 0)
+ return -EFAULT;
+
+ for (p = 0; p < vg_ptr->pv_max; p++) {
+ pv_ptr = vg_ptr->pv[p];
+ if (pv_ptr != NULL &&
+ strcmp(pv_ptr->pv_name,
+ pv_status_req.pv_name) == 0) {
+ if (copy_to_user(pv_status_req.pv,
+ pv_ptr,
+ sizeof(pv_t)) != 0)
+ return -EFAULT;
+ return 0;
+ }
+ }
+ return -ENXIO;
+} /* lvm_do_pv_status() */
+
+
+/*
+ * support function initialize gendisk variables
+ */
+#ifdef __initfunc
+__initfunc(void lvm_geninit(struct gendisk *lvm_gdisk))
+#else
+void __init
+ lvm_geninit(struct gendisk *lvm_gdisk)
+#endif
+{
+ int i = 0;
+
+#ifdef DEBUG_GENDISK
+ printk(KERN_DEBUG "%s -- lvm_gendisk\n", lvm_name);
+#endif
+
+ for (i = 0; i < MAX_LV; i++) {
+ lvm_gendisk.part[i].start_sect = -1; /* avoid partition check */
+ lvm_size[i] = lvm_gendisk.part[i].nr_sects = 0;
+ lvm_blocksizes[i] = BLOCK_SIZE;
+ }
+
+ blksize_size[MAJOR_NR] = lvm_blocksizes;
+ blk_size[MAJOR_NR] = lvm_size;
+
+ return;
+} /* lvm_gen_init() */
+
+
+#ifdef LVM_GET_INODE
+/*
+ * support function to get an empty inode
+ *
+ * Gets an empty inode to be inserted into the inode hash,
+ * so that a physical volume can't be mounted.
+ * This is analog to drivers/block/md.c
+ *
+ * Is this the real thing?
+ *
+ * No, it's bollocks. md.c tries to do a bit different thing that might
+ * _somewhat_ work eons ago. Neither does any good these days. mount() couldn't
+ * care less for icache (it cares only for ->s_root->d_count and if we want
+ * loopback mounts even that will stop). BTW, with the form used here mount()
+ * would have to scan the _whole_ icache to detect the attempt - how on the
+ * Earth could it guess the i_ino of your dummy inode? Official line on the
+ * exclusion between mount()/swapon()/open()/etc. is Just Don't Do It(tm).
+ * If you can convince Linus that it's worth changing - fine, then you'll need
+ * to do blkdev_get()/blkdev_put(). Until then...
+ */
+struct inode *lvm_get_inode(kdev_t dev)
+{
+ struct inode *inode_this = NULL;
+
+ /* Lock the device by inserting a dummy inode. */
+ inode_this = get_empty_inode();
+ inode_this->i_dev = dev;
+ insert_inode_hash(inode_this);
+ return inode_this;
+}
+
+
+/*
+ * support function to clear an inode
+ *
+ */
+void lvm_clear_inode(struct inode *inode)
+{
+#ifdef I_FREEING
+ inode->i_state |= I_FREEING;
+#endif
+ clear_inode(inode);
+ return;
+}
+#endif /* #ifdef LVM_GET_INODE */
#include <linux/raid/xor.h>
#include <linux/devfs_fs_kernel.h>
+#include <linux/init.h>
+
#ifdef CONFIG_KMOD
#include <linux/kmod.h>
#endif
}
#endif
+__initcall(md_init);
+#ifdef CONFIG_AUTODETECT_RAID
+__initcall(md_run_setup);
+#endif
+
MD_EXPORT_SYMBOL(md_size);
MD_EXPORT_SYMBOL(register_md_personality);
MD_EXPORT_SYMBOL(unregister_md_personality);
adr = instr->addr - (chipnum << cfi->chipshift) * (cfi->interleave);
len = instr->len;
-printk("erase : 0x%x 0x%x 0x%x\n", adr, len, chipnum, mtd->size);
+printk("erase : 0x%lx 0x%lx 0x%x 0x%x\n", adr, len, chipnum, mtd->size);
while(len) {
//printk("erase : 0x%x 0x%x 0x%x 0x%x\n", chipnum, adr, len, cfi->chipshift);
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/sched.h>
+#include <linux/delay.h>
#include <asm/io.h>
-#include <asm/delay.h>
struct JEDECTable mtd_JEDEC_table[] =
{{0x01AD,"AMD Am29F016",2*1024*1024,64*1024,MTD_CAP_NORFLASH},
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate ' Novell/Eagle/Microdyne NE3210 EISA support (EXPERIMENTAL)' CONFIG_NE3210
fi
- if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- tristate ' RealTek 8129 (not 8019/8029!) support (EXPERIMENTAL)' CONFIG_RTL8129
- fi
- tristate ' RealTek RTL-8139 PCI Fast Ethernet Adapter support' CONFIG_8139TOO
- tristate ' SiS 900/7016 PCI Fast Ethernet Adapter support' CONFIG_SIS900
- # tristate ' Sundance Alta support' CONFIG_ALTA
- tristate ' TI ThunderLAN support' CONFIG_TLAN
- tristate ' VIA Rhine support' CONFIG_VIA_RHINE
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate ' Racal-Interlan EISA ES3210 support (EXPERIMENTAL)' CONFIG_ES3210
- tristate ' SMC EtherPower II (EXPERIMENTAL)' CONFIG_EPIC100
+ dep_tristate ' RealTek 8129 (not 8019/8029/8139!) support (EXPERIMENTAL)' CONFIG_RTL8129 $CONFIG_PCI
fi
+ dep_tristate ' RealTek RTL-8139 PCI Fast Ethernet Adapter support' CONFIG_8139TOO $CONFIG_PCI
+ dep_tristate ' SiS 900/7016 PCI Fast Ethernet Adapter support' CONFIG_SIS900 $CONFIG_PCI
+ dep_tristate ' SMC EtherPower II' CONFIG_EPIC100 $CONFIG_PCI
+ dep_tristate ' Sundance Alta support' CONFIG_SUNDANCE $CONFIG_PCI
+ tristate ' TI ThunderLAN support' CONFIG_TLAN
+ dep_tristate ' VIA Rhine support' CONFIG_VIA_RHINE $CONFIG_PCI
+ dep_tristate ' Winbond W89c840 Ethernet support' CONFIG_WINBOND_840 $CONFIG_PCI
if [ "$CONFIG_OBSOLETE" = "y" ]; then
bool ' Zenith Z-Note support (EXPERIMENTAL)' CONFIG_ZNET
fi
mainmenu_option next_comment
comment 'Ethernet (1000 Mbit)'
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- # tristate 'Packet Engines Hamachi GNIC-II support (EXPERIMENTAL)' CONFIG_HAMACHI
- tristate 'Packet Engines Yellowfin Gigabit-NIC support (EXPERIMENTAL)' CONFIG_YELLOWFIN
-fi
-tristate 'Alteon AceNIC/3Com 3C985/NetGear GA620 Gigabit support' CONFIG_ACENIC
+dep_tristate 'Alteon AceNIC/3Com 3C985/NetGear GA620 Gigabit support' CONFIG_ACENIC $CONFIG_PCI
if [ "$CONFIG_ACENIC" != "n" ]; then
bool ' Omit support for old Tigon I based AceNICs' CONFIG_ACENIC_OMIT_TIGON_I
fi
+dep_tristate 'Packet Engines Hamachi GNIC-II support' CONFIG_HAMACHI $CONFIG_PCI
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ dep_tristate 'Packet Engines Yellowfin Gigabit-NIC support (EXPERIMENTAL)' CONFIG_YELLOWFIN $CONFIG_PCI
+fi
tristate 'SysKonnect SK-98xx support' CONFIG_SK98LIN
endmenu
bool 'FDDI driver support' CONFIG_FDDI
if [ "$CONFIG_FDDI" = "y" ]; then
- tristate ' Digital DEFEA and DEFPA adapter support' CONFIG_DEFXX
+ dep_tristate ' Digital DEFEA and DEFPA adapter support' CONFIG_DEFXX $CONFIG_PCI
tristate ' SysKonnect FDDI PCI support' CONFIG_SKFP
fi
obj-$(CONFIG_AIRONET4500_PROC) += aironet4500_proc.o
obj-$(CONFIG_AIRONET4500_CS) += aironet4500_proc.o
+obj-$(CONFIG_WINBOND_840) += winbond-840.o
+obj-$(CONFIG_SUNDANCE) += sundance.o
+obj-$(CONFIG_HAMACHI) += hamachi.o
obj-$(CONFIG_NET) += Space.o setup.o net_init.o loopback.o
obj-$(CONFIG_SEEQ8005) += seeq8005.o
obj-$(CONFIG_ETHERTAP) += ethertap.o
#define NEXT_DEV (&escon0_dev)
#endif
-#ifdef CONFIG_TUN
- extern int tun_init(struct net_device *dev);
- static struct net_device tun_dev = {
- "tun", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, tun_init };
-# undef NEXT_DEV
-# define NEXT_DEV (&tun_dev)
-#endif
-
/*
* The loopback device is global so it can be directly referenced
* by the network code. Also, it must be first on device list.
long long jiff;
- DEBUG(2, " awc_reset dev %x \n", (int)dev);
+ DEBUG(2, " awc_reset dev %p \n", dev);
DEBUG(2, "%s: awc_reset \n", dev->name);
awc_issue_soft_reset(dev);
obj- :=
export-objs :=
-obj-$(CONFIG_LTPC) += ltpc.o
-obj-$(CONFIG_COPS) += cops.o
obj-$(CONFIG_IPDDP) += ipddp.o
+obj-$(CONFIG_COPS) += cops.o
+obj-$(CONFIG_LTPC) += ltpc.o
-L_TARGET := appletalk.a
-L_OBJS := $(filter-out $(export-objs), $(obj-y))
-LX_OBJS := $(filter $(export-objs), $(obj-y))
+O_TARGET := appletalk.o
+O_OBJS := $(filter-out $(export-objs), $(obj-y))
+OX_OBJS := $(filter $(export-objs), $(obj-y))
M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m)))
MX_OBJS := $(sort $(filter $(export-objs), $(obj-m)))
"ipddp.c:v0.01 8/28/97 Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n";
#include <linux/config.h>
-#ifdef MODULE
#include <linux/module.h>
-#include <linux/version.h>
-#endif
-
#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/malloc.h>
-#include <linux/string.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <linux/errno.h>
+#include <linux/init.h>
#include <linux/netdevice.h>
-#include <linux/inetdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/atalk.h>
#include <linux/ip.h>
+#include <linux/atalk.h>
+#include <linux/if_arp.h>
#include <net/route.h>
-#include <linux/inet.h>
+#include <asm/uaccess.h>
#include "ipddp.h" /* Our stuff */
static int ipddp_open(struct net_device *dev)
{
-#ifdef MODULE
MOD_INC_USE_COUNT;
-#endif
-
return 0;
}
static int ipddp_close(struct net_device *dev)
{
-#ifdef MODULE
MOD_DEC_USE_COUNT;
-#endif
-
return 0;
}
-int ipddp_init(struct net_device *dev)
+static int __init ipddp_init(struct net_device *dev)
{
static unsigned version_printed = 0;
*/
static struct net_device_stats *ipddp_get_stats(struct net_device *dev)
{
- return (struct net_device_stats *)dev->priv;
+ return dev->priv;
}
/*
}
}
-#ifdef MODULE /* Module specific functions for ipddp.c */
-
-static struct net_device dev_ipddp=
-{
- "ipddp0\0 ",
- 0, 0, 0, 0,
- 0x0, 0,
- 0, 0, 0, NULL, ipddp_init
-};
+static struct net_device dev_ipddp = { init: ipddp_init };
MODULE_PARM(ipddp_mode, "i");
-int init_module(void)
+static int __init ipddp_init_module(void)
{
int err;
return 0;
}
-void cleanup_module(void)
+static void __exit ipddp_cleanup_module(void)
{
unregister_netdev(&dev_ipddp);
kfree(dev_ipddp.priv);
- dev_ipddp.priv = NULL;
+
+ memset(&dev_ipddp, 0, sizeof(dev_ipddp));
+ dev_ipddp.init = ipddp_init;
}
-#endif /* MODULE */
+module_init(ipddp_init_module);
+module_exit(ipddp_cleanup_module);
/* atp.c: Attached (pocket) ethernet adapter driver for linux. */
/*
- This is a driver for a commonly OEMed pocket (parallel port)
- ethernet adapter.
+ This is a driver for commonly OEM pocket (parallel port)
+ ethernet adapters based on the Realtek RTL8002 and RTL8012 chips.
- Written 1993,1994,1995 by Donald Becker.
+ Written 1993-2000 by Donald Becker.
- Copyright 1993 United States Government as represented by the
- Director, National Security Agency.
+ This software may be used and distributed according to the terms of
+ the GNU General Public License (GPL), incorporated herein by reference.
+ Drivers based on or derived from this code fall under the GPL and must
+ retain the authorship, copyright and license notice. This file is not
+ a complete program and may only be used when the entire operating
+ system is licensed under the GPL.
- This software may be used and distributed according to the terms
- of the GNU Public License, incorporated herein by reference.
+ Copyright 1993 United States Government as represented by the Director,
+ National Security Agency. Copyright 1994-2000 retained by the original
+ author, Donald Becker. The timer-based reset code was supplied in 1995
+ by Bill Carlson, wwc@super.org.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+ Support information and updates available at
+ http://www.scyld.com/network/atp.html
- The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
- Center of Excellence in Space Data and Information Sciences
- Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
- The timer-based reset code was written by Bill Carlson, wwc@super.org.
-
Modular support/softnet added by Alan Cox.
+
*/
-static const char *version =
- "atp.c:v1.01 1/18/95 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+static const char versionA[] =
+"atp.c:v1.09 8/9/2000 Donald Becker <becker@scyld.com>\n";
+static const char versionB[] =
+" http://www.scyld.com/network/atp.html\n";
+
+/* The user-configurable values.
+ These may be modified when a driver module is loaded.*/
+
+static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */
+#define net_debug debug
+
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+static int max_interrupt_work = 15;
+
+#define NUM_UNITS 2
+/* The standard set of ISA module parameters. */
+static int io[NUM_UNITS] = {0, 0};
+static int irq[NUM_UNITS] = {0, 0};
+static int xcvr[NUM_UNITS] = {0, 0}; /* The data transfer mode. */
+
+/* Operational parameters that are set at compile time. */
+
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT (400*HZ/1000)
/*
This file is a device driver for the RealTek (aka AT-Lan-Tec) pocket
description is written based on guesses and writing lots of special-purpose
code to test my theorized operation.
+ In 1997 Realtek made available the documentation for the second generation
+ RTL8012 chip, which has lead to several driver improvements.
+ http://www.realtek.com.tw/cn/cn.html
+
Theory of Operation
-
+
The RTL8002 adapter seems to be built around a custom spin of the SEEQ
controller core. It probably has a 16K or 64K internal packet buffer, of
which the first 4K is devoted to transmit and the rest to receive.
timing and control bits. The data is then read from status port or written
to the data port.
+ Correction: the controller has two banks of 16 registers. The second
+ bank contains only the multicast filter table (now used) and the EEPROM
+ access registers.
+
Since the bulk data transfer of the actual packets through the slow
parallel port dominates the driver's running time, four distinct data
(non-register) transfer modes are provided by the adapter, two in each
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
+#include <linux/delay.h>
#include "atp.h"
-/* use 0 for production, 1 for verification, >2 for debug */
-#ifndef NET_DEBUG
-#define NET_DEBUG 1
-#endif
-static unsigned int net_debug = NET_DEBUG;
+MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
+MODULE_DESCRIPTION("RealTek RTL8002/8012 parallel port Ethernet driver");
+MODULE_PARM(max_interrupt_work, "i");
+MODULE_PARM(debug, "i");
+MODULE_PARM(io, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(xcvr, "1-" __MODULE_STRING(MAX_UNITS) "i");
+
+#define RUN_AT(x) (jiffies + (x))
/* The number of low I/O ports used by the ethercard. */
#define ETHERCARD_TOTAL_SIZE 3
+/* Sequence to switch an 8012 from printer mux to ethernet mode. */
+static char mux_8012[] = { 0xff, 0xf7, 0xff, 0xfb, 0xf3, 0xfb, 0xff, 0xf7,};
+
+struct net_local {
+ spinlock_t lock;
+ struct net_device *next_module;
+ struct net_device_stats stats;
+ struct timer_list timer; /* Media selection timer. */
+ long last_rx_time; /* Last Rx, in jiffies, to handle Rx hang. */
+ int saved_tx_size;
+ unsigned int tx_unit_busy:1;
+ unsigned char re_tx, /* Number of packet retransmissions. */
+ addr_mode, /* Current Rx filter e.g. promiscuous, etc. */
+ pac_cnt_in_tx_buf,
+ chip_type;
+};
+
/* This code, written by wwc@super.org, resets the adapter every
TIMED_CHECKER ticks. This recovers from an unknown error which
hangs the device. */
#ifdef TIMED_CHECKER
#include <linux/timer.h>
static void atp_timed_checker(unsigned long ignored);
-static struct net_device *atp_timed_dev;
-static struct timer_list atp_timer = { function: atp_timed_checker };
#endif
/* Index to functions, as function prototypes. */
-static int atp_probe1(struct net_device *dev, short ioaddr);
+static int atp_probe1(struct net_device *dev, long ioaddr);
static void get_node_ID(struct net_device *dev);
-static unsigned short eeprom_op(short ioaddr, unsigned int cmd);
+static unsigned short eeprom_op(long ioaddr, unsigned int cmd);
static int net_open(struct net_device *dev);
static void hardware_init(struct net_device *dev);
-static void tx_timeout(struct net_device *dev);
-static void write_packet(short ioaddr, int length, unsigned char *packet, int mode);
-static void trigger_send(short ioaddr, int length);
-static int net_send_packet(struct sk_buff *skb, struct net_device *dev);
-static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void write_packet(long ioaddr, int length, unsigned char *packet, int mode);
+static void trigger_send(long ioaddr, int length);
+static int atp_send_packet(struct sk_buff *skb, struct net_device *dev);
+static void atp_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static void net_rx(struct net_device *dev);
-static void read_block(short ioaddr, int length, unsigned char *buffer, int data_mode);
+static void read_block(long ioaddr, int length, unsigned char *buffer, int data_mode);
static int net_close(struct net_device *dev);
static struct net_device_stats *net_get_stats(struct net_device *dev);
-static void set_multicast_list(struct net_device *dev);
+static void set_rx_mode_8002(struct net_device *dev);
+static void set_rx_mode_8012(struct net_device *dev);
+static void tx_timeout(struct net_device *dev);
+
+
+/* A list of all installed ATP devices, for removing the driver module. */
+static struct net_device *root_atp_dev = NULL;
-\f
/* Check for a network adapter of this type, and return '0' iff one exists.
If dev->base_addr == 0, probe all likely locations.
If dev->base_addr == 1, always return failure.
If dev->base_addr == 2, allocate space for the device and return success
(detachable devices only).
*/
-
static int __init atp_init(struct net_device *dev)
{
int *port, ports[] = {0x378, 0x278, 0x3bc, 0};
- int base_addr = dev->base_addr;
+ int base_addr = dev ? dev->base_addr : io[0];
if (base_addr > 0x1ff) /* Check a single specified location. */
return atp_probe1(dev, base_addr);
return -ENXIO;
for (port = ports; *port; port++) {
- int ioaddr = *port;
+ long ioaddr = *port;
outb(0x57, ioaddr + PAR_DATA);
if (inb(ioaddr + PAR_DATA) != 0x57)
continue;
return -ENODEV;
}
-static int __init atp_probe1(struct net_device *dev, short ioaddr)
+static int __init atp_probe1(struct net_device *dev, long ioaddr)
{
- int saved_ctrl_reg, status;
+ struct net_local *lp;
+ int saved_ctrl_reg, status, i;
outb(0xff, ioaddr + PAR_DATA);
/* Save the original value of the Control register, in case we guessed
wrong. */
saved_ctrl_reg = inb(ioaddr + PAR_CONTROL);
+ if (net_debug > 3)
+ printk("atp: Control register was %#2.2x.\n", saved_ctrl_reg);
/* IRQEN=0, SLCTB=high INITB=high, AUTOFDB=high, STBB=high. */
outb(0x04, ioaddr + PAR_CONTROL);
+#ifndef final_version
+ if (net_debug > 3) {
+ /* Turn off the printer multiplexer on the 8012. */
+ for (i = 0; i < 8; i++)
+ outb(mux_8012[i], ioaddr + PAR_DATA);
+ write_reg(ioaddr, MODSEL, 0x00);
+ printk("atp: Registers are ");
+ for (i = 0; i < 32; i++)
+ printk(" %2.2x", read_nibble(ioaddr, i));
+ printk(".\n");
+ }
+#endif
+ /* Turn off the printer multiplexer on the 8012. */
+ for (i = 0; i < 8; i++)
+ outb(mux_8012[i], ioaddr + PAR_DATA);
write_reg_high(ioaddr, CMR1, CMR1h_RESET);
- udelay(100);
+ /* udelay() here? */
status = read_nibble(ioaddr, CMR1);
+ if (net_debug > 3) {
+ printk(KERN_DEBUG "atp: Status nibble was %#2.2x..", status);
+ for (i = 0; i < 32; i++)
+ printk(" %2.2x", read_nibble(ioaddr, i));
+ printk("\n");
+ }
+
if ((status & 0x78) != 0x08) {
/* The pocket adapter probe failed, restore the control register. */
outb(saved_ctrl_reg, ioaddr + PAR_CONTROL);
- return 1;
+ return -ENODEV;
}
status = read_nibble(ioaddr, CMR2_h);
if ((status & 0x78) != 0x10) {
outb(saved_ctrl_reg, ioaddr + PAR_CONTROL);
- return 1;
+ return -ENODEV;
}
+
+ dev = init_etherdev(dev, sizeof(struct net_local));
+ if (!dev)
+ return -ENOMEM;
+
/* Find the IRQ used by triggering an interrupt. */
write_reg_byte(ioaddr, CMR2, 0x01); /* No accept mode, IRQ out. */
write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE); /* Enable Tx and Rx. */
/* Omit autoIRQ routine for now. Use "table lookup" instead. Uhgggh. */
- if (ioaddr == 0x378)
+ if (irq[0])
+ dev->irq = irq[0];
+ else if (ioaddr == 0x378)
dev->irq = 7;
else
dev->irq = 5;
/* Read the station address PROM. */
get_node_ID(dev);
- printk("%s: Pocket adapter found at %#3lx, IRQ %d, SAPROM "
+#ifndef MODULE
+ if (net_debug)
+ printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB);
+#endif
+
+ printk(KERN_NOTICE "%s: Pocket adapter found at %#3lx, IRQ %d, SAPROM "
"%02X:%02X:%02X:%02X:%02X:%02X.\n", dev->name, dev->base_addr,
dev->irq, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
- /* Leave the hardware in a reset state. */
- write_reg_high(ioaddr, CMR1, CMR1h_RESET);
-
- if (net_debug)
- printk(version);
+ /* Reset the ethernet hardware and activate the printer pass-through. */
+ write_reg_high(ioaddr, CMR1, CMR1h_RESET | CMR1h_MUX);
/* Initialize the device structure. */
ether_setup(dev);
- dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
if (dev->priv == NULL)
return -ENOMEM;
memset(dev->priv, 0, sizeof(struct net_local));
- {
- struct net_local *lp = (struct net_local *)dev->priv;
- lp->addr_mode = CMR2h_Normal;
- spin_lock_init(&lp->lock);
- }
+
+ lp = (struct net_local *)dev->priv;
+ lp->chip_type = RTL8002;
+ lp->addr_mode = CMR2h_Normal;
+ spin_lock_init(&lp->lock);
+
+ lp->next_module = root_atp_dev;
+ root_atp_dev = dev;
/* For the ATP adapter the "if_port" is really the data transfer mode. */
- dev->if_port = (dev->mem_start & 0xf) ? dev->mem_start & 0x7 : 4;
+ if (xcvr[0])
+ dev->if_port = xcvr[0];
+ else
+ dev->if_port = (dev->mem_start & 0xf) ? (dev->mem_start & 0x7) : 4;
if (dev->mem_end & 0xf)
net_debug = dev->mem_end & 7;
dev->open = net_open;
dev->stop = net_close;
- dev->hard_start_xmit = net_send_packet;
+ dev->hard_start_xmit = atp_send_packet;
dev->get_stats = net_get_stats;
- dev->set_multicast_list = set_multicast_list;
+ dev->set_multicast_list =
+ lp->chip_type == RTL8002 ? &set_rx_mode_8002 : &set_rx_mode_8012;
dev->tx_timeout = tx_timeout;
- dev->watchdog_timeo = HZ/20;
+ dev->watchdog_timeo = TX_TIMEOUT;
-#ifdef TIMED_CHECKER
- del_timer(&atp_timer);
- atp_timer.expires = jiffies + TIMED_CHECKER;
- atp_timed_dev = dev;
- add_timer(&atp_timer);
-#endif
return 0;
}
/* Read the station address PROM, usually a word-wide EEPROM. */
static void __init get_node_ID(struct net_device *dev)
{
- short ioaddr = dev->base_addr;
+ long ioaddr = dev->base_addr;
int sa_offset = 0;
int i;
-
+
write_reg(ioaddr, CMR2, CMR2_EEPROM); /* Point to the EEPROM control registers. */
-
+
/* Some adapters have the station address at offset 15 instead of offset
zero. Check for it, and fix it if needed. */
if (eeprom_op(ioaddr, EE_READ(0)) == 0xffff)
sa_offset = 15;
-
+
for (i = 0; i < 3; i++)
- ((unsigned short *)dev->dev_addr)[i] =
- ntohs(eeprom_op(ioaddr, EE_READ(sa_offset + i)));
-
+ ((u16 *)dev->dev_addr)[i] =
+ be16_to_cpu(eeprom_op(ioaddr, EE_READ(sa_offset + i)));
+
write_reg(ioaddr, CMR2, CMR2_NULL);
}
* DO : _________X_______X
*/
-static unsigned short __init eeprom_op(short ioaddr, unsigned int cmd)
+static unsigned short __init eeprom_op(long ioaddr, unsigned int cmd)
{
unsigned eedata_out = 0;
int num_bits = EE_CMD_SIZE;
-
+
while (--num_bits >= 0) {
char outval = test_bit(num_bits, &cmd) ? EE_DATA_WRITE : 0;
write_reg_high(ioaddr, PROM_CMD, outval | EE_CLK_LOW);
- udelay(5);
write_reg_high(ioaddr, PROM_CMD, outval | EE_CLK_HIGH);
eedata_out <<= 1;
if (read_nibble(ioaddr, PROM_DATA) & EE_DATA_READ)
eedata_out++;
- udelay(5);
}
write_reg_high(ioaddr, PROM_CMD, EE_CLK_LOW & ~EE_CS);
return eedata_out;
}
-\f
+
/* Open/initialize the board. This is called (in the current kernel)
sometime after booting when the 'ifconfig' program is run.
*/
static int net_open(struct net_device *dev)
{
+ struct net_local *lp = (struct net_local *)dev->priv;
+
+ MOD_INC_USE_COUNT;
/* The interrupt line is turned off (tri-stated) when the device isn't in
use. That's especially important for "attached" interfaces where the
port or interrupt may be shared. */
-
- if (request_irq(dev->irq, &net_interrupt, 0, "ATP", dev)) {
+ if (request_irq(dev->irq, &atp_interrupt, 0, "ATP Ethernet", dev)) {
+ MOD_DEC_USE_COUNT;
return -EAGAIN;
}
+
hardware_init(dev);
- MOD_INC_USE_COUNT;
+ init_timer(&lp->timer);
+ lp->timer.expires = RUN_AT(TIMED_CHECKER);
+ lp->timer.data = (unsigned long)dev;
+ lp->timer.function = &atp_timed_checker; /* timer handler */
+ add_timer(&lp->timer);
netif_start_queue(dev);
return 0;
static void hardware_init(struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
- int ioaddr = dev->base_addr;
- int i;
+ long ioaddr = dev->base_addr;
+ int i;
+ /* Turn off the printer multiplexer on the 8012. */
+ for (i = 0; i < 8; i++)
+ outb(mux_8012[i], ioaddr + PAR_DATA);
write_reg_high(ioaddr, CMR1, CMR1h_RESET);
-
- for (i = 0; i < 6; i++)
+
+ for (i = 0; i < 6; i++)
write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]);
write_reg_high(ioaddr, CMR2, lp->addr_mode);
if (net_debug > 2) {
- printk("%s: Reset: current Rx mode %d.\n", dev->name,
+ printk(KERN_DEBUG "%s: Reset: current Rx mode %d.\n", dev->name,
(read_nibble(ioaddr, CMR2_h) >> 3) & 0x0f);
}
- write_reg(ioaddr, CMR2, CMR2_IRQOUT);
- write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE);
+ write_reg(ioaddr, CMR2, CMR2_IRQOUT);
+ write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE);
/* Enable the interrupt line from the serial port. */
outb(Ctrl_SelData + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
/* Unmask the interesting interrupts. */
- write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
- write_reg_high(ioaddr, IMR, ISRh_RxErr);
+ write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
+ write_reg_high(ioaddr, IMR, ISRh_RxErr);
lp->tx_unit_busy = 0;
- lp->pac_cnt_in_tx_buf = 0;
+ lp->pac_cnt_in_tx_buf = 0;
lp->saved_tx_size = 0;
}
-static void trigger_send(short ioaddr, int length)
+static void trigger_send(long ioaddr, int length)
{
write_reg_byte(ioaddr, TxCNT0, length & 0xff);
write_reg(ioaddr, TxCNT1, length >> 8);
write_reg(ioaddr, CMR1, CMR1_Xmit);
}
-static void write_packet(short ioaddr, int length, unsigned char *packet, int data_mode)
+static void write_packet(long ioaddr, int length, unsigned char *packet, int data_mode)
{
- length = (length + 1) & ~1; /* Round up to word length. */
- outb(EOC+MAR, ioaddr + PAR_DATA);
- if ((data_mode & 1) == 0) {
+ length = (length + 1) & ~1; /* Round up to word length. */
+ outb(EOC+MAR, ioaddr + PAR_DATA);
+ if ((data_mode & 1) == 0) {
/* Write the packet out, starting with the write addr. */
outb(WrAddr+MAR, ioaddr + PAR_DATA);
do {
write_byte_mode0(ioaddr, *packet++);
} while (--length > 0) ;
- } else {
+ } else {
/* Write the packet out in slow mode. */
unsigned char outbyte = *packet++;
outb(Ctrl_HNibWrite + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
while (--length > 0)
write_byte_mode1(ioaddr, *packet++);
- }
- /* Terminate the Tx frame. End of write: ECB. */
- outb(0xff, ioaddr + PAR_DATA);
- outb(Ctrl_HNibWrite | Ctrl_SelData | Ctrl_IRQEN, ioaddr + PAR_CONTROL);
+ }
+ /* Terminate the Tx frame. End of write: ECB. */
+ outb(0xff, ioaddr + PAR_DATA);
+ outb(Ctrl_HNibWrite | Ctrl_SelData | Ctrl_IRQEN, ioaddr + PAR_CONTROL);
}
static void tx_timeout(struct net_device *dev)
{
- struct net_local *lp = (struct net_local *)dev->priv;
- int ioaddr = dev->base_addr;
- /* If we get here, some higher level has decided we are broken. */
- printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name,
+ struct net_local *np = (struct net_local *)dev->priv;
+ long ioaddr = dev->base_addr;
+
+ printk(KERN_WARNING "%s: Transmit timed out, %s?\n", dev->name,
inb(ioaddr + PAR_CONTROL) & 0x10 ? "network cable problem"
: "IRQ conflict");
- lp->stats.tx_errors++;
+ np->stats.tx_errors++;
/* Try to restart the adapter. */
hardware_init(dev);
dev->trans_start = jiffies;
netif_wake_queue(dev);
+ np->stats.tx_errors++;
+ return;
}
-static int net_send_packet(struct sk_buff *skb, struct net_device *dev)
+static int atp_send_packet(struct sk_buff *skb, struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
- int ioaddr = dev->base_addr;
- short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
- unsigned char *buf = skb->data;
- unsigned long flags;
-
+ long ioaddr = dev->base_addr;
+ int length;
+ long flags;
+
+ length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+
netif_stop_queue(dev);
-
+
/* Disable interrupts by writing 0x00 to the Interrupt Mask Register.
This sequence must not be interrupted by an incoming packet. */
-
+
spin_lock_irqsave(&lp->lock, flags);
write_reg(ioaddr, IMR, 0);
write_reg_high(ioaddr, IMR, 0);
spin_unlock_irqrestore(&lp->lock, flags);
-
- write_packet(ioaddr, length, buf, dev->if_port);
+
+ write_packet(ioaddr, length, skb->data, dev->if_port);
lp->pac_cnt_in_tx_buf++;
if (lp->tx_unit_busy == 0) {
trigger_send(ioaddr, length);
lp->saved_tx_size = 0; /* Redundant */
lp->re_tx = 0;
- lp->tx_unit_busy = 1;
+ lp->tx_unit_busy = 1;
} else
lp->saved_tx_size = length;
-
- dev->trans_start = jiffies;
/* Re-enable the LPT interrupts. */
write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
write_reg_high(ioaddr, IMR, ISRh_RxErr);
+
+ dev->trans_start = jiffies;
dev_kfree_skb (skb);
return 0;
}
-\f
+
+
/* The typical workload of the driver:
Handle the network interface interrupts. */
-static void
-net_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+static void atp_interrupt(int irq, void *dev_instance, struct pt_regs * regs)
{
- struct net_device *dev = dev_id;
+ struct net_device *dev = (struct net_device *)dev_instance;
struct net_local *lp;
- int ioaddr, status, boguscount = 20;
+ long ioaddr;
static int num_tx_since_rx = 0;
+ int boguscount = max_interrupt_work;
+ if (dev == NULL) {
+ printk(KERN_ERR "ATP_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
ioaddr = dev->base_addr;
lp = (struct net_local *)dev->priv;
-
+
spin_lock(&lp->lock);
/* Disable additional spurious interrupts. */
write_reg(ioaddr, CMR2, CMR2_NULL);
write_reg(ioaddr, IMR, 0);
- if (net_debug > 5)
- printk("%s: In interrupt ", dev->name);
-
- while (--boguscount > 0)
- {
- status = read_nibble(ioaddr, ISR);
- if (net_debug > 5)
- printk("loop status %02x..", status);
+ if (net_debug > 5) printk(KERN_DEBUG "%s: In interrupt ", dev->name);
+ while (--boguscount > 0) {
+ int status = read_nibble(ioaddr, ISR);
+ if (net_debug > 5) printk("loop status %02x..", status);
if (status & (ISR_RxOK<<3)) {
write_reg(ioaddr, ISR, ISR_RxOK); /* Clear the Rx interrupt. */
break;
}
/* Attempt to retransmit. */
- if (net_debug > 6)
- printk("attempting to ReTx");
+ if (net_debug > 6) printk("attempting to ReTx");
write_reg(ioaddr, CMR1, CMR1_ReXmit + CMR1_Xmit);
} else {
/* Finish up the transmit. */
}
num_tx_since_rx++;
} else if (num_tx_since_rx > 8
- && jiffies - dev->last_rx > 100) {
+ && jiffies > dev->last_rx + HZ) {
if (net_debug > 2)
- printk("%s: Missed packet? No Rx after %d Tx and %ld jiffies"
- " status %02x CMR1 %02x.\n", dev->name,
+ printk(KERN_DEBUG "%s: Missed packet? No Rx after %d Tx and "
+ "%ld jiffies status %02x CMR1 %02x.\n", dev->name,
num_tx_since_rx, jiffies - dev->last_rx, status,
(read_nibble(ioaddr, CMR1) >> 3) & 15);
lp->stats.rx_missed_errors++;
break;
} else
break;
- }
+ }
/* This following code fixes a rare (and very difficult to track down)
problem where the adapter forgets its ethernet address. */
int i;
for (i = 0; i < 6; i++)
write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]);
-#ifdef TIMED_CHECKER
- mod_timer(&atp_timer, jiffies+TIMED_CHECKER);
+#if 0 && defined(TIMED_CHECKER)
+ mod_timer(&lp->timer, RUN_AT(TIMED_CHECKER));
#endif
}
/* Tell the adapter that it can go back to using the output line as IRQ. */
- write_reg(ioaddr, CMR2, CMR2_IRQOUT);
+ write_reg(ioaddr, CMR2, CMR2_IRQOUT);
/* Enable the physical interrupt line, which is sure to be low until.. */
outb(Ctrl_SelData + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
/* .. we enable the interrupt sources. */
write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
write_reg_high(ioaddr, IMR, ISRh_RxErr); /* Hmmm, really needed? */
- if (net_debug > 5)
- printk("exiting interrupt.\n");
-
spin_unlock(&lp->lock);
+
+ if (net_debug > 5) printk("exiting interrupt.\n");
return;
}
#ifdef TIMED_CHECKER
/* This following code fixes a rare (and very difficult to track down)
problem where the adapter forgets its ethernet address. */
-static void atp_timed_checker(unsigned long ignored)
+static void atp_timed_checker(unsigned long data)
{
+ struct net_device *dev = (struct net_device *)data;
+ long ioaddr = dev->base_addr;
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int tickssofar = jiffies - lp->last_rx_time;
int i;
- struct net_local *lp = (struct net_local *)atp_timed_dev->priv;
- int ioaddr = atp_timed_dev->base_addr;
spin_lock(&lp->lock);
- for (i = 0; i < 6; i++)
- write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]);
+ if (tickssofar > 2*HZ) {
+#if 1
+ for (i = 0; i < 6; i++)
+ write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]);
+ lp->last_rx_time = jiffies;
+#else
+ for (i = 0; i < 6; i++)
+ if (read_cmd_byte(ioaddr, PAR0 + i) != atp_timed_dev->dev_addr[i])
+ {
+ struct net_local *lp = (struct net_local *)atp_timed_dev->priv;
+ write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]);
+ if (i == 2)
+ lp->stats.tx_errors++;
+ else if (i == 3)
+ lp->stats.tx_dropped++;
+ else if (i == 4)
+ lp->stats.collisions++;
+ else
+ lp->stats.rx_errors++;
+ }
+#endif
+ }
spin_unlock(&lp->lock);
- mod_timer(&atp_timer, jiffies+TIMED_CHECKER);
+ lp->timer.expires = RUN_AT(TIMED_CHECKER);
+ add_timer(&lp->timer);
}
#endif
static void net_rx(struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
- int ioaddr = dev->base_addr;
-#ifdef notdef
- ushort header[4];
-#else
+ long ioaddr = dev->base_addr;
struct rx_header rx_head;
-#endif
/* Process the received packet. */
outb(EOC+MAR, ioaddr + PAR_DATA);
read_block(ioaddr, 8, (unsigned char*)&rx_head, dev->if_port);
if (net_debug > 5)
- printk(" rx_count %04x %04x %04x %04x..", rx_head.pad,
+ printk(KERN_DEBUG " rx_count %04x %04x %04x %04x..", rx_head.pad,
rx_head.rx_count, rx_head.rx_status, rx_head.cur_addr);
if ((rx_head.rx_status & 0x77) != 0x01) {
lp->stats.rx_errors++;
- /* Ackkk! I don't have any documentation on what the error bits mean!
- The best I can do is slap the device around a bit. */
- if (net_debug > 3) printk("%s: Unknown ATP Rx error %04x.\n",
- dev->name, rx_head.rx_status);
- hardware_init(dev);
+ if (rx_head.rx_status & 0x0004) lp->stats.rx_frame_errors++;
+ else if (rx_head.rx_status & 0x0002) lp->stats.rx_crc_errors++;
+ if (net_debug > 3)
+ printk(KERN_DEBUG "%s: Unknown ATP Rx error %04x.\n",
+ dev->name, rx_head.rx_status);
+ if (rx_head.rx_status & 0x0020) {
+ lp->stats.rx_fifo_errors++;
+ write_reg_high(ioaddr, CMR1, CMR1h_TxENABLE);
+ write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE);
+ } else if (rx_head.rx_status & 0x0050)
+ hardware_init(dev);
return;
} else {
- /* Malloc up new buffer. */
- int pkt_len = (rx_head.rx_count & 0x7ff) - 4; /* The "-4" is omits the FCS (CRC). */
+ /* Malloc up new buffer. The "-4" omits the FCS (CRC). */
+ int pkt_len = (rx_head.rx_count & 0x7ff) - 4;
struct sk_buff *skb;
-
- skb = dev_alloc_skb(pkt_len);
+
+ skb = dev_alloc_skb(pkt_len + 2);
if (skb == NULL) {
- printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+ printk(KERN_ERR "%s: Memory squeeze, dropping packet.\n",
+ dev->name);
lp->stats.rx_dropped++;
goto done;
}
skb->dev = dev;
-
- read_block(ioaddr, pkt_len, skb_put(skb,pkt_len), dev->if_port);
- if (net_debug > 6) {
- unsigned char *data = skb->data;
- printk(" data %02x%02x%02x %02x%02x%02x %02x%02x%02x"
- "%02x%02x%02x %02x%02x..",
- data[0], data[1], data[2], data[3], data[4], data[5],
- data[6], data[7], data[8], data[9], data[10], data[11],
- data[12], data[13]);
- }
-
- skb->protocol=eth_type_trans(skb,dev);
+ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
+ read_block(ioaddr, pkt_len, skb_put(skb,pkt_len), dev->if_port);
+ skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
lp->stats.rx_packets++;
+ lp->stats.rx_bytes += pkt_len;
}
done:
write_reg(ioaddr, CMR1, CMR1_NextPkt);
+ lp->last_rx_time = jiffies;
return;
}
-static void read_block(short ioaddr, int length, unsigned char *p, int data_mode)
+static void read_block(long ioaddr, int length, unsigned char *p, int data_mode)
{
if (data_mode <= 3) { /* Mode 0 or 1 */
else
do *p++ = read_byte_mode6(ioaddr); while (--length > 0);
- outb(EOC+HNib+MAR, ioaddr + PAR_DATA);
+ outb(EOC+HNib+MAR, ioaddr + PAR_DATA);
outb(Ctrl_SelData, ioaddr + PAR_CONTROL);
}
/* The inverse routine to net_open(). */
-static int net_close(struct net_device *dev)
+static int
+net_close(struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
- int ioaddr = dev->base_addr;
+ long ioaddr = dev->base_addr;
netif_stop_queue(dev);
+ del_timer_sync(&lp->timer);
+
/* Flush the Tx and disable Rx here. */
lp->addr_mode = CMR2h_OFF;
write_reg_high(ioaddr, CMR2, CMR2h_OFF);
outb(0x00, ioaddr + PAR_CONTROL);
free_irq(dev->irq, dev);
- /* Leave the hardware in a reset state. */
- write_reg_high(ioaddr, CMR1, CMR1h_RESET);
+ /* Reset the ethernet hardware and activate the printer pass-through. */
+ write_reg_high(ioaddr, CMR1, CMR1h_RESET | CMR1h_MUX);
MOD_DEC_USE_COUNT;
/* Get the current statistics. This may be called with the card open or
closed. */
-static struct net_device_stats *net_get_stats(struct net_device *dev)
+static struct net_device_stats *
+net_get_stats(struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
return &lp->stats;
/*
* Set or clear the multicast filter for this adapter.
*/
-
-static void set_multicast_list(struct net_device *dev)
+
+/* The little-endian AUTODIN32 ethernet CRC calculation.
+ This is common code and should be moved to net/core/crc.c */
+static unsigned const ethernet_polynomial_le = 0xedb88320U;
+static inline unsigned ether_crc_le(int length, unsigned char *data)
+{
+ unsigned int crc = 0xffffffff; /* Initial value. */
+ while(--length >= 0) {
+ unsigned char current_octet = *data++;
+ int bit;
+ for (bit = 8; --bit >= 0; current_octet >>= 1) {
+ if ((crc ^ current_octet) & 1) {
+ crc >>= 1;
+ crc ^= ethernet_polynomial_le;
+ } else
+ crc >>= 1;
+ }
+ }
+ return crc;
+}
+
+static void set_rx_mode_8002(struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
- short ioaddr = dev->base_addr;
- int num_addrs=dev->mc_count;
-
- if(dev->flags&(IFF_ALLMULTI|IFF_PROMISC))
- num_addrs=1;
- /*
- * We must make the kernel realise we had to move
- * into promisc mode or we start all out war on
- * the cable. - AC
- */
- if(num_addrs)
- dev->flags|=IFF_PROMISC;
- lp->addr_mode = num_addrs ? CMR2h_PROMISC : CMR2h_Normal;
+ long ioaddr = dev->base_addr;
+
+ if ( dev->mc_count > 0 || (dev->flags & (IFF_ALLMULTI|IFF_PROMISC))) {
+ /* We must make the kernel realise we had to move
+ * into promisc mode or we start all out war on
+ * the cable. - AC
+ */
+ dev->flags|=IFF_PROMISC;
+ lp->addr_mode = CMR2h_PROMISC;
+ } else
+ lp->addr_mode = CMR2h_Normal;
write_reg_high(ioaddr, CMR2, lp->addr_mode);
}
-/* module stuff */
-static int io;
-static struct net_device atp_dev = { init: atp_init };
-MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
-MODULE_DESCRIPTION("Realtek 8002/8012 Pocket Lan Adapter");
-MODULE_PARM(io, "I/O port of the pocket adapter");
+static void set_rx_mode_8012(struct net_device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ long ioaddr = dev->base_addr;
+ unsigned char new_mode, mc_filter[8]; /* Multicast hash filter */
+ int i;
-static int __init atp_init_module(void) {
- atp_dev.base_addr = io;
+ if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
+ new_mode = CMR2h_PROMISC;
+ } else if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) {
+ /* Too many to filter perfectly -- accept all multicasts. */
+ memset(mc_filter, 0xff, sizeof(mc_filter));
+ new_mode = CMR2h_Normal;
+ } else {
+ struct dev_mc_list *mclist;
+
+ memset(mc_filter, 0, sizeof(mc_filter));
+ for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+ i++, mclist = mclist->next)
+ set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f,
+ mc_filter);
+ new_mode = CMR2h_Normal;
+ }
+ lp->addr_mode = new_mode;
+ write_reg(ioaddr, CMR2, CMR2_IRQOUT | 0x04); /* Switch to page 1. */
+ for (i = 0; i < 8; i++)
+ write_reg_byte(ioaddr, i, mc_filter[i]);
+ if (net_debug > 2 || 1) {
+ lp->addr_mode = 1;
+ printk(KERN_DEBUG "%s: Mode %d, setting multicast filter to",
+ dev->name, lp->addr_mode);
+ for (i = 0; i < 8; i++)
+ printk(" %2.2x", mc_filter[i]);
+ printk(".\n");
+ }
- if (register_netdev(&atp_dev) != 0)
- return -EIO;
+ write_reg_high(ioaddr, CMR2, lp->addr_mode);
+ write_reg(ioaddr, CMR2, CMR2_IRQOUT); /* Switch back to page 0 */
+}
- return 0;
+static int __init atp_init_module(void) {
+ if (debug) /* Emit version even if no cards detected. */
+ printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB);
+ return atp_init(NULL);
}
static void __exit atp_cleanup_module(void) {
- unregister_netdev(&atp_dev);
+ struct net_device *next_dev;
+
+ while (root_atp_dev) {
+ next_dev = ((struct net_local *)root_atp_dev->priv)->next_module;
+ unregister_netdev(root_atp_dev);
+ /* No need to release_region(), since we never snarf it. */
+ kfree(root_atp_dev);
+ root_atp_dev = next_dev;
+ }
}
module_init(atp_init_module);
module_exit(atp_cleanup_module);
-
+/* Linux header file for the ATP pocket ethernet adapter. */
+/* v1.09 8/9/2000 becker@scyld.com. */
+
#include <linux/if_ether.h>
#include <linux/types.h>
-#include <asm/io.h>
-
-struct net_local
-{
- struct net_device_stats stats;
- ushort saved_tx_size;
- unsigned char
- re_tx, /* Number of packet retransmissions. */
- tx_unit_busy,
- addr_mode, /* Current Rx filter e.g. promiscuous, etc. */
- pac_cnt_in_tx_buf;
- spinlock_t lock; /* Safety lock */
-};
+/* The header prepended to received packets. */
struct rx_header {
- ushort pad; /* The first read is always corrupted. */
- ushort rx_count;
- ushort rx_status; /* Unknown bit assignments :-<. */
- ushort cur_addr; /* Apparently the current buffer address(?) */
+ ushort pad; /* Pad. */
+ ushort rx_count;
+ ushort rx_status; /* Unknown bit assignments :-<. */
+ ushort cur_addr; /* Apparently the current buffer address(?) */
};
#define PAR_DATA 0
#define PAR_STATUS 1
#define PAR_CONTROL 2
+enum chip_type { RTL8002, RTL8012 };
+
#define Ctrl_LNibRead 0x08 /* LP_PSELECP */
#define Ctrl_HNibRead 0
#define Ctrl_LNibWrite 0x08 /* LP_PSELECP */
enum page0_regs
{
- /* The first six registers hold the ethernet physical station address. */
- PAR0 = 0, PAR1 = 1, PAR2 = 2, PAR3 = 3, PAR4 = 4, PAR5 = 5,
- TxCNT0 = 6, TxCNT1 = 7, /* The transmit byte count. */
- TxSTAT = 8, RxSTAT = 9, /* Tx and Rx status. */
- ISR = 10, IMR = 11, /* Interrupt status and mask. */
- CMR1 = 12, /* Command register 1. */
- CMR2 = 13, /* Command register 2. */
- MAR = 14, /* Memory address register. */
- CMR2_h = 0x1d,
-};
+ /* The first six registers hold the ethernet physical station address. */
+ PAR0 = 0, PAR1 = 1, PAR2 = 2, PAR3 = 3, PAR4 = 4, PAR5 = 5,
+ TxCNT0 = 6, TxCNT1 = 7, /* The transmit byte count. */
+ TxSTAT = 8, RxSTAT = 9, /* Tx and Rx status. */
+ ISR = 10, IMR = 11, /* Interrupt status and mask. */
+ CMR1 = 12, /* Command register 1. */
+ CMR2 = 13, /* Command register 2. */
+ MODSEL = 14, /* Mode select register. */
+ MAR = 14, /* Memory address register (?). */
+ CMR2_h = 0x1d, };
-enum eepage_regs { PROM_CMD = 6, PROM_DATA = 7 }; /* Note that PROM_CMD is in the "high" bits. */
+enum eepage_regs
+{ PROM_CMD = 6, PROM_DATA = 7 }; /* Note that PROM_CMD is in the "high" bits. */
#define ISR_TxOK 0x01
#define ISR_TxErr 0x02
#define ISRh_RxErr 0x11 /* ISR, high nibble */
+#define CMR1h_MUX 0x08 /* Select printer multiplexor on 8012. */
#define CMR1h_RESET 0x04 /* Reset. */
#define CMR1h_RxENABLE 0x02 /* Rx unit enable. */
#define CMR1h_TxENABLE 0x01 /* Tx unit enable. */
/* An inline function used below: it differs from inb() by explicitly return an unsigned
char, saving a truncation. */
-
-extern inline unsigned char inbyte(unsigned short port)
+static inline unsigned char inbyte(unsigned short port)
{
- unsigned char _v;
- __asm__ __volatile__ ("inb %w1,%b0" :"=a" (_v):"d" (port));
- return _v;
+ unsigned char _v;
+ __asm__ __volatile__ ("inb %w1,%b0" :"=a" (_v):"d" (port));
+ return _v;
}
/* Read register OFFSET.
This command should always be terminated with read_end(). */
-
-extern inline unsigned char read_nibble(short port, unsigned char offset)
+static inline unsigned char read_nibble(short port, unsigned char offset)
{
- unsigned char retval;
- outb(EOC+offset, port + PAR_DATA);
- outb(RdAddr+offset, port + PAR_DATA);
- inbyte(port + PAR_STATUS); /* Settling time delay */
- retval = inbyte(port + PAR_STATUS);
- outb(EOC+offset, port + PAR_DATA);
-
- return retval;
+ unsigned char retval;
+ outb(EOC+offset, port + PAR_DATA);
+ outb(RdAddr+offset, port + PAR_DATA);
+ inbyte(port + PAR_STATUS); /* Settling time delay */
+ retval = inbyte(port + PAR_STATUS);
+ outb(EOC+offset, port + PAR_DATA);
+
+ return retval;
}
/* Functions for bulk data read. The interrupt line is always disabled. */
/* Get a byte using read mode 0, reading data from the control lines. */
-
-extern inline unsigned char read_byte_mode0(short ioaddr)
+static inline unsigned char read_byte_mode0(short ioaddr)
{
- unsigned char low_nib;
-
- outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL);
- inbyte(ioaddr + PAR_STATUS);
- low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
- outb(Ctrl_HNibRead, ioaddr + PAR_CONTROL);
- inbyte(ioaddr + PAR_STATUS); /* Settling time delay -- needed! */
- inbyte(ioaddr + PAR_STATUS); /* Settling time delay -- needed! */
- return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
+ unsigned char low_nib;
+
+ outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL);
+ inbyte(ioaddr + PAR_STATUS);
+ low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
+ outb(Ctrl_HNibRead, ioaddr + PAR_CONTROL);
+ inbyte(ioaddr + PAR_STATUS); /* Settling time delay -- needed! */
+ inbyte(ioaddr + PAR_STATUS); /* Settling time delay -- needed! */
+ return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
}
/* The same as read_byte_mode0(), but does multiple inb()s for stability. */
-
-extern inline unsigned char read_byte_mode2(short ioaddr)
+static inline unsigned char read_byte_mode2(short ioaddr)
{
- unsigned char low_nib;
-
- outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL);
- inbyte(ioaddr + PAR_STATUS);
- low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
- outb(Ctrl_HNibRead, ioaddr + PAR_CONTROL);
- inbyte(ioaddr + PAR_STATUS); /* Settling time delay -- needed! */
- return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
+ unsigned char low_nib;
+
+ outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL);
+ inbyte(ioaddr + PAR_STATUS);
+ low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
+ outb(Ctrl_HNibRead, ioaddr + PAR_CONTROL);
+ inbyte(ioaddr + PAR_STATUS); /* Settling time delay -- needed! */
+ return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
}
/* Read a byte through the data register. */
-
-extern inline unsigned char read_byte_mode4(short ioaddr)
+static inline unsigned char read_byte_mode4(short ioaddr)
{
- unsigned char low_nib;
+ unsigned char low_nib;
- outb(RdAddr | MAR, ioaddr + PAR_DATA);
- low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
- outb(RdAddr | HNib | MAR, ioaddr + PAR_DATA);
- return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
+ outb(RdAddr | MAR, ioaddr + PAR_DATA);
+ low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
+ outb(RdAddr | HNib | MAR, ioaddr + PAR_DATA);
+ return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
}
/* Read a byte through the data register, double reading to allow settling. */
-
-extern inline unsigned char read_byte_mode6(short ioaddr)
+static inline unsigned char read_byte_mode6(short ioaddr)
{
- unsigned char low_nib;
-
- outb(RdAddr | MAR, ioaddr + PAR_DATA);
- inbyte(ioaddr + PAR_STATUS);
- low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
- outb(RdAddr | HNib | MAR, ioaddr + PAR_DATA);
- inbyte(ioaddr + PAR_STATUS);
- return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
+ unsigned char low_nib;
+
+ outb(RdAddr | MAR, ioaddr + PAR_DATA);
+ inbyte(ioaddr + PAR_STATUS);
+ low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
+ outb(RdAddr | HNib | MAR, ioaddr + PAR_DATA);
+ inbyte(ioaddr + PAR_STATUS);
+ return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
}
-extern inline void write_reg(short port, unsigned char reg, unsigned char value)
+static inline void
+write_reg(short port, unsigned char reg, unsigned char value)
{
- unsigned char outval;
- outb(EOC | reg, port + PAR_DATA);
- outval = WrAddr | reg;
- outb(outval, port + PAR_DATA);
- outb(outval, port + PAR_DATA); /* Double write for PS/2. */
-
- outval &= 0xf0;
- outval |= value;
- outb(outval, port + PAR_DATA);
- outval &= 0x1f;
- outb(outval, port + PAR_DATA);
- outb(outval, port + PAR_DATA);
-
- outb(EOC | outval, port + PAR_DATA);
+ unsigned char outval;
+ outb(EOC | reg, port + PAR_DATA);
+ outval = WrAddr | reg;
+ outb(outval, port + PAR_DATA);
+ outb(outval, port + PAR_DATA); /* Double write for PS/2. */
+
+ outval &= 0xf0;
+ outval |= value;
+ outb(outval, port + PAR_DATA);
+ outval &= 0x1f;
+ outb(outval, port + PAR_DATA);
+ outb(outval, port + PAR_DATA);
+
+ outb(EOC | outval, port + PAR_DATA);
}
-extern inline void write_reg_high(short port, unsigned char reg, unsigned char value)
+static inline void
+write_reg_high(short port, unsigned char reg, unsigned char value)
{
- unsigned char outval = EOC | HNib | reg;
+ unsigned char outval = EOC | HNib | reg;
- outb(outval, port + PAR_DATA);
- outval &= WrAddr | HNib | 0x0f;
- outb(outval, port + PAR_DATA);
- outb(outval, port + PAR_DATA); /* Double write for PS/2. */
+ outb(outval, port + PAR_DATA);
+ outval &= WrAddr | HNib | 0x0f;
+ outb(outval, port + PAR_DATA);
+ outb(outval, port + PAR_DATA); /* Double write for PS/2. */
- outval = WrAddr | HNib | value;
- outb(outval, port + PAR_DATA);
- outval &= HNib | 0x0f; /* HNib | value */
- outb(outval, port + PAR_DATA);
- outb(outval, port + PAR_DATA);
+ outval = WrAddr | HNib | value;
+ outb(outval, port + PAR_DATA);
+ outval &= HNib | 0x0f; /* HNib | value */
+ outb(outval, port + PAR_DATA);
+ outb(outval, port + PAR_DATA);
- outb(EOC | HNib | outval, port + PAR_DATA);
+ outb(EOC | HNib | outval, port + PAR_DATA);
}
/* Write a byte out using nibble mode. The low nibble is written first. */
-
-extern inline void write_reg_byte(short port, unsigned char reg, unsigned char value)
+static inline void
+write_reg_byte(short port, unsigned char reg, unsigned char value)
{
- unsigned char outval;
- outb(EOC | reg, port + PAR_DATA); /* Reset the address register. */
- outval = WrAddr | reg;
- outb(outval, port + PAR_DATA);
- outb(outval, port + PAR_DATA); /* Double write for PS/2. */
-
- outb((outval & 0xf0) | (value & 0x0f), port + PAR_DATA);
- outb(value & 0x0f, port + PAR_DATA);
- value >>= 4;
- outb(value, port + PAR_DATA);
- outb(0x10 | value, port + PAR_DATA);
- outb(0x10 | value, port + PAR_DATA);
-
- outb(EOC | value, port + PAR_DATA); /* Reset the address register. */
+ unsigned char outval;
+ outb(EOC | reg, port + PAR_DATA); /* Reset the address register. */
+ outval = WrAddr | reg;
+ outb(outval, port + PAR_DATA);
+ outb(outval, port + PAR_DATA); /* Double write for PS/2. */
+
+ outb((outval & 0xf0) | (value & 0x0f), port + PAR_DATA);
+ outb(value & 0x0f, port + PAR_DATA);
+ value >>= 4;
+ outb(value, port + PAR_DATA);
+ outb(0x10 | value, port + PAR_DATA);
+ outb(0x10 | value, port + PAR_DATA);
+
+ outb(EOC | value, port + PAR_DATA); /* Reset the address register. */
}
/*
* It should only be needed when there is skew between the individual data
* lines.
*/
-
-extern inline void write_byte_mode0(short ioaddr, unsigned char value)
+static inline void write_byte_mode0(short ioaddr, unsigned char value)
{
- outb(value & 0x0f, ioaddr + PAR_DATA);
- outb((value>>4) | 0x10, ioaddr + PAR_DATA);
+ outb(value & 0x0f, ioaddr + PAR_DATA);
+ outb((value>>4) | 0x10, ioaddr + PAR_DATA);
}
-extern inline void write_byte_mode1(short ioaddr, unsigned char value)
+static inline void write_byte_mode1(short ioaddr, unsigned char value)
{
- outb(value & 0x0f, ioaddr + PAR_DATA);
- outb(Ctrl_IRQEN | Ctrl_LNibWrite, ioaddr + PAR_CONTROL);
- outb((value>>4) | 0x10, ioaddr + PAR_DATA);
- outb(Ctrl_IRQEN | Ctrl_HNibWrite, ioaddr + PAR_CONTROL);
+ outb(value & 0x0f, ioaddr + PAR_DATA);
+ outb(Ctrl_IRQEN | Ctrl_LNibWrite, ioaddr + PAR_CONTROL);
+ outb((value>>4) | 0x10, ioaddr + PAR_DATA);
+ outb(Ctrl_IRQEN | Ctrl_HNibWrite, ioaddr + PAR_CONTROL);
}
/* Write 16bit VALUE to the packet buffer: the same as above just doubled. */
-
-extern inline void write_word_mode0(short ioaddr, unsigned short value)
+static inline void write_word_mode0(short ioaddr, unsigned short value)
{
- outb(value & 0x0f, ioaddr + PAR_DATA);
- value >>= 4;
- outb((value & 0x0f) | 0x10, ioaddr + PAR_DATA);
- value >>= 4;
- outb(value & 0x0f, ioaddr + PAR_DATA);
- value >>= 4;
- outb((value & 0x0f) | 0x10, ioaddr + PAR_DATA);
+ outb(value & 0x0f, ioaddr + PAR_DATA);
+ value >>= 4;
+ outb((value & 0x0f) | 0x10, ioaddr + PAR_DATA);
+ value >>= 4;
+ outb(value & 0x0f, ioaddr + PAR_DATA);
+ value >>= 4;
+ outb((value & 0x0f) | 0x10, ioaddr + PAR_DATA);
}
/* EEPROM_Ctrl bits. */
#define EE_DATA_WRITE 0x01 /* EEPROM chip data in. */
#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
+/* Delay between EEPROM clock transitions. */
+#define eeprom_delay(ticks) \
+do { int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
+
/* The EEPROM commands include the alway-set leading bit. */
#define EE_WRITE_CMD(offset) (((5 << 6) + (offset)) << 17)
#define EE_READ(offset) (((6 << 6) + (offset)) << 17)
*
*/
-#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/malloc.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/uaccess.h>
-#include <linux/errno.h>
-
+#include <linux/kernel.h>
#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-
+#include <linux/init.h>
#include <linux/if_bonding.h>
typedef struct slave
return NOTIFY_DONE;
}
-struct notifier_block bond_netdev_notifier={
+static struct notifier_block bond_netdev_notifier={
bond_event,
NULL,
0
};
-int __init bond_init(struct net_device *dev)
+static int __init bond_init(struct net_device *dev)
{
bonding_t *bond;
return &bond->stats;
}
-#ifdef MODULE
-
static struct net_device dev_bond = {
"",
0, 0, 0, 0,
0x0, 0,
0, 0, 0, NULL, bond_init };
-int init_module(void)
+static int __init bonding_init(void)
{
/* Find a name for this unit */
int err=dev_alloc_name(&dev_bond,"bond%d");
return 0;
}
-void cleanup_module(void)
+static void __exit bonding_exit(void)
{
unregister_netdevice_notifier(&bond_netdev_notifier);
kfree(dev_bond.priv);
}
-#endif /* MODULE */
+
+module_init(bonding_init);
+module_exit(bonding_exit);
/*
* Local variables:
/* To have statistics (just packets sent) define this */
#include <linux/config.h>
-#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/malloc.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <linux/errno.h>
-
+#include <linux/kernel.h>
#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
+#include <linux/init.h>
static int dummy_xmit(struct sk_buff *skb, struct net_device *dev);
static struct net_device_stats *dummy_get_stats(struct net_device *dev);
}
#endif
-int __init dummy_init(struct net_device *dev)
+static int __init dummy_init(struct net_device *dev)
{
/* Initialize the device structure. */
dev->hard_start_xmit = dummy_xmit;
static struct net_device_stats *dummy_get_stats(struct net_device *dev)
{
- struct net_device_stats *stats = (struct net_device_stats *) dev->priv;
- return stats;
-}
-
-#ifdef MODULE
-
-static int __init dummy_probe(struct net_device *dev)
-{
- dummy_init(dev);
- return 0;
+ return dev->priv;
}
-static struct net_device dev_dummy = {
- "",
- 0, 0, 0, 0,
- 0x0, 0,
- 0, 0, 0, NULL, dummy_probe };
+static struct net_device dev_dummy = { init: dummy_init };
-int init_module(void)
+static int __init dummy_init_module(void)
{
/* Find a name for this unit */
int err=dev_alloc_name(&dev_dummy,"dummy%d");
return 0;
}
-void cleanup_module(void)
+static void __exit dummy_cleanup_module(void)
{
unregister_netdev(&dev_dummy);
kfree(dev_dummy.priv);
- dev_dummy.priv = NULL;
+
+ memset(&dev_dummy, 0, sizeof(dev_dummy));
+ dev_dummy.init = dummy_init;
}
-#endif /* MODULE */
+
+module_init(dummy_init_module);
+module_exit(dummy_cleanup_module);
PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ID1030,
PCI_ANY_ID, PCI_ANY_ID, },
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82820FW_4,
+ PCI_ANY_ID, PCI_ANY_ID, },
{ 0,}
};
MODULE_DEVICE_TABLE(pci, eepro100_pci_tbl);
*/
#include <linux/module.h>
-
#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/malloc.h>
-#include <linux/string.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/uaccess.h>
-#include <linux/errno.h>
#include <linux/init.h>
-
+#include <linux/timer.h>
#include <linux/netdevice.h>
+
#include <linux/if.h>
#include <linux/if_arp.h>
-#include <linux/timer.h>
-
#include <linux/if_eql.h>
+#include <asm/uaccess.h>
+
+
#ifndef EQL_DEBUG
/* #undef EQL_DEBUG -* print nothing at all, not even a boot-banner */
/* #define EQL_DEBUG 1 -* print only the boot-banner */
#endif
static unsigned int eql_debug = EQL_DEBUG;
-int eql_init(struct net_device *dev); /* */
+static int eql_init(struct net_device *dev); /* */
static int eql_open(struct net_device *dev); /* */
static int eql_close(struct net_device *dev); /* */
static int eql_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); /* */
---------------------------------------------------------
*/
-int __init eql_init(struct net_device *dev)
+static int __init eql_init(struct net_device *dev)
{
static unsigned version_printed = 0;
/* static unsigned num_masters = 0; */
equalizer_t *eql = (equalizer_t *) dev->priv;
slave_queue_t *new_queue;
+ MOD_INC_USE_COUNT;
+
#ifdef EQL_DEBUG
if (eql_debug >= 5)
printk ("%s: open\n", dev->name);
eql->timer_on = 1;
add_timer (&eql->timer);
- MOD_INC_USE_COUNT;
return 0;
}
- return 1;
+ MOD_DEC_USE_COUNT;
+ return -ENOMEM;
}
struct net_device *master_dev;
struct net_device *slave_dev;
slaving_request_t srq;
- int err;
- err = copy_from_user(&srq, srqp, sizeof (slaving_request_t));
- if (err)
+ if (copy_from_user(&srq, srqp, sizeof (slaving_request_t)))
{
#ifdef EQL_DEBUG
if (eql_debug >= 20)
printk ("EQL enslave: error detected by copy_from_user\n");
#endif
- return err;
+ return -EFAULT;
}
#ifdef EQL_DEBUG
struct net_device *master_dev;
struct net_device *slave_dev;
slaving_request_t srq;
- int err;
- err = copy_from_user(&srq, srqp, sizeof (slaving_request_t));
- if (err)
- return err;
+ if (copy_from_user(&srq, srqp, sizeof (slaving_request_t)))
+ return -EFAULT;
#ifdef EQL_DEBUG
if (eql_debug >= 20)
equalizer_t *eql;
struct net_device *slave_dev;
slave_config_t sc;
- int err;
- err = copy_from_user (&sc, scp, sizeof (slave_config_t));
- if (err)
- return err;
+ if (copy_from_user (&sc, scp, sizeof (slave_config_t)))
+ return -EFAULT;
#ifdef EQL_DEBUG
if (eql_debug >= 20)
if (slave != 0)
{
sc.priority = slave->priority;
- err = verify_area(VERIFY_WRITE, (void *)scp, sizeof (slave_config_t));
- if (err)
- return err;
- copy_to_user (scp, &sc, sizeof (slave_config_t));
+ if (copy_to_user (scp, &sc, sizeof (slave_config_t)))
+ return -EFAULT;
return 0;
}
}
equalizer_t *eql;
struct net_device *slave_dev;
slave_config_t sc;
- int err;
- err = copy_from_user (&sc, scp, sizeof (slave_config_t));
- if (err)
- return err;
+ if (copy_from_user (&sc, scp, sizeof (slave_config_t)))
+ return -EFAULT;
#ifdef EQL_DEBUG
if (eql_debug >= 20)
if ( eql_is_master (dev) )
{
- int err;
eql = (equalizer_t *) dev->priv;
mc.max_slaves = eql->max_slaves;
mc.min_slaves = eql->min_slaves;
- err = copy_to_user (mcp, &mc, sizeof (master_config_t));
- if (err)
- return err;
+ if (copy_to_user (mcp, &mc, sizeof (master_config_t)))
+ return -EFAULT;
return 0;
}
return -EINVAL;
{
equalizer_t *eql;
master_config_t mc;
- int err;
- err = copy_from_user (&mc, mcp, sizeof (master_config_t));
- if (err)
- return err;
+ if (copy_from_user (&mc, mcp, sizeof (master_config_t)))
+ return -EFAULT;
#if EQL_DEBUG
if (eql_debug >= 20)
printk ("%s: set master config\n", dev->name);
slave = (slave_t *) kmalloc (sizeof (slave_t), GFP_KERNEL);
if (slave)
- {
memset(slave, 0, sizeof (slave_t));
- return slave;
- }
- return 0;
+ return slave;
}
slave_t *tail_slave;
queue = (slave_queue_t *) kmalloc (sizeof (slave_queue_t), GFP_KERNEL);
- if (queue == NULL)
- return 0;
- memset (queue, 0, sizeof (slave_queue_t));
+ if (!queue)
+ goto err_out;
+
head_slave = eql_new_slave ();
+ if (!head_slave)
+ goto err_out_queue;
+
tail_slave = eql_new_slave ();
+ if (!tail_slave)
+ goto err_out_hs;
- if ( head_slave != 0 &&
- tail_slave != 0 )
- {
- head_slave->next = tail_slave;
- tail_slave->next = 0;
- queue->head = head_slave;
- queue->num_slaves = 0;
- queue->master_dev = dev;
- }
- else
- {
- if (head_slave)
- kfree(head_slave);
- if (tail_slave)
- kfree(tail_slave);
- kfree (queue);
- return 0;
- }
+ memset (queue, 0, sizeof (slave_queue_t));
+
+ head_slave->next = tail_slave;
+ tail_slave->next = 0;
+ queue->head = head_slave;
+ queue->num_slaves = 0;
+ queue->master_dev = dev;
return queue;
+
+err_out_hs:
+ kfree (head_slave);
+err_out_queue:
+ kfree (queue);
+err_out:
+ return NULL;
}
}
}
-#ifdef MODULE
static struct net_device dev_eql =
{
- "eql", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, eql_init
+ name: "eql",
+ init: eql_init,
};
-int init_module(void)
+static int __init eql_init_module(void)
{
if (register_netdev(&dev_eql) != 0) {
printk("eql: register_netdev() returned non-zero.\n");
return 0;
}
-void cleanup_module(void)
+static void __exit eql_cleanup_module(void)
{
kfree(((equalizer_t *)dev_eql.priv)->stats );
kfree(dev_eql.priv);
unregister_netdev(&dev_eql);
}
-#endif /* MODULE */
+
+module_init(eql_init_module);
+module_exit(eql_cleanup_module);
/*
* Local Variables:
--- /dev/null
+/* hamachi.c: A Packet Engines GNIC-II Gigabit Ethernet driver for Linux. */
+/*
+ Written 1998-2000 by Donald Becker.
+ Updates 2000 by Keith Underwood.
+
+ This software may be used and distributed according to the terms of
+ the GNU Public License (GPL), incorporated herein by reference.
+ Drivers based on or derived from this code fall under the GPL and must
+ retain the authorship, copyright and license notice. This file is not
+ a complete program and may only be used when the entire operating
+ system is licensed under the GPL.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+ This driver is for the Packet Engines GNIC-II PCI Gigabit Ethernet
+ adapter.
+
+ Support and updates available at
+ http://www.scyld.com/network/hamachi.html
+ or
+ http://www.parl.clemson.edu/~keithu/hamachi.html
+
+ For best viewing, set your tabs to 3.
+
+*/
+
+static const char *version =
+"hamachi.c:v1.01 5/16/2000 Written by Donald Becker\n"
+" Some modifications by Eric kasten <kasten@nscl.msu.edu>\n"
+" Further modifications by Keith Underwood <keithu@parl.clemson.edu>\n"
+" Support by many others\n"
+" http://www.scyld.com/network/hamachi.html\n"
+" or\n"
+" http://www.parl.clemson.edu/~keithu/drivers/hamachi.html\n";
+
+
+/* A few user-configurable values. */
+
+static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */
+#define final_version
+#define hamachi_debug debug
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+static int max_interrupt_work = 40;
+static int mtu = 0;
+/* Default values selected by testing on a dual processor PIII-450 */
+/* These six interrupt control parameters may be set directly when loading the
+ * module, or through the rx_params and tx_params variables
+ */
+static int max_rx_latency = 0x11;
+static int max_rx_gap = 0x05;
+static int min_rx_pkt = 0x18;
+static int max_tx_latency = 0x00;
+static int max_tx_gap = 0x00;
+static int min_tx_pkt = 0x30;
+
+/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
+ -Setting to > 1518 causes all frames to be copied
+ -Setting to 0 disables copies
+*/
+static int rx_copybreak = 0;
+
+/* An override for the hardware detection of bus width.
+ Set to 1 to force 32 bit PCI bus detection. Set to 4 to force 64 bit.
+ Add 2 to disable parity detection.
+*/
+static int force32 = 0;
+
+
+/* Used to pass the media type, etc.
+ These exist for driver interoperability.
+ No media types are currently defined.
+ - The lower 4 bits are reserved for the media type.
+ - The next three bits may be set to one of the following:
+ 0x00000000 : Autodetect PCI bus
+ 0x00000010 : Force 32 bit PCI bus
+ 0x00000020 : Disable parity detection
+ 0x00000040 : Force 64 bit PCI bus
+ Default is autodetect
+ - The next bit can be used to force half-duplex. This is a bad
+ idea since no known implementations implement half-duplex, and,
+ in general, half-duplex for gigabit ethernet is a bad idea.
+ 0x00000080 : Force half-duplex
+ Default is full-duplex.
+ - In the original driver, the ninth bit could be used to force
+ full-duplex. Maintain that for compatibility
+ 0x00000200 : Force full-duplex
+*/
+#define MAX_UNITS 8 /* More are supported, limit only on options */
+static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+/* The Hamachi chipset supports 3 parameters each for Rx and Tx
+ * interruput management. Parameters will be loaded as specified into
+ * the TxIntControl and RxIntControl registers.
+ *
+ * The registers are arranged as follows:
+ * 23 - 16 15 - 8 7 - 0
+ * _________________________________
+ * | min_pkt | max_gap | max_latency |
+ * ---------------------------------
+ * min_pkt : The minimum number of packets processed between
+ * interrupts.
+ * max_gap : The maximum inter-packet gap in units of 8.192 us
+ * max_latency : The absolute time between interrupts in units of 8.192 us
+ *
+ */
+static int rx_params[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int tx_params[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+
+/* Operational parameters that are set at compile time. */
+
+/* Keep the ring sizes a power of two for compile efficiency.
+ The compiler will convert <unsigned>'%'<2^N> into a bit mask.
+ Making the Tx ring too large decreases the effectiveness of channel
+ bonding and packet priority.
+ There are no ill effects from too-large receive rings, except for
+ excessive memory usage */
+/* Empirically it appears that the Tx ring needs to be a little bigger
+ for these Gbit adapters or you get into an overrun condition really
+ easily. Also, things appear to work a bit better in back-to-back
+ configurations if the Rx ring is 8 times the size of the Tx ring
+*/
+#define TX_RING_SIZE 64
+#define RX_RING_SIZE 512
+
+/*
+ * Enable mii_ioctl. Added interrupt coalescing parameter adjustment.
+ * 2/19/99 Pete Wyckoff <wyckoff@ca.sandia.gov>
+ */
+#define HAVE_PRIVATE_IOCTL
+
+/* play with 64-bit addrlen; seems to be a teensy bit slower --pw */
+/* #define ADDRLEN 64 */
+
+/*
+ * RX_CHECKSUM turns on card-generated receive checksum generation for
+ * TCP and UDP packets. Otherwise the upper layers do the calculation.
+ * TX_CHECKSUM won't do anything too useful, even if it works. There's no
+ * easy mechanism by which to tell the TCP/UDP stack that it need not
+ * generate checksums for this device. But if somebody can find a way
+ * to get that to work, most of the card work is in here already.
+ * 3/10/1999 Pete Wyckoff <wyckoff@ca.sandia.gov>
+ */
+#undef TX_CHECKSUM
+#define RX_CHECKSUM
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT (5*HZ)
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/time.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+
+#include <asm/processor.h> /* Processor type for cache alignment. */
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
+#include <asm/cache.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/delay.h>
+
+/* IP_MF appears to be only defined in <netinet/ip.h>, however,
+ we need it for hardware checksumming support. FYI... some of
+ the definitions in <netinet/ip.h> conflict/duplicate those in
+ other linux headers causing many compiler warnings.
+*/
+#ifndef IP_MF
+ #define IP_MF 0x2000 /* IP more frags from <netinet/ip.h> */
+#endif
+
+/* Define IP_OFFSET to be IPOPT_OFFSET */
+#ifndef IP_OFFSET
+ #ifdef IPOPT_OFFSET
+ #define IP_OFFSET IPOPT_OFFSET
+ #else
+ #define IP_OFFSET 2
+ #endif
+#endif
+
+#define RUN_AT(x) (jiffies + (x))
+
+/* Condensed bus+endian portability operations. */
+#if ADDRLEN == 64
+#define virt_to_desc(addr) cpu_to_le64(virt_to_bus(addr))
+#else
+#define virt_to_desc(addr) cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+#endif
+
+
+/*
+ Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the Packet Engines "Hamachi"
+Gigabit Ethernet chip. The only PCA currently supported is the GNIC-II 64-bit
+66Mhz PCI card.
+
+II. Board-specific settings
+
+No jumpers exist on the board. The chip supports software correction of
+various motherboard wiring errors, however this driver does not support
+that feature.
+
+III. Driver operation
+
+IIIa. Ring buffers
+
+The Hamachi uses a typical descriptor based bus-master architecture.
+The descriptor list is similar to that used by the Digital Tulip.
+This driver uses two statically allocated fixed-size descriptor lists
+formed into rings by a branch from the final descriptor to the beginning of
+the list. The ring sizes are set at compile time by RX/TX_RING_SIZE.
+
+This driver uses a zero-copy receive and transmit scheme similar my other
+network drivers.
+The driver allocates full frame size skbuffs for the Rx ring buffers at
+open() time and passes the skb->data field to the Hamachi as receive data
+buffers. When an incoming frame is less than RX_COPYBREAK bytes long,
+a fresh skbuff is allocated and the frame is copied to the new skbuff.
+When the incoming frame is larger, the skbuff is passed directly up the
+protocol stack and replaced by a newly allocated skbuff.
+
+The RX_COPYBREAK value is chosen to trade-off the memory wasted by
+using a full-sized skbuff for small frames vs. the copying costs of larger
+frames. Gigabit cards are typically used on generously configured machines
+and the underfilled buffers have negligible impact compared to the benefit of
+a single allocation size, so the default value of zero results in never
+copying packets.
+
+IIIb/c. Transmit/Receive Structure
+
+The Rx and Tx descriptor structure are straight-forward, with no historical
+baggage that must be explained. Unlike the awkward DBDMA structure, there
+are no unused fields or option bits that had only one allowable setting.
+
+Two details should be noted about the descriptors: The chip supports both 32
+bit and 64 bit address structures, and the length field is overwritten on
+the receive descriptors. The descriptor length is set in the control word
+for each channel. The development driver uses 32 bit addresses only, however
+64 bit addresses may be enabled for 64 bit architectures e.g. the Alpha.
+
+IIId. Synchronization
+
+This driver is very similar to my other network drivers.
+The driver runs as two independent, single-threaded flows of control. One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag. The other thread is the interrupt handler, which is single
+threaded by the hardware and other software.
+
+The send packet thread has partial control over the Tx ring and 'dev->tbusy'
+flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next
+queue slot is empty, it clears the tbusy flag when finished otherwise it sets
+the 'hmp->tx_full' flag.
+
+The interrupt handler has exclusive control over the Rx ring and records stats
+from the Tx ring. After reaping the stats, it marks the Tx queue entry as
+empty by incrementing the dirty_tx mark. Iff the 'hmp->tx_full' flag is set, it
+clears both the tx_full and tbusy flags.
+
+IV. Notes
+
+Thanks to Kim Stearns of Packet Engines for providing a pair of GNIC-II boards.
+
+IVb. References
+
+Hamachi Engineering Design Specification, 5/15/97
+(Note: This version was marked "Confidential".)
+
+IVc. Errata
+
+None noted.
+
+V. Recent Changes
+
+01/15/1999 EPK Enlargement of the TX and RX ring sizes. This appears
+ to help avoid some stall conditions -- this needs further research.
+
+01/15/1999 EPK Creation of the hamachi_tx function. This function cleans
+ the Tx ring and is called from hamachi_start_xmit (this used to be
+ called from hamachi_interrupt but it tends to delay execution of the
+ interrupt handler and thus reduce bandwidth by reducing the latency
+ between hamachi_rx()'s). Notably, some modification has been made so
+ that the cleaning loop checks only to make sure that the DescOwn bit
+ isn't set in the status flag since the card is not required
+ to set the entire flag to zero after processing.
+
+01/15/1999 EPK In the hamachi_start_tx function, the Tx ring full flag is
+ checked before attempting to add a buffer to the ring. If the ring is full
+ an attempt is made to free any dirty buffers and thus find space for
+ the new buffer or the function returns non-zero which should case the
+ scheduler to reschedule the buffer later.
+
+01/15/1999 EPK Some adjustments were made to the chip intialization.
+ End-to-end flow control should now be fully active and the interrupt
+ algorithm vars have been changed. These could probably use further tuning.
+
+01/15/1999 EPK Added the max_{rx,tx}_latency options. These are used to
+ set the rx and tx latencies for the Hamachi interrupts. If you're having
+ problems with network stalls, try setting these to higher values.
+ Valid values are 0x00 through 0xff.
+
+01/15/1999 EPK In general, the overall bandwidth has increased and
+ latencies are better (sometimes by a factor of 2). Stalls are rare at
+ this point, however there still appears to be a bug somewhere between the
+ hardware and driver. TCP checksum errors under load also appear to be
+ eliminated at this point.
+
+01/18/1999 EPK Ensured that the DescEndRing bit was being set on both the
+ Rx and Tx rings. This appears to have been affecting whether a particular
+ peer-to-peer connection would hang under high load. I believe the Rx
+ rings was typically getting set correctly, but the Tx ring wasn't getting
+ the DescEndRing bit set during initialization. ??? Does this mean the
+ hamachi card is using the DescEndRing in processing even if a particular
+ slot isn't in use -- hypothetically, the card might be searching the
+ entire Tx ring for slots with the DescOwn bit set and then processing
+ them. If the DescEndRing bit isn't set, then it might just wander off
+ through memory until it hits a chunk of data with that bit set
+ and then looping back.
+
+02/09/1999 EPK Added Michel Mueller's TxDMA Interrupt and Tx-timeout
+ problem (TxCmd and RxCmd need only to be set when idle or stopped.
+
+02/09/1999 EPK Added code to check/reset dev->tbusy in hamachi_interrupt.
+ (Michel Mueller pointed out the ``permanently busy'' potential
+ problem here).
+
+02/22/1999 EPK Added Pete Wyckoff's ioctl to control the Tx/Rx latencies.
+
+02/23/1999 EPK Verified that the interrupt status field bits for Tx were
+ incorrectly defined and corrected (as per Michel Mueller).
+
+02/23/1999 EPK Corrected the Tx full check to check that at least 4 slots
+ were available before reseting the tbusy and tx_full flags
+ (as per Michel Mueller).
+
+03/11/1999 EPK Added Pete Wyckoff's hardware checksumming support.
+
+12/31/1999 KDU Cleaned up assorted things and added Don's code to force
+32 bit.
+
+02/20/2000 KDU Some of the control was just plain odd. Cleaned up the
+hamachi_start_xmit() and hamachi_interrupt() code. There is still some
+re-structuring I would like to do.
+
+03/01/2000 KDU Experimenting with a WIDE range of interrupt mitigation
+parameters on a dual P3-450 setup yielded the new default interrupt
+mitigation parameters. Tx should interrupt VERY infrequently due to
+Eric's scheme. Rx should be more often...
+
+03/13/2000 KDU Added a patch to make the Rx Checksum code interact
+nicely with non-linux machines.
+
+03/13/2000 KDU Experimented with some of the configuration values:
+
+ -It seems that enabling PCI performance commands for descriptors
+ (changing RxDMACtrl and TxDMACtrl lower nibble from 5 to D) has minimal
+ performance impact for any of my tests. (ttcp, netpipe, netperf) I will
+ leave them that way until I hear further feedback.
+
+ -Increasing the PCI_LATENCY_TIMER to 130
+ (2 + (burst size of 128 * (0 wait states + 1))) seems to slightly
+ degrade performance. Leaving default at 64 pending further information.
+
+03/14/2000 KDU Further tuning:
+
+ -adjusted boguscnt in hamachi_rx() to depend on interrupt
+ mitigation parameters chosen.
+
+ -Selected a set of interrupt parameters based on some extensive testing.
+ These may change with more testing.
+
+TO DO:
+
+-Consider borrowing from the acenic driver code to check PCI_COMMAND for
+PCI_COMMAND_INVALIDATE. Set maximum burst size to cache line size in
+that case.
+
+-fix the reset procedure. It doesn't quite work.
+*/
+
+/* A few values that may be tweaked. */
+/* Size of each temporary Rx buffer, calculated as:
+ * 1518 bytes (ethernet packet) + 2 bytes (to get 8 byte alignment for
+ * the card) + 8 bytes of status info + 8 bytes for the Rx Checksum +
+ * 2 more because we use skb_reserve.
+ */
+#define PKT_BUF_SZ 1538
+
+/* For now, this is going to be set to the maximum size of an ethernet
+ * packet. Eventually, we may want to make it a variable that is
+ * related to the MTU
+ */
+#define MAX_FRAME_SIZE 1518
+
+/* The rest of these values should never change. */
+
+static void hamachi_timer(unsigned long data);
+
+enum capability_flags {CanHaveMII=1, };
+static struct chip_info {
+ u16 vendor_id, device_id, device_id_mask, pad;
+ const char *name;
+ void (*media_timer)(unsigned long data);
+ int flags;
+} chip_tbl[] = {
+ {0x1318, 0x0911, 0xffff, 0, "Hamachi GNIC-II", hamachi_timer, 0},
+ {0,},
+};
+
+/* Offsets to the Hamachi registers. Various sizes. */
+enum hamachi_offsets {
+ TxDMACtrl=0x00, TxCmd=0x04, TxStatus=0x06, TxPtr=0x08, TxCurPtr=0x10,
+ RxDMACtrl=0x20, RxCmd=0x24, RxStatus=0x26, RxPtr=0x28, RxCurPtr=0x30,
+ PCIClkMeas=0x060, MiscStatus=0x066, ChipRev=0x68, ChipReset=0x06B,
+ LEDCtrl=0x06C, VirtualJumpers=0x06D, GPIO=0x6E,
+ TxChecksum=0x074, RxChecksum=0x076,
+ TxIntrCtrl=0x078, RxIntrCtrl=0x07C,
+ InterruptEnable=0x080, InterruptClear=0x084, IntrStatus=0x088,
+ EventStatus=0x08C,
+ MACCnfg=0x0A0, FrameGap0=0x0A2, FrameGap1=0x0A4,
+ /* See enum MII_offsets below. */
+ MACCnfg2=0x0B0, RxDepth=0x0B8, FlowCtrl=0x0BC, MaxFrameSize=0x0CE,
+ AddrMode=0x0D0, StationAddr=0x0D2,
+ /* Gigabit AutoNegotiation. */
+ ANCtrl=0x0E0, ANStatus=0x0E2, ANXchngCtrl=0x0E4, ANAdvertise=0x0E8,
+ ANLinkPartnerAbility=0x0EA,
+ EECmdStatus=0x0F0, EEData=0x0F1, EEAddr=0x0F2,
+ FIFOcfg=0x0F8,
+};
+
+/* Offsets to the MII-mode registers. */
+enum MII_offsets {
+ MII_Cmd=0xA6, MII_Addr=0xA8, MII_Wr_Data=0xAA, MII_Rd_Data=0xAC,
+ MII_Status=0xAE,
+};
+
+/* Bits in the interrupt status/mask registers. */
+enum intr_status_bits {
+ IntrRxDone=0x01, IntrRxPCIFault=0x02, IntrRxPCIErr=0x04,
+ IntrTxDone=0x100, IntrTxPCIFault=0x200, IntrTxPCIErr=0x400,
+ LinkChange=0x10000, NegotiationChange=0x20000, StatsMax=0x40000, };
+
+/* The Hamachi Rx and Tx buffer descriptors. */
+struct hamachi_desc {
+ u32 status_n_length;
+#if ADDRLEN == 64
+ u32 pad;
+ u64 addr;
+#else
+ u32 addr;
+#endif
+};
+
+/* Bits in hamachi_desc.status_n_length */
+enum desc_status_bits {
+ DescOwn=0x80000000, DescEndPacket=0x40000000, DescEndRing=0x20000000,
+ DescIntr=0x10000000,
+};
+
+#define PRIV_ALIGN 15 /* Required alignment mask */
+struct hamachi_private {
+ /* Descriptor rings first for alignment. Tx requires a second descriptor
+ for status. */
+ struct hamachi_desc rx_ring[RX_RING_SIZE];
+ struct hamachi_desc tx_ring[TX_RING_SIZE];
+ /* The addresses of receive-in-place skbuffs. */
+ struct sk_buff* rx_skbuff[RX_RING_SIZE];
+ /* The saved address of a sent-in-place packet/buffer, for skfree(). */
+ struct sk_buff* tx_skbuff[TX_RING_SIZE];
+ struct net_device_stats stats;
+ struct timer_list timer; /* Media selection timer. */
+ /* Frequently used and paired value: keep adjacent for cache effect. */
+ spinlock_t lock;
+ int chip_id;
+ struct hamachi_desc *rx_head_desc;
+ unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */
+ unsigned int cur_tx, dirty_tx;
+ unsigned int rx_buf_sz; /* Based on MTU+slack. */
+ unsigned int tx_full:1; /* The Tx queue is full. */
+ unsigned int full_duplex:1; /* Full-duplex operation requested. */
+ unsigned int duplex_lock:1;
+ unsigned int medialock:1; /* Do not sense media. */
+ unsigned int default_port:4; /* Last dev->if_port value. */
+ /* MII transceiver section. */
+ int mii_cnt; /* MII device addresses. */
+ u16 advertising; /* NWay media advertisement */
+ unsigned char phys[2]; /* MII device addresses. */
+ u_int32_t rx_int_var, tx_int_var; /* interrupt control variables */
+ u_int32_t option; /* Hold on to a copy of the options */
+ u_int8_t pad[16]; /* Used for 32-byte alignment */
+};
+
+MODULE_AUTHOR("Donald Becker <becker@scyld.com>, Eric Kasten <kasten@nscl.msu.edu>, Keith Underwood <keithu@parl.clemson.edu>");
+MODULE_DESCRIPTION("Packet Engines 'Hamachi' GNIC-II Gigabit Ethernet driver");
+MODULE_PARM(max_interrupt_work, "i");
+MODULE_PARM(mtu, "i");
+MODULE_PARM(debug, "i");
+MODULE_PARM(min_rx_pkt, "i");
+MODULE_PARM(max_rx_gap, "i");
+MODULE_PARM(max_rx_latency, "i");
+MODULE_PARM(min_tx_pkt, "i");
+MODULE_PARM(max_tx_gap, "i");
+MODULE_PARM(max_tx_latency, "i");
+MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM(rx_params, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(tx_params, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(force32, "i");
+
+static int read_eeprom(long ioaddr, int location);
+static int mdio_read(long ioaddr, int phy_id, int location);
+static void mdio_write(long ioaddr, int phy_id, int location, int value);
+static int hamachi_open(struct net_device *dev);
+#ifdef HAVE_PRIVATE_IOCTL
+static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+#endif
+static void hamachi_timer(unsigned long data);
+static void hamachi_tx_timeout(struct net_device *dev);
+static void hamachi_init_ring(struct net_device *dev);
+static int hamachi_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void hamachi_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
+static inline int hamachi_rx(struct net_device *dev);
+static inline int hamachi_tx(struct net_device *dev);
+static void hamachi_error(struct net_device *dev, int intr_status);
+static int hamachi_close(struct net_device *dev);
+static struct net_device_stats *hamachi_get_stats(struct net_device *dev);
+static void set_rx_mode(struct net_device *dev);
+
+
+static int __init hamachi_init_one (struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ static int did_version = 0; /* Already printed version info. */
+ struct hamachi_private *hmp;
+ int option, i, rx_int_var, tx_int_var, boguscnt;
+ int chip_id = ent->driver_data;
+ int irq = pdev->irq;
+ long ioaddr;
+ static int card_idx = 0;
+ struct net_device *dev;
+
+ if (hamachi_debug > 0 && did_version++ == 0)
+ printk(version);
+
+ ioaddr = pci_resource_start(pdev, 0);
+#ifdef __alpha__ /* Really "64 bit addrs" */
+ ioaddr |= (pci_resource_start(pdev, 1) << 32);
+#endif
+
+ if (pci_enable_device(pdev))
+ return -EIO;
+ pci_set_master(pdev);
+
+ ioaddr = (long) ioremap(ioaddr, 0x400);
+ if (!ioaddr)
+ return -ENOMEM;
+
+ dev = init_etherdev(NULL, sizeof(struct hamachi_private));
+ if (!dev) {
+ iounmap((char *)ioaddr);
+ return -ENOMEM;
+ }
+
+#ifdef TX_CHECKSUM
+ printk("check that skbcopy in ip_queue_xmit isn't happening\n");
+ dev->hard_header_len += 8; /* for cksum tag */
+#endif
+
+ printk(KERN_INFO "%s: %s type %x at 0x%lx, ",
+ dev->name, chip_tbl[chip_id].name, readl(ioaddr + ChipRev),
+ ioaddr);
+
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = 1 ? read_eeprom(ioaddr, 4 + i)
+ : readb(ioaddr + StationAddr + i);
+ for (i = 0; i < 5; i++)
+ printk("%2.2x:", dev->dev_addr[i]);
+ printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq);
+
+#if ! defined(final_version)
+ if (hamachi_debug > 4)
+ for (i = 0; i < 0x10; i++)
+ printk("%2.2x%s",
+ read_eeprom(ioaddr, i), i % 16 != 15 ? " " : "\n");
+#endif
+
+#if 0 /* Moving this until after the force 32 check and reset. */
+ i = readb(ioaddr + PCIClkMeas);
+ printk(KERN_INFO "%s: %d-bit %d Mhz PCI bus (%d), Virtual Jumpers "
+ "%2.2x, LPA %4.4x.\n",
+ dev->name, readw(ioaddr + MiscStatus) & 1 ? 64 : 32,
+ i ? 2000/(i&0x7f) : 0, i&0x7f, (int)readb(ioaddr + VirtualJumpers),
+ readw(ioaddr + ANLinkPartnerAbility));
+#endif
+
+ hmp = dev->priv;
+ spin_lock_init(&hmp->lock);
+
+ /* Check for options being passed in */
+ option = card_idx < MAX_UNITS ? options[card_idx] : 0;
+ if (dev->mem_start)
+ option = dev->mem_start;
+
+ /* If the bus size is misidentified, do the following. */
+ force32 = force32 ? force32 :
+ ((option >= 0) ? ((option & 0x00000070) >> 4) : 0 );
+ if (force32)
+ writeb(force32, ioaddr + VirtualJumpers);
+
+ /* Hmmm, do we really need to reset the chip???. */
+ writeb(0x01, ioaddr + ChipReset);
+
+ /* After a reset, the clock speed measurement of the PCI bus will not
+ * be valid for a moment. Wait for a little while until it is. If
+ * it takes more than 10ms, forget it.
+ */
+ udelay(10);
+ i = readb(ioaddr + PCIClkMeas);
+ for (boguscnt = 0; (!(i & 0x080)) && boguscnt < 1000; boguscnt++){
+ udelay(10);
+ i = readb(ioaddr + PCIClkMeas);
+ }
+
+ printk(KERN_INFO "%s: %d-bit %d Mhz PCI bus (%d), Virtual Jumpers "
+ "%2.2x, LPA %4.4x.\n",
+ dev->name, readw(ioaddr + MiscStatus) & 1 ? 64 : 32,
+ i ? 2000/(i&0x7f) : 0, i&0x7f, (int)readb(ioaddr + VirtualJumpers),
+ readw(ioaddr + ANLinkPartnerAbility));
+
+ dev->base_addr = ioaddr;
+ dev->irq = irq;
+
+ hmp->chip_id = chip_id;
+
+ /* The lower four bits are the media type. */
+ if (option > 0) {
+ hmp->option = option;
+ if (option & 0x200)
+ hmp->full_duplex = 1;
+ else if (option & 0x080)
+ hmp->full_duplex = 0;
+ hmp->default_port = option & 15;
+ if (hmp->default_port)
+ hmp->medialock = 1;
+ }
+ if (card_idx < MAX_UNITS && full_duplex[card_idx] > 0)
+ hmp->full_duplex = 1;
+
+ /* lock the duplex mode if someone specified a value */
+ if (hmp->full_duplex || (option & 0x080))
+ hmp->duplex_lock = 1;
+
+ /* Set interrupt tuning parameters */
+ max_rx_latency = max_rx_latency & 0x00ff;
+ max_rx_gap = max_rx_gap & 0x00ff;
+ min_rx_pkt = min_rx_pkt & 0x00ff;
+ max_tx_latency = max_tx_latency & 0x00ff;
+ max_tx_gap = max_tx_gap & 0x00ff;
+ min_tx_pkt = min_tx_pkt & 0x00ff;
+
+ rx_int_var = card_idx < MAX_UNITS ? rx_params[card_idx] : -1;
+ tx_int_var = card_idx < MAX_UNITS ? tx_params[card_idx] : -1;
+ hmp->rx_int_var = rx_int_var >= 0 ? rx_int_var :
+ (min_rx_pkt << 16 | max_rx_gap << 8 | max_rx_latency);
+ hmp->tx_int_var = tx_int_var >= 0 ? tx_int_var :
+ (min_tx_pkt << 16 | max_tx_gap << 8 | max_tx_latency);
+
+
+ /* The Hamachi-specific entries in the device structure. */
+ dev->open = &hamachi_open;
+ dev->hard_start_xmit = &hamachi_start_xmit;
+ dev->stop = &hamachi_close;
+ dev->get_stats = &hamachi_get_stats;
+ dev->set_multicast_list = &set_rx_mode;
+#ifdef HAVE_PRIVATE_IOCTL
+ dev->do_ioctl = &mii_ioctl;
+#endif
+ dev->tx_timeout = &hamachi_tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+ if (mtu)
+ dev->mtu = mtu;
+
+ if (chip_tbl[hmp->chip_id].flags & CanHaveMII) {
+ int phy, phy_idx = 0;
+ for (phy = 0; phy < 32 && phy_idx < 4; phy++) {
+ int mii_status = mdio_read(ioaddr, phy, 1);
+ if (mii_status != 0xffff &&
+ mii_status != 0x0000) {
+ hmp->phys[phy_idx++] = phy;
+ hmp->advertising = mdio_read(ioaddr, phy, 4);
+ printk(KERN_INFO "%s: MII PHY found at address %d, status "
+ "0x%4.4x advertising %4.4x.\n",
+ dev->name, phy, mii_status, hmp->advertising);
+ }
+ }
+ hmp->mii_cnt = phy_idx;
+ }
+ /* Configure gigabit autonegotiation. */
+ writew(0x0400, ioaddr + ANXchngCtrl); /* Enable legacy links. */
+ writew(0x08e0, ioaddr + ANAdvertise); /* Set our advertise word. */
+ writew(0x1000, ioaddr + ANCtrl); /* Enable negotiation */
+
+ card_idx++;
+ return 0;
+}
+
+static int read_eeprom(long ioaddr, int location)
+{
+ int bogus_cnt = 1000;
+
+ /* We should check busy first - per docs -KDU */
+ while ((readb(ioaddr + EECmdStatus) & 0x40) && --bogus_cnt > 0);
+ writew(location, ioaddr + EEAddr);
+ writeb(0x02, ioaddr + EECmdStatus);
+ bogus_cnt = 1000;
+ while ((readb(ioaddr + EECmdStatus) & 0x40) && --bogus_cnt > 0);
+ if (hamachi_debug > 5)
+ printk(" EEPROM status is %2.2x after %d ticks.\n",
+ (int)readb(ioaddr + EECmdStatus), 1000- bogus_cnt);
+ return readb(ioaddr + EEData);
+}
+
+/* MII Managemen Data I/O accesses.
+ These routines assume the MDIO controller is idle, and do not exit until
+ the command is finished. */
+
+static int mdio_read(long ioaddr, int phy_id, int location)
+{
+ int i;
+
+ /* We should check busy first - per docs -KDU */
+ for (i = 10000; i >= 0; i--)
+ if ((readw(ioaddr + MII_Status) & 1) == 0)
+ break;
+ writew((phy_id<<8) + location, ioaddr + MII_Addr);
+ writew(0x0001, ioaddr + MII_Cmd);
+ for (i = 10000; i >= 0; i--)
+ if ((readw(ioaddr + MII_Status) & 1) == 0)
+ break;
+ return readw(ioaddr + MII_Rd_Data);
+}
+
+static void mdio_write(long ioaddr, int phy_id, int location, int value)
+{
+ int i;
+
+ /* We should check busy first - per docs -KDU */
+ for (i = 10000; i >= 0; i--)
+ if ((readw(ioaddr + MII_Status) & 1) == 0)
+ break;
+ writew((phy_id<<8) + location, ioaddr + MII_Addr);
+ writew(value, ioaddr + MII_Wr_Data);
+
+ /* Wait for the command to finish. */
+ for (i = 10000; i >= 0; i--)
+ if ((readw(ioaddr + MII_Status) & 1) == 0)
+ break;
+ return;
+}
+
+\f
+static int hamachi_open(struct net_device *dev)
+{
+ struct hamachi_private *hmp = (struct hamachi_private *)dev->priv;
+ long ioaddr = dev->base_addr;
+ int i;
+ u_int32_t rx_int_var, tx_int_var;
+ u_int16_t fifo_info;
+
+ MOD_INC_USE_COUNT;
+
+ if (request_irq(dev->irq, &hamachi_interrupt, SA_SHIRQ, dev->name, dev)) {
+ MOD_DEC_USE_COUNT;
+ return -EAGAIN;
+ }
+
+ if (hamachi_debug > 1)
+ printk(KERN_DEBUG "%s: hamachi_open() irq %d.\n",
+ dev->name, dev->irq);
+
+ hamachi_init_ring(dev);
+
+#if ADDRLEN == 64
+ writel(virt_to_bus(hmp->rx_ring), ioaddr + RxPtr);
+ writel(virt_to_bus(hmp->rx_ring) >> 32, ioaddr + RxPtr + 4);
+ writel(virt_to_bus(hmp->tx_ring), ioaddr + TxPtr);
+ writel(virt_to_bus(hmp->tx_ring) >> 32, ioaddr + TxPtr + 4);
+#else
+ writel(virt_to_bus(hmp->rx_ring), ioaddr + RxPtr);
+ writel(virt_to_bus(hmp->tx_ring), ioaddr + TxPtr);
+#endif
+
+ /* TODO: It would make sense to organize this as words since the card
+ * documentation does. -KDU
+ */
+ for (i = 0; i < 6; i++)
+ writeb(dev->dev_addr[i], ioaddr + StationAddr + i);
+
+ /* Initialize other registers: with so many this eventually this will
+ converted to an offset/value list. */
+
+ /* Configure the FIFO */
+ fifo_info = (readw(ioaddr + GPIO) & 0x00C0) >> 6;
+ switch (fifo_info){
+ case 0 :
+ /* No FIFO */
+ writew(0x0000, ioaddr + FIFOcfg);
+ break;
+ case 1 :
+ /* Configure the FIFO for 512K external, 16K used for Tx. */
+ writew(0x0028, ioaddr + FIFOcfg);
+ break;
+ case 2 :
+ /* Configure the FIFO for 1024 external, 32K used for Tx. */
+ writew(0x004C, ioaddr + FIFOcfg);
+ break;
+ case 3 :
+ /* Configure the FIFO for 2048 external, 32K used for Tx. */
+ writew(0x006C, ioaddr + FIFOcfg);
+ break;
+ default :
+ printk(KERN_WARNING "%s: Unsupported external memory config!\n",
+ dev->name);
+ /* Default to no FIFO */
+ writew(0x0000, ioaddr + FIFOcfg);
+ break;
+ }
+
+ if (dev->if_port == 0)
+ dev->if_port = hmp->default_port;
+
+
+ /* Setting the Rx mode will start the Rx process. */
+ /* If someone didn't choose a duplex, default to full-duplex */
+ if (hmp->duplex_lock != 1)
+ hmp->full_duplex = 1;
+
+ /* always 1, takes no more time to do it */
+ writew(0x0001, ioaddr + RxChecksum);
+#ifdef TX_CHECKSUM
+ writew(0x0001, ioaddr + TxChecksum);
+#else
+ writew(0x0000, ioaddr + TxChecksum);
+#endif
+ writew(0x8000, ioaddr + MACCnfg); /* Soft reset the MAC */
+ writew(0x215F, ioaddr + MACCnfg);
+ writew(0x000C, ioaddr + FrameGap0);
+ /* WHAT?!?!? Why isn't this documented somewhere? -KDU */
+ writew(0x1018, ioaddr + FrameGap1);
+ /* Why do we enable receives/transmits here? -KDU */
+ writew(0x0780, ioaddr + MACCnfg2); /* Upper 16 bits control LEDs. */
+ /* Enable automatic generation of flow control frames, period 0xffff. */
+ writel(0x0030FFFF, ioaddr + FlowCtrl);
+ writew(MAX_FRAME_SIZE, ioaddr + MaxFrameSize); /* dev->mtu+14 ??? */
+
+ /* Enable legacy links. */
+ writew(0x0400, ioaddr + ANXchngCtrl); /* Enable legacy links. */
+ /* Initial Link LED to blinking red. */
+ writeb(0x03, ioaddr + LEDCtrl);
+
+ /* Configure interrupt mitigation. This has a great effect on
+ performance, so systems tuning should start here!. */
+
+ rx_int_var = hmp->rx_int_var;
+ tx_int_var = hmp->tx_int_var;
+
+ if (hamachi_debug > 1) {
+ printk("max_tx_latency: %d, max_tx_gap: %d, min_tx_pkt: %d\n",
+ tx_int_var & 0x00ff, (tx_int_var & 0x00ff00) >> 8,
+ (tx_int_var & 0x00ff0000) >> 16);
+ printk("max_rx_latency: %d, max_rx_gap: %d, min_rx_pkt: %d\n",
+ rx_int_var & 0x00ff, (rx_int_var & 0x00ff00) >> 8,
+ (rx_int_var & 0x00ff0000) >> 16);
+ printk("rx_int_var: %x, tx_int_var: %x\n", rx_int_var, tx_int_var);
+ }
+
+ writel(tx_int_var, ioaddr + TxIntrCtrl);
+ writel(rx_int_var, ioaddr + RxIntrCtrl);
+
+ set_rx_mode(dev);
+
+ netif_start_queue(dev);
+
+ /* Enable interrupts by setting the interrupt mask. */
+ writel(0x80878787, ioaddr + InterruptEnable);
+ writew(0x0000, ioaddr + EventStatus); /* Clear non-interrupting events */
+
+ /* Configure and start the DMA channels. */
+ /* Burst sizes are in the low three bits: size = 4<<(val&7) */
+#if ADDRLEN == 64
+ writew(0x005D, ioaddr + RxDMACtrl); /* 128 dword bursts */
+ writew(0x005D, ioaddr + TxDMACtrl);
+#else
+ writew(0x001D, ioaddr + RxDMACtrl);
+ writew(0x001D, ioaddr + TxDMACtrl);
+#endif
+ writew(0x0001, dev->base_addr + RxCmd);
+
+ if (hamachi_debug > 2) {
+ printk(KERN_DEBUG "%s: Done hamachi_open(), status: Rx %x Tx %x.\n",
+ dev->name, readw(ioaddr + RxStatus), readw(ioaddr + TxStatus));
+ }
+ /* Set the timer to check for link beat. */
+ init_timer(&hmp->timer);
+ hmp->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */
+ hmp->timer.data = (unsigned long)dev;
+ hmp->timer.function = &hamachi_timer; /* timer handler */
+ add_timer(&hmp->timer);
+
+ return 0;
+}
+
+static inline int hamachi_tx(struct net_device *dev)
+{
+ struct hamachi_private *hmp = (struct hamachi_private *)dev->priv;
+
+ /* Update the dirty pointer until we find an entry that is
+ still owned by the card */
+ for (; hmp->cur_tx - hmp->dirty_tx > 0; hmp->dirty_tx++) {
+ int entry = hmp->dirty_tx % TX_RING_SIZE;
+ if (hmp->tx_ring[entry].status_n_length & cpu_to_le32(DescOwn))
+ break;
+ /* Free the original skb. */
+ if (hmp->tx_skbuff[entry] != 0) {
+ dev_kfree_skb(hmp->tx_skbuff[entry]);
+ hmp->tx_skbuff[entry] = 0;
+ }
+ hmp->tx_ring[entry].status_n_length = 0;
+ if (entry >= TX_RING_SIZE-1)
+ hmp->tx_ring[TX_RING_SIZE-1].status_n_length |=
+ cpu_to_le32(DescEndRing);
+ hmp->stats.tx_packets++;
+ }
+
+ return 0;
+}
+
+static void hamachi_timer(unsigned long data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct hamachi_private *hmp = (struct hamachi_private *)dev->priv;
+ long ioaddr = dev->base_addr;
+ int next_tick = 10*HZ;
+
+ if (hamachi_debug > 2) {
+ printk(KERN_INFO "%s: Hamachi Autonegotiation status %4.4x, LPA "
+ "%4.4x.\n", dev->name, readw(ioaddr + ANStatus),
+ readw(ioaddr + ANLinkPartnerAbility));
+ printk(KERN_INFO "%s: Autonegotiation regs %4.4x %4.4x %4.4x "
+ "%4.4x %4.4x %4.4x.\n", dev->name,
+ readw(ioaddr + 0x0e0),
+ readw(ioaddr + 0x0e2),
+ readw(ioaddr + 0x0e4),
+ readw(ioaddr + 0x0e6),
+ readw(ioaddr + 0x0e8),
+ readw(ioaddr + 0x0eA));
+ }
+ /* We could do something here... nah. */
+ hmp->timer.expires = RUN_AT(next_tick);
+ add_timer(&hmp->timer);
+}
+
+static void hamachi_tx_timeout(struct net_device *dev)
+{
+ int i;
+ struct hamachi_private *hmp = (struct hamachi_private *)dev->priv;
+ long ioaddr = dev->base_addr;
+
+ printk(KERN_WARNING "%s: Hamachi transmit timed out, status %8.8x,"
+ " resetting...\n", dev->name, (int)readw(ioaddr + TxStatus));
+
+#ifndef __alpha__
+ {
+ int i;
+ printk(KERN_DEBUG " Rx ring %8.8x: ", (int)hmp->rx_ring);
+ for (i = 0; i < RX_RING_SIZE; i++)
+ printk(" %8.8x", (unsigned int)hmp->rx_ring[i].status_n_length);
+ printk("\n"KERN_DEBUG" Tx ring %8.8x: ", (int)hmp->tx_ring);
+ for (i = 0; i < TX_RING_SIZE; i++)
+ printk(" %4.4x", hmp->tx_ring[i].status_n_length);
+ printk("\n");
+ }
+#endif
+
+ /* Reinit the hardware and make sure the Rx and Tx processes
+ are up and running.
+ */
+ dev->if_port = 0;
+ /* The right way to do Reset. -KDU
+ * -Clear OWN bit in all Rx/Tx descriptors
+ * -Wait 50 uS for channels to go idle
+ * -Turn off MAC receiver
+ * -Issue Reset
+ */
+
+ for (i = 0; i < RX_RING_SIZE; i++)
+ hmp->rx_ring[i].status_n_length &= ~DescOwn;
+
+ /* Presume that all packets in the Tx queue are gone if we have to
+ * re-init the hardware.
+ */
+ for (i = 0; i < TX_RING_SIZE; i++){
+ if (i >= TX_RING_SIZE - 1)
+ hmp->tx_ring[i].status_n_length = DescEndRing |
+ (hmp->tx_ring[i].status_n_length & 0x0000FFFF);
+ else
+ hmp->tx_ring[i].status_n_length &= 0x0000ffff;
+ if (hmp->tx_skbuff[i]){
+ dev_kfree_skb(hmp->tx_skbuff[i]);
+ hmp->tx_skbuff[i] = 0;
+ }
+ }
+
+ udelay(60); /* Sleep 60 us just for safety sake */
+ writew(0x0002, dev->base_addr + RxCmd); /* STOP Rx */
+
+ writeb(0x01, ioaddr + ChipReset); /* Reinit the hardware */
+
+ hmp->tx_full = 0;
+ hmp->cur_rx = hmp->cur_tx = 0;
+ hmp->dirty_rx = hmp->dirty_tx = 0;
+ hmp->rx_head_desc = &hmp->rx_ring[0];
+ /* Rx packets are also presumed lost; however, we need to make sure a
+ * ring of buffers is in tact. -KDU
+ */
+ for (i = 0; i < RX_RING_SIZE; i++){
+ if (hmp->rx_skbuff[i]){
+ dev_kfree_skb(hmp->rx_skbuff[i]);
+ hmp->rx_skbuff[i] = 0;
+ }
+ }
+ /* Fill in the Rx buffers. Handle allocation failure gracefully. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ struct sk_buff *skb = dev_alloc_skb(hmp->rx_buf_sz);
+ hmp->rx_skbuff[i] = skb;
+ if (skb == NULL)
+ break;
+ skb->dev = dev; /* Mark as being used by this device. */
+ skb_reserve(skb, 2); /* 16 byte align the IP header. */
+ hmp->rx_ring[i].addr = virt_to_desc(skb->tail);
+ hmp->rx_ring[i].status_n_length =
+ cpu_to_le32(DescOwn | DescEndPacket | DescIntr | (hmp->rx_buf_sz - 2));
+ }
+ hmp->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
+ /* Mark the last entry as wrapping the ring. */
+ hmp->rx_ring[RX_RING_SIZE-1].status_n_length |= cpu_to_le32(DescEndRing);
+
+
+ /* Trigger an immediate transmit demand. */
+ dev->trans_start = jiffies;
+ hmp->stats.tx_errors++;
+
+ /* Restart the chip's Tx/Rx processes . */
+ writew(0x0002, dev->base_addr + TxCmd); /* STOP Tx */
+ writew(0x0001, dev->base_addr + TxCmd); /* START Tx */
+ writew(0x0001, dev->base_addr + RxCmd); /* START Rx */
+}
+
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void hamachi_init_ring(struct net_device *dev)
+{
+ struct hamachi_private *hmp = (struct hamachi_private *)dev->priv;
+ int i;
+
+ hmp->tx_full = 0;
+ hmp->cur_rx = hmp->cur_tx = 0;
+ hmp->dirty_rx = hmp->dirty_tx = 0;
+
+#if 0
+ /* This is wrong. I'm not sure what the original plan was, but this
+ * is wrong. An MTU of 1 gets you a buffer of 1536, while an MTU
+ * of 1501 gets a buffer of 1533? -KDU
+ */
+ hmp->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
+#endif
+ /* My attempt at a reasonable correction */
+ /* +26 gets the maximum ethernet encapsulation, +7 & ~7 because the
+ * card needs room to do 8 byte alignment, +2 so we can reserve
+ * the first 2 bytes, and +16 gets room for the status word from the
+ * card. -KDU
+ */
+ hmp->rx_buf_sz = (dev->mtu <= 1492 ? PKT_BUF_SZ :
+ (((dev->mtu+26+7) & ~7) + 2 + 16));
+
+ hmp->rx_head_desc = &hmp->rx_ring[0];
+
+ /* Initialize all Rx descriptors. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ hmp->rx_ring[i].status_n_length = 0;
+ hmp->rx_skbuff[i] = 0;
+ }
+ /* Fill in the Rx buffers. Handle allocation failure gracefully. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ struct sk_buff *skb = dev_alloc_skb(hmp->rx_buf_sz);
+ hmp->rx_skbuff[i] = skb;
+ if (skb == NULL)
+ break;
+ skb->dev = dev; /* Mark as being used by this device. */
+ skb_reserve(skb, 2); /* 16 byte align the IP header. */
+ hmp->rx_ring[i].addr = virt_to_desc(skb->tail);
+ /* -2 because it doesn't REALLY have that first 2 bytes -KDU */
+ hmp->rx_ring[i].status_n_length =
+ cpu_to_le32(DescOwn | DescEndPacket | DescIntr | (hmp->rx_buf_sz -2));
+ }
+ hmp->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
+ /* Mark the last entry as wrapping the ring. */
+ hmp->rx_ring[RX_RING_SIZE-1].status_n_length |= cpu_to_le32(DescEndRing);
+
+
+ /* Mark the last entry as wrapping the ring. */
+ hmp->rx_ring[RX_RING_SIZE-1].status_n_length |= DescEndRing;
+
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ hmp->tx_skbuff[i] = 0;
+ hmp->tx_ring[i].status_n_length = 0;
+ }
+ /* Mark the last entry as wrapping the ring. */
+ hmp->tx_ring[TX_RING_SIZE-1].status_n_length |= cpu_to_le32(DescEndRing);
+
+ return;
+}
+
+
+#ifdef TX_CHECKSUM
+#define csum_add(it, val) \
+do { \
+ it += (u16) (val); \
+ if (it & 0xffff0000) { \
+ it &= 0xffff; \
+ ++it; \
+ } \
+} while (0)
+ /* printk("add %04x --> %04x\n", val, it); \ */
+
+/* uh->len already network format, do not swap */
+#define pseudo_csum_udp(sum,ih,uh) do { \
+ sum = 0; \
+ csum_add(sum, (ih)->saddr >> 16); \
+ csum_add(sum, (ih)->saddr & 0xffff); \
+ csum_add(sum, (ih)->daddr >> 16); \
+ csum_add(sum, (ih)->daddr & 0xffff); \
+ csum_add(sum, __constant_htons(IPPROTO_UDP)); \
+ csum_add(sum, (uh)->len); \
+} while (0)
+
+/* swap len */
+#define pseudo_csum_tcp(sum,ih,len) do { \
+ sum = 0; \
+ csum_add(sum, (ih)->saddr >> 16); \
+ csum_add(sum, (ih)->saddr & 0xffff); \
+ csum_add(sum, (ih)->daddr >> 16); \
+ csum_add(sum, (ih)->daddr & 0xffff); \
+ csum_add(sum, __constant_htons(IPPROTO_TCP)); \
+ csum_add(sum, htons(len)); \
+} while (0)
+#endif
+
+static int hamachi_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct hamachi_private *hmp = (struct hamachi_private *)dev->priv;
+ unsigned entry;
+ u16 status;
+
+ /* Ok, now make sure that the queue has space before trying to
+ add another skbuff. if we return non-zero the scheduler
+ should interpret this as a queue full and requeue the buffer
+ for later.
+ */
+ if (hmp->tx_full) {
+ /* We should NEVER reach this point -KDU */
+ printk(KERN_WARNING "%s: Hamachi transmit queue full at slot %d.\n",dev->name, hmp->cur_tx);
+
+ /* Wake the potentially-idle transmit channel. */
+ /* If we don't need to read status, DON'T -KDU */
+ status=readw(dev->base_addr + TxStatus);
+ if( !(status & 0x0001) || (status & 0x0002))
+ writew(0x0001, dev->base_addr + TxCmd);
+ return 1;
+ }
+
+ /* Caution: the write order is important here, set the field
+ with the "ownership" bits last. */
+
+ /* Calculate the next Tx descriptor entry. */
+ entry = hmp->cur_tx % TX_RING_SIZE;
+
+ hmp->tx_skbuff[entry] = skb;
+
+#ifdef TX_CHECKSUM
+ {
+ /* tack on checksum tag */
+ u32 tagval = 0;
+ struct ethhdr *eh = (struct ethhdr *)skb->data;
+ if (eh->h_proto == __constant_htons(ETH_P_IP)) {
+ struct iphdr *ih = (struct iphdr *)((char *)eh + ETH_HLEN);
+ if (ih->protocol == IPPROTO_UDP) {
+ struct udphdr *uh
+ = (struct udphdr *)((char *)ih + ih->ihl*4);
+ u32 offset = ((unsigned char *)uh + 6) - skb->data;
+ u32 pseudo;
+ pseudo_csum_udp(pseudo, ih, uh);
+ pseudo = htons(pseudo);
+ printk("udp cksum was %04x, sending pseudo %04x\n",
+ uh->check, pseudo);
+ uh->check = 0; /* zero out uh->check before card calc */
+ /*
+ * start at 14 (skip ethhdr), store at offset (uh->check),
+ * use pseudo value given.
+ */
+ tagval = (14 << 24) | (offset << 16) | pseudo;
+ } else if (ih->protocol == IPPROTO_TCP) {
+ printk("tcp, no auto cksum\n");
+ }
+ }
+ *(u32 *)skb_push(skb, 8) = tagval;
+ }
+#endif
+
+ hmp->tx_ring[entry].addr = virt_to_desc(skb->data);
+
+ /* Hmmmm, could probably put a DescIntr on these, but the way
+ the driver is currently coded makes Tx interrupts unnecessary
+ since the clearing of the Tx ring is handled by the start_xmit
+ routine. This organization helps mitigate the interrupts a
+ bit and probably renders the max_tx_latency param useless.
+
+ Update: Putting a DescIntr bit on all of the descriptors and
+ mitigating interrupt frequency with the tx_min_pkt parameter. -KDU
+ */
+ if (entry >= TX_RING_SIZE-1) /* Wrap ring */
+ hmp->tx_ring[entry].status_n_length =
+ cpu_to_le32(DescOwn|DescEndPacket|DescEndRing|DescIntr | skb->len);
+ else
+ hmp->tx_ring[entry].status_n_length =
+ cpu_to_le32(DescOwn|DescEndPacket|DescIntr | skb->len);
+ hmp->cur_tx++;
+
+ /* Non-x86 Todo: explicitly flush cache lines here. */
+
+ /* Wake the potentially-idle transmit channel. */
+ /* If we don't need to read status, DON'T -KDU */
+ status=readw(dev->base_addr + TxStatus);
+ if( !(status & 0x0001) || (status & 0x0002))
+ writew(0x0001, dev->base_addr + TxCmd);
+
+ /* Immediately before returning, let's clear as many entries as we can. */
+ hamachi_tx(dev);
+
+ /* We should kick the bottom half here, since we are not accepting
+ * interrupts with every packet. i.e. realize that Gigabit ethernet
+ * can transmit faster than ordinary machines can load packets;
+ * hence, any packet that got put off because we were in the transmit
+ * routine should IMMEDIATELY get a chance to be re-queued. -KDU
+ */
+ if ((hmp->cur_tx - hmp->dirty_tx) < (TX_RING_SIZE - 4))
+ netif_wake_queue(dev); /* Typical path */
+ else {
+ hmp->tx_full = 1;
+ netif_stop_queue(dev);
+ }
+ dev->trans_start = jiffies;
+
+ if (hamachi_debug > 4) {
+ printk(KERN_DEBUG "%s: Hamachi transmit frame #%d queued in slot %d.\n",
+ dev->name, hmp->cur_tx, entry);
+ }
+ return 0;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+ after the Tx thread. */
+static void hamachi_interrupt(int irq, void *dev_instance, struct pt_regs *rgs)
+{
+ struct net_device *dev = (struct net_device *)dev_instance;
+ struct hamachi_private *hmp;
+ long ioaddr, boguscnt = max_interrupt_work;
+
+#ifndef final_version /* Can never occur. */
+ if (dev == NULL) {
+ printk (KERN_ERR "hamachi_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+#endif
+
+ ioaddr = dev->base_addr;
+ hmp = (struct hamachi_private *)dev->priv;
+ spin_lock(&hmp->lock);
+
+ do {
+ u32 intr_status = readl(ioaddr + InterruptClear);
+
+ if (hamachi_debug > 4)
+ printk(KERN_DEBUG "%s: Hamachi interrupt, status %4.4x.\n",
+ dev->name, intr_status);
+
+ if (intr_status == 0)
+ break;
+
+ if (intr_status & IntrRxDone)
+ hamachi_rx(dev);
+
+ if (intr_status & IntrTxDone){
+ /* This code should RARELY need to execute. After all, this is
+ * a gigabit link, it should consume packets as fast as we put
+ * them in AND we clear the Tx ring in hamachi_start_xmit().
+ */
+ if (hmp->tx_full){
+ for (; hmp->cur_tx - hmp->dirty_tx > 0; hmp->dirty_tx++){
+ int entry = hmp->dirty_tx % TX_RING_SIZE;
+ if (hmp->tx_ring[entry].status_n_length & cpu_to_le32(DescOwn))
+ break;
+ /* Free the original skb. */
+ if (hmp->tx_skbuff[entry]){
+ dev_kfree_skb_irq(hmp->tx_skbuff[entry]);
+ hmp->tx_skbuff[entry] = 0;
+ }
+ hmp->tx_ring[entry].status_n_length = 0;
+ if (entry >= TX_RING_SIZE-1)
+ hmp->tx_ring[TX_RING_SIZE-1].status_n_length |=
+ cpu_to_le32(DescEndRing);
+ hmp->stats.tx_packets++;
+ }
+ if (hmp->cur_tx - hmp->dirty_tx < TX_RING_SIZE - 4){
+ /* The ring is no longer full */
+ hmp->tx_full = 0;
+ netif_wake_queue(dev);
+ }
+ } else {
+ netif_wake_queue(dev);
+ }
+ }
+
+
+ /* Abnormal error summary/uncommon events handlers. */
+ if (intr_status &
+ (IntrTxPCIFault | IntrTxPCIErr | IntrRxPCIFault | IntrRxPCIErr |
+ LinkChange | NegotiationChange | StatsMax))
+ hamachi_error(dev, intr_status);
+
+ if (--boguscnt < 0) {
+ printk(KERN_WARNING "%s: Too much work at interrupt, status=0x%4.4x.\n",
+ dev->name, intr_status);
+ break;
+ }
+ } while (1);
+
+ if (hamachi_debug > 3)
+ printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
+ dev->name, readl(ioaddr + IntrStatus));
+
+#ifndef final_version
+ /* Code that should never be run! Perhaps remove after testing.. */
+ {
+ static int stopit = 10;
+ if (dev->start == 0 && --stopit < 0) {
+ printk(KERN_ERR "%s: Emergency stop, looping startup interrupt.\n",
+ dev->name);
+ free_irq(irq, dev);
+ }
+ }
+#endif
+
+ spin_unlock(&hmp->lock);
+}
+
+#ifdef TX_CHECKSUM
+/*
+ * Copied from eth_type_trans(), with reduced header, since we don't
+ * get it on RX, only on TX.
+ */
+static unsigned short hamachi_eth_type_trans(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct ethhdr *eth;
+ unsigned char *rawp;
+
+ skb->mac.raw=skb->data;
+ skb_pull(skb,dev->hard_header_len-8); /* artificially enlarged on tx */
+ eth= skb->mac.ethernet;
+
+ if(*eth->h_dest&1)
+ {
+ if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0)
+ skb->pkt_type=PACKET_BROADCAST;
+ else
+ skb->pkt_type=PACKET_MULTICAST;
+ }
+
+ /*
+ * This ALLMULTI check should be redundant by 1.4
+ * so don't forget to remove it.
+ *
+ * Seems, you forgot to remove it. All silly devices
+ * seems to set IFF_PROMISC.
+ */
+
+ else if(dev->flags&(IFF_PROMISC/*|IFF_ALLMULTI*/))
+ {
+ if(memcmp(eth->h_dest,dev->dev_addr, ETH_ALEN))
+ skb->pkt_type=PACKET_OTHERHOST;
+ }
+
+ if (ntohs(eth->h_proto) >= 1536)
+ return eth->h_proto;
+
+ rawp = skb->data;
+
+ /*
+ * This is a magic hack to spot IPX packets. Older Novell breaks
+ * the protocol design and runs IPX over 802.3 without an 802.2 LLC
+ * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
+ * won't work for fault tolerant netware but does for the rest.
+ */
+ if (*(unsigned short *)rawp == 0xFFFF)
+ return htons(ETH_P_802_3);
+
+ /*
+ * Real 802.2 LLC
+ */
+ return htons(ETH_P_802_2);
+}
+#endif /* TX_CHECKSUM */
+
+/* This routine is logically part of the interrupt handler, but seperated
+ for clarity and better register allocation. */
+static int hamachi_rx(struct net_device *dev)
+{
+ struct hamachi_private *hmp = (struct hamachi_private *)dev->priv;
+ int entry = hmp->cur_rx % RX_RING_SIZE;
+ int boguscnt = (hmp->dirty_rx + RX_RING_SIZE) - hmp->cur_rx;
+
+ if (hamachi_debug > 4) {
+ printk(KERN_DEBUG " In hamachi_rx(), entry %d status %4.4x.\n",
+ entry, hmp->rx_ring[entry].status_n_length);
+ }
+
+ /* If EOP is set on the next entry, it's a new packet. Send it up. */
+ while ( ! (hmp->rx_head_desc->status_n_length & cpu_to_le32(DescOwn))) {
+ struct hamachi_desc *desc = hmp->rx_head_desc;
+ u32 desc_status = le32_to_cpu(desc->status_n_length);
+ u16 data_size = desc_status; /* Implicit truncate */
+ u8 *buf_addr = le32desc_to_virt(desc->addr);
+ s32 frame_status =
+ le32_to_cpu(get_unaligned((s32*)&(buf_addr[data_size - 12])));
+
+ if (hamachi_debug > 4)
+ printk(KERN_DEBUG " hamachi_rx() status was %8.8x.\n",
+ frame_status);
+ if (--boguscnt < 0)
+ break;
+ if ( ! (desc_status & DescEndPacket)) {
+ printk(KERN_WARNING "%s: Oversized Ethernet frame spanned "
+ "multiple buffers, entry %#x length %d status %4.4x!\n",
+ dev->name, hmp->cur_rx, data_size, desc_status);
+ printk(KERN_WARNING "%s: Oversized Ethernet frame %p vs %p.\n",
+ dev->name, desc, &hmp->rx_ring[hmp->cur_rx % RX_RING_SIZE]);
+ printk(KERN_WARNING "%s: Oversized Ethernet frame -- next status %x/%x last status %x.\n",
+ dev->name,
+ hmp->rx_ring[(hmp->cur_rx+1) % RX_RING_SIZE].status_n_length & 0xffff0000,
+ hmp->rx_ring[(hmp->cur_rx+1) % RX_RING_SIZE].status_n_length & 0x0000ffff,
+ hmp->rx_ring[(hmp->cur_rx-1) % RX_RING_SIZE].status_n_length);
+ hmp->stats.rx_length_errors++;
+ } /* else Omit for prototype errata??? */
+ if (frame_status & 0x00380000) {
+ /* There was an error. */
+ if (hamachi_debug > 2)
+ printk(KERN_DEBUG " hamachi_rx() Rx error was %8.8x.\n",
+ frame_status);
+ hmp->stats.rx_errors++;
+ if (frame_status & 0x00600000) hmp->stats.rx_length_errors++;
+ if (frame_status & 0x00080000) hmp->stats.rx_frame_errors++;
+ if (frame_status & 0x00100000) hmp->stats.rx_crc_errors++;
+ if (frame_status < 0) hmp->stats.rx_dropped++;
+ } else {
+ struct sk_buff *skb;
+ /* Omit CRC */
+ u16 pkt_len = (frame_status & 0x07ff) - 4;
+#ifdef RX_CHECKSUM
+ u32 pfck = *(u32 *) &buf_addr[data_size - 8];
+#endif
+
+
+#ifndef final_version
+ if (hamachi_debug > 4)
+ printk(KERN_DEBUG " hamachi_rx() normal Rx pkt length %d"
+ " of %d, bogus_cnt %d.\n",
+ pkt_len, data_size, boguscnt);
+ if (hamachi_debug > 5)
+ printk(KERN_DEBUG"%s: rx status %8.8x %8.8x %8.8x %8.8x %8.8x.\n",
+ dev->name,
+ *(s32*)&(buf_addr[data_size - 20]),
+ *(s32*)&(buf_addr[data_size - 16]),
+ *(s32*)&(buf_addr[data_size - 12]),
+ *(s32*)&(buf_addr[data_size - 8]),
+ *(s32*)&(buf_addr[data_size - 4]));
+#endif
+ /* Check if the packet is long enough to accept without copying
+ to a minimally-sized skbuff. */
+ if (pkt_len < rx_copybreak
+ && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
+#ifdef RX_CHECKSUM
+ printk(KERN_ERR "%s: rx_copybreak non-zero "
+ "not good with RX_CHECKSUM\n", dev->name);
+#endif
+ skb->dev = dev;
+ skb_reserve(skb, 2); /* 16 byte align the IP header */
+ /* Call copy + cksum if available. */
+#if 1 || USE_IP_COPYSUM
+ eth_copy_and_sum(skb, bus_to_virt(desc->addr), pkt_len, 0);
+ skb_put(skb, pkt_len);
+#else
+ memcpy(skb_put(skb, pkt_len), bus_to_virt(desc->addr),pkt_len);
+#endif
+ } else {
+ char *temp = skb_put(skb = hmp->rx_skbuff[entry], pkt_len);
+ hmp->rx_skbuff[entry] = NULL;
+#ifndef final_version /* Remove after testing. */
+ if (bus_to_virt(desc->addr) != temp)
+ printk(KERN_ERR "%s: Internal fault: The skbuff addresses "
+ "do not match in hamachi_rx: %p vs. %p / %p.\n",
+ dev->name, bus_to_virt(desc->addr),
+ skb->head, temp);
+#else
+ (void) temp;
+#endif
+ }
+#ifdef TX_CHECKSUM
+ /* account for extra TX hard_header bytes */
+ skb->protocol = hamachi_eth_type_trans(skb, dev);
+#else
+ skb->protocol = eth_type_trans(skb, dev);
+#endif
+
+
+#ifdef RX_CHECKSUM
+ /* TCP or UDP on ipv4, DIX encoding */
+ if (pfck>>24 == 0x91 || pfck>>24 == 0x51) {
+ struct iphdr *ih = (struct iphdr *) skb->data;
+ /* Check that IP packet is at least 46 bytes, otherwise,
+ * there may be pad bytes included in the hardware checksum.
+ * This wouldn't happen if everyone padded with 0.
+ */
+ if (ntohs(ih->tot_len) >= 46){
+ /* don't worry about frags */
+ if (!(ih->frag_off & __constant_htons(IP_MF|IP_OFFSET))) {
+ u32 inv = *(u32 *) &buf_addr[data_size - 16];
+ u32 *p = (u32 *) &buf_addr[data_size - 20];
+ register u32 crc, p_r, p_r1;
+
+ if (inv & 4) {
+ inv &= ~4;
+ --p;
+ }
+ p_r = *p;
+ p_r1 = *(p-1);
+ switch (inv) {
+ case 0:
+ crc = (p_r & 0xffff) + (p_r >> 16);
+ break;
+ case 1:
+ crc = (p_r >> 16) + (p_r & 0xffff)
+ + (p_r1 >> 16 & 0xff00);
+ break;
+ case 2:
+ crc = p_r + (p_r1 >> 16);
+ break;
+ case 3:
+ crc = p_r + (p_r1 & 0xff00) + (p_r1 >> 16);
+ break;
+ default: /*NOTREACHED*/ crc = 0;
+ }
+ if (crc & 0xffff0000) {
+ crc &= 0xffff;
+ ++crc;
+ }
+ /* tcp/udp will add in pseudo */
+ skb->csum = ntohs(pfck & 0xffff);
+ if (skb->csum > crc)
+ skb->csum -= crc;
+ else
+ skb->csum += (~crc & 0xffff);
+ /*
+ * could do the pseudo myself and return
+ * CHECKSUM_UNNECESSARY
+ */
+ skb->ip_summed = CHECKSUM_HW;
+ }
+ }
+ }
+#endif /* RX_CHECKSUM */
+
+ netif_rx(skb);
+ dev->last_rx = jiffies;
+ hmp->stats.rx_packets++;
+ }
+ entry = (++hmp->cur_rx) % RX_RING_SIZE;
+ hmp->rx_head_desc = &hmp->rx_ring[entry];
+ }
+
+ /* Refill the Rx ring buffers. */
+ for (; hmp->cur_rx - hmp->dirty_rx > 0; hmp->dirty_rx++) {
+ struct sk_buff *skb;
+ entry = hmp->dirty_rx % RX_RING_SIZE;
+ if (hmp->rx_skbuff[entry] == NULL) {
+ skb = dev_alloc_skb(hmp->rx_buf_sz);
+ hmp->rx_skbuff[entry] = skb;
+ if (skb == NULL)
+ break; /* Better luck next round. */
+ skb->dev = dev; /* Mark as being used by this device. */
+ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
+ hmp->rx_ring[entry].addr = virt_to_desc(skb->tail);
+ }
+ hmp->rx_ring[entry].status_n_length = cpu_to_le32(hmp->rx_buf_sz);
+ if (entry >= RX_RING_SIZE-1)
+ hmp->rx_ring[entry].status_n_length |=
+ cpu_to_le32(DescOwn | DescEndPacket | DescEndRing | DescIntr);
+ else
+ hmp->rx_ring[entry].status_n_length |=
+ cpu_to_le32(DescOwn | DescEndPacket | DescIntr);
+ }
+
+ /* Restart Rx engine if stopped. */
+ /* If we don't need to check status, don't. -KDU */
+ if (readw(dev->base_addr + RxStatus) & 0x0002)
+ writew(0x0001, dev->base_addr + RxCmd);
+
+ return 0;
+}
+
+/* This is more properly named "uncommon interrupt events", as it covers more
+ than just errors. */
+static void hamachi_error(struct net_device *dev, int intr_status)
+{
+ long ioaddr = dev->base_addr;
+ struct hamachi_private *hmp = (struct hamachi_private *)dev->priv;
+
+ if (intr_status & (LinkChange|NegotiationChange)) {
+ if (hamachi_debug > 1)
+ printk(KERN_INFO "%s: Link changed: AutoNegotiation Ctrl"
+ " %4.4x, Status %4.4x %4.4x Intr status %4.4x.\n",
+ dev->name, readw(ioaddr + 0x0E0), readw(ioaddr + 0x0E2),
+ readw(ioaddr + ANLinkPartnerAbility),
+ readl(ioaddr + IntrStatus));
+ if (readw(ioaddr + ANStatus) & 0x20)
+ writeb(0x01, ioaddr + LEDCtrl);
+ else
+ writeb(0x03, ioaddr + LEDCtrl);
+ }
+ if (intr_status & StatsMax) {
+ hamachi_get_stats(dev);
+ /* Read the overflow bits to clear. */
+ readl(ioaddr + 0x370);
+ readl(ioaddr + 0x3F0);
+ }
+ if ((intr_status & ~(LinkChange|StatsMax|NegotiationChange|IntrRxDone|IntrTxDone))
+ && hamachi_debug)
+ printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n",
+ dev->name, intr_status);
+ /* Hmmmmm, it's not clear how to recover from PCI faults. */
+ if (intr_status & (IntrTxPCIErr | IntrTxPCIFault))
+ hmp->stats.tx_fifo_errors++;
+ if (intr_status & (IntrRxPCIErr | IntrRxPCIFault))
+ hmp->stats.rx_fifo_errors++;
+}
+
+static int hamachi_close(struct net_device *dev)
+{
+ long ioaddr = dev->base_addr;
+ struct hamachi_private *hmp = (struct hamachi_private *)dev->priv;
+ int i;
+
+ netif_stop_queue(dev);
+
+ if (hamachi_debug > 1) {
+ printk(KERN_DEBUG "%s: Shutting down ethercard, status was Tx %4.4x Rx %4.4x Int %2.2x.\n",
+ dev->name, readw(ioaddr + TxStatus),
+ readw(ioaddr + RxStatus), readl(ioaddr + IntrStatus));
+ printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d, Rx %d / %d.\n",
+ dev->name, hmp->cur_tx, hmp->dirty_tx, hmp->cur_rx, hmp->dirty_rx);
+ }
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ writel(0x0000, ioaddr + InterruptEnable);
+
+ /* Stop the chip's Tx and Rx processes. */
+ writel(2, ioaddr + RxCmd);
+ writew(2, ioaddr + TxCmd);
+
+#ifdef __i386__
+ if (hamachi_debug > 2) {
+ printk("\n"KERN_DEBUG" Tx ring at %8.8x:\n",
+ (int)virt_to_bus(hmp->tx_ring));
+ for (i = 0; i < TX_RING_SIZE; i++)
+ printk(" %c #%d desc. %8.8x %8.8x.\n",
+ readl(ioaddr + TxCurPtr) == (long)&hmp->tx_ring[i] ? '>' : ' ',
+ i, hmp->tx_ring[i].status_n_length, hmp->tx_ring[i].addr);
+ printk("\n"KERN_DEBUG " Rx ring %8.8x:\n",
+ (int)virt_to_bus(hmp->rx_ring));
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ printk(KERN_DEBUG " %c #%d desc. %4.4x %8.8x\n",
+ readl(ioaddr + RxCurPtr) == (long)&hmp->rx_ring[i] ? '>' : ' ',
+ i, hmp->rx_ring[i].status_n_length, hmp->rx_ring[i].addr);
+ if (hamachi_debug > 6) {
+ if (*(u8*)bus_to_virt(hmp->rx_ring[i].addr) != 0x69) {
+ int j;
+ for (j = 0; j < 0x50; j++)
+ printk(" %4.4x",((u16*)le32desc_to_virt(hmp->rx_ring[i].addr))[j]);
+ printk("\n");
+ }
+ }
+ }
+ }
+#endif /* __i386__ debugging only */
+
+ free_irq(dev->irq, dev);
+
+ del_timer_sync(&hmp->timer);
+
+ /* Free all the skbuffs in the Rx queue. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ hmp->rx_ring[i].status_n_length = 0;
+ hmp->rx_ring[i].addr = 0xBADF00D0; /* An invalid address. */
+ if (hmp->rx_skbuff[i]) {
+#if LINUX_VERSION_CODE < 0x20100
+ hmp->rx_skbuff[i]->free = 1;
+#endif
+ dev_kfree_skb(hmp->rx_skbuff[i]);
+ }
+ hmp->rx_skbuff[i] = 0;
+ }
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ if (hmp->tx_skbuff[i])
+ dev_kfree_skb(hmp->tx_skbuff[i]);
+ hmp->tx_skbuff[i] = 0;
+ }
+
+ writeb(0x00, ioaddr + LEDCtrl);
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+static struct net_device_stats *hamachi_get_stats(struct net_device *dev)
+{
+ long ioaddr = dev->base_addr;
+ struct hamachi_private *hmp = (struct hamachi_private *)dev->priv;
+
+ /* We should lock this segment of code for SMP eventually, although
+ the vulnerability window is very small and statistics are
+ non-critical. */
+ /* Ok, what goes here? This appears to be stuck at 21 packets
+ according to ifconfig. It does get incremented in hamachi_tx(),
+ so I think I'll comment it out here and see if better things
+ happen.
+ */
+ /* hmp->stats.tx_packets = readl(ioaddr + 0x000); */
+
+#if LINUX_VERSION_CODE >= 0x20119
+ hmp->stats.rx_bytes = readl(ioaddr + 0x330); /* Total Uni+Brd+Multi */
+ hmp->stats.tx_bytes = readl(ioaddr + 0x3B0); /* Total Uni+Brd+Multi */
+#endif
+ hmp->stats.multicast = readl(ioaddr + 0x320); /* Multicast Rx */
+
+ hmp->stats.rx_length_errors = readl(ioaddr + 0x368); /* Over+Undersized */
+ hmp->stats.rx_over_errors = readl(ioaddr + 0x35C); /* Jabber */
+ hmp->stats.rx_crc_errors = readl(ioaddr + 0x360); /* Jabber */
+ hmp->stats.rx_frame_errors = readl(ioaddr + 0x364); /* Symbol Errs */
+ hmp->stats.rx_missed_errors = readl(ioaddr + 0x36C); /* Dropped */
+
+ return &hmp->stats;
+}
+
+static void set_rx_mode(struct net_device *dev)
+{
+ long ioaddr = dev->base_addr;
+
+ if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
+ /* Unconditionally log net taps. */
+ printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name);
+ writew(0x000F, ioaddr + AddrMode);
+ } else if ((dev->mc_count > 63) || (dev->flags & IFF_ALLMULTI)) {
+ /* Too many to match, or accept all multicasts. */
+ writew(0x000B, ioaddr + AddrMode);
+ } else if (dev->mc_count > 0) { /* Must use the CAM filter. */
+ struct dev_mc_list *mclist;
+ int i;
+ for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+ i++, mclist = mclist->next) {
+ writel(*(u32*)(mclist->dmi_addr), ioaddr + 0x100 + i*8);
+ writel(0x20000 | (*(u16*)&mclist->dmi_addr[4]),
+ ioaddr + 0x104 + i*8);
+ }
+ /* Clear remaining entries. */
+ for (; i < 64; i++)
+ writel(0, ioaddr + 0x104 + i*8);
+ writew(0x0003, ioaddr + AddrMode);
+ } else { /* Normal, unicast/broadcast-only mode. */
+ writew(0x0001, ioaddr + AddrMode);
+ }
+}
+
+#ifdef HAVE_PRIVATE_IOCTL
+static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ long ioaddr = dev->base_addr;
+ u16 *data = (u16 *)&rq->ifr_data;
+
+ switch(cmd) {
+ case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
+ data[0] = ((struct hamachi_private *)dev->priv)->phys[0] & 0x1f;
+ /* Fall Through */
+ case SIOCDEVPRIVATE+1: /* Read the specified MII register. */
+ data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f);
+ return 0;
+ case SIOCDEVPRIVATE+2: /* Write the specified MII register */
+ /* TODO: Check the sequencing of this. Might need to stop and
+ * restart Rx and Tx engines. -KDU
+ */
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+ return 0;
+ case SIOCDEVPRIVATE+3: { /* set rx,tx intr params */
+ u32 *d = (u32 *)&rq->ifr_data;
+ /* Should add this check here or an ordinary user can do nasty
+ * things. -KDU
+ *
+ * TODO: Shut down the Rx and Tx engines while doing this.
+ */
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ writel(d[0], dev->base_addr + TxIntrCtrl);
+ writel(d[1], dev->base_addr + RxIntrCtrl);
+ printk(KERN_NOTICE "%s: tx %08x, rx %08x intr\n", dev->name,
+ (u32) readl(dev->base_addr + TxIntrCtrl),
+ (u32) readl(dev->base_addr + RxIntrCtrl));
+ return 0;
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+#endif /* HAVE_PRIVATE_IOCTL */
+
+
+static void __exit hamachi_remove_one (struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+ if (dev) {
+ unregister_netdev(dev);
+ iounmap((char *)dev->base_addr);
+ kfree(dev);
+ pci_set_drvdata(pdev, NULL);
+ }
+}
+
+static struct pci_device_id hamachi_pci_tbl[] __initdata = {
+ { 0x1318, 0x0911, PCI_ANY_ID, PCI_ANY_ID, },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, hamachi_pci_tbl);
+
+static struct pci_driver hamachi_driver = {
+ name: "hamachi",
+ id_table: hamachi_pci_tbl,
+ probe: hamachi_init_one,
+ remove: hamachi_remove_one,
+};
+
+static int __init hamachi_init (void)
+{
+ if (pci_register_driver(&hamachi_driver) > 0)
+ return 0;
+ pci_unregister_driver(&hamachi_driver);
+ return -ENODEV;
+}
+
+static void __exit hamachi_exit (void)
+{
+ pci_unregister_driver(&hamachi_driver);
+}
+
+
+module_init(hamachi_init);
+module_exit(hamachi_exit);
#ifdef MODULE
int init_module(void)
#else
-void __init pppox_proto_init(struct net_proto *pro)
+int __init pppox_proto_init(struct net_proto *pro)
#endif
{
int err = 0;
err = sock_register(&pppox_proto_family);
- if (err == 0)
+ if (err == 0) {
printk(KERN_INFO "Registered PPPoX v0.5\n");
-
- pppoe_init();
+ pppoe_init();
+ }
return err;
}
+
/*
* New style setup code for the network devices
*/
}
-static void __init appletalk_device_init(void)
+static void __init special_device_init(void)
{
-#if defined(CONFIG_IPDDP)
- extern int ipddp_init(struct net_device *dev);
- static struct net_device dev_ipddp = {
- "ipddp0" __PAD6,
- 0, 0, 0, 0,
- 0x0, 0,
- 0, 0, 0, NULL, ipddp_init
- };
-
- dev_ipddp.init(&dev_ipddp);
-#endif /* CONFIG_IPDDP */
-}
-
-static void special_device_init(void)
-{
-#ifdef CONFIG_DUMMY
- {
- extern int dummy_init(struct net_device *dev);
- static struct net_device dummy_dev = {
- "dummy" __PAD5, 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NULL, dummy_init,
- };
- register_netdev(&dummy_dev);
- }
-#endif
-#ifdef CONFIG_EQUALIZER
- {
- extern int eql_init(struct net_device *dev);
- static struct net_device eql_dev =
- {
- "eql" __PAD3, /* Master device for IP traffic load balancing */
- 0x0, 0x0, 0x0, 0x0, /* recv end/start; mem end/start */
- 0, /* base I/O address */
- 0, /* IRQ */
- 0, 0, 0, /* flags */
- NULL, /* next device */
- eql_init /* set up the rest */
- };
- register_netdev(&eql_dev);
- }
-#endif
-#ifdef CONFIG_APBIF
- {
- extern int bif_init(struct net_device *dev);
- static struct net_device bif_dev =
- {
- "bif" __PAD3, 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NULL, bif_init
- };
- register_netdev(&bif_dev);
- }
-#endif
#ifdef CONFIG_NET_SB1000
{
extern int sb1000_probe(struct net_device *dev);
register_netdev(&sb1000_dev);
}
#endif
-#ifdef CONFIG_BONDING
- {
- extern int bond_init(struct net_device *dev);
- static struct net_device bond_dev = {
- "bond" __PAD4, 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NULL, bond_init,
- };
- register_netdev(&bond_dev);
- }
-#endif
}
/*
network_probe();
/* Line disciplines */
network_ldisc_init();
- /* Appletalk */
- appletalk_device_init();
/* Special devices */
special_device_init();
/* That kicks off the legacy init functions */
--- /dev/null
+/* sundance.c: A Linux device driver for the Sundance ST201 "Alta". */
+/*
+ Written 1999-2000 by Donald Becker.
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License (GPL), incorporated herein by reference.
+ Drivers based on or derived from this code fall under the GPL and must
+ retain the authorship, copyright and license notice. This file is not
+ a complete program and may only be used when the entire operating
+ system is licensed under the GPL.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+ Support and updates available at
+ http://www.scyld.com/network/sundance.html
+*/
+
+/* These identify the driver base version and may not be removed. */
+static const char version1[] =
+"sundance.c:v1.01 4/09/00 Written by Donald Becker\n";
+static const char version2[] =
+" http://www.scyld.com/network/sundance.html\n";
+
+/* The user-configurable values.
+ These may be modified when a driver module is loaded.*/
+
+static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+static int max_interrupt_work = 20;
+static int mtu = 0;
+/* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
+ Typical is a 64 element hash table based on the Ethernet CRC. */
+static int multicast_filter_limit = 32;
+
+/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
+ Setting to > 1518 effectively disables this feature.
+ This chip can receive into offset buffers, so the Alpha does not
+ need a copy-align. */
+static int rx_copybreak = 0;
+
+/* Used to pass the media type, etc.
+ Both 'options[]' and 'full_duplex[]' should exist for driver
+ interoperability.
+ The media type is usually passed in 'options[]'.
+*/
+#define MAX_UNITS 8 /* More are supported, limit only on options */
+static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+
+/* Operational parameters that are set at compile time. */
+
+/* Keep the ring sizes a power of two for compile efficiency.
+ The compiler will convert <unsigned>'%'<2^N> into a bit mask.
+ Making the Tx ring too large decreases the effectiveness of channel
+ bonding and packet priority, and more than 128 requires modifying the
+ Tx error recovery.
+ Large receive rings merely waste memory. */
+#define TX_RING_SIZE 16
+#define TX_QUEUE_LEN 10 /* Limit ring entries actually used. */
+#define RX_RING_SIZE 32
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT (2*HZ)
+
+#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/
+
+#ifndef __KERNEL__
+#define __KERNEL__
+#endif
+#if !defined(__OPTIMIZE__)
+#warning You must compile this file with the correct options!
+#warning See the last lines of the source file.
+#error You must compile this driver with "-O".
+#endif
+
+/* Include files, designed to support most kernel versions 2.0.0 and later. */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <asm/processor.h> /* Processor type for cache alignment. */
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#include <linux/spinlock.h>
+
+
+/* Condensed operations for readability. */
+#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+
+
+MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
+MODULE_DESCRIPTION("Sundance Alta Ethernet driver");
+MODULE_PARM(max_interrupt_work, "i");
+MODULE_PARM(mtu, "i");
+MODULE_PARM(debug, "i");
+MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
+
+/*
+ Theory of Operation
+
+I. Board Compatibility
+
+This driver is designed for the Sundance Technologies "Alta" ST201 chip.
+
+II. Board-specific settings
+
+III. Driver operation
+
+IIIa. Ring buffers
+
+This driver uses two statically allocated fixed-size descriptor lists
+formed into rings by a branch from the final descriptor to the beginning of
+the list. The ring sizes are set at compile time by RX/TX_RING_SIZE.
+Some chips explicitly use only 2^N sized rings, while others use a
+'next descriptor' pointer that the driver forms into rings.
+
+IIIb/c. Transmit/Receive Structure
+
+This driver uses a zero-copy receive and transmit scheme.
+The driver allocates full frame size skbuffs for the Rx ring buffers at
+open() time and passes the skb->data field to the chip as receive data
+buffers. When an incoming frame is less than RX_COPYBREAK bytes long,
+a fresh skbuff is allocated and the frame is copied to the new skbuff.
+When the incoming frame is larger, the skbuff is passed directly up the
+protocol stack. Buffers consumed this way are replaced by newly allocated
+skbuffs in a later phase of receives.
+
+The RX_COPYBREAK value is chosen to trade-off the memory wasted by
+using a full-sized skbuff for small frames vs. the copying costs of larger
+frames. New boards are typically used in generously configured machines
+and the underfilled buffers have negligible impact compared to the benefit of
+a single allocation size, so the default value of zero results in never
+copying packets. When copying is done, the cost is usually mitigated by using
+a combined copy/checksum routine. Copying also preloads the cache, which is
+most useful with small frames.
+
+A subtle aspect of the operation is that the IP header at offset 14 in an
+ethernet frame isn't longword aligned for further processing.
+Unaligned buffers are permitted by the Sundance hardware, so
+frames are received into the skbuff at an offset of "+2", 16-byte aligning
+the IP header.
+
+IIId. Synchronization
+
+The driver runs as two independent, single-threaded flows of control. One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag. The other thread is the interrupt handler, which is single
+threaded by the hardware and interrupt handling software.
+
+The send packet thread has partial control over the Tx ring and 'dev->tbusy'
+flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next
+queue slot is empty, it clears the tbusy flag when finished otherwise it sets
+the 'lp->tx_full' flag.
+
+The interrupt handler has exclusive control over the Rx ring and records stats
+from the Tx ring. After reaping the stats, it marks the Tx queue entry as
+empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it
+clears both the tx_full and tbusy flags.
+
+IV. Notes
+
+IVb. References
+
+The Sundance ST201 datasheet, preliminary version.
+http://cesdis.gsfc.nasa.gov/linux/misc/100mbps.html
+http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
+
+IVc. Errata
+
+*/
+
+\f
+
+enum pci_id_flags_bits {
+ /* Set PCI command register bits before calling probe1(). */
+ PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
+ /* Read and map the single following PCI BAR. */
+ PCI_ADDR0=0<<4, PCI_ADDR1=1<<4, PCI_ADDR2=2<<4, PCI_ADDR3=3<<4,
+ PCI_ADDR_64BITS=0x100, PCI_NO_ACPI_WAKE=0x200, PCI_NO_MIN_LATENCY=0x400,
+};
+enum chip_capability_flags {CanHaveMII=1, };
+#ifdef USE_IO_OPS
+#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_IO | PCI_ADDR0)
+#else
+#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR1)
+#endif
+
+static struct pci_device_id sundance_pci_tbl[] __devinitdata = {
+ { 0x1186, 0x1002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0x13F0, 0x0201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, sundance_pci_tbl);
+
+struct pci_id_info {
+ const char *name;
+ struct match_info {
+ int pci, pci_mask, subsystem, subsystem_mask;
+ int revision, revision_mask; /* Only 8 bits. */
+ } id;
+ enum pci_id_flags_bits pci_flags;
+ int io_size; /* Needed for I/O region check or ioremap(). */
+ int drv_flags; /* Driver use, intended as capability flags. */
+};
+static struct pci_id_info pci_id_tbl[] = {
+ {"OEM Sundance Technology ST201", {0x10021186, 0xffffffff, },
+ PCI_IOTYPE, 128, CanHaveMII},
+ {"Sundance Technology Alta", {0x020113F0, 0xffffffff, },
+ PCI_IOTYPE, 128, CanHaveMII},
+ {0,}, /* 0 terminated list. */
+};
+
+/* This driver was written to use PCI memory space, however x86-oriented
+ hardware often uses I/O space accesses. */
+#ifdef USE_IO_OPS
+#undef readb
+#undef readw
+#undef readl
+#undef writeb
+#undef writew
+#undef writel
+#define readb inb
+#define readw inw
+#define readl inl
+#define writeb outb
+#define writew outw
+#define writel outl
+#endif
+
+/* Offsets to the device registers.
+ Unlike software-only systems, device drivers interact with complex hardware.
+ It's not useful to define symbolic names for every register bit in the
+ device. The name can only partially document the semantics and make
+ the driver longer and more difficult to read.
+ In general, only the important configuration values or bits changed
+ multiple times should be defined symbolically.
+*/
+enum alta_offsets {
+ DMACtrl=0x00, TxListPtr=0x04, TxDMACtrl=0x08, TxDescPoll=0x0a,
+ RxDMAStatus=0x0c, RxListPtr=0x10, RxDMACtrl=0x14, RxDescPoll=0x16,
+ LEDCtrl=0x1a, ASICCtrl=0x30,
+ EEData=0x34, EECtrl=0x36, TxThreshold=0x3c,
+ FlashAddr=0x40, FlashData=0x44, TxStatus=0x46, DownCounter=0x48,
+ IntrClear=0x4a, IntrEnable=0x4c, IntrStatus=0x4e,
+ MACCtrl0=0x50, MACCtrl1=0x52, StationAddr=0x54,
+ MaxTxSize=0x5A, RxMode=0x5c, MIICtrl=0x5e,
+ MulticastFilter0=0x60, MulticastFilter1=0x64,
+ RxOctetsLow=0x68, RxOctetsHigh=0x6a, TxOctetsLow=0x6c, TxOctetsHigh=0x6e,
+ TxFramesOK=0x70, RxFramesOK=0x72, StatsCarrierError=0x74,
+ StatsLateColl=0x75, StatsMultiColl=0x76, StatsOneColl=0x77,
+ StatsTxDefer=0x78, RxMissed=0x79, StatsTxXSDefer=0x7a, StatsTxAbort=0x7b,
+ StatsBcastTx=0x7c, StatsBcastRx=0x7d, StatsMcastTx=0x7e, StatsMcastRx=0x7f,
+ /* Aliased and bogus values! */
+ RxStatus=0x0c,
+};
+
+/* Bits in the interrupt status/mask registers. */
+enum intr_status_bits {
+ IntrSummary=0x0001, IntrPCIErr=0x0002, IntrMACCtrl=0x0008,
+ IntrTxDone=0x0004, IntrRxDone=0x0010, IntrRxStart=0x0020,
+ IntrDrvRqst=0x0040,
+ StatsMax=0x0080, LinkChange=0x0100,
+ IntrTxDMADone=0x0200, IntrRxDMADone=0x0400,
+};
+
+/* Bits in the RxMode register. */
+enum rx_mode_bits {
+ AcceptAllIPMulti=0x20, AcceptMultiHash=0x10, AcceptAll=0x08,
+ AcceptBroadcast=0x04, AcceptMulticast=0x02, AcceptMyPhys=0x01,
+};
+/* Bits in MACCtrl. */
+enum mac_ctrl0_bits {
+ EnbFullDuplex=0x20, EnbRcvLargeFrame=0x40,
+ EnbFlowCtrl=0x100, EnbPassRxCRC=0x200,
+};
+enum mac_ctrl1_bits {
+ StatsEnable=0x0020, StatsDisable=0x0040, StatsEnabled=0x0080,
+ TxEnable=0x0100, TxDisable=0x0200, TxEnabled=0x0400,
+ RxEnable=0x0800, RxDisable=0x1000, RxEnabled=0x2000,
+};
+
+/* The Rx and Tx buffer descriptors. */
+/* Note that using only 32 bit fields simplifies conversion to big-endian
+ architectures. */
+struct netdev_desc {
+ u32 next_desc;
+ u32 status;
+ struct desc_frag { u32 addr, length; } frag[1];
+};
+
+/* Bits in netdev_desc.status */
+enum desc_status_bits {
+ DescOwn=0x8000, DescEndPacket=0x4000, DescEndRing=0x2000,
+ LastFrag=0x80000000, DescIntrOnTx=0x8000, DescIntrOnDMADone=0x80000000,
+};
+
+#define PRIV_ALIGN 15 /* Required alignment mask */
+/* Use __attribute__((aligned (L1_CACHE_BYTES))) to maintain alignment
+ within the structure. */
+struct netdev_private {
+ /* Descriptor rings first for alignment. */
+ struct netdev_desc rx_ring[RX_RING_SIZE];
+ struct netdev_desc tx_ring[TX_RING_SIZE];
+ /* The addresses of receive-in-place skbuffs. */
+ struct sk_buff* rx_skbuff[RX_RING_SIZE];
+ /* The saved address of a sent-in-place packet/buffer, for later free(). */
+ struct sk_buff* tx_skbuff[TX_RING_SIZE];
+ struct net_device_stats stats;
+ struct timer_list timer; /* Media monitoring timer. */
+ /* Frequently used values: keep some adjacent for cache effect. */
+ spinlock_t lock;
+ int chip_id, drv_flags;
+ /* Note: Cache paragraph grouped variables. */
+ struct netdev_desc *rx_head_desc;
+ unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */
+ unsigned int rx_buf_sz; /* Based on MTU+slack. */
+ spinlock_t txlock; /* Group with Tx control cache line. */
+ struct netdev_desc *last_tx; /* Last Tx descriptor used. */
+ unsigned int cur_tx, dirty_tx;
+ unsigned int tx_full:1; /* The Tx queue is full. */
+ /* These values are keep track of the transceiver/media in use. */
+ unsigned int full_duplex:1; /* Full-duplex operation requested. */
+ unsigned int duplex_lock:1;
+ unsigned int medialock:1; /* Do not sense media. */
+ unsigned int default_port:4; /* Last dev->if_port value. */
+ /* Multicast and receive mode. */
+ spinlock_t mcastlock; /* SMP lock multicast updates. */
+ u16 mcast_filter[4];
+ /* MII transceiver section. */
+ int mii_cnt; /* MII device addresses. */
+ u16 advertising; /* NWay media advertisement */
+ unsigned char phys[2]; /* MII device addresses. */
+};
+
+/* The station address location in the EEPROM. */
+#define EEPROM_SA_OFFSET 0x10
+
+static int eeprom_read(long ioaddr, int location);
+static int mdio_read(struct net_device *dev, int phy_id, int location);
+static void mdio_write(struct net_device *dev, int phy_id, int location, int value);
+static int netdev_open(struct net_device *dev);
+static void check_duplex(struct net_device *dev);
+static void netdev_timer(unsigned long data);
+static void tx_timeout(struct net_device *dev);
+static void init_ring(struct net_device *dev);
+static int start_tx(struct sk_buff *skb, struct net_device *dev);
+static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs);
+static void netdev_error(struct net_device *dev, int intr_status);
+static int netdev_rx(struct net_device *dev);
+static void netdev_error(struct net_device *dev, int intr_status);
+static void set_rx_mode(struct net_device *dev);
+static struct net_device_stats *get_stats(struct net_device *dev);
+static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static int netdev_close(struct net_device *dev);
+
+\f
+
+static int __devinit sundance_probe1 (struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct net_device *dev;
+ struct netdev_private *np;
+ static int card_idx;
+ int chip_idx = ent->driver_data;
+ int irq = pdev->irq;
+ int i, option = card_idx < MAX_UNITS ? options[card_idx] : 0;
+ long ioaddr;
+
+ if (pci_enable_device(pdev))
+ return -EIO;
+ pci_set_master(pdev);
+
+ dev = init_etherdev(NULL, sizeof(*np));
+ if (!dev)
+ return -ENOMEM;
+
+#ifdef USE_IO_OPS
+ ioaddr = pci_resource_start(pdev, 0);
+ if (!request_region(ioaddr, pci_id_tbl[chip_idx].io_size, dev->name))
+ goto err_out_netdev;
+#else
+ ioaddr = pci_resource_start(pdev, 1);
+ if (!request_mem_region(ioaddr, pci_id_tbl[chip_idx].io_size, dev->name))
+ goto err_out_netdev;
+ ioaddr = (long) ioremap (ioaddr, pci_id_tbl[chip_idx].io_size);
+ if (!ioaddr)
+ goto err_out_iomem;
+#endif
+
+ printk(KERN_INFO "%s: %s at 0x%lx, ",
+ dev->name, pci_id_tbl[chip_idx].name, ioaddr);
+
+ for (i = 0; i < 3; i++)
+ ((u16 *)dev->dev_addr)[i] =
+ le16_to_cpu(eeprom_read(ioaddr, i + EEPROM_SA_OFFSET));
+ for (i = 0; i < 5; i++)
+ printk("%2.2x:", dev->dev_addr[i]);
+ printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq);
+
+ dev->base_addr = ioaddr;
+ dev->irq = irq;
+
+ np = dev->priv;
+ np->chip_id = chip_idx;
+ np->drv_flags = pci_id_tbl[chip_idx].drv_flags;
+ spin_lock_init(&np->lock);
+
+ if (dev->mem_start)
+ option = dev->mem_start;
+
+ /* The lower four bits are the media type. */
+ if (option > 0) {
+ if (option & 0x200)
+ np->full_duplex = 1;
+ np->default_port = option & 15;
+ if (np->default_port)
+ np->medialock = 1;
+ }
+ if (card_idx < MAX_UNITS && full_duplex[card_idx] > 0)
+ np->full_duplex = 1;
+
+ if (np->full_duplex)
+ np->duplex_lock = 1;
+
+ /* The chip-specific entries in the device structure. */
+ dev->open = &netdev_open;
+ dev->hard_start_xmit = &start_tx;
+ dev->stop = &netdev_close;
+ dev->get_stats = &get_stats;
+ dev->set_multicast_list = &set_rx_mode;
+ dev->do_ioctl = &mii_ioctl;
+ dev->tx_timeout = &tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+ if (mtu)
+ dev->mtu = mtu;
+
+ if (1) {
+ int phy, phy_idx = 0;
+ np->phys[0] = 1; /* Default setting */
+ for (phy = 0; phy < 32 && phy_idx < 4; phy++) {
+ int mii_status = mdio_read(dev, phy, 1);
+ if (mii_status != 0xffff && mii_status != 0x0000) {
+ np->phys[phy_idx++] = phy;
+ np->advertising = mdio_read(dev, phy, 4);
+ printk(KERN_INFO "%s: MII PHY found at address %d, status "
+ "0x%4.4x advertising %4.4x.\n",
+ dev->name, phy, mii_status, np->advertising);
+ }
+ }
+ np->mii_cnt = phy_idx;
+ if (phy_idx == 0)
+ printk(KERN_INFO "%s: No MII transceiver found!, ASIC status %x\n",
+ dev->name, readl(ioaddr + ASICCtrl));
+ }
+
+ /* Perhaps move the reset here? */
+ /* Reset the chip to erase previous misconfiguration. */
+ if (debug > 1)
+ printk("ASIC Control is %x.\n", readl(ioaddr + ASICCtrl));
+ writew(0x007f, ioaddr + ASICCtrl + 2);
+ if (debug > 1)
+ printk("ASIC Control is now %x.\n", readl(ioaddr + ASICCtrl));
+
+ card_idx++;
+ return 0;
+
+#ifndef USE_IO_OPS
+err_out_iomem:
+ release_mem_region(pci_resource_start(pdev, 1),
+ pci_id_tbl[chip_idx].io_size);
+#endif
+err_out_netdev:
+ unregister_netdev (dev);
+ kfree (dev);
+ return -ENODEV;
+}
+
+\f
+/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. */
+static int eeprom_read(long ioaddr, int location)
+{
+ int boguscnt = 1000; /* Typical 190 ticks. */
+ writew(0x0200 | (location & 0xff), ioaddr + EECtrl);
+ do {
+ if (! (readw(ioaddr + EECtrl) & 0x8000)) {
+ return readw(ioaddr + EEData);
+ }
+ } while (--boguscnt > 0);
+ return 0;
+}
+
+/* MII transceiver control section.
+ Read and write the MII registers using software-generated serial
+ MDIO protocol. See the MII specifications or DP83840A data sheet
+ for details.
+
+ The maximum data clock rate is 2.5 Mhz. The minimum timing is usually
+ met by back-to-back 33Mhz PCI cycles. */
+#define mdio_delay() readb(mdio_addr)
+
+/* Set iff a MII transceiver on any interface requires mdio preamble.
+ This only set with older tranceivers, so the extra
+ code size of a per-interface flag is not worthwhile. */
+static char mii_preamble_required = 0;
+
+enum mii_reg_bits {
+ MDIO_ShiftClk=0x0001, MDIO_Data=0x0002, MDIO_EnbOutput=0x0004,
+};
+#define MDIO_EnbIn (0)
+#define MDIO_WRITE0 (MDIO_EnbOutput)
+#define MDIO_WRITE1 (MDIO_Data | MDIO_EnbOutput)
+
+/* Generate the preamble required for initial synchronization and
+ a few older transceivers. */
+static void mdio_sync(long mdio_addr)
+{
+ int bits = 32;
+
+ /* Establish sync by sending at least 32 logic ones. */
+ while (--bits >= 0) {
+ writeb(MDIO_WRITE1, mdio_addr);
+ mdio_delay();
+ writeb(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr);
+ mdio_delay();
+ }
+}
+
+static int mdio_read(struct net_device *dev, int phy_id, int location)
+{
+ long mdio_addr = dev->base_addr + MIICtrl;
+ int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+ int i, retval = 0;
+
+ if (mii_preamble_required)
+ mdio_sync(mdio_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 15; i >= 0; i--) {
+ int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
+
+ writeb(dataval, mdio_addr);
+ mdio_delay();
+ writeb(dataval | MDIO_ShiftClk, mdio_addr);
+ mdio_delay();
+ }
+ /* Read the two transition, 16 data, and wire-idle bits. */
+ for (i = 19; i > 0; i--) {
+ writeb(MDIO_EnbIn, mdio_addr);
+ mdio_delay();
+ retval = (retval << 1) | ((readb(mdio_addr) & MDIO_Data) ? 1 : 0);
+ writeb(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
+ mdio_delay();
+ }
+ return (retval>>1) & 0xffff;
+}
+
+static void mdio_write(struct net_device *dev, int phy_id, int location, int value)
+{
+ long mdio_addr = dev->base_addr + MIICtrl;
+ int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
+ int i;
+
+ if (mii_preamble_required)
+ mdio_sync(mdio_addr);
+
+ /* Shift the command bits out. */
+ for (i = 31; i >= 0; i--) {
+ int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
+
+ writeb(dataval, mdio_addr);
+ mdio_delay();
+ writeb(dataval | MDIO_ShiftClk, mdio_addr);
+ mdio_delay();
+ }
+ /* Clear out extra bits. */
+ for (i = 2; i > 0; i--) {
+ writeb(MDIO_EnbIn, mdio_addr);
+ mdio_delay();
+ writeb(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
+ mdio_delay();
+ }
+ return;
+}
+
+\f
+static int netdev_open(struct net_device *dev)
+{
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ long ioaddr = dev->base_addr;
+ int i;
+
+ /* Do we need to reset the chip??? */
+
+ MOD_INC_USE_COUNT;
+
+ if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) {
+ MOD_DEC_USE_COUNT;
+ return -EAGAIN;
+ }
+
+ if (debug > 1)
+ printk(KERN_DEBUG "%s: netdev_open() irq %d.\n",
+ dev->name, dev->irq);
+
+ init_ring(dev);
+
+ writel(virt_to_bus(np->rx_ring), ioaddr + RxListPtr);
+ /* The Tx list pointer is written as packets are queued. */
+
+ for (i = 0; i < 6; i++)
+ writeb(dev->dev_addr[i], ioaddr + StationAddr + i);
+
+ /* Initialize other registers. */
+ /* Configure the PCI bus bursts and FIFO thresholds. */
+
+ if (dev->if_port == 0)
+ dev->if_port = np->default_port;
+
+ np->full_duplex = np->duplex_lock;
+ np->mcastlock = (spinlock_t) SPIN_LOCK_UNLOCKED;
+
+ set_rx_mode(dev);
+ writew(0, ioaddr + DownCounter);
+ /* Set the chip to poll every N*320nsec. */
+ writeb(100, ioaddr + RxDescPoll);
+ writeb(127, ioaddr + TxDescPoll);
+ netif_start_queue(dev);
+
+ /* Enable interrupts by setting the interrupt mask. */
+ writew(IntrRxDone | IntrRxDMADone | IntrPCIErr | IntrDrvRqst | IntrTxDone
+ | StatsMax | LinkChange, ioaddr + IntrEnable);
+
+ writew(StatsEnable | RxEnable | TxEnable, ioaddr + MACCtrl1);
+
+ if (debug > 2)
+ printk(KERN_DEBUG "%s: Done netdev_open(), status: Rx %x Tx %x "
+ "MAC Control %x, %4.4x %4.4x.\n",
+ dev->name, readl(ioaddr + RxStatus), readb(ioaddr + TxStatus),
+ readl(ioaddr + MACCtrl0),
+ readw(ioaddr + MACCtrl1), readw(ioaddr + MACCtrl0));
+
+ /* Set the timer to check for link beat. */
+ init_timer(&np->timer);
+ np->timer.expires = jiffies + 3*HZ;
+ np->timer.data = (unsigned long)dev;
+ np->timer.function = &netdev_timer; /* timer handler */
+ add_timer(&np->timer);
+
+ return 0;
+}
+
+static void check_duplex(struct net_device *dev)
+{
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ long ioaddr = dev->base_addr;
+ int mii_reg5 = mdio_read(dev, np->phys[0], 5);
+ int negotiated = mii_reg5 & np->advertising;
+ int duplex;
+
+ if (np->duplex_lock || mii_reg5 == 0xffff)
+ return;
+ duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040;
+ if (np->full_duplex != duplex) {
+ np->full_duplex = duplex;
+ if (debug)
+ printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d "
+ "negotiated capability %4.4x.\n", dev->name,
+ duplex ? "full" : "half", np->phys[0], negotiated);
+ writew(duplex ? 0x20 : 0, ioaddr + MACCtrl0);
+ }
+}
+
+static void netdev_timer(unsigned long data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ long ioaddr = dev->base_addr;
+ int next_tick = 10*HZ;
+
+ if (debug > 3) {
+ printk(KERN_DEBUG "%s: Media selection timer tick, intr status %4.4x, "
+ "Tx %x Rx %x.\n",
+ dev->name, readw(ioaddr + IntrEnable),
+ readb(ioaddr + TxStatus), readl(ioaddr + RxStatus));
+ }
+ check_duplex(dev);
+ np->timer.expires = jiffies + next_tick;
+ add_timer(&np->timer);
+}
+
+static void tx_timeout(struct net_device *dev)
+{
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ long ioaddr = dev->base_addr;
+
+ printk(KERN_WARNING "%s: Transmit timed out, status %2.2x,"
+ " resetting...\n", dev->name, readb(ioaddr + TxStatus));
+
+#ifndef __alpha__
+ {
+ int i;
+ printk(KERN_DEBUG " Rx ring %8.8x: ", (int)np->rx_ring);
+ for (i = 0; i < RX_RING_SIZE; i++)
+ printk(" %8.8x", (unsigned int)np->rx_ring[i].status);
+ printk("\n"KERN_DEBUG" Tx ring %8.8x: ", (int)np->tx_ring);
+ for (i = 0; i < TX_RING_SIZE; i++)
+ printk(" %4.4x", np->tx_ring[i].status);
+ printk("\n");
+ }
+#endif
+
+ /* Perhaps we should reinitialize the hardware here. */
+ dev->if_port = 0;
+ /* Stop and restart the chip's Tx processes . */
+
+ /* Trigger an immediate transmit demand. */
+ writew(IntrRxDone | IntrRxDMADone | IntrPCIErr | IntrDrvRqst | IntrTxDone
+ | StatsMax | LinkChange, ioaddr + IntrEnable);
+
+ dev->trans_start = jiffies;
+ np->stats.tx_errors++;
+ return;
+}
+
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void init_ring(struct net_device *dev)
+{
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ int i;
+
+ np->tx_full = 0;
+ np->cur_rx = np->cur_tx = 0;
+ np->dirty_rx = np->dirty_tx = 0;
+
+ np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
+ np->rx_head_desc = &np->rx_ring[0];
+
+ /* Initialize all Rx descriptors. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ np->rx_ring[i].next_desc = virt_to_le32desc(&np->rx_ring[i+1]);
+ np->rx_ring[i].status = 0;
+ np->rx_ring[i].frag[0].length = 0;
+ np->rx_skbuff[i] = 0;
+ }
+ /* Wrap the ring. */
+ np->rx_ring[i-1].next_desc = virt_to_le32desc(&np->rx_ring[0]);
+
+ /* Fill in the Rx buffers. Handle allocation failure gracefully. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz);
+ np->rx_skbuff[i] = skb;
+ if (skb == NULL)
+ break;
+ skb->dev = dev; /* Mark as being used by this device. */
+ skb_reserve(skb, 2); /* 16 byte align the IP header. */
+ np->rx_ring[i].frag[0].addr = virt_to_le32desc(skb->tail);
+ np->rx_ring[i].frag[0].length = cpu_to_le32(np->rx_buf_sz | LastFrag);
+ }
+ np->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
+
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ np->tx_skbuff[i] = 0;
+ np->tx_ring[i].status = 0;
+ }
+ return;
+}
+
+static int start_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ struct netdev_desc *txdesc;
+ unsigned entry;
+
+ /* Note: Ordering is important here, set the field with the
+ "ownership" bit last, and only then increment cur_tx. */
+
+ /* Calculate the next Tx descriptor entry. */
+ entry = np->cur_tx % TX_RING_SIZE;
+ np->tx_skbuff[entry] = skb;
+ txdesc = &np->tx_ring[entry];
+
+ txdesc->next_desc = 0;
+ /* Note: disable the interrupt generation here before releasing. */
+ txdesc->status =
+ cpu_to_le32((entry<<2) | DescIntrOnDMADone | DescIntrOnTx);
+ txdesc->frag[0].addr = virt_to_le32desc(skb->data);
+ txdesc->frag[0].length = cpu_to_le32(skb->len | LastFrag);
+ if (np->last_tx)
+ np->last_tx->next_desc = virt_to_le32desc(txdesc);
+ np->last_tx = txdesc;
+ np->cur_tx++;
+
+ /* On some architectures: explicitly flush cache lines here. */
+
+ if (np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 1) {
+ /* do nothing */
+ } else {
+ np->tx_full = 1;
+ netif_stop_queue(dev);
+ }
+ /* Side effect: The read wakes the potentially-idle transmit channel. */
+ if (readl(dev->base_addr + TxListPtr) == 0)
+ writel(virt_to_bus(&np->tx_ring[entry]), dev->base_addr + TxListPtr);
+
+ dev->trans_start = jiffies;
+
+ if (debug > 4) {
+ printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n",
+ dev->name, np->cur_tx, entry);
+ }
+ return 0;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+ after the Tx thread. */
+static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs)
+{
+ struct net_device *dev = (struct net_device *)dev_instance;
+ struct netdev_private *np;
+ long ioaddr;
+ int boguscnt = max_interrupt_work;
+
+ ioaddr = dev->base_addr;
+ np = (struct netdev_private *)dev->priv;
+ spin_lock(&np->lock);
+
+ do {
+ int intr_status = readw(ioaddr + IntrStatus);
+ writew(intr_status & (IntrRxDone | IntrRxDMADone | IntrPCIErr |
+ IntrDrvRqst |IntrTxDone|IntrTxDMADone |
+ StatsMax | LinkChange),
+ ioaddr + IntrStatus);
+
+ if (debug > 4)
+ printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n",
+ dev->name, intr_status);
+
+ if (intr_status == 0)
+ break;
+
+ if (intr_status & (IntrRxDone|IntrRxDMADone))
+ netdev_rx(dev);
+
+ if (intr_status & IntrTxDone) {
+ int boguscnt = 32;
+ int tx_status = readw(ioaddr + TxStatus);
+ while (tx_status & 0x80) {
+ if (debug > 4)
+ printk("%s: Transmit status is %2.2x.\n",
+ dev->name, tx_status);
+ if (tx_status & 0x1e) {
+ np->stats.tx_errors++;
+ if (tx_status & 0x10) np->stats.tx_fifo_errors++;
+#ifdef ETHER_STATS
+ if (tx_status & 0x08) np->stats.collisions16++;
+#else
+ if (tx_status & 0x08) np->stats.collisions++;
+#endif
+ if (tx_status & 0x04) np->stats.tx_fifo_errors++;
+ if (tx_status & 0x02) np->stats.tx_window_errors++;
+ /* This reset has not been verified!. */
+ if (tx_status & 0x10) { /* Reset the Tx. */
+ writew(0x001c, ioaddr + ASICCtrl + 2);
+#if 0 /* Do we need to reset the Tx pointer here? */
+ writel(virt_to_bus(&np->tx_ring[np->dirty_tx]),
+ dev->base_addr + TxListPtr);
+#endif
+ }
+ if (tx_status & 0x1e) /* Restart the Tx. */
+ writew(TxEnable, ioaddr + MACCtrl1);
+ }
+ /* Yup, this is a documentation bug. It cost me *hours*. */
+ writew(0, ioaddr + TxStatus);
+ tx_status = readb(ioaddr + TxStatus);
+ if (--boguscnt < 0)
+ break;
+ }
+ }
+ for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) {
+ int entry = np->dirty_tx % TX_RING_SIZE;
+ if ( ! (np->tx_ring[entry].status & 0x00010000))
+ break;
+ /* Free the original skb. */
+ dev_kfree_skb_irq(np->tx_skbuff[entry]);
+ np->tx_skbuff[entry] = 0;
+ }
+ if (np->tx_full
+ && np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) {
+ /* The ring is no longer full, clear tbusy. */
+ np->tx_full = 0;
+ netif_wake_queue(dev);
+ }
+
+ /* Abnormal error summary/uncommon events handlers. */
+ if (intr_status & (IntrDrvRqst | IntrPCIErr | LinkChange | StatsMax))
+ netdev_error(dev, intr_status);
+
+ if (--boguscnt < 0) {
+ get_stats(dev);
+ printk(KERN_WARNING "%s: Too much work at interrupt, "
+ "status=0x%4.4x / 0x%4.4x.\n",
+ dev->name, intr_status, readw(ioaddr + IntrClear));
+ /* Re-enable us in 3.2msec. */
+ writew(1000, ioaddr + DownCounter);
+ writew(IntrDrvRqst, ioaddr + IntrEnable);
+ break;
+ }
+ } while (1);
+
+ if (debug > 3)
+ printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
+ dev->name, readw(ioaddr + IntrStatus));
+
+ spin_unlock(&np->lock);
+}
+
+/* This routine is logically part of the interrupt handler, but separated
+ for clarity and better register allocation. */
+static int netdev_rx(struct net_device *dev)
+{
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ int entry = np->cur_rx % RX_RING_SIZE;
+ int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx;
+
+ if (debug > 4) {
+ printk(KERN_DEBUG " In netdev_rx(), entry %d status %4.4x.\n",
+ entry, np->rx_ring[entry].status);
+ }
+
+ /* If EOP is set on the next entry, it's a new packet. Send it up. */
+ while (np->rx_head_desc->status & DescOwn) {
+ struct netdev_desc *desc = np->rx_head_desc;
+ u32 frame_status = le32_to_cpu(desc->status);
+ int pkt_len = frame_status & 0x1fff; /* Chip omits the CRC. */
+
+ if (debug > 4)
+ printk(KERN_DEBUG " netdev_rx() status was %8.8x.\n",
+ frame_status);
+ if (--boguscnt < 0)
+ break;
+ if (frame_status & 0x001f4000) {
+ /* There was a error. */
+ if (debug > 2)
+ printk(KERN_DEBUG " netdev_rx() Rx error was %8.8x.\n",
+ frame_status);
+ np->stats.rx_errors++;
+ if (frame_status & 0x00100000) np->stats.rx_length_errors++;
+ if (frame_status & 0x00010000) np->stats.rx_fifo_errors++;
+ if (frame_status & 0x00060000) np->stats.rx_frame_errors++;
+ if (frame_status & 0x00080000) np->stats.rx_crc_errors++;
+ if (frame_status & 0x00100000) {
+ printk(KERN_WARNING "%s: Oversized Ethernet frame,"
+ " status %8.8x.\n",
+ dev->name, frame_status);
+ }
+ } else {
+ struct sk_buff *skb;
+
+#ifndef final_version
+ if (debug > 4)
+ printk(KERN_DEBUG " netdev_rx() normal Rx pkt length %d"
+ ", bogus_cnt %d.\n",
+ pkt_len, boguscnt);
+#endif
+ /* Check if the packet is long enough to accept without copying
+ to a minimally-sized skbuff. */
+ if (pkt_len < rx_copybreak
+ && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
+ skb->dev = dev;
+ skb_reserve(skb, 2); /* 16 byte align the IP header */
+ eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0);
+ skb_put(skb, pkt_len);
+ } else {
+ skb_put(skb = np->rx_skbuff[entry], pkt_len);
+ np->rx_skbuff[entry] = NULL;
+ }
+ skb->protocol = eth_type_trans(skb, dev);
+ /* Note: checksum -> skb->ip_summed = CHECKSUM_UNNECESSARY; */
+ netif_rx(skb);
+ dev->last_rx = jiffies;
+ }
+ entry = (++np->cur_rx) % RX_RING_SIZE;
+ np->rx_head_desc = &np->rx_ring[entry];
+ }
+
+ /* Refill the Rx ring buffers. */
+ for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) {
+ struct sk_buff *skb;
+ entry = np->dirty_rx % RX_RING_SIZE;
+ if (np->rx_skbuff[entry] == NULL) {
+ skb = dev_alloc_skb(np->rx_buf_sz);
+ np->rx_skbuff[entry] = skb;
+ if (skb == NULL)
+ break; /* Better luck next round. */
+ skb->dev = dev; /* Mark as being used by this device. */
+ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
+ np->rx_ring[entry].frag[0].addr = virt_to_le32desc(skb->tail);
+ }
+ /* Perhaps we need not reset this field. */
+ np->rx_ring[entry].frag[0].length =
+ cpu_to_le32(np->rx_buf_sz | LastFrag);
+ np->rx_ring[entry].status = 0;
+ }
+
+ /* No need to restart Rx engine, it will poll. */
+ return 0;
+}
+
+static void netdev_error(struct net_device *dev, int intr_status)
+{
+ long ioaddr = dev->base_addr;
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+
+ if (intr_status & IntrDrvRqst) {
+ /* Stop the down counter and turn interrupts back on. */
+ printk("%s: Turning interrupts back on.\n", dev->name);
+ writew(0, ioaddr + DownCounter);
+ writew(IntrRxDone | IntrRxDMADone | IntrPCIErr | IntrDrvRqst |
+ IntrTxDone | StatsMax | LinkChange, ioaddr + IntrEnable);
+ }
+ if (intr_status & LinkChange) {
+ printk(KERN_ERR "%s: Link changed: Autonegotiation advertising"
+ " %4.4x partner %4.4x.\n", dev->name,
+ mdio_read(dev, np->phys[0], 4),
+ mdio_read(dev, np->phys[0], 5));
+ check_duplex(dev);
+ }
+ if (intr_status & StatsMax) {
+ get_stats(dev);
+ }
+ if (intr_status & IntrPCIErr) {
+ printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n",
+ dev->name, intr_status);
+ /* We must do a global reset of DMA to continue. */
+ }
+}
+
+static struct net_device_stats *get_stats(struct net_device *dev)
+{
+ long ioaddr = dev->base_addr;
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ int i;
+
+ /* We should lock this segment of code for SMP eventually, although
+ the vulnerability window is very small and statistics are
+ non-critical. */
+ /* The chip only need report frame silently dropped. */
+ np->stats.rx_missed_errors += readb(ioaddr + RxMissed);
+ np->stats.tx_packets += readw(ioaddr + TxFramesOK);
+ np->stats.rx_packets += readw(ioaddr + RxFramesOK);
+ np->stats.collisions += readb(ioaddr + StatsLateColl);
+ np->stats.collisions += readb(ioaddr + StatsMultiColl);
+ np->stats.collisions += readb(ioaddr + StatsOneColl);
+ readb(ioaddr + StatsCarrierError);
+ readb(ioaddr + StatsTxDefer);
+ for (i = StatsTxDefer; i <= StatsMcastRx; i++)
+ readb(ioaddr + i);
+ np->stats.tx_bytes += readw(ioaddr + TxOctetsLow);
+ np->stats.tx_bytes += readw(ioaddr + TxOctetsHigh) << 16;
+ np->stats.rx_bytes += readw(ioaddr + RxOctetsLow);
+ np->stats.rx_bytes += readw(ioaddr + RxOctetsHigh) << 16;
+
+ return &np->stats;
+}
+
+/* The little-endian AUTODIN II ethernet CRC calculations.
+ A big-endian version is also available.
+ This is slow but compact code. Do not use this routine for bulk data,
+ use a table-based routine instead.
+ This is common code and should be moved to net/core/crc.c.
+ Chips may use the upper or lower CRC bits, and may reverse and/or invert
+ them. Select the endian-ness that results in minimal calculations.
+*/
+static unsigned const ethernet_polynomial_le = 0xedb88320U;
+static inline unsigned ether_crc_le(int length, unsigned char *data)
+{
+ unsigned int crc = 0xffffffff; /* Initial value. */
+ while(--length >= 0) {
+ unsigned char current_octet = *data++;
+ int bit;
+ for (bit = 8; --bit >= 0; current_octet >>= 1) {
+ if ((crc ^ current_octet) & 1) {
+ crc >>= 1;
+ crc ^= ethernet_polynomial_le;
+ } else
+ crc >>= 1;
+ }
+ }
+ return crc;
+}
+
+static void set_rx_mode(struct net_device *dev)
+{
+ long ioaddr = dev->base_addr;
+ u16 mc_filter[4]; /* Multicast hash filter */
+ u32 rx_mode;
+ int i;
+
+ if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
+ /* Unconditionally log net taps. */
+ printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name);
+ memset(mc_filter, 0xff, sizeof(mc_filter));
+ rx_mode = AcceptBroadcast | AcceptMulticast | AcceptAll | AcceptMyPhys;
+ } else if ((dev->mc_count > multicast_filter_limit)
+ || (dev->flags & IFF_ALLMULTI)) {
+ /* Too many to match, or accept all multicasts. */
+ memset(mc_filter, 0xff, sizeof(mc_filter));
+ rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+ } else if (dev->mc_count) {
+ struct dev_mc_list *mclist;
+ memset(mc_filter, 0, sizeof(mc_filter));
+ for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+ i++, mclist = mclist->next) {
+ set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f,
+ mc_filter);
+ }
+ rx_mode = AcceptBroadcast | AcceptMultiHash | AcceptMyPhys;
+ } else {
+ writeb(AcceptBroadcast | AcceptMyPhys, ioaddr + RxMode);
+ return;
+ }
+ for (i = 0; i < 4; i++)
+ writew(mc_filter[i], ioaddr + MulticastFilter0 + i*2);
+ writeb(rx_mode, ioaddr + RxMode);
+}
+
+static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ u16 *data = (u16 *)&rq->ifr_data;
+
+ switch(cmd) {
+ case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
+ data[0] = ((struct netdev_private *)dev->priv)->phys[0] & 0x1f;
+ /* Fall Through */
+ case SIOCDEVPRIVATE+1: /* Read the specified MII register. */
+ data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
+ return 0;
+ case SIOCDEVPRIVATE+2: /* Write the specified MII register */
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int netdev_close(struct net_device *dev)
+{
+ long ioaddr = dev->base_addr;
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ int i;
+
+ netif_stop_queue(dev);
+
+ if (debug > 1) {
+ printk(KERN_DEBUG "%s: Shutting down ethercard, status was Tx %2.2x "
+ "Rx %4.4x Int %2.2x.\n",
+ dev->name, readb(ioaddr + TxStatus),
+ readl(ioaddr + RxStatus), readw(ioaddr + IntrStatus));
+ printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d, Rx %d / %d.\n",
+ dev->name, np->cur_tx, np->dirty_tx, np->cur_rx, np->dirty_rx);
+ }
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ writew(0x0000, ioaddr + IntrEnable);
+
+ /* Stop the chip's Tx and Rx processes. */
+ writew(TxDisable | RxDisable | StatsDisable, ioaddr + MACCtrl1);
+
+#ifdef __i386__
+ if (debug > 2) {
+ printk("\n"KERN_DEBUG" Tx ring at %8.8x:\n",
+ (int)virt_to_bus(np->tx_ring));
+ for (i = 0; i < TX_RING_SIZE; i++)
+ printk(" #%d desc. %4.4x %8.8x %8.8x.\n",
+ i, np->tx_ring[i].status, np->tx_ring[i].frag[0].addr,
+ np->tx_ring[i].frag[0].length);
+ printk("\n"KERN_DEBUG " Rx ring %8.8x:\n",
+ (int)virt_to_bus(np->rx_ring));
+ for (i = 0; i < /*RX_RING_SIZE*/4 ; i++) {
+ printk(KERN_DEBUG " #%d desc. %4.4x %4.4x %8.8x\n",
+ i, np->rx_ring[i].status, np->rx_ring[i].frag[0].addr,
+ np->rx_ring[i].frag[0].length);
+ }
+ }
+#endif /* __i386__ debugging only */
+
+ free_irq(dev->irq, dev);
+
+ del_timer_sync(&np->timer);
+
+ /* Free all the skbuffs in the Rx queue. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ np->rx_ring[i].status = 0;
+ np->rx_ring[i].frag[0].addr = 0xBADF00D0; /* An invalid address. */
+ if (np->rx_skbuff[i]) {
+ dev_kfree_skb(np->rx_skbuff[i]);
+ }
+ np->rx_skbuff[i] = 0;
+ }
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ if (np->tx_skbuff[i])
+ dev_kfree_skb(np->tx_skbuff[i]);
+ np->tx_skbuff[i] = 0;
+ }
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+static void __devexit sundance_remove1 (struct pci_dev *pdev)
+{
+ struct net_device *dev = pdev->driver_data;
+
+ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+ while (dev) {
+ struct netdev_private *np = (void *)(dev->priv);
+ unregister_netdev(dev);
+#ifdef USE_IO_OPS
+ release_region(dev->base_addr, pci_id_tbl[np->chip_id].io_size);
+#else
+ release_mem_region(pci_resource_start(pdev, 1),
+ pci_id_tbl[np->chip_id].io_size);
+ iounmap((char *)(dev->base_addr));
+#endif
+ kfree(dev);
+ }
+
+ pdev->driver_data = NULL;
+}
+
+static struct pci_driver sundance_driver = {
+ name: "sundance",
+ id_table: sundance_pci_tbl,
+ probe: sundance_probe1,
+ remove: sundance_remove1,
+};
+
+static int __init sundance_init(void)
+{
+ return pci_module_init(&sundance_driver);
+}
+
+static void __exit sundance_exit(void)
+{
+ pci_unregister_driver(&sundance_driver);
+}
+
+module_init(sundance_init);
+module_exit(sundance_exit);
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/pci.h>
+#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * $Id: tun.c,v 1.1 2000/08/23 05:59:28 davem Exp $
+ * $Id: tun.c,v 1.2 2000/09/22 12:40:31 maxk Exp $
*/
/*
* Modifications for 2.3.99-pre5 kernel.
*/
-#define TUN_VER "1.1"
+#define TUN_VER "1.2"
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/init.h>
-#include <linux/devfs_fs_kernel.h>
#include <linux/random.h>
+#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
+#include <linux/miscdevice.h>
#include <linux/rtnetlink.h>
#include <linux/if.h>
#include <linux/if_arp.h>
fasync: tun_chr_fasync
};
-static devfs_handle_t devfs_handle = NULL;
+static struct miscdevice tun_miscdev=
+{
+ TUN_MINOR,
+ "net/tun",
+ &tun_fops
+};
int __init tun_init(void)
{
printk(KERN_INFO "Universal TUN/TAP device driver %s "
"(C)1999-2000 Maxim Krasnyansky\n", TUN_VER);
- if (devfs_register_chrdev(TUN_MAJOR, "tun", &tun_fops)) {
- printk(KERN_ERR "tun: Can't register char device %d\n",
- TUN_MAJOR);
+ if (misc_register(&tun_miscdev)) {
+ printk(KERN_ERR "tun: Can't register misc device %d\n", TUN_MINOR);
return -EIO;
}
- devfs_handle = devfs_register(NULL, "net/tun", DEVFS_FL_DEFAULT,
- TUN_MAJOR, 0,
- S_IFCHR | S_IRUSR | S_IWUSR,
- &tun_fops, NULL);
-
-#ifdef MODULE
return 0;
-#else
- /* If driver is not module, tun_init will be called from Space.c.
- * Return non-zero not to register fake device.
- */
- return 1;
-#endif
}
void tun_cleanup(void)
{
- devfs_unregister_chrdev(TUN_MAJOR,"tun");
- devfs_unregister(devfs_handle);
+ misc_deregister(&tun_miscdev);
}
module_init(tun_init);
--- /dev/null
+/* winbond-840.c: A Linux PCI network adapter skeleton device driver. */
+/*
+ Written 1998-2000 by Donald Becker.
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License (GPL), incorporated herein by reference.
+ Drivers based on or derived from this code fall under the GPL and must
+ retain the authorship, copyright and license notice. This file is not
+ a complete program and may only be used when the entire operating
+ system is licensed under the GPL.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+ Support and updates available at
+ http://www.scyld.com/network/drivers.html
+
+ Do not remove the copyright infomation.
+ Do not change the version information unless an improvement has been made.
+ Merely removing my name, as Compex has done in the past, does not count
+ as an improvement.
+*/
+
+/* These identify the driver base version and may not be removed. */
+static const char version1[] =
+"winbond-840.c:v1.01 5/15/2000 Donald Becker <becker@scyld.com>\n";
+static const char version2[] =
+" http://www.scyld.com/network/drivers.html\n";
+
+/* Automatically extracted configuration info:
+probe-func: winbond840_probe
+config-in: tristate 'Winbond W89c840 Ethernet support' CONFIG_WINBOND_840
+
+c-help-name: Winbond W89c840 PCI Ethernet support
+c-help-symbol: CONFIG_WINBOND_840
+c-help: This driver is for the Winbond W89c840 chip. It also works with
+c-help: the TX9882 chip on the Compex RL100-ATX board.
+c-help: More specific information and updates are available from
+c-help: http://www.scyld.com/network/drivers.html
+*/
+
+/* The user-configurable values.
+ These may be modified when a driver module is loaded.*/
+
+static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */
+static int max_interrupt_work = 20;
+/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
+ The '840 uses a 64 element hash table based on the Ethernet CRC. */
+static int multicast_filter_limit = 32;
+
+/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
+ Setting to > 1518 effectively disables this feature. */
+static int rx_copybreak = 0;
+
+/* Used to pass the media type, etc.
+ Both 'options[]' and 'full_duplex[]' should exist for driver
+ interoperability.
+ The media type is usually passed in 'options[]'.
+*/
+#define MAX_UNITS 8 /* More are supported, limit only on options */
+static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+
+/* Operational parameters that are set at compile time. */
+
+/* Keep the ring sizes a power of two for compile efficiency.
+ The compiler will convert <unsigned>'%'<2^N> into a bit mask.
+ Making the Tx ring too large decreases the effectiveness of channel
+ bonding and packet priority.
+ There are no ill effects from too-large receive rings. */
+#define TX_RING_SIZE 16
+#define TX_QUEUE_LEN 10 /* Limit ring entries actually used. */
+#define RX_RING_SIZE 32
+
+/* The presumed FIFO size for working around the Tx-FIFO-overflow bug.
+ To avoid overflowing we don't queue again until we have room for a
+ full-size packet.
+ */
+#define TX_FIFO_SIZE (2048)
+#define TX_BUG_FIFO_LIMIT (TX_FIFO_SIZE-1514-16)
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT (2*HZ)
+
+#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/
+
+#ifndef __KERNEL__
+#define __KERNEL__
+#endif
+#if !defined(__OPTIMIZE__)
+#warning You must compile this file with the correct options!
+#warning See the last lines of the source file.
+#error You must compile this driver with "-O".
+#endif
+
+/* Include files, designed to support most kernel versions 2.0.0 and later. */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <asm/processor.h> /* Processor type for cache alignment. */
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+/* Condensed operations for readability.
+ The compatibility defines are in kern_compat.h */
+
+#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+
+MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
+MODULE_DESCRIPTION("Winbond W89c840 Ethernet driver");
+MODULE_PARM(max_interrupt_work, "i");
+MODULE_PARM(debug, "i");
+MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM(multicast_filter_limit, "i");
+MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
+
+/*
+ Theory of Operation
+
+I. Board Compatibility
+
+This driver is for the Winbond w89c840 chip.
+
+II. Board-specific settings
+
+None.
+
+III. Driver operation
+
+This chip is very similar to the Digital 21*4* "Tulip" family. The first
+twelve registers and the descriptor format are nearly identical. Read a
+Tulip manual for operational details.
+
+A significant difference is that the multicast filter and station address are
+stored in registers rather than loaded through a pseudo-transmit packet.
+
+Unlike the Tulip, transmit buffers are limited to 1KB. To transmit a
+full-sized packet we must use both data buffers in a descriptor. Thus the
+driver uses ring mode where descriptors are implicitly sequential in memory,
+rather than using the second descriptor address as a chain pointer to
+subsequent descriptors.
+
+IV. Notes
+
+If you are going to almost clone a Tulip, why not go all the way and avoid
+the need for a new driver?
+
+IVb. References
+
+http://www.scyld.com/expert/100mbps.html
+http://www.scyld.com/expert/NWay.html
+http://www.winbond.com.tw/
+
+IVc. Errata
+
+A horrible bug exists in the transmit FIFO. Apparently the chip doesn't
+correctly detect a full FIFO, and queuing more than 2048 bytes may result in
+silent data corruption.
+
+*/
+
+\f
+
+/*
+ PCI probe table.
+*/
+enum pci_id_flags_bits {
+ /* Set PCI command register bits before calling probe1(). */
+ PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
+ /* Read and map the single following PCI BAR. */
+ PCI_ADDR0=0<<4, PCI_ADDR1=1<<4, PCI_ADDR2=2<<4, PCI_ADDR3=3<<4,
+ PCI_ADDR_64BITS=0x100, PCI_NO_ACPI_WAKE=0x200, PCI_NO_MIN_LATENCY=0x400,
+};
+enum chip_capability_flags {CanHaveMII=1, HasBrokenTx=2};
+#ifdef USE_IO_OPS
+#define W840_FLAGS (PCI_USES_IO | PCI_ADDR0 | PCI_USES_MASTER)
+#else
+#define W840_FLAGS (PCI_USES_MEM | PCI_ADDR1 | PCI_USES_MASTER)
+#endif
+
+static struct pci_device_id w840_pci_tbl[] __devinitdata = {
+ { 0x1050, 0x0840, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0x11f6, 0x2011, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, w840_pci_tbl);
+
+struct pci_id_info {
+ const char *name;
+ struct match_info {
+ int pci, pci_mask, subsystem, subsystem_mask;
+ int revision, revision_mask; /* Only 8 bits. */
+ } id;
+ enum pci_id_flags_bits pci_flags;
+ int io_size; /* Needed for I/O region check or ioremap(). */
+ int drv_flags; /* Driver use, intended as capability flags. */
+};
+static struct pci_id_info pci_id_tbl[] = {
+ {"Winbond W89c840", { 0x08401050, 0xffffffff, },
+ W840_FLAGS, 128, CanHaveMII | HasBrokenTx},
+ {"Compex RL100-ATX", { 0x201111F6, 0xffffffff,},
+ W840_FLAGS, 128, CanHaveMII | HasBrokenTx},
+ {0,}, /* 0 terminated list. */
+};
+
+/* This driver was written to use PCI memory space, however some x86 systems
+ work only with I/O space accesses. Pass -DUSE_IO_OPS to use PCI I/O space
+ accesses instead of memory space. */
+
+#ifdef USE_IO_OPS
+#undef readb
+#undef readw
+#undef readl
+#undef writeb
+#undef writew
+#undef writel
+#define readb inb
+#define readw inw
+#define readl inl
+#define writeb outb
+#define writew outw
+#define writel outl
+#endif
+
+/* Offsets to the Command and Status Registers, "CSRs".
+ While similar to the Tulip, these registers are longword aligned.
+ Note: It's not useful to define symbolic names for every register bit in
+ the device. The name can only partially document the semantics and make
+ the driver longer and more difficult to read.
+*/
+enum w840_offsets {
+ PCIBusCfg=0x00, TxStartDemand=0x04, RxStartDemand=0x08,
+ RxRingPtr=0x0C, TxRingPtr=0x10,
+ IntrStatus=0x14, NetworkConfig=0x18, IntrEnable=0x1C,
+ RxMissed=0x20, EECtrl=0x24, MIICtrl=0x24, BootRom=0x28, GPTimer=0x2C,
+ CurRxDescAddr=0x30, CurRxBufAddr=0x34, /* Debug use */
+ MulticastFilter0=0x38, MulticastFilter1=0x3C, StationAddr=0x40,
+ CurTxDescAddr=0x4C, CurTxBufAddr=0x50,
+};
+
+/* Bits in the interrupt status/enable registers. */
+/* The bits in the Intr Status/Enable registers, mostly interrupt sources. */
+enum intr_status_bits {
+ NormalIntr=0x10000, AbnormalIntr=0x8000,
+ IntrPCIErr=0x2000, TimerInt=0x800,
+ IntrRxDied=0x100, RxNoBuf=0x80, IntrRxDone=0x40,
+ TxFIFOUnderflow=0x20, RxErrIntr=0x10,
+ TxIdle=0x04, IntrTxStopped=0x02, IntrTxDone=0x01,
+};
+
+/* Bits in the NetworkConfig register. */
+enum rx_mode_bits {
+ AcceptErr=0x80, AcceptRunt=0x40,
+ AcceptBroadcast=0x20, AcceptMulticast=0x10,
+ AcceptAllPhys=0x08, AcceptMyPhys=0x02,
+};
+
+enum mii_reg_bits {
+ MDIO_ShiftClk=0x10000, MDIO_DataIn=0x80000, MDIO_DataOut=0x20000,
+ MDIO_EnbOutput=0x40000, MDIO_EnbIn = 0x00000,
+};
+
+/* The Tulip Rx and Tx buffer descriptors. */
+struct w840_rx_desc {
+ s32 status;
+ s32 length;
+ u32 buffer1;
+ u32 next_desc;
+};
+
+struct w840_tx_desc {
+ s32 status;
+ s32 length;
+ u32 buffer1, buffer2; /* We use only buffer 1. */
+};
+
+/* Bits in network_desc.status */
+enum desc_status_bits {
+ DescOwn=0x80000000, DescEndRing=0x02000000, DescUseLink=0x01000000,
+ DescWholePkt=0x60000000, DescStartPkt=0x20000000, DescEndPkt=0x40000000,
+ DescIntr=0x80000000,
+};
+
+#define PRIV_ALIGN 15 /* Required alignment mask */
+struct netdev_private {
+ /* Descriptor rings first for alignment. */
+ struct w840_rx_desc rx_ring[RX_RING_SIZE];
+ struct w840_tx_desc tx_ring[TX_RING_SIZE];
+ /* The addresses of receive-in-place skbuffs. */
+ struct sk_buff* rx_skbuff[RX_RING_SIZE];
+ /* The saved address of a sent-in-place packet/buffer, for later free(). */
+ struct sk_buff* tx_skbuff[TX_RING_SIZE];
+ struct net_device_stats stats;
+ struct timer_list timer; /* Media monitoring timer. */
+ /* Frequently used values: keep some adjacent for cache effect. */
+ spinlock_t lock;
+ int chip_id, drv_flags;
+ int csr6;
+ struct w840_rx_desc *rx_head_desc;
+ unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */
+ unsigned int rx_buf_sz; /* Based on MTU+slack. */
+ unsigned int cur_tx, dirty_tx;
+ int tx_q_bytes;
+ unsigned int tx_full:1; /* The Tx queue is full. */
+ /* These values are keep track of the transceiver/media in use. */
+ unsigned int full_duplex:1; /* Full-duplex operation requested. */
+ unsigned int duplex_lock:1;
+ unsigned int medialock:1; /* Do not sense media. */
+ unsigned int default_port:4; /* Last dev->if_port value. */
+ /* MII transceiver section. */
+ int mii_cnt; /* MII device addresses. */
+ u16 advertising; /* NWay media advertisement */
+ unsigned char phys[2]; /* MII device addresses. */
+};
+
+static int eeprom_read(long ioaddr, int location);
+static int mdio_read(struct net_device *dev, int phy_id, int location);
+static void mdio_write(struct net_device *dev, int phy_id, int location, int value);
+static int netdev_open(struct net_device *dev);
+static void check_duplex(struct net_device *dev);
+static void netdev_timer(unsigned long data);
+static void tx_timeout(struct net_device *dev);
+static void init_ring(struct net_device *dev);
+static int start_tx(struct sk_buff *skb, struct net_device *dev);
+static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs);
+static void netdev_error(struct net_device *dev, int intr_status);
+static int netdev_rx(struct net_device *dev);
+static void netdev_error(struct net_device *dev, int intr_status);
+static inline unsigned ether_crc(int length, unsigned char *data);
+static void set_rx_mode(struct net_device *dev);
+static struct net_device_stats *get_stats(struct net_device *dev);
+static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static int netdev_close(struct net_device *dev);
+
+\f
+
+static int __devinit w840_probe1 (struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct net_device *dev;
+ struct netdev_private *np;
+ static int find_cnt;
+ int chip_idx = ent->driver_data;
+ int irq = pdev->irq;
+ int i, option = find_cnt < MAX_UNITS ? options[find_cnt] : 0;
+ long ioaddr;
+
+ if (pci_enable_device(pdev))
+ return -EIO;
+ pci_set_master(pdev);
+
+ dev = init_etherdev(NULL, sizeof(*np));
+ if (!dev)
+ return -ENOMEM;
+
+#ifdef USE_IO_OPS
+ ioaddr = pci_resource_start(pdev, 0);
+ if (!request_region(ioaddr, pci_id_tbl[chip_idx].io_size, dev->name))
+ goto err_out_netdev;
+#else
+ ioaddr = pci_resource_start(pdev, 1);
+ if (!request_mem_region(ioaddr, pci_id_tbl[chip_idx].io_size, dev->name))
+ goto err_out_netdev;
+ ioaddr = (long) ioremap (ioaddr, pci_id_tbl[chip_idx].io_size);
+ if (!ioaddr)
+ goto err_out_iomem;
+#endif
+
+ printk(KERN_INFO "%s: %s at 0x%lx, ",
+ dev->name, pci_id_tbl[chip_idx].name, ioaddr);
+
+ /* Warning: broken for big-endian machines. */
+ for (i = 0; i < 3; i++)
+ ((u16 *)dev->dev_addr)[i] = le16_to_cpu(eeprom_read(ioaddr, i));
+
+ for (i = 0; i < 5; i++)
+ printk("%2.2x:", dev->dev_addr[i]);
+ printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq);
+
+ /* Reset the chip to erase previous misconfiguration.
+ No hold time required! */
+ writel(0x00000001, ioaddr + PCIBusCfg);
+
+ dev->base_addr = ioaddr;
+ dev->irq = irq;
+
+ np = dev->priv;
+ np->chip_id = chip_idx;
+ np->drv_flags = pci_id_tbl[chip_idx].drv_flags;
+ spin_lock_init(&np->lock);
+
+ pdev->driver_data = dev;
+
+ if (dev->mem_start)
+ option = dev->mem_start;
+
+ /* The lower four bits are the media type. */
+ if (option > 0) {
+ if (option & 0x200)
+ np->full_duplex = 1;
+ np->default_port = option & 15;
+ if (np->default_port)
+ np->medialock = 1;
+ }
+ if (find_cnt < MAX_UNITS && full_duplex[find_cnt] > 0)
+ np->full_duplex = 1;
+
+ if (np->full_duplex)
+ np->duplex_lock = 1;
+
+ /* The chip-specific entries in the device structure. */
+ dev->open = &netdev_open;
+ dev->hard_start_xmit = &start_tx;
+ dev->stop = &netdev_close;
+ dev->get_stats = &get_stats;
+ dev->set_multicast_list = &set_rx_mode;
+ dev->do_ioctl = &mii_ioctl;
+ dev->tx_timeout = &tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+ if (np->drv_flags & CanHaveMII) {
+ int phy, phy_idx = 0;
+ for (phy = 1; phy < 32 && phy_idx < 4; phy++) {
+ int mii_status = mdio_read(dev, phy, 1);
+ if (mii_status != 0xffff && mii_status != 0x0000) {
+ np->phys[phy_idx++] = phy;
+ np->advertising = mdio_read(dev, phy, 4);
+ printk(KERN_INFO "%s: MII PHY found at address %d, status "
+ "0x%4.4x advertising %4.4x.\n",
+ dev->name, phy, mii_status, np->advertising);
+ }
+ }
+ np->mii_cnt = phy_idx;
+ if (phy_idx == 0) {
+ printk(KERN_WARNING "%s: MII PHY not found -- this device may "
+ "not operate correctly.\n", dev->name);
+ }
+ }
+
+ find_cnt++;
+ return 0;
+
+#ifndef USE_IO_OPS
+err_out_iomem:
+ release_mem_region(pci_resource_start(pdev, 1),
+ pci_id_tbl[chip_idx].io_size);
+#endif
+err_out_netdev:
+ unregister_netdev (dev);
+ kfree (dev);
+ return -ENODEV;
+}
+
+\f
+/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. These are
+ often serial bit streams generated by the host processor.
+ The example below is for the common 93c46 EEPROM, 64 16 bit words. */
+
+/* Delay between EEPROM clock transitions.
+ No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need
+ a delay. Note that pre-2.0.34 kernels had a cache-alignment bug that
+ made udelay() unreliable.
+ The old method of using an ISA access as a delay, __SLOW_DOWN_IO__, is
+ depricated.
+*/
+#define eeprom_delay(ee_addr) readl(ee_addr)
+
+enum EEPROM_Ctrl_Bits {
+ EE_ShiftClk=0x02, EE_Write0=0x801, EE_Write1=0x805,
+ EE_ChipSelect=0x801, EE_DataIn=0x08,
+};
+
+/* The EEPROM commands include the alway-set leading bit. */
+enum EEPROM_Cmds {
+ EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6),
+};
+
+static int eeprom_read(long addr, int location)
+{
+ int i;
+ int retval = 0;
+ int ee_addr = addr + EECtrl;
+ int read_cmd = location | EE_ReadCmd;
+ writel(EE_ChipSelect, ee_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 10; i >= 0; i--) {
+ short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0;
+ writel(dataval, ee_addr);
+ eeprom_delay(ee_addr);
+ writel(dataval | EE_ShiftClk, ee_addr);
+ eeprom_delay(ee_addr);
+ }
+ writel(EE_ChipSelect, ee_addr);
+
+ for (i = 16; i > 0; i--) {
+ writel(EE_ChipSelect | EE_ShiftClk, ee_addr);
+ eeprom_delay(ee_addr);
+ retval = (retval << 1) | ((readl(ee_addr) & EE_DataIn) ? 1 : 0);
+ writel(EE_ChipSelect, ee_addr);
+ eeprom_delay(ee_addr);
+ }
+
+ /* Terminate the EEPROM access. */
+ writel(0, ee_addr);
+ return retval;
+}
+
+/* MII transceiver control section.
+ Read and write the MII registers using software-generated serial
+ MDIO protocol. See the MII specifications or DP83840A data sheet
+ for details.
+
+ The maximum data clock rate is 2.5 Mhz. The minimum timing is usually
+ met by back-to-back 33Mhz PCI cycles. */
+#define mdio_delay(mdio_addr) readl(mdio_addr)
+
+/* Set iff a MII transceiver on any interface requires mdio preamble.
+ This only set with older tranceivers, so the extra
+ code size of a per-interface flag is not worthwhile. */
+static char mii_preamble_required = 1;
+
+#define MDIO_WRITE0 (MDIO_EnbOutput)
+#define MDIO_WRITE1 (MDIO_DataOut | MDIO_EnbOutput)
+
+/* Generate the preamble required for initial synchronization and
+ a few older transceivers. */
+static void mdio_sync(long mdio_addr)
+{
+ int bits = 32;
+
+ /* Establish sync by sending at least 32 logic ones. */
+ while (--bits >= 0) {
+ writel(MDIO_WRITE1, mdio_addr);
+ mdio_delay(mdio_addr);
+ writel(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+}
+
+static int mdio_read(struct net_device *dev, int phy_id, int location)
+{
+ long mdio_addr = dev->base_addr + MIICtrl;
+ int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+ int i, retval = 0;
+
+ if (mii_preamble_required)
+ mdio_sync(mdio_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 15; i >= 0; i--) {
+ int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
+
+ writel(dataval, mdio_addr);
+ mdio_delay(mdio_addr);
+ writel(dataval | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+ /* Read the two transition, 16 data, and wire-idle bits. */
+ for (i = 20; i > 0; i--) {
+ writel(MDIO_EnbIn, mdio_addr);
+ mdio_delay(mdio_addr);
+ retval = (retval << 1) | ((readl(mdio_addr) & MDIO_DataIn) ? 1 : 0);
+ writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+ return (retval>>1) & 0xffff;
+}
+
+static void mdio_write(struct net_device *dev, int phy_id, int location, int value)
+{
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ long mdio_addr = dev->base_addr + MIICtrl;
+ int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
+ int i;
+
+ if (location == 4 && phy_id == np->phys[0])
+ np->advertising = value;
+
+ if (mii_preamble_required)
+ mdio_sync(mdio_addr);
+
+ /* Shift the command bits out. */
+ for (i = 31; i >= 0; i--) {
+ int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
+
+ writel(dataval, mdio_addr);
+ mdio_delay(mdio_addr);
+ writel(dataval | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+ /* Clear out extra bits. */
+ for (i = 2; i > 0; i--) {
+ writel(MDIO_EnbIn, mdio_addr);
+ mdio_delay(mdio_addr);
+ writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+ return;
+}
+
+\f
+static int netdev_open(struct net_device *dev)
+{
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ long ioaddr = dev->base_addr;
+ int i;
+
+ writel(0x00000001, ioaddr + PCIBusCfg); /* Reset */
+
+ MOD_INC_USE_COUNT;
+
+ if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) {
+ MOD_DEC_USE_COUNT;
+ return -EAGAIN;
+ }
+
+ if (debug > 1)
+ printk(KERN_DEBUG "%s: w89c840_open() irq %d.\n",
+ dev->name, dev->irq);
+
+ init_ring(dev);
+
+ writel(virt_to_bus(np->rx_ring), ioaddr + RxRingPtr);
+ writel(virt_to_bus(np->tx_ring), ioaddr + TxRingPtr);
+
+ for (i = 0; i < 6; i++)
+ writeb(dev->dev_addr[i], ioaddr + StationAddr + i);
+
+ /* Initialize other registers. */
+ /* Configure the PCI bus bursts and FIFO thresholds.
+ 486: Set 8 longword cache alignment, 8 longword burst.
+ 586: Set 16 longword cache alignment, no burst limit.
+ Cache alignment bits 15:14 Burst length 13:8
+ 0000 <not allowed> 0000 align to cache 0800 8 longwords
+ 4000 8 longwords 0100 1 longword 1000 16 longwords
+ 8000 16 longwords 0200 2 longwords 2000 32 longwords
+ C000 32 longwords 0400 4 longwords
+ Wait the specified 50 PCI cycles after a reset by initializing
+ Tx and Rx queues and the address filter list. */
+#if defined(__powerpc__) /* Big-endian */
+ writel(0x00100080 | 0xE010, ioaddr + PCIBusCfg);
+#elif defined(__alpha__)
+ writel(0xE010, ioaddr + PCIBusCfg);
+#elif defined(__i386__)
+#if defined(MODULE)
+ writel(0xE010, ioaddr + PCIBusCfg);
+#else
+ /* When not a module we can work around broken '486 PCI boards. */
+#define x86 boot_cpu_data.x86
+ writel((x86 <= 4 ? 0x4810 : 0xE010), ioaddr + PCIBusCfg);
+ if (x86 <= 4)
+ printk(KERN_INFO "%s: This is a 386/486 PCI system, setting cache "
+ "alignment to %x.\n", dev->name,
+ (x86 <= 4 ? 0x4810 : 0x8010));
+#endif
+#else
+ writel(0xE010, ioaddr + PCIBusCfg);
+#warning Processor architecture undefined!
+#endif
+
+ if (dev->if_port == 0)
+ dev->if_port = np->default_port;
+
+ writel(0, ioaddr + RxStartDemand);
+ np->csr6 = 0x20022002;
+ check_duplex(dev);
+ set_rx_mode(dev);
+
+ netif_start_queue(dev);
+
+ /* Clear and Enable interrupts by setting the interrupt mask. */
+ writel(0x1A0F5, ioaddr + IntrStatus);
+ writel(0x1A0F5, ioaddr + IntrEnable);
+
+ if (debug > 2)
+ printk(KERN_DEBUG "%s: Done netdev_open().\n", dev->name);
+
+ /* Set the timer to check for link beat. */
+ init_timer(&np->timer);
+ np->timer.expires = jiffies + 3*HZ;
+ np->timer.data = (unsigned long)dev;
+ np->timer.function = &netdev_timer; /* timer handler */
+ add_timer(&np->timer);
+
+ return 0;
+}
+
+static void check_duplex(struct net_device *dev)
+{
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ int mii_reg5 = mdio_read(dev, np->phys[0], 5);
+ int negotiated = mii_reg5 & np->advertising;
+ int duplex;
+
+ if (np->duplex_lock || mii_reg5 == 0xffff)
+ return;
+ duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040;
+ if (np->full_duplex != duplex) {
+ np->full_duplex = duplex;
+ if (debug)
+ printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d "
+ "negotiated capability %4.4x.\n", dev->name,
+ duplex ? "full" : "half", np->phys[0], negotiated);
+ np->csr6 &= ~0x200;
+ np->csr6 |= duplex ? 0x200 : 0;
+ }
+}
+
+static void netdev_timer(unsigned long data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ long ioaddr = dev->base_addr;
+ int next_tick = 10*HZ;
+ int old_csr6 = np->csr6;
+
+ if (debug > 2)
+ printk(KERN_DEBUG "%s: Media selection timer tick, status %8.8x "
+ "config %8.8x.\n",
+ dev->name, (int)readl(ioaddr + IntrStatus),
+ (int)readl(ioaddr + NetworkConfig));
+ check_duplex(dev);
+ if (np->csr6 != old_csr6) {
+ writel(np->csr6 & ~0x0002, ioaddr + NetworkConfig);
+ writel(np->csr6 | 0x2002, ioaddr + NetworkConfig);
+ }
+ np->timer.expires = jiffies + next_tick;
+ add_timer(&np->timer);
+}
+
+static void tx_timeout(struct net_device *dev)
+{
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ long ioaddr = dev->base_addr;
+
+ printk(KERN_WARNING "%s: Transmit timed out, status %8.8x,"
+ " resetting...\n", dev->name, (int)readl(ioaddr + IntrStatus));
+
+#ifndef __alpha__
+ {
+ int i;
+ printk(KERN_DEBUG " Rx ring %8.8x: ", (int)np->rx_ring);
+ for (i = 0; i < RX_RING_SIZE; i++)
+ printk(" %8.8x", (unsigned int)np->rx_ring[i].status);
+ printk("\n"KERN_DEBUG" Tx ring %8.8x: ", (int)np->tx_ring);
+ for (i = 0; i < TX_RING_SIZE; i++)
+ printk(" %4.4x", np->tx_ring[i].status);
+ printk("\n");
+ }
+#endif
+
+ /* Perhaps we should reinitialize the hardware here. Just trigger a
+ Tx demand for now. */
+ writel(0, ioaddr + TxStartDemand);
+ dev->if_port = 0;
+ /* Stop and restart the chip's Tx processes . */
+
+ dev->trans_start = jiffies;
+ np->stats.tx_errors++;
+ return;
+}
+
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void init_ring(struct net_device *dev)
+{
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ int i;
+
+ np->tx_full = 0;
+ np->tx_q_bytes = np->cur_rx = np->cur_tx = 0;
+ np->dirty_rx = np->dirty_tx = 0;
+
+ np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
+ np->rx_head_desc = &np->rx_ring[0];
+
+ /* Initial all Rx descriptors. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ np->rx_ring[i].length = cpu_to_le32(np->rx_buf_sz);
+ np->rx_ring[i].status = 0;
+ np->rx_ring[i].next_desc = virt_to_le32desc(&np->rx_ring[i+1]);
+ np->rx_skbuff[i] = 0;
+ }
+ /* Mark the last entry as wrapping the ring. */
+ np->rx_ring[i-1].length |= cpu_to_le32(DescEndRing);
+ np->rx_ring[i-1].next_desc = virt_to_le32desc(&np->rx_ring[0]);
+
+ /* Fill in the Rx buffers. Handle allocation failure gracefully. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz);
+ np->rx_skbuff[i] = skb;
+ if (skb == NULL)
+ break;
+ skb->dev = dev; /* Mark as being used by this device. */
+ np->rx_ring[i].buffer1 = virt_to_le32desc(skb->tail);
+ np->rx_ring[i].status = cpu_to_le32(DescOwn | DescIntr);
+ }
+ np->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
+
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ np->tx_skbuff[i] = 0;
+ np->tx_ring[i].status = 0;
+ }
+ return;
+}
+
+static int start_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ unsigned entry;
+
+ /* Caution: the write order is important here, set the field
+ with the "ownership" bits last. */
+
+ /* Calculate the next Tx descriptor entry. */
+ entry = np->cur_tx % TX_RING_SIZE;
+
+ np->tx_skbuff[entry] = skb;
+ np->tx_ring[entry].buffer1 = virt_to_le32desc(skb->data);
+
+#define one_buffer
+#define BPT 1022
+#if defined(one_buffer)
+ np->tx_ring[entry].length = cpu_to_le32(DescWholePkt | skb->len);
+ if (entry >= TX_RING_SIZE-1) /* Wrap ring */
+ np->tx_ring[entry].length |= cpu_to_le32(DescIntr | DescEndRing);
+ np->tx_ring[entry].status = cpu_to_le32(DescOwn);
+ np->cur_tx++;
+#elif defined(two_buffer)
+ if (skb->len > BPT) {
+ unsigned int entry1 = ++np->cur_tx % TX_RING_SIZE;
+ np->tx_ring[entry].length = cpu_to_le32(DescStartPkt | BPT);
+ np->tx_ring[entry1].length = cpu_to_le32(DescEndPkt | (skb->len - BPT));
+ np->tx_ring[entry1].buffer1 = virt_to_le32desc((skb->data) + BPT);
+ np->tx_ring[entry1].status = cpu_to_le32(DescOwn);
+ np->tx_ring[entry].status = cpu_to_le32(DescOwn);
+ if (entry >= TX_RING_SIZE-1)
+ np->tx_ring[entry].length |= cpu_to_le32(DescIntr|DescEndRing);
+ else if (entry1 >= TX_RING_SIZE-1)
+ np->tx_ring[entry1].length |= cpu_to_le32(DescIntr|DescEndRing);
+ np->cur_tx++;
+ } else {
+ np->tx_ring[entry].length = cpu_to_le32(DescWholePkt | skb->len);
+ if (entry >= TX_RING_SIZE-1) /* Wrap ring */
+ np->tx_ring[entry].length |= cpu_to_le32(DescIntr | DescEndRing);
+ np->tx_ring[entry].status = cpu_to_le32(DescOwn);
+ np->cur_tx++;
+ }
+#elif defined(split_buffer)
+ {
+ /* Work around the Tx-FIFO-full bug by splitting our transmit packet
+ into two pieces, the first which may be loaded without overflowing
+ the FIFO, and the second which contains the remainder of the
+ packet. When we get a Tx-done interrupt that frees enough room
+ in the FIFO we mark the remainder of the packet as loadable.
+
+ This has the problem that the Tx descriptors are written both
+ here and in the interrupt handler.
+ */
+
+ int buf1size = TX_FIFO_SIZE - np->tx_q_bytes;
+ int buf2size = skb->len - buf1size;
+
+ if (buf2size <= 0) { /* We fit into one descriptor. */
+ np->tx_ring[entry].length = cpu_to_le32(DescWholePkt | skb->len);
+ } else { /* We must use two descriptors. */
+ unsigned int entry2;
+ np->tx_ring[entry].length =
+ cpu_to_le32(DescIntr | DescStartPkt | buf1size);
+ if (entry >= TX_RING_SIZE-1) { /* Wrap ring */
+ np->tx_ring[entry].length |= cpu_to_le32(DescEndRing);
+ entry2 = 0;
+ } else
+ entry2 = entry + 1;
+ np->cur_tx++;
+ np->tx_ring[entry2].buffer1 =
+ virt_to_le32desc(skb->data + buf1size);
+ np->tx_ring[entry2].length = cpu_to_le32(DescEndPkt | buf2size);
+ if (entry2 >= TX_RING_SIZE-1) /* Wrap ring */
+ np->tx_ring[entry2].length |= cpu_to_le32(DescEndRing);
+ }
+ np->tx_ring[entry].status = cpu_to_le32(DescOwn);
+ np->cur_tx++;
+ }
+#endif
+ np->tx_q_bytes += skb->len;
+ writel(0, dev->base_addr + TxStartDemand);
+
+ /* Work around horrible bug in the chip by marking the queue as full
+ when we do not have FIFO room for a maximum sized packet. */
+ if (np->cur_tx - np->dirty_tx > TX_QUEUE_LEN)
+ np->tx_full = 1;
+ else if ((np->drv_flags & HasBrokenTx)
+ && np->tx_q_bytes > TX_BUG_FIFO_LIMIT)
+ np->tx_full = 1;
+ if (np->tx_full)
+ netif_stop_queue(dev);
+
+ dev->trans_start = jiffies;
+
+ if (debug > 4) {
+ printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n",
+ dev->name, np->cur_tx, entry);
+ }
+ return 0;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+ after the Tx thread. */
+static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs)
+{
+ struct net_device *dev = (struct net_device *)dev_instance;
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ long ioaddr = dev->base_addr;
+ int work_limit = max_interrupt_work;
+
+ spin_lock(&np->lock);
+
+ do {
+ u32 intr_status = readl(ioaddr + IntrStatus);
+
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ writel(intr_status & 0x001ffff, ioaddr + IntrStatus);
+
+ if (debug > 4)
+ printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n",
+ dev->name, intr_status);
+
+ if ((intr_status & (NormalIntr|AbnormalIntr)) == 0)
+ break;
+
+ if (intr_status & (IntrRxDone | RxNoBuf))
+ netdev_rx(dev);
+
+ for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) {
+ int entry = np->dirty_tx % TX_RING_SIZE;
+ int tx_status = le32_to_cpu(np->tx_ring[entry].status);
+
+ if (tx_status < 0)
+ break;
+ if (tx_status & 0x8000) { /* There was an error, log it. */
+#ifndef final_version
+ if (debug > 1)
+ printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
+ dev->name, tx_status);
+#endif
+ np->stats.tx_errors++;
+ if (tx_status & 0x0104) np->stats.tx_aborted_errors++;
+ if (tx_status & 0x0C80) np->stats.tx_carrier_errors++;
+ if (tx_status & 0x0200) np->stats.tx_window_errors++;
+ if (tx_status & 0x0002) np->stats.tx_fifo_errors++;
+ if ((tx_status & 0x0080) && np->full_duplex == 0)
+ np->stats.tx_heartbeat_errors++;
+#ifdef ETHER_STATS
+ if (tx_status & 0x0100) np->stats.collisions16++;
+#endif
+ } else {
+#ifdef ETHER_STATS
+ if (tx_status & 0x0001) np->stats.tx_deferred++;
+#endif
+ np->stats.tx_bytes += np->tx_skbuff[entry]->len;
+ np->stats.collisions += (tx_status >> 3) & 15;
+ np->stats.tx_packets++;
+ }
+ /* Free the original skb. */
+ np->tx_q_bytes -= np->tx_skbuff[entry]->len;
+ dev_kfree_skb_irq(np->tx_skbuff[entry]);
+ np->tx_skbuff[entry] = 0;
+ }
+ if (np->tx_full &&
+ np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4
+ && np->tx_q_bytes < TX_BUG_FIFO_LIMIT) {
+ /* The ring is no longer full, clear tbusy. */
+ np->tx_full = 0;
+ netif_wake_queue(dev);
+ }
+
+ /* Abnormal error summary/uncommon events handlers. */
+ if (intr_status & (AbnormalIntr | TxFIFOUnderflow | IntrPCIErr |
+ TimerInt | IntrTxStopped))
+ netdev_error(dev, intr_status);
+
+ if (--work_limit < 0) {
+ printk(KERN_WARNING "%s: Too much work at interrupt, "
+ "status=0x%4.4x.\n", dev->name, intr_status);
+ /* Set the timer to re-enable the other interrupts after
+ 10*82usec ticks. */
+ writel(AbnormalIntr | TimerInt, ioaddr + IntrEnable);
+ writel(10, ioaddr + GPTimer);
+ break;
+ }
+ } while (1);
+
+ if (debug > 3)
+ printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
+ dev->name, (int)readl(ioaddr + IntrStatus));
+
+ spin_unlock(&np->lock);
+}
+
+/* This routine is logically part of the interrupt handler, but separated
+ for clarity and better register allocation. */
+static int netdev_rx(struct net_device *dev)
+{
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ int entry = np->cur_rx % RX_RING_SIZE;
+ int work_limit = np->dirty_rx + RX_RING_SIZE - np->cur_rx;
+
+ if (debug > 4) {
+ printk(KERN_DEBUG " In netdev_rx(), entry %d status %4.4x.\n",
+ entry, np->rx_ring[entry].status);
+ }
+
+ /* If EOP is set on the next entry, it's a new packet. Send it up. */
+ while (--work_limit >= 0) {
+ struct w840_rx_desc *desc = np->rx_head_desc;
+ s32 status = le32_to_cpu(desc->status);
+
+ if (debug > 4)
+ printk(KERN_DEBUG " netdev_rx() status was %8.8x.\n",
+ status);
+ if (status < 0)
+ break;
+ if ((status & 0x38008300) != 0x0300) {
+ if ((status & 0x38000300) != 0x0300) {
+ /* Ingore earlier buffers. */
+ if ((status & 0xffff) != 0x7fff) {
+ printk(KERN_WARNING "%s: Oversized Ethernet frame spanned "
+ "multiple buffers, entry %#x status %4.4x!\n",
+ dev->name, np->cur_rx, status);
+ np->stats.rx_length_errors++;
+ }
+ } else if (status & 0x8000) {
+ /* There was a fatal error. */
+ if (debug > 2)
+ printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n",
+ dev->name, status);
+ np->stats.rx_errors++; /* end of a packet.*/
+ if (status & 0x0890) np->stats.rx_length_errors++;
+ if (status & 0x004C) np->stats.rx_frame_errors++;
+ if (status & 0x0002) np->stats.rx_crc_errors++;
+ }
+ } else {
+ struct sk_buff *skb;
+ /* Omit the four octet CRC from the length. */
+ int pkt_len = ((status >> 16) & 0x7ff) - 4;
+
+#ifndef final_version
+ if (debug > 4)
+ printk(KERN_DEBUG " netdev_rx() normal Rx pkt length %d"
+ " status %x.\n", pkt_len, status);
+#endif
+ /* Check if the packet is long enough to accept without copying
+ to a minimally-sized skbuff. */
+ if (pkt_len < rx_copybreak
+ && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
+ skb->dev = dev;
+ skb_reserve(skb, 2); /* 16 byte align the IP header */
+ /* Call copy + cksum if available. */
+#if HAS_IP_COPYSUM
+ eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0);
+ skb_put(skb, pkt_len);
+#else
+ memcpy(skb_put(skb, pkt_len), np->rx_skbuff[entry]->tail,
+ pkt_len);
+#endif
+ } else {
+ char *temp = skb_put(skb = np->rx_skbuff[entry], pkt_len);
+ np->rx_skbuff[entry] = NULL;
+#ifndef final_version /* Remove after testing. */
+ if (le32desc_to_virt(desc->buffer1) != temp)
+ printk(KERN_ERR "%s: Internal fault: The skbuff addresses "
+ "do not match in netdev_rx: %p vs. %p / %p.\n",
+ dev->name, le32desc_to_virt(desc->buffer1),
+ skb->head, temp);
+#endif
+ }
+#ifndef final_version /* Remove after testing. */
+ /* You will want this info for the initial debug. */
+ if (debug > 5)
+ printk(KERN_DEBUG " Rx data %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:"
+ "%2.2x %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x %2.2x%2.2x "
+ "%d.%d.%d.%d.\n",
+ skb->data[0], skb->data[1], skb->data[2], skb->data[3],
+ skb->data[4], skb->data[5], skb->data[6], skb->data[7],
+ skb->data[8], skb->data[9], skb->data[10],
+ skb->data[11], skb->data[12], skb->data[13],
+ skb->data[14], skb->data[15], skb->data[16],
+ skb->data[17]);
+#endif
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ dev->last_rx = jiffies;
+ np->stats.rx_packets++;
+ np->stats.rx_bytes += pkt_len;
+ }
+ entry = (++np->cur_rx) % RX_RING_SIZE;
+ np->rx_head_desc = &np->rx_ring[entry];
+ }
+
+ /* Refill the Rx ring buffers. */
+ for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) {
+ struct sk_buff *skb;
+ entry = np->dirty_rx % RX_RING_SIZE;
+ if (np->rx_skbuff[entry] == NULL) {
+ skb = dev_alloc_skb(np->rx_buf_sz);
+ np->rx_skbuff[entry] = skb;
+ if (skb == NULL)
+ break; /* Better luck next round. */
+ skb->dev = dev; /* Mark as being used by this device. */
+ np->rx_ring[entry].buffer1 = virt_to_le32desc(skb->tail);
+ }
+ np->rx_ring[entry].status = cpu_to_le32(DescOwn);
+ }
+
+ return 0;
+}
+
+static void netdev_error(struct net_device *dev, int intr_status)
+{
+ long ioaddr = dev->base_addr;
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+
+ if (debug > 2)
+ printk(KERN_DEBUG "%s: Abnormal event, %8.8x.\n",
+ dev->name, intr_status);
+ if (intr_status == 0xffffffff)
+ return;
+ if (intr_status & TxFIFOUnderflow) {
+ np->csr6 += 0x4000; /* Bump up the Tx threshold */
+ printk(KERN_DEBUG "%s: Tx underflow, increasing threshold to %8.8x.\n",
+ dev->name, np->csr6);
+ writel(np->csr6, ioaddr + NetworkConfig);
+ }
+ if (intr_status & IntrRxDied) { /* Missed a Rx frame. */
+ np->stats.rx_errors++;
+ }
+ if (intr_status & TimerInt) {
+ /* Re-enable other interrupts. */
+ writel(0x1A0F5, ioaddr + IntrEnable);
+ }
+ np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff;
+ writel(0, ioaddr + RxStartDemand);
+}
+
+static struct net_device_stats *get_stats(struct net_device *dev)
+{
+ long ioaddr = dev->base_addr;
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+
+ /* The chip only need report frame silently dropped. */
+ if (netif_running(dev))
+ np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff;
+
+ return &np->stats;
+}
+
+static unsigned const ethernet_polynomial = 0x04c11db7U;
+static inline u32 ether_crc(int length, unsigned char *data)
+{
+ int crc = -1;
+
+ while(--length >= 0) {
+ unsigned char current_octet = *data++;
+ int bit;
+ for (bit = 0; bit < 8; bit++, current_octet >>= 1) {
+ crc = (crc << 1) ^
+ ((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0);
+ }
+ }
+ return crc;
+}
+
+static void set_rx_mode(struct net_device *dev)
+{
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ long ioaddr = dev->base_addr;
+ u32 mc_filter[2]; /* Multicast hash filter */
+ u32 rx_mode;
+
+ if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
+ /* Unconditionally log net taps. */
+ printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name);
+ memset(mc_filter, 0xff, sizeof(mc_filter));
+ rx_mode = AcceptBroadcast | AcceptMulticast | AcceptAllPhys
+ | AcceptMyPhys;
+ } else if ((dev->mc_count > multicast_filter_limit)
+ || (dev->flags & IFF_ALLMULTI)) {
+ /* Too many to match, or accept all multicasts. */
+ memset(mc_filter, 0xff, sizeof(mc_filter));
+ rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+ } else {
+ struct dev_mc_list *mclist;
+ int i;
+ memset(mc_filter, 0, sizeof(mc_filter));
+ for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+ i++, mclist = mclist->next) {
+ set_bit((ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26) ^ 0x3F,
+ mc_filter);
+ }
+ rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+ }
+ writel(mc_filter[0], ioaddr + MulticastFilter0);
+ writel(mc_filter[1], ioaddr + MulticastFilter1);
+ np->csr6 &= ~0x00F8;
+ np->csr6 |= rx_mode;
+ writel(np->csr6, ioaddr + NetworkConfig);
+}
+
+static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ u16 *data = (u16 *)&rq->ifr_data;
+
+ switch(cmd) {
+ case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
+ data[0] = ((struct netdev_private *)dev->priv)->phys[0] & 0x1f;
+ /* Fall Through */
+ case SIOCDEVPRIVATE+1: /* Read the specified MII register. */
+ data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
+ return 0;
+ case SIOCDEVPRIVATE+2: /* Write the specified MII register */
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int netdev_close(struct net_device *dev)
+{
+ long ioaddr = dev->base_addr;
+ struct netdev_private *np = (struct netdev_private *)dev->priv;
+ int i;
+
+ netif_stop_queue(dev);
+
+ if (debug > 1) {
+ printk(KERN_DEBUG "%s: Shutting down ethercard, status was %8.8x "
+ "Config %8.8x.\n", dev->name, (int)readl(ioaddr + IntrStatus),
+ (int)readl(ioaddr + NetworkConfig));
+ printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d, Rx %d / %d.\n",
+ dev->name, np->cur_tx, np->dirty_tx, np->cur_rx, np->dirty_rx);
+ }
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ writel(0x0000, ioaddr + IntrEnable);
+
+ /* Stop the chip's Tx and Rx processes. */
+ writel(np->csr6 &= ~0x20FA, ioaddr + NetworkConfig);
+
+ if (readl(ioaddr + NetworkConfig) != 0xffffffff)
+ np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff;
+
+#ifdef __i386__
+ if (debug > 2) {
+ printk("\n"KERN_DEBUG" Tx ring at %8.8x:\n",
+ (int)virt_to_le32desc(np->tx_ring));
+ for (i = 0; i < TX_RING_SIZE; i++)
+ printk(" #%d desc. %4.4x %4.4x %8.8x.\n",
+ i, np->tx_ring[i].length,
+ np->tx_ring[i].status, np->tx_ring[i].buffer1);
+ printk("\n"KERN_DEBUG " Rx ring %8.8x:\n",
+ (int)virt_to_le32desc(np->rx_ring));
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ printk(KERN_DEBUG " #%d desc. %4.4x %4.4x %8.8x\n",
+ i, np->rx_ring[i].length,
+ np->rx_ring[i].status, np->rx_ring[i].buffer1);
+ }
+ }
+#endif /* __i386__ debugging only */
+
+ free_irq(dev->irq, dev);
+
+ del_timer_sync(&np->timer);
+
+ /* Free all the skbuffs in the Rx queue. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ np->rx_ring[i].status = 0;
+ if (np->rx_skbuff[i]) {
+ dev_kfree_skb(np->rx_skbuff[i]);
+ }
+ np->rx_skbuff[i] = 0;
+ }
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ if (np->tx_skbuff[i])
+ dev_kfree_skb(np->tx_skbuff[i]);
+ np->tx_skbuff[i] = 0;
+ }
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+static void __devexit w840_remove1 (struct pci_dev *pdev)
+{
+ struct net_device *dev = pdev->driver_data;
+
+ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+ while (dev) {
+ struct netdev_private *np = (void *)(dev->priv);
+ unregister_netdev(dev);
+#ifdef USE_IO_OPS
+ release_region(dev->base_addr, pci_id_tbl[np->chip_id].io_size);
+#else
+ release_mem_region(pci_resource_start(pdev, 1),
+ pci_id_tbl[np->chip_id].io_size);
+ iounmap((char *)(dev->base_addr));
+#endif
+ kfree(dev);
+ }
+
+ pdev->driver_data = NULL;
+}
+
+static struct pci_driver w840_driver = {
+ name: "winbond-840",
+ id_table: w840_pci_tbl,
+ probe: w840_probe1,
+ remove: w840_remove1,
+};
+
+static int __init w840_init(void)
+{
+ return pci_module_init(&w840_driver);
+}
+
+static void __exit w840_exit(void)
+{
+ pci_unregister_driver(&w840_driver);
+}
+
+module_init(w840_init);
+module_exit(w840_exit);
0105 Cyclom_8Y above first megabyte
0200 Cyclom_Z below first megabyte
0201 Cyclom_Z above first megabyte
+ 0300 PC300 RX 2
+ 0301 PC300 RX 1
+ 0310 PC300 TE 2
+ 0311 PC300 TE 1
120f Essential Communications
0001 Roadrunner serial HIPPI
1210 Hyperparallel Technologies
121a 0060 Voodoo3 3500 TV (NTSC)
121a 0061 Voodoo3 3500 TV (PAL)
121a 0062 Voodoo3 3500 TV (SECAM)
+ 0009 Voodoo 4
121b Advanced Telecommunications Modules
121c Nippon Texaco., Ltd
121d Lippert Automationstechnik GmbH
#include <linux/string.h>
-#include <linux/types.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/sched.h>
#define IDESCSI_VERSION "0.9"
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/string.h>
#ifndef _IPS_H_
#define _IPS_H_
- #include <linux/config.h>
#include <asm/uaccess.h>
#include <asm/io.h>
* any later version.
*
*/
- static char * sg_version_str = "Version: 3.1.16 (20000716)";
- static int sg_version_num = 30116; /* 2 digits for each component */
+#include <linux/config.h>
+#ifdef CONFIG_PROC_FS
+ static char * sg_version_str = "Version: 3.1.17 (20000921)";
+#endif
+ static int sg_version_num = 30117; /* 2 digits for each component */
/*
* D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au), notes:
* - scsi logging is available via SCSI_LOG_TIMEOUT macros. First
* # cat /proc/scsi/sg/debug
*
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/fs.h>
static int sg_ms_to_jif(unsigned int msecs);
static unsigned sg_jif_to_ms(int jifs);
static int sg_allow_access(unsigned char opcode, char dev_type);
-static int sg_last_dev(void);
static int sg_build_dir(Sg_request * srp, Sg_fd * sfp, int dxfer_len);
static void sg_unmap_and(Sg_scatter_hold * schp, int free_also);
static Sg_device * sg_get_dev(int dev);
+#ifdef CONFIG_PROC_FS
+static int sg_last_dev(void);
+#endif
static Sg_device ** sg_dev_arr = NULL;
}
#ifdef MODULE
-
MODULE_PARM(def_reserved_size, "i");
MODULE_PARM_DESC(def_reserved_size, "size of buffer reserved for each fd");
+#endif /* MODULE */
-int init_module(void) {
+static int __init init_sg(void) {
+#ifdef MODULE
if (def_reserved_size >= 0)
sg_big_buff = def_reserved_size;
+#endif /* MODULE */
sg_template.module = THIS_MODULE;
return scsi_register_module(MODULE_SCSI_DEV, &sg_template);
}
-void cleanup_module( void)
+static void __exit exit_sg( void)
{
#ifdef CONFIG_PROC_FS
sg_proc_cleanup();
}
sg_template.dev_max = 0;
}
-#endif /* MODULE */
#if 0
return resp;
}
+#ifdef CONFIG_PROC_FS
static Sg_request * sg_get_nth_request(Sg_fd * sfp, int nth)
{
Sg_request * resp;
read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
return resp;
}
+#endif
/* always adds to end of list */
static Sg_request * sg_add_request(Sg_fd * sfp)
return res;
}
+#ifdef CONFIG_PROC_FS
static Sg_fd * sg_get_nth_sfp(Sg_device * sdp, int nth)
{
Sg_fd * resp;
read_unlock_irqrestore(&sg_dev_arr_lock, iflags);
return resp;
}
+#endif
static Sg_fd * sg_add_sfp(Sg_device * sdp, int dev)
{
}
+#ifdef CONFIG_PROC_FS
static int sg_last_dev()
{
int k;
read_unlock_irqrestore(&sg_dev_arr_lock, iflags);
return k + 1; /* origin 1 */
}
+#endif
static Sg_device * sg_get_dev(int dev)
{
return 1;
}
#endif /* CONFIG_PROC_FS */
+
+
+module_init(init_sg);
+module_exit(exit_sg);
static int update_partition(Scsi_Tape *);
static int st_int_ioctl(Scsi_Tape *, unsigned int, unsigned long);
+
+\f
+/* #include "osst_detect.h" */
+#ifndef SIGS_FROM_OSST
+#define SIGS_FROM_OSST \
+ {"OnStream", "SC-", "", "osst"}, \
+ {"OnStream", "DI-", "", "osst"}, \
+ {"OnStream", "DP-", "", "osst"}, \
+ {"OnStream", "USB", "", "osst"}, \
+ {"OnStream", "FW-", "", "osst"}
+#endif
+
+struct st_reject_data {
+ char *vendor;
+ char *model;
+ char *rev;
+ char *driver_hint; /* Name of the correct driver, NULL if unknown */
+};
+
+static struct st_reject_data reject_list[] = {
+ /* {"XXX", "Yy-", "", NULL}, example */
+ SIGS_FROM_OSST,
+ {NULL, }};
+
+/* If the device signature is on the list of incompatible drives, the
+ function returns a pointer to the name of the correct driver (if known) */
+static char * st_incompatible(Scsi_Device* SDp)
+{
+ struct st_reject_data *rp;
+
+ for (rp=&(reject_list[0]); rp->vendor != NULL; rp++)
+ if (!strncmp(rp->vendor, SDp->vendor, strlen(rp->vendor)) &&
+ !strncmp(rp->model, SDp->model, strlen(rp->model)) &&
+ !strncmp(rp->rev, SDp->rev, strlen(rp->rev))) {
+ if (rp->driver_hint)
+ return rp->driver_hint;
+ else
+ return "unknown";
+ }
+ return NULL;
+}
\f
/* Convert the result to success code */
ST_partstat *STps;
int i, mode, target_nbr;
unsigned long flags = 0;
+ char *stp;
if (SDp->type != TYPE_TAPE)
return 1;
+ if ((stp = st_incompatible(SDp))) {
+ printk(KERN_INFO
+ "st: Found incompatible tape at scsi%d, channel %d, id %d, lun %d\n",
+ SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
+ printk(KERN_INFO "st: The suggested driver is %s.\n", stp);
+ return 1;
+ }
write_lock_irqsave(&st_dev_arr_lock, flags);
if (st_template.nr_dev >= st_template.dev_max) {
static int st_detect(Scsi_Device * SDp)
{
- if (SDp->type != TYPE_TAPE)
+ if (SDp->type != TYPE_TAPE || st_incompatible(SDp))
return 0;
printk(KERN_WARNING
if (get_user(val, (int *) arg))
return -EFAULT;
+
DPD(2, "val is %d\n", val);
if (val > 0) {
if (get_user(val, (int *) arg))
return -EFAULT;
+
DPD(2, " val is %d\n", val);
if (file->f_mode & FMODE_READ) {
if (get_user(val, (int *) arg))
return -EFAULT;
+
DPD(2, " val is %d\n", val);
if (val > 0) {
if (get_user(val, (int *) arg))
return -EFAULT;
+
DPD(2, " val is %d\n", val);
if (val != AFMT_QUERY) {
#include "8010.h"
#include "hwaccess.h"
#include <linux/init.h>
+#include <linux/sched.h>
/* In A1 Silicon, these bits are in the HC register */
#define HOOKN_BIT (1L << 12)
/* stereo voice */
card->waveout.send_a[1] = 0x00;
- card->waveout.send_b[1] = 0xff;
- card->waveout.send_c[1] = 0x00;
+ card->waveout.send_b[1] = 0x00;
+ card->waveout.send_c[1] = 0xff;
card->waveout.send_d[1] = 0x00;
card->waveout.send_routing[1] = 0xd01c;
card->waveout.send_a[2] = 0x00;
- card->waveout.send_b[2] = 0x00;
- card->waveout.send_c[2] = 0xff;
+ card->waveout.send_b[2] = 0xff;
+ card->waveout.send_c[2] = 0x00;
card->waveout.send_d[2] = 0x00;
card->waveout.send_routing[2] = 0xd01c;
#include "cardmi.h"
#include "midi.h"
-static spinlock_t midi_spinlock = SPIN_LOCK_UNLOCKED;
+static spinlock_t midi_spinlock __attribute((unused)) = SPIN_LOCK_UNLOCKED;
static void init_midi_hdr(struct midi_hdr *midihdr)
{
if (get_user(val, (int *) arg))
return -EFAULT;
+
i = hweight32(val);
if (i == 0)
return 0; /* val = mixer_recmask(s); */
return -EINVAL;
if (get_user(val, (int *) arg))
return -EFAULT;
+
if (emu10k1_mixer_wrch(card, i, val))
return -EINVAL;
* (http://www.freecom.de/)
*/
-#include <linux/config.h>
#include "transport.h"
#include "protocol.h"
#include "usb.h"
#include "usb.h"
#include "debug.h"
+#include <linux/config.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/malloc.h>
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <linux/config.h>
#include "usb.h"
#include "scsiglue.h"
#include "transport.h"
#include "freecom.h"
#endif
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/errno.h>
/* ignore timers firing during PM suspend, etc */
if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER)
- return;
+ goto out;
if(ohci->rh.send) {
len = rh_send_irq (ohci, urb->transfer_buffer, urb->transfer_buffer_length);
#endif
if (urb->complete) urb->complete (urb);
}
- }
+ }
+ out:
rh_init_int_timer (urb);
}
* (C) Copyright 1999 Johannes Erdfelt
* (C) Copyright 1999 Randy Dunlap
*
- * $Id: usb-uhci.c,v 1.237 2000/08/08 14:58:17 acher Exp $
+ * $Id: usb-uhci.c,v 1.239 2000/09/19 20:15:12 acher Exp $
*/
#include <linux/config.h>
/* This enables an extra UHCI slab for memory debugging */
#define DEBUG_SLAB
-#define VERSTR "$Revision: 1.237 $ time " __TIME__ " " __DATE__
+#define VERSTR "$Revision: 1.239 $ time " __TIME__ " " __DATE__
#include <linux/usb.h>
#include "usb-uhci.h"
}
}
/*-------------------------------------------------------------------*/
+// Release bandwidth for Interrupt or Isoc. transfers
+_static void uhci_release_bandwidth(urb_t *urb)
+{
+ if (urb->bandwidth) {
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_INTERRUPT:
+ usb_release_bandwidth (urb->dev, urb, 0);
+ break;
+ case PIPE_ISOCHRONOUS:
+ usb_release_bandwidth (urb->dev, urb, 1);
+ break;
+ default:
+ break;
+ }
+ }
+}
+/*-------------------------------------------------------------------*/
// unlinks an urb by dequeuing its qh, waits some frames and forgets it
_static int uhci_unlink_urb_sync (uhci_t *s, urb_t *urb)
{
if (!in_interrupt())
spin_unlock(&urb->lock);
+ uhci_release_bandwidth(urb);
spin_unlock_irqrestore (&s->urb_list_lock, flags);
urb->status = -ENOENT; // mark urb as killed
if (!in_interrupt())
spin_lock(&urb->lock);
+ uhci_release_bandwidth(urb);
ret = uhci_unlink_urb_async(s, urb);
if (!in_interrupt())
int ret = 0;
unsigned long flags;
urb_t *bulk_urb=NULL;
+ int bustime;
if (!urb->dev || !urb->dev->bus)
return -ENODEV;
else {
spin_unlock_irqrestore (&s->urb_list_lock, flags);
switch (usb_pipetype (urb->pipe)) {
- case PIPE_ISOCHRONOUS:
- ret = uhci_submit_iso_urb (urb);
+ case PIPE_ISOCHRONOUS:
+ if (urb->bandwidth == 0) { /* not yet checked/allocated */
+ if (urb->number_of_packets <= 0) {
+ ret = -EINVAL;
+ break;
+ }
+
+ bustime = usb_check_bandwidth (urb->dev, urb);
+ if (bustime < 0) {
+ ret = bustime;
+ break;
+ }
+
+ ret = uhci_submit_iso_urb(urb);
+ if (ret == 0)
+ usb_claim_bandwidth (urb->dev, urb, bustime, 1);
+ } else { /* bandwidth is already set */
+ ret = uhci_submit_iso_urb(urb);
+ }
break;
case PIPE_INTERRUPT:
- ret = uhci_submit_int_urb (urb);
+ if (urb->bandwidth == 0) { /* not yet checked/allocated */
+ bustime = usb_check_bandwidth (urb->dev, urb);
+ if (bustime < 0)
+ ret = bustime;
+ else {
+ ret = uhci_submit_int_urb(urb);
+ if (ret == 0)
+ usb_claim_bandwidth (urb->dev, urb, bustime, 0);
+ }
+ } else { /* bandwidth is already set */
+ ret = uhci_submit_int_urb(urb);
+ }
break;
case PIPE_CONTROL:
ret = uhci_submit_control_urb (urb);
if (urb->status != -EINPROGRESS) {
int proceed = 0;
+ /* Release bandwidth for Interrupt or Iso transfers */
+ if (urb->bandwidth) {
+ if (usb_pipetype(urb->pipe)==PIPE_ISOCHRONOUS)
+ usb_release_bandwidth (urb->dev, urb, 1);
+ else if (usb_pipetype(urb->pipe)==PIPE_INTERRUPT && urb->interval)
+ usb_release_bandwidth (urb->dev, urb, 0);
+ }
+
dbg("dequeued urb: %p", urb);
dequeue_urb (s, urb);
dev->bus->bandwidth_int_reqs++;
urb->bandwidth = bustime;
- dbg("bw_alloc increased by %d to %d for %d requesters",
+ dbg("bandwidth alloc increased by %d to %d for %d requesters",
bustime,
dev->bus->bandwidth_allocated,
dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
else
dev->bus->bandwidth_int_reqs--;
- dbg("bw_alloc reduced by %d to %d for %d requesters",
+ dbg("bandwidth alloc reduced by %d to %d for %d requesters",
urb->bandwidth,
dev->bus->bandwidth_allocated,
dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
O_OBJS = open.o read_write.o devices.o file_table.o buffer.o \
super.o block_dev.o stat.o exec.o pipe.o namei.o fcntl.o \
ioctl.o readdir.o select.o fifo.o locks.o \
- dcache.o inode.o attr.o bad_inode.o file.o iobuf.o \
+ dcache.o inode.o attr.o bad_inode.o file.o iobuf.o dnotify.o \
$(BINFMTS) $(FILESYSTEMS)
OX_OBJS := filesystems.o
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/smp_lock.h>
+#include <linux/dnotify.h>
+#include <linux/fcntl.h>
/* Taken over from the old code... */
mark_inode_dirty(inode);
}
+static int setattr_mask(unsigned int ia_valid)
+{
+ unsigned long dn_mask = 0;
+
+ if (ia_valid & ATTR_UID)
+ dn_mask |= DN_ATTRIB;
+ if (ia_valid & ATTR_GID)
+ dn_mask |= DN_ATTRIB;
+ if (ia_valid & ATTR_SIZE)
+ dn_mask |= DN_MODIFY;
+ /* both times implies a utime(s) call */
+ if ((ia_valid & (ATTR_ATIME|ATTR_MTIME)) == (ATTR_ATIME|ATTR_MTIME))
+ dn_mask |= DN_ATTRIB;
+ else if (ia_valid & ATTR_ATIME)
+ dn_mask |= DN_ACCESS;
+ else if (ia_valid & ATTR_MTIME)
+ dn_mask |= DN_MODIFY;
+ if (ia_valid & ATTR_MODE)
+ dn_mask |= DN_ATTRIB;
+ return dn_mask;
+}
+
int notify_change(struct dentry * dentry, struct iattr * attr)
{
struct inode *inode = dentry->d_inode;
inode_setattr(inode, attr);
}
unlock_kernel();
+ if (!error) {
+ unsigned long dn_mask = setattr_mask(ia_valid);
+ if (dn_mask)
+ inode_dir_notify(dentry->d_parent->d_inode, dn_mask);
+ }
return error;
}
static void refill_freelist(int size)
{
if (!grow_buffers(size)) {
- balance_dirty(NODEV);
- wakeup_kswapd(0); /* We can't wait because of __GFP_IO */
- schedule();
+ try_to_free_pages(GFP_BUFFER);
}
}
list_for_each(tmp, &sbi->sbi_cihead)
{
cii = list_entry(tmp, struct coda_inode_info, c_cilist);
- if ( cii->c_magic != CODA_CNODE_MAGIC ) continue;
+ if ( cii->c_magic != CODA_CNODE_MAGIC ) BUG();
if (!cred || coda_cred_eq(cred, &cii->c_cached_cred))
cii->c_cached_perm = 0;
/* check if the inode is already initialized */
cii = ITOC(inode);
- if (cii->c_magic == CODA_CNODE_MAGIC) {
+ if (cii->c_fid.Volume != 0 || cii->c_fid.Vnode != 0 || cii->c_fid.Unique != 0) {
/* see if it is the right one (might have an inode collision) */
if ( !coda_fideq(fid, &cii->c_fid) ) {
printk("coda_iget: initialized inode old %s new %s!\n",
/* new, empty inode found... initializing */
/* Initialize the Coda inode info structure */
- cii->c_magic = CODA_CNODE_MAGIC;
cii->c_fid = *fid;
cii->c_vnode = inode;
return NULL;
}
- if ( !fid ) {
- printk("coda_fid_to_inode: no fid!\n");
- return NULL;
- }
CDEBUG(D_INODE, "%s\n", coda_f2s(fid));
list_for_each(le, &sbi->sbi_cihead)
{
cii = list_entry(le, struct coda_inode_info, c_cilist);
- if ( cii->c_magic != CODA_CNODE_MAGIC ) continue;
+ if ( cii->c_magic != CODA_CNODE_MAGIC ) BUG();
CDEBUG(D_DOWNCALL, "iterating, now doing %s, ino %ld\n",
coda_f2s(&cii->c_fid), cii->c_vnode->i_ino);
cii = ITOC(inode);
/* make sure this is the one we want */
- if ( cii->c_magic == CODA_CNODE_MAGIC && coda_fideq(fid, &cii->c_fid) ) {
+ if ( coda_fideq(fid, &cii->c_fid) ) {
CDEBUG(D_INODE, "found %ld\n", inode->i_ino);
return inode;
}
memset(cii, 0, sizeof(struct coda_inode_info));
list_add(&cii->c_cilist, &sbi->sbi_cihead);
+ cii->c_magic = CODA_CNODE_MAGIC;
}
static void coda_clear_inode(struct inode *inode)
coda_cache_clear_inode(inode);
out:
inode->u.coda_i.c_magic = 0;
+ memset(&inode->u.coda_i.c_fid, 0, sizeof(struct ViceFid));
EXIT;
}
--- /dev/null
+/*
+ * Directory notifications for Linux.
+ *
+ * Copyright (C) 2000 Stephen Rothwell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/dnotify.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+
+extern void send_sigio(struct fown_struct *fown, int fd, int band);
+
+int dir_notify_enable = 1;
+
+static rwlock_t dn_lock = RW_LOCK_UNLOCKED;
+static kmem_cache_t *dn_cache;
+
+static void redo_inode_mask(struct inode *inode)
+{
+ unsigned long new_mask;
+ struct dnotify_struct *dn;
+
+ new_mask = 0;
+ for (dn = inode->i_dnotify; dn != NULL; dn = dn->dn_next)
+ new_mask |= dn->dn_mask & ~DN_MULTISHOT;
+ inode->i_dnotify_mask = new_mask;
+}
+
+int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
+{
+ struct dnotify_struct *dn = NULL;
+ struct dnotify_struct *odn;
+ struct dnotify_struct **prev;
+ struct inode *inode;
+ int turning_off = (arg & ~DN_MULTISHOT) == 0;
+
+ if (!turning_off && !dir_notify_enable)
+ return -EINVAL;
+ inode = filp->f_dentry->d_inode;
+ if (!S_ISDIR(inode->i_mode))
+ return -ENOTDIR;
+ if (!turning_off) {
+ dn = kmem_cache_alloc(dn_cache, SLAB_KERNEL);
+ if (dn == NULL)
+ return -ENOMEM;
+ }
+ write_lock(&dn_lock);
+ prev = &inode->i_dnotify;
+ for (odn = *prev; odn != NULL; prev = &odn->dn_next, odn = *prev)
+ if (odn->dn_filp == filp)
+ break;
+ if (odn != NULL) {
+ if (turning_off) {
+ *prev = odn->dn_next;
+ redo_inode_mask(inode);
+ dn = odn;
+ goto out_free;
+ }
+ odn->dn_fd = fd;
+ odn->dn_mask |= arg;
+ inode->i_dnotify_mask |= arg & ~DN_MULTISHOT;
+ goto out_free;
+ }
+ if (turning_off)
+ goto out;
+ filp->f_owner.pid = current->pid;
+ filp->f_owner.uid = current->uid;
+ filp->f_owner.euid = current->euid;
+ dn->dn_magic = DNOTIFY_MAGIC;
+ dn->dn_mask = arg;
+ dn->dn_fd = fd;
+ dn->dn_filp = filp;
+ inode->i_dnotify_mask |= arg & ~DN_MULTISHOT;
+ dn->dn_next = inode->i_dnotify;
+ inode->i_dnotify = dn;
+out:
+ write_unlock(&dn_lock);
+ return 0;
+out_free:
+ kmem_cache_free(dn_cache, dn);
+ goto out;
+}
+
+void __inode_dir_notify(struct inode *inode, unsigned long event)
+{
+ struct dnotify_struct * dn;
+ struct dnotify_struct **prev;
+ struct fown_struct * fown;
+ int changed = 0;
+
+ write_lock(&dn_lock);
+ prev = &inode->i_dnotify;
+ while ((dn = *prev) != NULL) {
+ if ((dn->dn_mask & event) == 0) {
+ prev = &dn->dn_next;
+ continue;
+ }
+ if (dn->dn_magic != DNOTIFY_MAGIC) {
+ printk(KERN_ERR "__inode_dir_notify: bad magic "
+ "number in dnotify_struct!\n");
+ return;
+ }
+ fown = &dn->dn_filp->f_owner;
+ if (fown->pid)
+ send_sigio(fown, dn->dn_fd, POLL_MSG);
+ if (dn->dn_mask & DN_MULTISHOT)
+ prev = &dn->dn_next;
+ else {
+ *prev = dn->dn_next;
+ changed = 1;
+ kmem_cache_free(dn_cache, dn);
+ }
+ }
+ if (changed)
+ redo_inode_mask(inode);
+ write_unlock(&dn_lock);
+}
+
+static int __init dnotify_init(void)
+{
+ dn_cache = kmem_cache_create("dnotify cache",
+ sizeof(struct dnotify_struct), 0, 0, NULL, NULL);
+ if (!dn_cache)
+ panic("cannot create dnotify slab cache");
+ return 0;
+}
+
+module_init(dnotify_init)
* Copyright (C) 1991, 1992 Linus Torvalds
*/
+#include <linux/init.h>
#include <linux/mm.h>
#include <linux/file.h>
+#include <linux/dnotify.h>
#include <linux/smp_lock.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
extern int sock_fcntl (struct file *, unsigned int cmd, unsigned long arg);
+extern int fcntl_setlease(unsigned int fd, struct file *filp, long arg);
+extern int fcntl_getlease(struct file *filp);
/* Expand files. Return <0 on error; 0 nothing done; 1 files expanded,
* we may have blocked.
static int setfl(int fd, struct file * filp, unsigned long arg)
{
struct inode * inode = filp->f_dentry->d_inode;
+ int error;
/*
* In the case of an append-only file, O_APPEND
/* Did FASYNC state change? */
if ((arg ^ filp->f_flags) & FASYNC) {
- if (filp->f_op && filp->f_op->fasync)
- filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0);
+ if (filp->f_op && filp->f_op->fasync) {
+ error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0);
+ if (error < 0)
+ return error;
+ }
}
/* required for strict SunOS emulation */
static long do_fcntl(unsigned int fd, unsigned int cmd,
unsigned long arg, struct file * filp)
{
- long err = 0;
+ long err = -EINVAL;
switch (cmd) {
case F_DUPFD:
- err = -EINVAL;
if (arg < NR_OPEN) {
get_file(filp);
err = dupfd(filp, arg);
err = get_close_on_exec(fd);
break;
case F_SETFD:
+ err = 0;
set_close_on_exec(fd, arg&1);
break;
case F_GETFL:
err = filp->f_flags;
break;
case F_SETFL:
+ lock_kernel();
err = setfl(fd, filp, arg);
+ unlock_kernel();
break;
case F_GETLK:
err = fcntl_getlk(fd, (struct flock *) arg);
break;
case F_SETLK:
- err = fcntl_setlk(fd, cmd, (struct flock *) arg);
- break;
case F_SETLKW:
err = fcntl_setlk(fd, cmd, (struct flock *) arg);
break;
err = filp->f_owner.pid;
break;
case F_SETOWN:
+ lock_kernel();
filp->f_owner.pid = arg;
filp->f_owner.uid = current->uid;
filp->f_owner.euid = current->euid;
+ err = 0;
if (S_ISSOCK (filp->f_dentry->d_inode->i_mode))
err = sock_fcntl (filp, F_SETOWN, arg);
+ unlock_kernel();
break;
case F_GETSIG:
err = filp->f_owner.signum;
case F_SETSIG:
/* arg == 0 restores default behaviour. */
if (arg < 0 || arg > _NSIG) {
- err = -EINVAL;
break;
}
err = 0;
filp->f_owner.signum = arg;
break;
+ case F_GETLEASE:
+ err = fcntl_getlease(filp);
+ break;
+ case F_SETLEASE:
+ err = fcntl_setlease(fd, filp, arg);
+ break;
+ case F_NOTIFY:
+ err = fcntl_dirnotify(fd, filp, arg);
+ break;
default:
/* sockets need a few special fcntls. */
err = -EINVAL;
if (!filp)
goto out;
- lock_kernel();
err = do_fcntl(fd, cmd, arg, filp);
- unlock_kernel();
fput(filp);
out:
static void send_sigio_to_task(struct task_struct *p,
struct fown_struct *fown,
- struct fasync_struct *fa,
+ int fd,
int reason)
{
if ((fown->euid != 0) &&
si.si_band = ~0L;
else
si.si_band = band_table[reason - POLL_IN];
- si.si_fd = fa->fa_fd;
+ si.si_fd = fd;
if (!send_sig_info(fown->signum, &si, p))
break;
/* fall-through: fall back on the old plain SIGIO signal */
}
}
-static void send_sigio(struct fown_struct *fown, struct fasync_struct *fa,
- int band)
+void send_sigio(struct fown_struct *fown, int fd, int band)
{
struct task_struct * p;
int pid = fown->pid;
read_lock(&tasklist_lock);
if ( (pid > 0) && (p = find_task_by_pid(pid)) ) {
- send_sigio_to_task(p, fown, fa, band);
+ send_sigio_to_task(p, fown, fd, band);
goto out;
}
for_each_task(p) {
match = -p->pgrp;
if (pid != match)
continue;
- send_sigio_to_task(p, fown, fa, band);
+ send_sigio_to_task(p, fown, fd, band);
}
out:
read_unlock(&tasklist_lock);
}
+static rwlock_t fasync_lock = RW_LOCK_UNLOCKED;
+static kmem_cache_t *fasync_cache;
+
/*
* fasync_helper() is used by some character device drivers (mainly mice)
* to set up the fasync queue. It returns negative on error, 0 if it did
* no changes and positive if it added/deleted the entry.
*/
-static rwlock_t fasync_lock = RW_LOCK_UNLOCKED;
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
{
struct fasync_struct *fa, **fp;
int result = 0;
if (on) {
- new = kmalloc(sizeof(struct fasync_struct), GFP_KERNEL);
+ new = kmem_cache_alloc(fasync_cache, SLAB_KERNEL);
if (!new)
return -ENOMEM;
}
if (fa->fa_file == filp) {
if(on) {
fa->fa_fd = fd;
- kfree(new);
+ kmem_cache_free(fasync_cache, new);
} else {
*fp = fa->fa_next;
- kfree(fa);
+ kmem_cache_free(fasync_cache, fa);
result = 1;
}
goto out;
while (fa) {
struct fown_struct * fown;
if (fa->magic != FASYNC_MAGIC) {
- printk("kill_fasync: bad magic number in "
+ printk(KERN_ERR "kill_fasync: bad magic number in "
"fasync_struct!\n");
return;
}
queued signum: SIGURG has its own default signalling
mechanism. */
if (fown->pid && !(sig == SIGURG && fown->signum == 0))
- send_sigio(fown, fa, band);
+ send_sigio(fown, fa->fa_fd, band);
fa = fa->fa_next;
}
}
__kill_fasync(*fp, sig, band);
read_unlock(&fasync_lock);
}
+
+static int __init fasync_init(void)
+{
+ fasync_cache = kmem_cache_create("fasync cache",
+ sizeof(struct fasync_struct), 0, 0, NULL, NULL);
+ if (!fasync_cache)
+ panic("cannot create fasync slab cache");
+ return 0;
+}
+
+module_init(fasync_init)
* reclaim is in progress */
lock_kernel();
lockd_up();
+ down(&file_lock_sem);
/* First, reclaim all locks that have been granted previously. */
restart:
}
tmp = tmp->next;
}
+ up(&file_lock_sem);
host->h_reclaiming = 0;
wake_up(&host->h_gracewait);
struct nlm_args *argp = &req->a_args;
struct nlm_lock *lock = &argp->lock;
- memset(argp, 0, sizeof(*argp));
nlmclnt_next_cookie(&argp->cookie);
argp->state = nsm_local_state;
memcpy(&lock->fh, NFS_FH(fl->fl_file->f_dentry), sizeof(struct nfs_fh));
lock->oh.data = req->a_owner;
lock->oh.len = sprintf(req->a_owner, "%d@%s",
current->pid, system_utsname.nodename);
- lock->fl = *fl;
+ locks_copy_lock(&lock->fl, fl);
}
/*
call->a_flags = RPC_TASK_ASYNC;
} else {
spin_unlock_irqrestore(¤t->sigmask_lock, flags);
- call->a_flags = 0;
+ memset(call, 0, sizeof(*call));
+ locks_init_lock(&call->a_args.lock.fl);
+ locks_init_lock(&call->a_res.lock.fl);
}
call->a_host = host;
while (!signalled()) {
call = (struct nlm_rqst *) kmalloc(sizeof(struct nlm_rqst), GFP_KERNEL);
- if (call)
+ if (call) {
+ memset(call, 0, sizeof(*call));
+ locks_init_lock(&call->a_args.lock.fl);
+ locks_init_lock(&call->a_res.lock.fl);
return call;
+ }
printk("nlmclnt_alloc_call: failed, waiting for memory\n");
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(5*HZ);
* Report the conflicting lock back to the application.
* FIXME: Is it OK to report the pid back as well?
*/
- memcpy(fl, &req->a_res.lock.fl, sizeof(*fl));
+ locks_copy_lock(fl, &req->a_res.lock.fl);
/* fl->fl_pid = 0; */
} else {
return nlm_stat_to_errno(req->a_res.status);
int status;
req = &reqst;
+ memset(req, 0, sizeof(*req));
+ locks_init_lock(&req->a_args.lock.fl);
+ locks_init_lock(&req->a_res.lock.fl);
req->a_host = host;
req->a_flags = 0;
if (!(block = (struct nlm_block *) kmalloc(sizeof(*block), GFP_KERNEL)))
goto failed;
memset(block, 0, sizeof(*block));
+ locks_init_lock(&block->b_call.a_args.lock.fl);
+ locks_init_lock(&block->b_call.a_res.lock.fl);
/* Set notifier function for VFS, and init args */
lock->fl.fl_notify = nlmsvc_notify_blocked;
/* Append to list of blocked */
nlmsvc_insert_block(block, NLM_NEVER);
- if (!list_empty(&block->b_call.a_args.lock.fl.fl_block)) {
+ if (list_empty(&block->b_call.a_args.lock.fl.fl_list)) {
/* Now add block to block list of the conflicting lock
if we haven't done so. */
dprintk("lockd: blocking on this lock.\n");
share->s_owner.len = oh->len;
share->s_next = file->f_shares;
file->f_shares = share;
- file->f_count += 1;
update:
share->s_access = argp->fsm_access;
|| !(p = nlm_decode_oh(p, &lock->oh)))
return NULL;
- memset(fl, 0, sizeof(*fl));
+ locks_init_lock(fl);
fl->fl_owner = current->files;
fl->fl_pid = ntohl(*p++);
fl->fl_flags = FL_POSIX;
int len;
memset(lock, 0, sizeof(*lock));
+ locks_init_lock(&lock->fl);
lock->fl.fl_pid = ~(u32) 0;
if (!(p = nlm_decode_cookie(p, &argp->cookie))
s32 start, len, end;
memset(&resp->lock, 0, sizeof(resp->lock));
+ locks_init_lock(fl);
excl = ntohl(*p++);
fl->fl_pid = ntohl(*p++);
if (!(p = nlm_decode_oh(p, &resp->lock.oh)))
|| !(p = nlm4_decode_oh(p, &lock->oh)))
return NULL;
- memset(fl, 0, sizeof(*fl));
+ locks_init_lock(fl);
fl->fl_owner = current->files;
fl->fl_pid = ntohl(*p++);
fl->fl_flags = FL_POSIX;
int len;
memset(lock, 0, sizeof(*lock));
+ locks_init_lock(&lock->fl);
lock->fl.fl_pid = ~(u32) 0;
if (!(p = nlm4_decode_cookie(p, &argp->cookie))
s64 start, end, len;
memset(&resp->lock, 0, sizeof(resp->lock));
+ locks_init_lock(fl);
excl = ntohl(*p++);
fl->fl_pid = ntohl(*p++);
if (!(p = nlm4_decode_oh(p, &resp->lock.oh)))
+#define MSNFS /* HACK HACK */
/*
* linux/fs/locks.c
*
* Use generic list implementation from <linux/list.h>.
* Sped up posix_locks_deadlock by only considering blocked locks.
* Matthew Wilcox <willy@thepuffingroup.com>, March, 2000.
+ *
+ * Leases and LOCK_MAND
+ * Matthew Wilcox <willy@linuxcare.com>, June, 2000.
+ * Stephen Rothwell <sfr@linuxcare.com>, June, 2000.
*/
#include <linux/malloc.h>
#include <linux/file.h>
#include <linux/smp_lock.h>
#include <linux/init.h>
+#include <linux/capability.h>
+#include <linux/sched.h>
+#include <asm/semaphore.h>
#include <asm/uaccess.h>
+DECLARE_MUTEX(file_lock_sem);
+
+#define acquire_fl_sem() down(&file_lock_sem)
+#define release_fl_sem() up(&file_lock_sem)
+
+int leases_enable = 1;
+int lease_break_time = 45;
+
LIST_HEAD(file_lock_list);
static LIST_HEAD(blocked_list);
static kmem_cache_t *filelock_cache;
/* Allocate an empty lock structure. */
-static struct file_lock *locks_alloc_lock(void)
+static struct file_lock *locks_alloc_lock(int account)
{
struct file_lock *fl;
+ if (account && current->locks >= current->rlim[RLIMIT_LOCKS].rlim_cur)
+ return NULL;
fl = kmem_cache_alloc(filelock_cache, SLAB_KERNEL);
+ if (fl)
+ current->locks++;
return fl;
}
BUG();
return;
}
-
+ current->locks--;
if (waitqueue_active(&fl->fl_wait))
panic("Attempting to free lock with active wait queue");
if (!list_empty(&fl->fl_block))
panic("Attempting to free lock with active block list");
- if (!list_empty(&fl->fl_link))
+ if (!list_empty(&fl->fl_link) || !list_empty(&fl->fl_list))
panic("Attempting to free lock on active lock list");
kmem_cache_free(filelock_cache, fl);
}
+void locks_init_lock(struct file_lock *fl)
+{
+ INIT_LIST_HEAD(&fl->fl_link);
+ INIT_LIST_HEAD(&fl->fl_block);
+ INIT_LIST_HEAD(&fl->fl_list);
+ init_waitqueue_head(&fl->fl_wait);
+ fl->fl_next = NULL;
+ fl->fl_fasync = NULL;
+ fl->fl_owner = 0;
+ fl->fl_pid = 0;
+ fl->fl_file = NULL;
+ fl->fl_flags = 0;
+ fl->fl_type = 0;
+ fl->fl_start = fl->fl_end = 0;
+ fl->fl_notify = NULL;
+ fl->fl_insert = NULL;
+ fl->fl_remove = NULL;
+}
+
/*
* Initialises the fields of the file lock which are invariant for
* free file_locks.
SLAB_CTOR_CONSTRUCTOR)
return;
- lock->fl_next = NULL;
- INIT_LIST_HEAD(&lock->fl_link);
- INIT_LIST_HEAD(&lock->fl_block);
- init_waitqueue_head(&lock->fl_wait);
+ locks_init_lock(lock);
}
/*
* Initialize a new lock from an existing file_lock structure.
*/
-static void locks_copy_lock(struct file_lock *new, struct file_lock *fl)
+void locks_copy_lock(struct file_lock *new, struct file_lock *fl)
{
new->fl_owner = fl->fl_owner;
new->fl_pid = fl->fl_pid;
/* Fill in a file_lock structure with an appropriate FLOCK lock. */
static struct file_lock *flock_make_lock(struct file *filp, unsigned int type)
{
- struct file_lock *fl = locks_alloc_lock();
+ struct file_lock *fl = locks_alloc_lock(1);
if (fl == NULL)
return NULL;
return fl;
}
+static int assign_type(struct file_lock *fl, int type)
+{
+ switch (type) {
+ case F_RDLCK:
+ case F_WRLCK:
+ case F_UNLCK:
+ fl->fl_type = type;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX
* style lock.
*/
fl->fl_end = start + l->l_len - 1;
if (l->l_len > 0 && fl->fl_end < 0)
return (0);
+ if (fl->fl_end > OFFT_OFFSET_MAX)
+ return 0;
fl->fl_start = start; /* we record the absolute position */
if (l->l_len == 0)
fl->fl_end = OFFSET_MAX;
fl->fl_insert = NULL;
fl->fl_remove = NULL;
- switch (l->l_type) {
- case F_RDLCK:
- case F_WRLCK:
- case F_UNLCK:
- fl->fl_type = l->l_type;
- break;
- default:
- return (0);
- }
-
- return (1);
+ return (assign_type(fl, l->l_type) == 0);
}
#if BITS_PER_LONG == 32
}
#endif
+/* Allocate a file_lock initialised to this type of lease */
+static int lease_alloc(struct file *filp, int type, struct file_lock **flp)
+{
+ struct file_lock *fl = locks_alloc_lock(1);
+ if (fl == NULL)
+ return -ENOMEM;
+
+ fl->fl_owner = current->files;
+ fl->fl_pid = current->pid;
+
+ fl->fl_file = filp;
+ fl->fl_flags = FL_LEASE;
+ if (assign_type(fl, type) != 0) {
+ locks_free_lock(fl);
+ return -EINVAL;
+ }
+ fl->fl_start = 0;
+ fl->fl_end = OFFSET_MAX;
+ fl->fl_notify = NULL;
+ fl->fl_insert = NULL;
+ fl->fl_remove = NULL;
+
+ *flp = fl;
+ return 0;
+}
+
/* Check if two locks overlap each other.
*/
static inline int locks_overlap(struct file_lock *fl1, struct file_lock *fl2)
*/
static void locks_delete_block(struct file_lock *waiter)
{
- list_del(&waiter->fl_block);
- INIT_LIST_HEAD(&waiter->fl_block);
+ list_del(&waiter->fl_list);
+ INIT_LIST_HEAD(&waiter->fl_list);
list_del(&waiter->fl_link);
INIT_LIST_HEAD(&waiter->fl_link);
}
static void locks_insert_block(struct file_lock *blocker,
struct file_lock *waiter)
{
- if (!list_empty(&waiter->fl_block)) {
+ if (!list_empty(&waiter->fl_list)) {
printk(KERN_ERR "locks_insert_block: removing duplicated lock "
"(pid=%d %Ld-%Ld type=%d)\n", waiter->fl_pid,
waiter->fl_start, waiter->fl_end, waiter->fl_type);
locks_delete_block(waiter);
}
- list_add_tail(&waiter->fl_block, &blocker->fl_block);
+ list_add_tail(&waiter->fl_list, &blocker->fl_block);
list_add(&waiter->fl_link, &blocked_list);
- waiter->fl_next = blocker;
}
/* Wake up processes blocked waiting for blocker.
static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait)
{
while (!list_empty(&blocker->fl_block)) {
- struct file_lock *waiter = list_entry(blocker->fl_block.next, struct file_lock, fl_block);
+ struct file_lock *waiter = list_entry(blocker->fl_block.next, struct file_lock, fl_list);
/* N.B. Is it possible for the notify function to block?? */
if (waiter->fl_notify)
waiter->fl_notify(waiter);
fl->fl_insert(fl);
}
-/* Delete a lock and free it.
- * First remove our lock from the active lock lists. Then call
- * locks_wake_up_blocks() to wake up processes that are blocked
- * waiting for this lock. Finally free the lock structure.
+/* Delete a lock and then free it.
+ * Remove our lock from the lock lists, wake up processes that are blocked
+ * waiting for this lock, notify the FS that the lock has been cleared and
+ * finally free the lock.
*/
static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait)
{
list_del(&fl->fl_link);
INIT_LIST_HEAD(&fl->fl_link);
+ fasync_helper(0, fl->fl_file, 0, &fl->fl_fasync);
+ if (fl->fl_fasync != NULL){
+ printk("locks_delete_lock: fasync == %p\n", fl->fl_fasync);
+ fl->fl_fasync = NULL;
+ }
+
if (fl->fl_remove)
fl->fl_remove(fl);
}
/* Determine if lock sys_fl blocks lock caller_fl. Common functionality
- * checks for overlapping locks and shared/exclusive status.
+ * checks for shared/exclusive status of overlapping locks.
*/
static int locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
{
- if (!locks_overlap(caller_fl, sys_fl))
- return (0);
-
switch (caller_fl->fl_type) {
case F_RDLCK:
return (sys_fl->fl_type == F_WRLCK);
-
+
case F_WRLCK:
return (1);
locks_same_owner(caller_fl, sys_fl))
return (0);
+ /* Check whether they overlap */
+ if (!locks_overlap(caller_fl, sys_fl))
+ return 0;
+
return (locks_conflict(caller_fl, sys_fl));
}
if (!(sys_fl->fl_flags & FL_FLOCK) ||
(caller_fl->fl_file == sys_fl->fl_file))
return (0);
+#ifdef MSNFS
+ if ((caller_fl->fl_type & LOCK_MAND) || (sys_fl->fl_type & LOCK_MAND))
+ return 0;
+#endif
return (locks_conflict(caller_fl, sys_fl));
}
+int interruptible_sleep_on_locked(wait_queue_head_t *fl_wait, struct semaphore *sem, int timeout)
+{
+ int result = 0;
+ wait_queue_t wait;
+ init_waitqueue_entry(&wait, current);
+
+ __add_wait_queue(fl_wait, &wait);
+ current->state = TASK_INTERRUPTIBLE;
+ up(sem);
+ if (timeout == 0)
+ schedule();
+ else
+ result = schedule_timeout(timeout);
+ if (signal_pending(current))
+ result = -ERESTARTSYS;
+ down(sem);
+ remove_wait_queue(fl_wait, &wait);
+ current->state = TASK_RUNNING;
+ return result;
+}
+
+static int locks_block_on(struct file_lock *blocker, struct file_lock *waiter)
+{
+ int result;
+ locks_insert_block(blocker, waiter);
+ result = interruptible_sleep_on_locked(&waiter->fl_wait, &file_lock_sem, 0);
+ locks_delete_block(waiter);
+ return result;
+}
+
+static int locks_block_on_timeout(struct file_lock *blocker, struct file_lock *waiter, int time)
+{
+ int result;
+ locks_insert_block(blocker, waiter);
+ result = interruptible_sleep_on_locked(&waiter->fl_wait, &file_lock_sem, time);
+ locks_delete_block(waiter);
+ return result;
+}
+
struct file_lock *
posix_test_lock(struct file *filp, struct file_lock *fl)
{
struct file_lock *cfl;
+ acquire_fl_sem();
for (cfl = filp->f_dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) {
if (!(cfl->fl_flags & FL_POSIX))
continue;
if (posix_locks_conflict(cfl, fl))
break;
}
+ release_fl_sem();
return (cfl);
}
/*
* Search the lock list for this inode for any POSIX locks.
*/
- lock_kernel();
+ acquire_fl_sem();
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
if (!(fl->fl_flags & FL_POSIX))
continue;
if (fl->fl_owner != owner)
break;
}
- unlock_kernel();
+ release_fl_sem();
return fl ? -EAGAIN : 0;
}
size_t count)
{
struct file_lock *fl;
- struct file_lock *new_fl = locks_alloc_lock();
+ struct file_lock *new_fl = locks_alloc_lock(0);
int error;
new_fl->fl_owner = current->files;
new_fl->fl_end = offset + count - 1;
error = 0;
- lock_kernel();
+ acquire_fl_sem();
repeat:
/* Search the lock list for this inode for locks that conflict with
* the proposed read/write.
*/
- for (fl = inode->i_flock; ; fl = fl->fl_next) {
- error = 0;
- if (!fl)
- break;
+ for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
if (!(fl->fl_flags & FL_POSIX))
continue;
- /* Block for writes against a "read" lock,
- * and both reads and writes against a "write" lock.
- */
+ if (fl->fl_start > new_fl->fl_end)
+ break;
if (posix_locks_conflict(new_fl, fl)) {
error = -EAGAIN;
if (filp && (filp->f_flags & O_NONBLOCK))
break;
- error = -ERESTARTSYS;
- if (signal_pending(current))
- break;
error = -EDEADLK;
if (posix_locks_deadlock(new_fl, fl))
break;
-
- locks_insert_block(fl, new_fl);
- interruptible_sleep_on(&new_fl->fl_wait);
- locks_delete_block(new_fl);
-
+
+ error = locks_block_on(fl, new_fl);
+ if (error != 0)
+ break;
+
/*
* If we've been sleeping someone might have
* changed the permissions behind our back.
goto repeat;
}
}
- unlock_kernel();
locks_free_lock(new_fl);
+ release_fl_sem();
return error;
}
-/* Try to create a FLOCK lock on filp. We always insert new FLOCK locks at
- * the head of the list, but that's secret knowledge known only to the next
- * two functions.
+/* Try to create a FLOCK lock on filp. We always insert new FLOCK locks
+ * at the head of the list, but that's secret knowledge known only to
+ * flock_lock_file and posix_lock_file.
*/
static int flock_lock_file(struct file *filp, unsigned int lock_type,
unsigned int wait)
error = -ENOLCK;
new_fl = flock_make_lock(filp, lock_type);
if (!new_fl)
- goto out;
+ return error;
}
error = 0;
}
before = &fl->fl_next;
}
- /* change means that we are changing the type of an existing lock, or
+ /* change means that we are changing the type of an existing lock,
* or else unlocking it.
*/
if (change) {
goto out;
repeat:
- /* Check signals each time we start */
- error = -ERESTARTSYS;
- if (signal_pending(current))
- goto out;
for (fl = inode->i_flock; (fl != NULL) && (fl->fl_flags & FL_FLOCK);
fl = fl->fl_next) {
if (!flock_locks_conflict(new_fl, fl))
error = -EAGAIN;
if (!wait)
goto out;
- locks_insert_block(fl, new_fl);
- interruptible_sleep_on(&new_fl->fl_wait);
- locks_delete_block(new_fl);
+ error = locks_block_on(fl, new_fl);
+ if (error != 0)
+ goto out;
goto repeat;
}
locks_insert_lock(&inode->i_flock, new_fl);
return error;
}
-/* Add a POSIX style lock to a file.
+/**
+ * posix_lock_file:
+ * @filp: The file to apply the lock to
+ * @caller: The lock to be applied
+ * @wait: 1 to retry automatically, 0 to return -EAGAIN
+ *
+ * Add a POSIX style lock to a file.
* We merge adjacent locks whenever possible. POSIX locks are sorted by owner
* task, then by starting address
*
* We may need two file_lock structures for this operation,
* so we get them in advance to avoid races.
*/
- new_fl = locks_alloc_lock();
- new_fl2 = locks_alloc_lock();
+ new_fl = locks_alloc_lock(0);
+ new_fl2 = locks_alloc_lock(0);
error = -ENOLCK; /* "no luck" */
if (!(new_fl && new_fl2))
goto out;
+ acquire_fl_sem();
if (caller->fl_type != F_UNLCK) {
repeat:
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
error = -EDEADLK;
if (posix_locks_deadlock(caller, fl))
goto out;
- error = -ERESTARTSYS;
- if (signal_pending(current))
+
+ error = locks_block_on(fl, caller);
+ if (error != 0)
goto out;
- locks_insert_block(fl, caller);
- interruptible_sleep_on(&caller->fl_wait);
- locks_delete_block(caller);
goto repeat;
}
}
locks_wake_up_blocks(left, 0);
}
out:
+ release_fl_sem();
/*
* Free any unused locks.
*/
}
static inline int flock_translate_cmd(int cmd) {
+#ifdef MSNFS
+ if (cmd & LOCK_MAND)
+ return cmd & (LOCK_MAND | LOCK_RW);
+#endif
switch (cmd &~ LOCK_NB) {
case LOCK_SH:
return F_RDLCK;
return -EINVAL;
}
-/* flock() system call entry point. Apply a FL_FLOCK style lock to
- * an open file descriptor.
+/**
+ * __get_lease - revoke all outstanding leases on file
+ * @inode: the inode of the file to return
+ * @mode: the open mode (read or write)
+ *
+ * get_lease (inlined for speed) has checked there already
+ * is a lease on this file. Leases are broken on a call to open()
+ * or truncate(). This function can sleep unless you
+ * specified %O_NONBLOCK to your open().
+ */
+int __get_lease(struct inode *inode, unsigned int mode)
+{
+ int error = 0, future;
+ struct file_lock *new_fl, *flock;
+ struct file_lock *fl;
+ int alloc_err;
+
+ alloc_err = lease_alloc(NULL, 0, &new_fl);
+
+ acquire_fl_sem();
+ flock = inode->i_flock;
+ if (flock->fl_type & F_INPROGRESS) {
+ if ((mode & O_NONBLOCK)
+ || (flock->fl_owner == current->files)) {
+ error = -EWOULDBLOCK;
+ goto out;
+ }
+ if (alloc_err != 0) {
+ error = alloc_err;
+ goto out;
+ }
+ do {
+ error = locks_block_on(flock, new_fl);
+ if (error != 0)
+ goto out;
+ flock = inode->i_flock;
+ if (!(flock && (flock->fl_flags & FL_LEASE)))
+ goto out;
+ } while (flock->fl_type & F_INPROGRESS);
+ }
+
+ if (mode & FMODE_WRITE) {
+ /* If we want write access, we have to revoke any lease. */
+ future = F_UNLCK | F_INPROGRESS;
+ } else if (flock->fl_type & F_WRLCK) {
+ /* Downgrade the exclusive lease to a read-only lease. */
+ future = F_RDLCK | F_INPROGRESS;
+ } else {
+ /* the existing lease was read-only, so we can read too. */
+ goto out;
+ }
+
+ if (alloc_err && (flock->fl_owner != current->files)) {
+ error = alloc_err;
+ goto out;
+ }
+
+ fl = flock;
+ do {
+ fl->fl_type = future;
+ fl = fl->fl_next;
+ } while (fl != NULL && (fl->fl_flags & FL_LEASE));
+
+ kill_fasync(&flock->fl_fasync, SIGIO, POLL_MSG);
+
+ if ((mode & O_NONBLOCK) || (flock->fl_owner == current->files)) {
+ error = -EWOULDBLOCK;
+ goto out;
+ }
+
+ if (lease_break_time > 0)
+ error = lease_break_time * HZ;
+ else
+ error = 0;
+restart:
+ error = locks_block_on_timeout(flock, new_fl, error);
+ if (error == 0) {
+ /* We timed out. Unilaterally break the lease. */
+ locks_delete_lock(&inode->i_flock, 0);
+ printk(KERN_WARNING "lease timed out\n");
+ } else if (error > 0) {
+ flock = inode->i_flock;
+ if (flock && (flock->fl_flags & FL_LEASE))
+ goto restart;
+ error = 0;
+ }
+
+out:
+ release_fl_sem();
+ if (!alloc_err)
+ locks_free_lock(new_fl);
+ return error;
+}
+
+/**
+ * lease_get_mtime
+ * @inode: the inode
+ *
+ * This is to force NFS clients to flush their caches for files with
+ * exclusive leases. The justification is that if someone has an
+ * exclusive lease, then they could be modifiying it.
+ */
+time_t lease_get_mtime(struct inode *inode)
+{
+ struct file_lock *flock = inode->i_flock;
+ if (flock && (flock->fl_flags & FL_LEASE) && (flock->fl_type & F_WRLCK))
+ return CURRENT_TIME;
+ return inode->i_mtime;
+}
+
+/**
+ * fcntl_getlease - Enquire what lease is currently active
+ * @filp: the file
+ *
+ * The value returned by this function will be one of
+ *
+ * %F_RDLCK to indicate a read-only (type II) lease is held.
+ *
+ * %F_WRLCK to indicate an exclusive lease is held.
+ *
+ * XXX: sfr & i disagree over whether F_INPROGRESS
+ * should be returned to userspace.
+ */
+int fcntl_getlease(struct file *filp)
+{
+ struct file_lock *fl;
+
+ fl = filp->f_dentry->d_inode->i_flock;
+ if ((fl == NULL) || ((fl->fl_flags & FL_LEASE) == 0))
+ return F_UNLCK;
+ return fl->fl_type & ~F_INPROGRESS;
+}
+
+/* We already had a lease on this file; just change its type */
+static int lease_modify(struct file_lock **before, int arg, int fd, struct file *filp)
+{
+ struct file_lock *fl = *before;
+ int error = assign_type(fl, arg);
+ if (error < 0)
+ goto out;
+
+ locks_wake_up_blocks(fl, 0);
+
+ if (arg == F_UNLCK) {
+ filp->f_owner.pid = 0;
+ filp->f_owner.uid = 0;
+ filp->f_owner.euid = 0;
+ filp->f_owner.signum = 0;
+ locks_delete_lock(before, 0);
+ fasync_helper(fd, filp, 0, &fl->fl_fasync);
+ }
+
+out:
+ return error;
+}
+
+/**
+ * fcntl_setlease - sets a lease on an open file
+ * @fd: open file descriptor
+ * @filp: file pointer
+ * @arg: type of lease to obtain
+ *
+ * Call this fcntl to establish a lease on the file.
+ * Note that you also need to call %F_SETSIG to
+ * receive a signal when the lease is broken.
+ */
+int fcntl_setlease(unsigned int fd, struct file *filp, long arg)
+{
+ struct file_lock *fl, **before, **my_before = NULL;
+ struct dentry *dentry;
+ struct inode *inode;
+ int error, rdlease_count = 0, wrlease_count = 0;
+
+ dentry = filp->f_dentry;
+ inode = dentry->d_inode;
+
+ if ((current->fsuid != inode->i_uid) && !capable(CAP_LEASE))
+ return -EACCES;
+ if (!S_ISREG(inode->i_mode))
+ return -EINVAL;
+
+ /*
+ * FIXME: What about F_RDLCK and files open for writing?
+ */
+ if ((arg == F_WRLCK)
+ && ((atomic_read(&dentry->d_count) > 1)
+ || (atomic_read(&inode->i_count) > 1)))
+ return -EAGAIN;
+
+ before = &inode->i_flock;
+
+ acquire_fl_sem();
+
+ while ((fl = *before) != NULL) {
+ if (fl->fl_flags != FL_LEASE)
+ break;
+ if (fl->fl_file == filp)
+ my_before = before;
+ else if (fl->fl_type & F_WRLCK)
+ wrlease_count++;
+ else
+ rdlease_count++;
+ before = &fl->fl_next;
+ }
+
+ if ((arg == F_RDLCK && (wrlease_count > 0)) ||
+ (arg == F_WRLCK && ((rdlease_count + wrlease_count) > 0))) {
+ error = -EAGAIN;
+ goto out_unlock;
+ }
+
+ if (my_before != NULL) {
+ error = lease_modify(my_before, arg, fd, filp);
+ goto out_unlock;
+ }
+
+ if (arg == F_UNLCK) {
+ error = 0;
+ goto out_unlock;
+ }
+
+ if (!leases_enable) {
+ error = -EINVAL;
+ goto out_unlock;
+ }
+
+ error = lease_alloc(filp, arg, &fl);
+ if (error)
+ goto out_unlock;
+
+ error = fasync_helper(fd, filp, 1, &fl->fl_fasync);
+ if (error < 0) {
+ locks_free_lock(fl);
+ goto out_unlock;
+ }
+ fl->fl_next = *before;
+ *before = fl;
+ list_add(&fl->fl_link, &file_lock_list);
+ filp->f_owner.pid = current->pid;
+ filp->f_owner.uid = current->uid;
+ filp->f_owner.euid = current->euid;
+out_unlock:
+ release_fl_sem();
+ return error;
+}
+
+/**
+ * sys_flock: - flock() system call.
+ * @fd: the file descriptor to lock.
+ * @cmd: the type of lock to apply.
+ *
+ * Apply a %FL_FLOCK style lock to an open file descriptor.
+ * The @cmd can be one of
+ *
+ * %LOCK_SH -- a shared lock.
+ *
+ * %LOCK_EX -- an exclusive lock.
+ *
+ * %LOCK_UN -- remove an existing lock.
+ *
+ * %LOCK_MAND -- a `mandatory' flock. This exists to emulate Windows Share Modes.
+ *
+ * %LOCK_MAND can be combined with %LOCK_READ or %LOCK_WRITE to allow other
+ * processes read and write access respectively.
*/
asmlinkage long sys_flock(unsigned int fd, unsigned int cmd)
{
type = error;
error = -EBADF;
- if ((type != F_UNLCK) && !(filp->f_mode & 3))
+ if ((type != F_UNLCK)
+#ifdef MSNFS
+ && !(type & LOCK_MAND)
+#endif
+ && !(filp->f_mode & 3))
goto out_putf;
- lock_kernel();
+ acquire_fl_sem();
error = flock_lock_file(filp, type,
(cmd & (LOCK_UN | LOCK_NB)) ? 0 : 1);
- unlock_kernel();
+ release_fl_sem();
out_putf:
fput(filp);
int fcntl_getlk(unsigned int fd, struct flock *l)
{
struct file *filp;
- struct file_lock *fl, *file_lock = locks_alloc_lock();
+ struct file_lock *fl, file_lock;
struct flock flock;
int error;
goto out;
error = -EINVAL;
- if (!flock_to_posix_lock(filp, file_lock, &flock))
+ if (!flock_to_posix_lock(filp, &file_lock, &flock))
goto out_putf;
if (filp->f_op->lock) {
- error = filp->f_op->lock(filp, F_GETLK, file_lock);
+ error = filp->f_op->lock(filp, F_GETLK, &file_lock);
if (error < 0)
goto out_putf;
else if (error == LOCK_USE_CLNT)
/* Bypass for NFS with no locking - 2.0.36 compat */
- fl = posix_test_lock(filp, file_lock);
+ fl = posix_test_lock(filp, &file_lock);
else
- fl = (file_lock->fl_type == F_UNLCK ? NULL : file_lock);
+ fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock);
} else {
- fl = posix_test_lock(filp, file_lock);
+ fl = posix_test_lock(filp, &file_lock);
}
flock.l_type = F_UNLCK;
out_putf:
fput(filp);
out:
- locks_free_lock(file_lock);
return error;
}
int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
{
struct file *filp;
- struct file_lock *file_lock = locks_alloc_lock();
+ struct file_lock *file_lock = locks_alloc_lock(0);
struct flock flock;
struct inode *inode;
int error;
int fcntl_getlk64(unsigned int fd, struct flock64 *l)
{
struct file *filp;
- struct file_lock *fl, *file_lock = locks_alloc_lock();
+ struct file_lock *fl, file_lock;
struct flock64 flock;
int error;
goto out;
error = -EINVAL;
- if (!flock64_to_posix_lock(filp, file_lock, &flock))
+ if (!flock64_to_posix_lock(filp, &file_lock, &flock))
goto out_putf;
if (filp->f_op->lock) {
- error = filp->f_op->lock(filp, F_GETLK, file_lock);
+ error = filp->f_op->lock(filp, F_GETLK, &file_lock);
if (error < 0)
goto out_putf;
else if (error == LOCK_USE_CLNT)
/* Bypass for NFS with no locking - 2.0.36 compat */
- fl = posix_test_lock(filp, file_lock);
+ fl = posix_test_lock(filp, &file_lock);
else
- fl = (file_lock->fl_type == F_UNLCK ? NULL : file_lock);
+ fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock);
} else {
- fl = posix_test_lock(filp, file_lock);
+ fl = posix_test_lock(filp, &file_lock);
}
flock.l_type = F_UNLCK;
out_putf:
fput(filp);
out:
- locks_free_lock(file_lock);
return error;
}
int fcntl_setlk64(unsigned int fd, unsigned int cmd, struct flock64 *l)
{
struct file *filp;
- struct file_lock *file_lock = locks_alloc_lock();
+ struct file_lock *file_lock = locks_alloc_lock(0);
struct flock64 flock;
struct inode *inode;
int error;
*/
return;
}
- lock_kernel();
-repeat:
+ acquire_fl_sem();
before = &inode->i_flock;
while ((fl = *before) != NULL) {
if ((fl->fl_flags & FL_POSIX) && fl->fl_owner == owner) {
locks_delete_lock(before, 0);
- goto repeat;
+ continue;
}
before = &fl->fl_next;
}
- unlock_kernel();
+ release_fl_sem();
}
/*
void locks_remove_flock(struct file *filp)
{
struct inode * inode = filp->f_dentry->d_inode;
- struct file_lock file_lock, *fl;
+ struct file_lock *fl;
struct file_lock **before;
+
if (!inode->i_flock)
return;
- lock_kernel();
-repeat:
+ acquire_fl_sem();
before = &inode->i_flock;
+
while ((fl = *before) != NULL) {
- if ((fl->fl_flags & FL_FLOCK) && fl->fl_file == filp) {
- int (*lock)(struct file *, int, struct file_lock *);
- lock = NULL;
- if (filp->f_op)
- lock = filp->f_op->lock;
- if (lock) {
- file_lock = *fl;
- file_lock.fl_type = F_UNLCK;
- }
+ if ((fl->fl_flags & (FL_FLOCK|FL_LEASE))
+ && (fl->fl_file == filp)) {
locks_delete_lock(before, 0);
- if (lock) {
- lock(filp, F_SETLK, &file_lock);
- /* List may have changed: */
- goto repeat;
- }
continue;
- }
+ }
before = &fl->fl_next;
}
- unlock_kernel();
+ release_fl_sem();
}
-/* The following two are for the benefit of lockd.
+/**
+ * posix_block_lock - blocks waiting for a file lock
+ * @blocker: the lock which is blocking
+ * @waiter: the lock which conflicts and has to wait
+ *
+ * lockd needs to block waiting for locks.
*/
void
posix_block_lock(struct file_lock *blocker, struct file_lock *waiter)
{
- lock_kernel();
+ acquire_fl_sem();
locks_insert_block(blocker, waiter);
- unlock_kernel();
+ release_fl_sem();
}
+/**
+ * posix_unblock_lock - stop waiting for a file lock
+ * @waiter: the lock which was waiting
+ *
+ * lockd needs to block waiting for locks.
+ */
void
posix_unblock_lock(struct file_lock *waiter)
{
- locks_delete_block(waiter);
- return;
+ acquire_fl_sem();
+ if (!list_empty(&waiter->fl_list)) {
+ locks_delete_block(waiter);
+ wake_up(&waiter->fl_wait);
+ }
+ release_fl_sem();
}
static void lock_get_status(char* out, struct file_lock *fl, int id, char *pfx)
{
- struct inode *inode;
+ struct inode *inode = NULL;
- inode = fl->fl_file->f_dentry->d_inode;
+ if (fl->fl_file != NULL)
+ inode = fl->fl_file->f_dentry->d_inode;
out += sprintf(out, "%d:%s ", id, pfx);
if (fl->fl_flags & FL_POSIX) {
out += sprintf(out, "%6s %s ",
(fl->fl_flags & FL_ACCESS) ? "ACCESS" : "POSIX ",
+ (inode == NULL) ? "*NOINODE*" :
(IS_MANDLOCK(inode) &&
(inode->i_mode & (S_IXGRP | S_ISGID)) == S_ISGID) ?
"MANDATORY" : "ADVISORY ");
+ } else if (fl->fl_flags & FL_FLOCK) {
+#ifdef MSNFS
+ if (fl->fl_type & LOCK_MAND) {
+ out += sprintf(out, "FLOCK MSNFS ");
+ } else
+#endif
+ out += sprintf(out, "FLOCK ADVISORY ");
+ } else if (fl->fl_flags & FL_LEASE) {
+ out += sprintf(out, "LEASE MANDATORY ");
+ } else {
+ out += sprintf(out, "UNKNOWN UNKNOWN ");
}
- else {
- out += sprintf(out, "FLOCK ADVISORY ");
- }
- out += sprintf(out, "%s ", (fl->fl_type == F_RDLCK) ? "READ " : "WRITE");
- out += sprintf(out, "%d %s:%ld %Ld %Ld ",
+#ifdef MSNFS
+ if (fl->fl_type & LOCK_MAND) {
+ out += sprintf(out, "%s ",
+ (fl->fl_type & LOCK_READ)
+ ? (fl->fl_type & LOCK_WRITE) ? "RW " : "READ "
+ : (fl->fl_type & LOCK_WRITE) ? "WRITE" : "NONE ");
+ } else
+#endif
+ out += sprintf(out, "%s ",
+ (fl->fl_type & F_WRLCK) ? "WRITE" : "READ ");
+ out += sprintf(out, "%d %s:%ld ",
fl->fl_pid,
- kdevname(inode->i_dev), inode->i_ino,
- (long long)fl->fl_start, (long long)fl->fl_end);
+ inode ? kdevname(inode->i_dev) : "<none>",
+ inode ? inode->i_ino : 0);
+ out += sprintf(out, "%Ld ", fl->fl_start);
+ if (fl->fl_end == OFFSET_MAX)
+ out += sprintf(out, "EOF ");
+ else
+ out += sprintf(out, "%Ld ", fl->fl_end);
sprintf(out, "%08lx %08lx %08lx %08lx %08lx\n",
(long)fl, (long)fl->fl_link.prev, (long)fl->fl_link.next,
(long)fl->fl_next, (long)fl->fl_block.next);
*pos += len;
}
+/**
+ * get_locks_status - reports lock usage in /proc/locks
+ * @buffer: address in userspace to write into
+ * @start: ?
+ * @offset: how far we are through the buffer
+ * @length: how much to read
+ */
+
int get_locks_status(char *buffer, char **start, off_t offset, int length)
{
struct list_head *tmp;
off_t pos = 0;
int i = 0;
- lock_kernel();
+ acquire_fl_sem();
list_for_each(tmp, &file_lock_list) {
struct list_head *btmp;
struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link);
list_for_each(btmp, &fl->fl_block) {
struct file_lock *bfl = list_entry(btmp,
- struct file_lock, fl_block);
+ struct file_lock, fl_list);
lock_get_status(q, bfl, i, " ->");
move_lock_status(&q, &pos, offset);
}
}
done:
- unlock_kernel();
+ release_fl_sem();
*start = buffer;
if(q-buffer < length)
return (q-buffer);
return length;
}
+#ifdef MSNFS
+/**
+ * lock_may_read - checks that the region is free of locks
+ * @inode: the inode that is being read
+ * @start: the first byte to read
+ * @len: the number of bytes to read
+ *
+ * Emulates Windows locking requirements. Whole-file
+ * mandatory locks (share modes) can prohibit a read and
+ * byte-range POSIX locks can prohibit a read if they overlap.
+ *
+ * N.B. this function is only ever called
+ * from knfsd and ownership of locks is never checked.
+ */
+int lock_may_read(struct inode *inode, loff_t start, unsigned long len)
+{
+ struct file_lock *fl;
+ int result = 1;
+ acquire_fl_sem();
+ for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
+ if (fl->fl_flags == FL_POSIX) {
+ if (fl->fl_type == F_RDLCK)
+ continue;
+ if ((fl->fl_end < start) || (fl->fl_start > (start + len)))
+ continue;
+ } else if (fl->fl_flags == FL_FLOCK) {
+ if (!(fl->fl_type & LOCK_MAND))
+ continue;
+ if (fl->fl_type & LOCK_READ)
+ continue;
+ } else
+ continue;
+ result = 0;
+ break;
+ }
+ release_fl_sem();
+ return result;
+}
+
+/**
+ * lock_may_write - checks that the region is free of locks
+ * @inode: the inode that is being written
+ * @start: the first byte to write
+ * @len: the number of bytes to write
+ *
+ * Emulates Windows locking requirements. Whole-file
+ * mandatory locks (share modes) can prohibit a write and
+ * byte-range POSIX locks can prohibit a write if they overlap.
+ *
+ * N.B. this function is only ever called
+ * from knfsd and ownership of locks is never checked.
+ */
+int lock_may_write(struct inode *inode, loff_t start, unsigned long len)
+{
+ struct file_lock *fl;
+ int result = 1;
+ acquire_fl_sem();
+ for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
+ if (fl->fl_flags == FL_POSIX) {
+ if ((fl->fl_end < start) || (fl->fl_start > (start + len)))
+ continue;
+ } else if (fl->fl_flags == FL_FLOCK) {
+ if (!(fl->fl_type & LOCK_MAND))
+ continue;
+ if (fl->fl_type & LOCK_WRITE)
+ continue;
+ } else
+ continue;
+ result = 0;
+ break;
+ }
+ release_fl_sem();
+ return result;
+}
+#endif
+
static int __init filelock_init(void)
{
filelock_cache = kmem_cache_create("file lock cache",
#include <linux/quotaops.h>
#include <linux/pagemap.h>
#include <linux/dcache.h>
+#include <linux/dnotify.h>
#include <asm/uaccess.h>
#include <asm/unaligned.h>
unlock_kernel();
exit_lock:
up(&dir->i_zombie);
+ if (!error)
+ inode_dir_notify(dir, DN_CREATE);
return error;
}
goto exit;
}
+ /*
+ * Ensure there are no outstanding leases on the file.
+ */
+ error = get_lease(inode, flag);
+ if (error)
+ goto exit;
+
if (flag & O_TRUNC) {
error = get_write_access(inode);
if (error)
unlock_kernel();
exit_lock:
up(&dir->i_zombie);
+ if (!error)
+ inode_dir_notify(dir, DN_CREATE);
return error;
}
exit_lock:
up(&dir->i_zombie);
+ if (!error)
+ inode_dir_notify(dir, DN_CREATE);
return error;
}
dentry->d_inode->i_flags |= S_DEAD;
}
double_up(&dir->i_zombie, &dentry->d_inode->i_zombie);
- if (!error)
+ if (!error) {
+ inode_dir_notify(dir, DN_DELETE);
d_delete(dentry);
+ }
dput(dentry);
return error;
}
}
up(&dir->i_zombie);
+ if (!error)
+ inode_dir_notify(dir, DN_DELETE);
return error;
}
exit_lock:
up(&dir->i_zombie);
+ if (!error)
+ inode_dir_notify(dir, DN_CREATE);
return error;
}
exit_lock:
up(&dir->i_zombie);
+ if (!error)
+ inode_dir_notify(dir, DN_CREATE);
return error;
}
int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
+ int error;
if (S_ISDIR(old_dentry->d_inode->i_mode))
- return vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry);
+ error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry);
else
- return vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry);
+ error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry);
+ if (!error) {
+ if (old_dir == new_dir)
+ inode_dir_notify(old_dir, DN_RENAME);
+ else {
+ inode_dir_notify(old_dir, DN_DELETE);
+ inode_dir_notify(new_dir, DN_CREATE);
+ }
+ }
+ return error;
}
static inline int do_rename(const char * oldname, const char * newname)
+#define MSNFS /* HACK HACK */
/*
* linux/fs/nfsd/export.c
*
{ NFSEXP_CROSSMNT, {"nohide", ""}},
{ NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}},
{ NFSEXP_NOAUTHNLM, {"insecure_locks", ""}},
+#ifdef NSMFS
+ { NFSEXP_MSNFS, {"msnfs", ""}},
+#endif
{ 0, {"", ""}}
};
p = xdr_encode_hyper(p, (u64) inode->i_dev);
p = xdr_encode_hyper(p, (u64) inode->i_ino);
p = encode_time3(p, inode->i_atime);
- p = encode_time3(p, inode->i_mtime);
+ p = encode_time3(p, lease_get_mtime(inode));
p = encode_time3(p, inode->i_ctime);
return p;
*p++ = htonl((u32) inode->i_ino);
*p++ = htonl((u32) inode->i_atime);
*p++ = 0;
- *p++ = htonl((u32) inode->i_mtime);
+ *p++ = htonl((u32) lease_get_mtime(inode));
*p++ = 0;
*p++ = htonl((u32) inode->i_ctime);
*p++ = 0;
+#define MSNFS /* HACK HACK */
/*
* linux/fs/nfsd/vfs.c
*
if (err)
goto out;
}
+
+ /*
+ * If we are changing the size of the file, then
+ * we need to break all leases.
+ */
+ err = get_lease(inode, FMODE_WRITE);
+ if (err)
+ goto out_nfserr;
+
err = get_write_access(inode);
if (err)
goto out_nfserr;
if (!inode->i_fop)
goto out;
+ /*
+ * Check to see if there are any leases on this file.
+ * This may block while leases are broken.
+ */
+ err = get_lease(inode, (access & MAY_WRITE) ? FMODE_WRITE : 0);
+ if (err)
+ goto out_nfserr;
+
if ((access & MAY_WRITE) && (err = get_write_access(inode)) != 0)
goto out_nfserr;
err = nfserr_perm;
if (!file.f_op->read)
goto out_close;
+#ifdef MSNFS
+ if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) &&
+ (!lock_may_read(file.f_dentry->d_inode, offset, *count)))
+ goto out_close;
+#endif
/* Get readahead parameters */
ra = nfsd_get_raparms(fhp->fh_export->ex_dev, fhp->fh_dentry->d_inode->i_ino);
err = nfserr_perm;
if (!file.f_op->write)
goto out_close;
+#ifdef MSNFS
+ if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) &&
+ (!lock_may_write(file.f_dentry->d_inode, offset, cnt)))
+ goto out_close;
+#endif
dentry = file.f_dentry;
inode = dentry->d_inode;
goto out_dput_old;
+#ifdef MSNFS
+ if ((ffhp->fh_export->ex_flags & NFSEXP_MSNFS) &&
+ ((atomic_read(&odentry->d_count) > 1)
+ || (atomic_read(&ndentry->d_count) > 1))) {
+ err = nfserr_perm;
+ } else
+#endif
err = vfs_rename(fdir, odentry, tdir, ndentry);
if (!err && EX_ISSYNC(tfhp->fh_export)) {
nfsd_sync_dir(tdentry);
}
if (type != S_IFDIR) { /* It's UNLINK */
+#ifdef MSNFS
+ if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) &&
+ (atomic_read(&rdentry->d_count) > 1)) {
+ err = nfserr_perm;
+ } else
+#endif
err = vfs_unlink(dirp, rdentry);
} else { /* It's RMDIR */
err = vfs_rmdir(dirp, rdentry);
#include <linux/file.h>
#include <linux/smp_lock.h>
#include <linux/quotaops.h>
+#include <linux/dnotify.h>
#include <linux/module.h>
#include <linux/slab.h>
if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
goto dput_and_out;
+ /*
+ * Make sure that there are no leases.
+ */
+ error = get_lease(inode, FMODE_WRITE);
+ if (error)
+ goto dput_and_out;
+
error = get_write_access(inode);
if (error)
goto dput_and_out;
retval = filp->f_op->flush(filp);
unlock_kernel();
}
+ fcntl_dirnotify(0, filp, 0);
locks_remove_posix(filp, id);
fput(filp);
return retval;
else
#endif
rd_load();
-#endif
-#ifdef CONFIG_BLK_DEV_MD
- md_run_setup();
#endif
return 0;
}
#include <linux/file.h>
#include <linux/uio.h>
#include <linux/smp_lock.h>
+#include <linux/dnotify.h>
#include <asm/uaccess.h>
ret = read(file, buf, count, &file->f_pos);
}
}
+ if (ret > 0)
+ inode_dir_notify(file->f_dentry->d_parent->d_inode,
+ DN_ACCESS);
fput(file);
}
return ret;
ret = write(file, buf, count, &file->f_pos);
}
}
+ if (ret > 0)
+ inode_dir_notify(file->f_dentry->d_parent->d_inode,
+ DN_MODIFY);
fput(file);
}
return ret;
if (iov != iovstack)
kfree(iov);
out_nofree:
+ /* VERIFY_WRITE actually means a read, as we write to user space */
+ if ((ret + (type == VERIFY_WRITE)) > 0)
+ inode_dir_notify(file->f_dentry->d_parent->d_inode,
+ (type == VERIFY_WRITE) ? DN_MODIFY : DN_ACCESS);
return ret;
}
if (pos < 0)
goto out;
ret = read(file, buf, count, &pos);
+ if (ret > 0)
+ inode_dir_notify(file->f_dentry->d_parent->d_inode, DN_ACCESS);
out:
fput(file);
bad_file:
goto out;
ret = write(file, buf, count, &pos);
+ if (ret > 0)
+ inode_dir_notify(file->f_dentry->d_parent->d_inode, DN_MODIFY);
out:
fput(file);
bad_file:
* than regular operations.
*/
-#ifdef CONFIG_SMP
+
+/*
+ * Counter is volatile to make sure gcc doesn't try to be clever
+ * and move things around on us. We need to use _exactly_ the address
+ * the user gave us, not some alias that contains the same information.
+ */
typedef struct { volatile int counter; } atomic_t;
-#else
-typedef struct { int counter; } atomic_t;
-#endif
#define ATOMIC_INIT(i) ( (atomic_t) { (i) } )
#define atomic_read(v) ((v)->counter)
#define atomic_set(v,i) ((v)->counter = (i))
-/*
- * Make sure gcc doesn't try to be clever and move things around
- * on us. We need to use _exactly_ the address the user gave us,
- * not some alias that contains the same information.
- */
-#define __atomic_fool_gcc(x) (*(struct { int a[100]; } *)x)
-
/*
* To get proper branch prediction for the main line, we must branch
* forward to code at the end of this object's .text section, then
* branch back to restart the operation.
*/
-extern __inline__ void atomic_add(int i, atomic_t * v)
+static __inline__ void atomic_add(int i, atomic_t * v)
{
unsigned long temp;
__asm__ __volatile__(
".subsection 2\n"
"2: br 1b\n"
".previous"
- :"=&r" (temp), "=m" (__atomic_fool_gcc(v))
- :"Ir" (i), "m" (__atomic_fool_gcc(v)));
+ :"=&r" (temp), "=m" (v->counter)
+ :"Ir" (i), "m" (v->counter));
}
-extern __inline__ void atomic_sub(int i, atomic_t * v)
+static __inline__ void atomic_sub(int i, atomic_t * v)
{
unsigned long temp;
__asm__ __volatile__(
".subsection 2\n"
"2: br 1b\n"
".previous"
- :"=&r" (temp), "=m" (__atomic_fool_gcc(v))
- :"Ir" (i), "m" (__atomic_fool_gcc(v)));
+ :"=&r" (temp), "=m" (v->counter)
+ :"Ir" (i), "m" (v->counter));
}
/*
* Same as above, but return the result value
*/
-extern __inline__ long atomic_add_return(int i, atomic_t * v)
+static __inline__ long atomic_add_return(int i, atomic_t * v)
{
long temp, result;
__asm__ __volatile__(
".subsection 2\n"
"2: br 1b\n"
".previous"
- :"=&r" (temp), "=m" (__atomic_fool_gcc(v)), "=&r" (result)
- :"Ir" (i), "m" (__atomic_fool_gcc(v)));
+ :"=&r" (temp), "=m" (v->counter), "=&r" (result)
+ :"Ir" (i), "m" (v->counter) : "memory");
return result;
}
-extern __inline__ long atomic_sub_return(int i, atomic_t * v)
+static __inline__ long atomic_sub_return(int i, atomic_t * v)
{
long temp, result;
__asm__ __volatile__(
".subsection 2\n"
"2: br 1b\n"
".previous"
- :"=&r" (temp), "=m" (__atomic_fool_gcc(v)), "=&r" (result)
- :"Ir" (i), "m" (__atomic_fool_gcc(v)));
+ :"=&r" (temp), "=m" (v->counter), "=&r" (result)
+ :"Ir" (i), "m" (v->counter) : "memory");
return result;
}
#ifndef _ALPHA_BITOPS_H
#define _ALPHA_BITOPS_H
+#include <linux/config.h>
+
/*
* Copyright 1994, Linus Torvalds.
*/
* bit 0 is the LSB of addr; bit 64 is the LSB of (addr+1).
*/
+#define BITOPS_NO_BRANCH
+
extern __inline__ void set_bit(unsigned long nr, volatile void * addr)
{
+#ifndef BITOPS_NO_BRANCH
unsigned long oldbit;
+#endif
unsigned long temp;
unsigned int * m = ((unsigned int *) addr) + (nr >> 5);
+#ifndef BITOPS_NO_BRANCH
__asm__ __volatile__(
- "1: ldl_l %0,%1\n"
+ "1: ldl_l %0,%4\n"
" and %0,%3,%2\n"
" bne %2,2f\n"
" xor %0,%3,%0\n"
".previous"
:"=&r" (temp), "=m" (*m), "=&r" (oldbit)
:"Ir" (1UL << (nr & 31)), "m" (*m));
+#else
+ __asm__ __volatile__(
+ "1: ldl_l %0,%3\n"
+ " bis %0,%2,%0\n"
+ " stl_c %0,%1\n"
+ " beq %0,2f\n"
+ ".subsection 2\n"
+ "2: br 1b\n"
+ ".previous"
+ :"=&r" (temp), "=m" (*m)
+ :"Ir" (1UL << (nr & 31)), "m" (*m));
+#endif
}
+/*
+ * WARNING: non atomic version.
+ */
+extern __inline__ void __set_bit(unsigned long nr, volatile void * addr)
+{
+ unsigned int * m = ((unsigned int *) addr) + (nr >> 5);
+ /*
+ * Asm and C produces the same thing so let
+ * the compiler to do its good work.
+ */
+#if 0
+ int tmp;
+
+ __asm__ __volatile__(
+ "ldl %0,%3\n\t"
+ "bis %0,%2,%0\n\t"
+ "stl %0,%1"
+ : "=&r" (tmp), "=m" (*m)
+ : "Ir" (1UL << (nr & 31)), "m" (*m));
+#else
+ *m |= 1UL << (nr & 31);
+#endif
+}
+
+#define smp_mb__before_clear_bit() smp_mb()
+#define smp_mb__after_clear_bit() smp_mb()
extern __inline__ void clear_bit(unsigned long nr, volatile void * addr)
{
+#ifndef BITOPS_NO_BRANCH
unsigned long oldbit;
+#endif
unsigned long temp;
unsigned int * m = ((unsigned int *) addr) + (nr >> 5);
+#ifndef BITOPS_NO_BRANCH
__asm__ __volatile__(
- "1: ldl_l %0,%1\n"
+ "1: ldl_l %0,%4\n"
" and %0,%3,%2\n"
" beq %2,2f\n"
" xor %0,%3,%0\n"
".previous"
:"=&r" (temp), "=m" (*m), "=&r" (oldbit)
:"Ir" (1UL << (nr & 31)), "m" (*m));
+#else
+ __asm__ __volatile__(
+ "1: ldl_l %0,%3\n"
+ " and %0,%2,%0\n"
+ " stl_c %0,%1\n"
+ " beq %0,2f\n"
+ ".subsection 2\n"
+ "2: br 1b\n"
+ ".previous"
+ :"=&r" (temp), "=m" (*m)
+ :"Ir" (~(1UL << (nr & 31))), "m" (*m));
+#endif
}
extern __inline__ void change_bit(unsigned long nr, volatile void * addr)
unsigned int * m = ((unsigned int *) addr) + (nr >> 5);
__asm__ __volatile__(
- "1: ldl_l %0,%1\n"
+ "1: ldl_l %0,%3\n"
" xor %0,%2,%0\n"
" stl_c %0,%1\n"
- " beq %0,3f\n"
+ " beq %0,2f\n"
".subsection 2\n"
- "3: br 1b\n"
+ "2: br 1b\n"
".previous"
:"=&r" (temp), "=m" (*m)
:"Ir" (1UL << (nr & 31)), "m" (*m));
unsigned int * m = ((unsigned int *) addr) + (nr >> 5);
__asm__ __volatile__(
- "1: ldl_l %0,%1\n"
+ "1: ldl_l %0,%4\n"
" and %0,%3,%2\n"
" bne %2,2f\n"
" xor %0,%3,%0\n"
" stl_c %0,%1\n"
" beq %0,3f\n"
+#ifdef CONFIG_SMP
" mb\n"
+#endif
"2:\n"
".subsection 2\n"
"3: br 1b\n"
".previous"
:"=&r" (temp), "=m" (*m), "=&r" (oldbit)
+ :"Ir" (1UL << (nr & 31)), "m" (*m) : "memory");
+
+ return oldbit != 0;
+}
+
+/*
+ * WARNING: non atomic version.
+ */
+extern __inline__ int __test_and_set_bit(unsigned long nr,
+ volatile void * addr)
+{
+ unsigned long oldbit;
+ unsigned long temp;
+ unsigned int * m = ((unsigned int *) addr) + (nr >> 5);
+
+ __asm__ __volatile__(
+ " ldl %0,%4\n"
+ " and %0,%3,%2\n"
+ " bne %2,1f\n"
+ " xor %0,%3,%0\n"
+ " stl %0,%1\n"
+ "1:\n"
+ :"=&r" (temp), "=m" (*m), "=&r" (oldbit)
:"Ir" (1UL << (nr & 31)), "m" (*m));
return oldbit != 0;
unsigned int * m = ((unsigned int *) addr) + (nr >> 5);
__asm__ __volatile__(
- "1: ldl_l %0,%1\n"
+ "1: ldl_l %0,%4\n"
" and %0,%3,%2\n"
" beq %2,2f\n"
" xor %0,%3,%0\n"
" stl_c %0,%1\n"
" beq %0,3f\n"
+#ifdef CONFIG_SMP
" mb\n"
+#endif
"2:\n"
".subsection 2\n"
"3: br 1b\n"
".previous"
:"=&r" (temp), "=m" (*m), "=&r" (oldbit)
+ :"Ir" (1UL << (nr & 31)), "m" (*m) : "memory");
+
+ return oldbit != 0;
+}
+
+/*
+ * WARNING: non atomic version.
+ */
+extern __inline__ int __test_and_clear_bit(unsigned long nr,
+ volatile void * addr)
+{
+ unsigned long oldbit;
+ unsigned long temp;
+ unsigned int * m = ((unsigned int *) addr) + (nr >> 5);
+
+ __asm__ __volatile__(
+ " ldl %0,%4\n"
+ " and %0,%3,%2\n"
+ " beq %2,1f\n"
+ " xor %0,%3,%0\n"
+ " stl %0,%1\n"
+ "1:\n"
+ :"=&r" (temp), "=m" (*m), "=&r" (oldbit)
:"Ir" (1UL << (nr & 31)), "m" (*m));
return oldbit != 0;
unsigned int * m = ((unsigned int *) addr) + (nr >> 5);
__asm__ __volatile__(
- "1: ldl_l %0,%1\n"
+ "1: ldl_l %0,%4\n"
" and %0,%3,%2\n"
" xor %0,%3,%0\n"
" stl_c %0,%1\n"
" beq %0,3f\n"
+#ifdef CONFIG_SMP
" mb\n"
+#endif
".subsection 2\n"
"3: br 1b\n"
".previous"
:"=&r" (temp), "=m" (*m), "=&r" (oldbit)
- :"Ir" (1UL << (nr & 31)), "m" (*m));
+ :"Ir" (1UL << (nr & 31)), "m" (*m) : "memory");
return oldbit != 0;
}
#ifdef __KERNEL__
-#define ext2_set_bit test_and_set_bit
-#define ext2_clear_bit test_and_clear_bit
+#define ext2_set_bit __test_and_set_bit
+#define ext2_clear_bit __test_and_clear_bit
#define ext2_test_bit test_bit
#define ext2_find_first_zero_bit find_first_zero_bit
#define ext2_find_next_zero_bit find_next_zero_bit
/* Bitmap functions for the minix filesystem. */
-#define minix_test_and_set_bit(nr,addr) test_and_set_bit(nr,addr)
-#define minix_set_bit(nr,addr) set_bit(nr,addr)
-#define minix_test_and_clear_bit(nr,addr) test_and_clear_bit(nr,addr)
+#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr)
+#define minix_set_bit(nr,addr) __set_bit(nr,addr)
+#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr)
#define minix_test_bit(nr,addr) test_bit(nr,addr)
#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size)
#ifdef __KERNEL__
#define SET_PERSONALITY(EX, IBCS2) \
- set_personality((EX).e_flags & EF_ALPHA_32BIT \
+ set_personality(((EX).e_flags & EF_ALPHA_32BIT) \
? PER_LINUX_32BIT : (IBCS2) ? PER_SVR4 : PER_LINUX)
#endif
".subsection 2\n"
"3: br 1b\n"
".previous"
- : "=r"(ret), "=r"(tmp), "=m"(__atomic_fool_gcc(&sem->waking))
+ : "=r"(ret), "=r"(tmp), "=m"(sem->waking.counter)
: "0"(0));
return ret > 0;
#include <linux/kernel.h>
#include <asm/current.h>
-#define DEBUG_SPINLOCK 1
-#define DEBUG_RWLOCK 1
+#define DEBUG_SPINLOCK 0
+#define DEBUG_RWLOCK 0
/*
* Simple spin lock operations. There are two variants, one clears IRQ's
#define spin_is_locked(x) ((x)->lock != 0)
#define spin_unlock_wait(x) ({ do { barrier(); } while ((x)->lock); })
-typedef struct { unsigned long a[100]; } __dummy_lock_t;
-#define __dummy_lock(lock) (*(__dummy_lock_t *)(lock))
-
#if DEBUG_SPINLOCK
extern void spin_unlock(spinlock_t * lock);
extern void debug_spin_lock(spinlock_t * lock, const char *, int);
" blbs %0,2b\n"
" br 1b\n"
".previous"
- : "=r" (tmp), "=m" (__dummy_lock(lock))
- : "m"(__dummy_lock(lock)));
+ : "=r" (tmp), "=m" (lock->lock)
+ : "m"(lock->lock) : "memory");
}
#define spin_trylock(lock) (!test_and_set_bit(0,(lock)))
" bne %1,6b\n"
" br 1b\n"
".previous"
- : "=m" (__dummy_lock(lock)), "=&r" (regx)
- : "0" (__dummy_lock(lock))
- );
+ : "=m" (*(volatile int *)lock), "=&r" (regx)
+ : "0" (*(volatile int *)lock) : "memory");
}
static inline void read_lock(rwlock_t * lock)
" blbs %1,6b\n"
" br 1b\n"
".previous"
- : "=m" (__dummy_lock(lock)), "=&r" (regx)
- : "m" (__dummy_lock(lock))
- );
+ : "=m" (*(volatile int *)lock), "=&r" (regx)
+ : "m" (*(volatile int *)lock) : "memory");
}
#endif /* DEBUG_RWLOCK */
{
long regx;
__asm__ __volatile__(
+ " mb\n"
"1: ldl_l %1,%0\n"
" addl %1,2,%1\n"
" stl_c %1,%0\n"
".subsection 2\n"
"6: br 1b\n"
".previous"
- : "=m" (__dummy_lock(lock)), "=&r" (regx)
- : "m" (__dummy_lock(lock)));
+ : "=m" (*(volatile int *)lock), "=&r" (regx)
+ : "m" (*(volatile int *)lock) : "memory");
}
#endif /* _ALPHA_SPINLOCK_H */
#define wmb() \
__asm__ __volatile__("wmb": : :"memory")
+#ifdef __SMP__
+#define smp_mb() mb()
+#define smp_rmb() rmb()
+#define smp_wmb() wmb()
+#else
+#define smp_mb() barrier()
+#define smp_rmb() barrier()
+#define smp_wmb() barrier()
+#endif
+
#define set_mb(var, value) \
do { var = value; mb(); } while (0)
-#define set_rmb(var, value) \
-do { var = value; rmb(); } while (0)
-
#define set_wmb(var, value) \
do { var = value; wmb(); } while (0)
#define getipl() (rdps() & 7)
#define setipl(ipl) ((void) swpipl(ipl))
-#define __cli() setipl(IPL_MAX)
-#define __sti() setipl(IPL_MIN)
+#define __cli() do { setipl(IPL_MAX); barrier(); } while(0)
+#define __sti() do { barrier(); setipl(IPL_MIN); } while(0)
#define __save_flags(flags) ((flags) = rdps())
-#define __save_and_cli(flags) ((flags) = swpipl(IPL_MAX))
-#define __restore_flags(flags) setipl(flags)
+#define __save_and_cli(flags) do { (flags) = swpipl(IPL_MAX); barrier(); } while(0)
+#define __restore_flags(flags) do { barrier(); setipl(flags); barrier(); } while(0)
#define local_irq_save(flags) __save_and_cli(flags)
#define local_irq_restore(flags) __restore_flags(flags)
/*
* Atomic exchange.
+ * Since it can be used to implement critical sections
+ * it must clobber "memory" (also for interrupts in UP).
*/
extern __inline__ unsigned long
unsigned long dummy;
__asm__ __volatile__(
- "1: ldl_l %0,%2\n"
+ "1: ldl_l %0,%4\n"
" bis $31,%3,%1\n"
" stl_c %1,%2\n"
" beq %1,2f\n"
+#ifdef CONFIG_SMP
" mb\n"
+#endif
".subsection 2\n"
"2: br 1b\n"
".previous"
: "=&r" (val), "=&r" (dummy), "=m" (*m)
- : "rI" (val), "m" (*m));
+ : "rI" (val), "m" (*m) : "memory");
return val;
}
unsigned long dummy;
__asm__ __volatile__(
- "1: ldq_l %0,%2\n"
+ "1: ldq_l %0,%4\n"
" bis $31,%3,%1\n"
" stq_c %1,%2\n"
" beq %1,2f\n"
+#ifdef CONFIG_SMP
" mb\n"
+#endif
".subsection 2\n"
"2: br 1b\n"
".previous"
: "=&r" (val), "=&r" (dummy), "=m" (*m)
- : "rI" (val), "m" (*m));
+ : "rI" (val), "m" (*m) : "memory");
return val;
}
* Atomic compare and exchange. Compare OLD with MEM, if identical,
* store NEW in MEM. Return the initial value in MEM. Success is
* indicated by comparing RETURN with OLD.
+ *
+ * The memory barrier should be placed in SMP only when we actually
+ * make the change. If we don't change anything (so if the returned
+ * prev is equal to old) then we aren't acquiring anything new and
+ * we don't need any memory barrier as far I can tell.
*/
#define __HAVE_ARCH_CMPXCHG 1
unsigned long prev, cmp;
__asm__ __volatile__(
- "1: ldl_l %0,%2\n"
+ "1: ldl_l %0,%5\n"
" cmpeq %0,%3,%1\n"
" beq %1,2f\n"
" mov %4,%1\n"
" stl_c %1,%2\n"
" beq %1,3f\n"
- "2: mb\n"
+#ifdef CONFIG_SMP
+ " mb\n"
+#endif
+ "2:\n"
".subsection 2\n"
"3: br 1b\n"
".previous"
: "=&r"(prev), "=&r"(cmp), "=m"(*m)
- : "r"((long) old), "r"(new), "m"(*m));
+ : "r"((long) old), "r"(new), "m"(*m) : "memory");
return prev;
}
unsigned long prev, cmp;
__asm__ __volatile__(
- "1: ldq_l %0,%2\n"
+ "1: ldq_l %0,%5\n"
" cmpeq %0,%3,%1\n"
" beq %1,2f\n"
" mov %4,%1\n"
" stq_c %1,%2\n"
" beq %1,3f\n"
- "2: mb\n"
+#ifdef CONFIG_SMP
+ " mb\n"
+#endif
+ "2:\n"
".subsection 2\n"
"3: br 1b\n"
".previous"
: "=&r"(prev), "=&r"(cmp), "=m"(*m)
- : "r"((long) old), "r"(new), "m"(*m));
+ : "r"((long) old), "r"(new), "m"(*m) : "memory");
return prev;
}
#define N_SLIP 1
#define N_MOUSE 2
#define N_PPP 3
+#define N_STRIP 4
#define N_AX25 5
#define N_X25 6 /* X.25 async */
#define N_6PACK 7
#define F_EXLCK 4 /* or 3 */
#define F_SHLCK 8 /* or 4 */
+/* for leases */
+#define F_INPROGRESS 16
+
/* operations for bsd flock(), also used by the kernel implementation */
#define LOCK_SH 1 /* shared lock */
#define LOCK_EX 2 /* exclusive lock */
blocking */
#define LOCK_UN 8 /* remove lock */
+#define LOCK_MAND 32 /* This is a mandatory flock */
+#define LOCK_READ 64 /* ... Which allows concurrent read operations */
+#define LOCK_WRITE 128 /* ... Which allows concurrent write operations */
+#define LOCK_RW 192 /* ... Which allows concurrent read & write ops */
+
struct flock {
short l_type;
short l_whence;
pid_t l_pid;
};
+#define F_LINUX_SPECIFIC_BASE 1024
#endif
#define RLIMIT_NOFILE 7 /* max number of open files */
#define RLIMIT_MEMLOCK 8 /* max locked-in-memory address space */
#define RLIMIT_AS 9 /* address space limit */
+#define RLIMIT_LOCKS 10 /* maximum file locks held */
-#define RLIM_NLIMITS 10
+#define RLIM_NLIMITS 11
#ifdef __KERNEL__
{ INR_OPEN, INR_OPEN }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
+ { RLIM_INFINITY, RLIM_INFINITY }, \
}
#endif /* __KERNEL__ */
* on us. We need to use _exactly_ the address the user gave us,
* not some alias that contains the same information.
*/
-#define __atomic_fool_gcc(x) (*(volatile struct { int a[100]; } *)x)
-
-#ifdef CONFIG_SMP
typedef struct { volatile int counter; } atomic_t;
-#else
-typedef struct { int counter; } atomic_t;
-#endif
#define ATOMIC_INIT(i) { (i) }
#define atomic_read(v) ((v)->counter)
#define atomic_set(v,i) (((v)->counter) = (i))
-static __inline__ void atomic_add(int i, volatile atomic_t *v)
+static __inline__ void atomic_add(int i, atomic_t *v)
{
__asm__ __volatile__(
LOCK "addl %1,%0"
- :"=m" (__atomic_fool_gcc(v))
- :"ir" (i), "m" (__atomic_fool_gcc(v)));
+ :"=m" (v->counter)
+ :"ir" (i), "m" (v->counter));
}
-static __inline__ void atomic_sub(int i, volatile atomic_t *v)
+static __inline__ void atomic_sub(int i, atomic_t *v)
{
__asm__ __volatile__(
LOCK "subl %1,%0"
- :"=m" (__atomic_fool_gcc(v))
- :"ir" (i), "m" (__atomic_fool_gcc(v)));
+ :"=m" (v->counter)
+ :"ir" (i), "m" (v->counter));
}
-static __inline__ int atomic_sub_and_test(int i, volatile atomic_t *v)
+static __inline__ int atomic_sub_and_test(int i, atomic_t *v)
{
unsigned char c;
__asm__ __volatile__(
LOCK "subl %2,%0; sete %1"
- :"=m" (__atomic_fool_gcc(v)), "=qm" (c)
- :"ir" (i), "m" (__atomic_fool_gcc(v)));
+ :"=m" (v->counter), "=qm" (c)
+ :"ir" (i), "m" (v->counter) : "memory");
return c;
}
-static __inline__ void atomic_inc(volatile atomic_t *v)
+static __inline__ void atomic_inc(atomic_t *v)
{
__asm__ __volatile__(
LOCK "incl %0"
- :"=m" (__atomic_fool_gcc(v))
- :"m" (__atomic_fool_gcc(v)));
+ :"=m" (v->counter)
+ :"m" (v->counter));
}
-static __inline__ void atomic_dec(volatile atomic_t *v)
+static __inline__ void atomic_dec(atomic_t *v)
{
__asm__ __volatile__(
LOCK "decl %0"
- :"=m" (__atomic_fool_gcc(v))
- :"m" (__atomic_fool_gcc(v)));
+ :"=m" (v->counter)
+ :"m" (v->counter));
}
-static __inline__ int atomic_dec_and_test(volatile atomic_t *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)));
+ :"=m" (v->counter), "=qm" (c)
+ :"m" (v->counter) : "memory");
return c != 0;
}
-static __inline__ int atomic_inc_and_test(volatile atomic_t *v)
+static __inline__ int atomic_inc_and_test(atomic_t *v)
{
unsigned char c;
__asm__ __volatile__(
LOCK "incl %0; sete %1"
- :"=m" (__atomic_fool_gcc(v)), "=qm" (c)
- :"m" (__atomic_fool_gcc(v)));
+ :"=m" (v->counter), "=qm" (c)
+ :"m" (v->counter) : "memory");
return c != 0;
}
-extern __inline__ int atomic_add_negative(int i, volatile atomic_t *v)
+static __inline__ int atomic_add_negative(int i, atomic_t *v)
{
unsigned char c;
__asm__ __volatile__(
LOCK "addl %2,%0; sets %1"
- :"=m" (__atomic_fool_gcc(v)), "=qm" (c)
- :"ir" (i), "m" (__atomic_fool_gcc(v)));
+ :"=m" (v->counter), "=qm" (c)
+ :"ir" (i), "m" (v->counter) : "memory");
return c;
}
/* These are x86-specific, used by some header files */
#define atomic_clear_mask(mask, addr) \
__asm__ __volatile__(LOCK "andl %0,%1" \
-: : "r" (~(mask)),"m" (__atomic_fool_gcc(addr)) : "memory")
+: : "r" (~(mask)),"m" (*addr) : "memory")
#define atomic_set_mask(mask, addr) \
__asm__ __volatile__(LOCK "orl %0,%1" \
-: : "r" (mask),"m" (__atomic_fool_gcc(addr)) : "memory")
+: : "r" (mask),"m" (*addr) : "memory")
#endif
#define LOCK_PREFIX ""
#endif
-/*
- * Function prototypes to keep gcc -Wall happy
- */
-extern void set_bit(int nr, volatile void * addr);
-extern void clear_bit(int nr, volatile void * addr);
-extern void change_bit(int nr, volatile void * addr);
-extern int test_and_set_bit(int nr, volatile void * addr);
-extern int test_and_clear_bit(int nr, volatile void * addr);
-extern int test_and_change_bit(int nr, volatile void * addr);
-extern int __constant_test_bit(int nr, const volatile void * addr);
-extern int __test_bit(int nr, volatile void * addr);
-extern int find_first_zero_bit(void * addr, unsigned size);
-extern int find_next_zero_bit (void * addr, int size, int offset);
-extern unsigned long ffz(unsigned long word);
-
-/*
- * Some hacks to defeat gcc over-optimizations..
- */
-struct __dummy { unsigned long a[100]; };
-#define ADDR (*(volatile struct __dummy *) addr)
-#define CONST_ADDR (*(volatile const struct __dummy *) addr)
+#define ADDR (*(volatile long *) addr)
-extern __inline__ void set_bit(int nr, volatile void * addr)
+static __inline__ void set_bit(int nr, volatile void * addr)
{
__asm__ __volatile__( LOCK_PREFIX
"btsl %1,%0"
:"Ir" (nr));
}
-extern __inline__ void clear_bit(int nr, volatile void * addr)
+/* WARNING: non atomic and it can be reordered! */
+static __inline__ void __set_bit(int nr, volatile void * addr)
+{
+ __asm__(
+ "btsl %1,%0"
+ :"=m" (ADDR)
+ :"Ir" (nr));
+}
+
+/*
+ * clear_bit() doesn't provide any barrier for the compiler.
+ */
+#define smp_mb__before_clear_bit() barrier()
+#define smp_mb__after_clear_bit() barrier()
+static __inline__ void clear_bit(int nr, volatile void * addr)
{
__asm__ __volatile__( LOCK_PREFIX
"btrl %1,%0"
:"Ir" (nr));
}
-extern __inline__ void change_bit(int nr, volatile void * addr)
+static __inline__ void change_bit(int nr, volatile void * addr)
{
__asm__ __volatile__( LOCK_PREFIX
"btcl %1,%0"
:"Ir" (nr));
}
-extern __inline__ int test_and_set_bit(int nr, volatile void * addr)
+/*
+ * It will also imply a memory barrier, thus it must clobber memory
+ * to make sure to reload anything that was cached into registers
+ * outside _this_ critical section.
+ */
+static __inline__ int test_and_set_bit(int nr, volatile void * addr)
{
int oldbit;
__asm__ __volatile__( LOCK_PREFIX
+ "btsl %2,%1\n\tsbbl %0,%0"
+ :"=r" (oldbit),"=m" (ADDR)
+ :"Ir" (nr) : "memory");
+ return oldbit;
+}
+
+/* WARNING: non atomic and it can be reordered! */
+static __inline__ int __test_and_set_bit(int nr, volatile void * addr)
+{
+ int oldbit;
+
+ __asm__(
"btsl %2,%1\n\tsbbl %0,%0"
:"=r" (oldbit),"=m" (ADDR)
:"Ir" (nr));
return oldbit;
}
-extern __inline__ int test_and_clear_bit(int nr, volatile void * addr)
+static __inline__ int test_and_clear_bit(int nr, volatile void * addr)
{
int oldbit;
__asm__ __volatile__( LOCK_PREFIX
+ "btrl %2,%1\n\tsbbl %0,%0"
+ :"=r" (oldbit),"=m" (ADDR)
+ :"Ir" (nr) : "memory");
+ return oldbit;
+}
+
+/* WARNING: non atomic and it can be reordered! */
+static __inline__ int __test_and_clear_bit(int nr, volatile void * addr)
+{
+ int oldbit;
+
+ __asm__(
"btrl %2,%1\n\tsbbl %0,%0"
:"=r" (oldbit),"=m" (ADDR)
:"Ir" (nr));
return oldbit;
}
-extern __inline__ int test_and_change_bit(int nr, volatile void * addr)
+static __inline__ int test_and_change_bit(int nr, volatile void * addr)
{
int oldbit;
__asm__ __volatile__( LOCK_PREFIX
"btcl %2,%1\n\tsbbl %0,%0"
:"=r" (oldbit),"=m" (ADDR)
- :"Ir" (nr));
+ :"Ir" (nr) : "memory");
return oldbit;
}
/*
* This routine doesn't need to be atomic.
*/
-extern __inline__ int __constant_test_bit(int nr, const volatile void * addr)
+static __inline__ int constant_test_bit(int nr, const volatile void * addr)
{
return ((1UL << (nr & 31)) & (((const volatile unsigned int *) addr)[nr >> 5])) != 0;
}
-extern __inline__ int __test_bit(int nr, volatile void * addr)
+static __inline__ int variable_test_bit(int nr, volatile void * addr)
{
int oldbit;
#define test_bit(nr,addr) \
(__builtin_constant_p(nr) ? \
- __constant_test_bit((nr),(addr)) : \
- __test_bit((nr),(addr)))
+ constant_test_bit((nr),(addr)) : \
+ variable_test_bit((nr),(addr)))
/*
* Find-bit routines..
*/
-extern __inline__ int find_first_zero_bit(void * addr, unsigned size)
+static __inline__ int find_first_zero_bit(void * addr, unsigned size)
{
int d0, d1, d2;
int res;
return res;
}
-extern __inline__ int find_next_zero_bit (void * addr, int size, int offset)
+static __inline__ int find_next_zero_bit (void * addr, int size, int offset)
{
unsigned long * p = ((unsigned long *) addr) + (offset >> 5);
int set = 0, bit = offset & 31, res;
* ffz = Find First Zero in word. Undefined if no zero exists,
* so code should check against ~0UL first..
*/
-extern __inline__ unsigned long ffz(unsigned long word)
+static __inline__ unsigned long ffz(unsigned long word)
{
__asm__("bsfl %1,%0"
:"=r" (word)
* differs in spirit from the above ffz (man ffs).
*/
-extern __inline__ int ffs(int x)
+static __inline__ int ffs(int x)
{
int r;
#ifdef __KERNEL__
-#define ext2_set_bit test_and_set_bit
-#define ext2_clear_bit test_and_clear_bit
+#define ext2_set_bit __test_and_set_bit
+#define ext2_clear_bit __test_and_clear_bit
#define ext2_test_bit test_bit
#define ext2_find_first_zero_bit find_first_zero_bit
#define ext2_find_next_zero_bit find_next_zero_bit
/* Bitmap functions for the minix filesystem. */
-#define minix_test_and_set_bit(nr,addr) test_and_set_bit(nr,addr)
-#define minix_set_bit(nr,addr) set_bit(nr,addr)
-#define minix_test_and_clear_bit(nr,addr) test_and_clear_bit(nr,addr)
+#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr)
+#define minix_set_bit(nr,addr) __set_bit(nr,addr)
+#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr)
#define minix_test_bit(nr,addr) test_bit(nr,addr)
#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size)
#define F_EXLCK 4 /* or 3 */
#define F_SHLCK 8 /* or 4 */
+/* for leases */
+#define F_INPROGRESS 16
+
/* operations for bsd flock(), also used by the kernel implementation */
#define LOCK_SH 1 /* shared lock */
#define LOCK_EX 2 /* exclusive lock */
blocking */
#define LOCK_UN 8 /* remove lock */
+#define LOCK_MAND 32 /* This is a mandatory flock */
+#define LOCK_READ 64 /* ... Which allows concurrent read operations */
+#define LOCK_WRITE 128 /* ... Which allows concurrent write operations */
+#define LOCK_RW 192 /* ... Which allows concurrent read & write ops */
+
struct flock {
short l_type;
short l_whence;
pid_t l_pid;
};
+#define F_LINUX_SPECIFIC_BASE 1024
+
#endif
#define RLIMIT_NOFILE 7 /* max number of open files */
#define RLIMIT_MEMLOCK 8 /* max locked-in-memory address space */
#define RLIMIT_AS 9 /* address space limit */
+#define RLIMIT_LOCKS 10 /* maximum file locks held */
-#define RLIM_NLIMITS 10
+#define RLIM_NLIMITS 11
/*
* SuS says limits have to be unsigned.
{ INR_OPEN, INR_OPEN }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
+ { RLIM_INFINITY, RLIM_INFINITY }, \
}
#endif /* __KERNEL__ */
#ifndef _ASM_I386_RWLOCK_H
#define _ASM_I386_RWLOCK_H
-typedef struct { unsigned long a[100]; } __dummy_lock_t;
-#define __dummy_lock(lock) (*(__dummy_lock_t *)(lock))
-
#define RW_LOCK_BIAS 0x01000000
#define RW_LOCK_BIAS_STR "0x01000000"
"popl %%eax\n\t" \
"jmp 1b\n" \
".previous" \
- :"=m" (__dummy_lock(rw)))
+ :"=m" (*(volatile int *)rw) : : "memory")
#define __build_read_lock(rw, helper) do { \
if (__builtin_constant_p(rw)) \
"popl %%eax\n\t" \
"jmp 1b\n" \
".previous" \
- :"=m" (__dummy_lock(rw)))
+ :"=m" (*(volatile int *)rw) : : "memory")
#define __build_write_lock(rw, helper) do { \
if (__builtin_constant_p(rw)) \
char oldval;
__asm__ __volatile__(
"xchgb %b0,%1"
- :"=q" (oldval), "=m" (__dummy_lock(lock))
- :"0" (0)
- :"memory");
+ :"=q" (oldval), "=m" (lock->lock)
+ :"0" (0) : "memory");
return oldval > 0;
}
-extern inline void spin_lock(spinlock_t *lock)
+static inline void spin_lock(spinlock_t *lock)
{
#if SPINLOCK_DEBUG
__label__ here;
#endif
__asm__ __volatile__(
spin_lock_string
- :"=m" (__dummy_lock(lock))
- : :"memory");
+ :"=m" (lock->lock) : : "memory");
}
-extern inline void spin_unlock(spinlock_t *lock)
+static inline void spin_unlock(spinlock_t *lock)
{
#if SPINLOCK_DEBUG
if (lock->magic != SPINLOCK_MAGIC)
#endif
__asm__ __volatile__(
spin_unlock_string
- :"=m" (__dummy_lock(lock))
- : :"memory");
+ :"=m" (lock->lock) : : "memory");
}
/*
*/
/* the spinlock helpers are in arch/i386/kernel/semaphore.S */
-extern inline void read_lock(rwlock_t *rw)
+static inline void read_lock(rwlock_t *rw)
{
#if SPINLOCK_DEBUG
if (rw->magic != RWLOCK_MAGIC)
__build_read_lock(rw, "__read_lock_failed");
}
-extern inline void write_lock(rwlock_t *rw)
+static inline void write_lock(rwlock_t *rw)
{
#if SPINLOCK_DEBUG
if (rw->magic != RWLOCK_MAGIC)
__build_write_lock(rw, "__write_lock_failed");
}
-#define read_unlock(rw) asm volatile("lock ; incl %0" :"=m" (__dummy_lock(&(rw)->lock)))
-#define write_unlock(rw) asm volatile("lock ; addl $" RW_LOCK_BIAS_STR ",%0":"=m" (__dummy_lock(&(rw)->lock)))
+#define read_unlock(rw) asm volatile("lock ; incl %0" :"=m" ((rw)->lock) : : "memory")
+#define write_unlock(rw) asm volatile("lock ; addl $" RW_LOCK_BIAS_STR ",%0":"=m" ((rw)->lock) : : "memory")
-extern inline int write_trylock(rwlock_t *lock)
+static inline int write_trylock(rwlock_t *lock)
{
atomic_t *count = (atomic_t *)lock;
if (atomic_sub_and_test(RW_LOCK_BIAS, count))
#endif
#define rmb() mb()
#define wmb() __asm__ __volatile__ ("": : :"memory")
+
+#ifdef __SMP__
+#define smp_mb() mb()
+#define smp_rmb() rmb()
+#define smp_wmb() wmb()
+#else
+#define smp_mb() barrier()
+#define smp_rmb() barrier()
+#define smp_wmb() barrier()
+#endif
+
#define set_mb(var, value) do { xchg(&var, value); } while (0)
#define set_wmb(var, value) do { var = value; wmb(); } while (0)
/* interrupt control.. */
-#define __save_flags(x) __asm__ __volatile__("pushfl ; popl %0":"=g" (x): /* no input */ :"memory")
+#define __save_flags(x) __asm__ __volatile__("pushfl ; popl %0":"=g" (x): /* no input */)
#define __restore_flags(x) __asm__ __volatile__("pushl %0 ; popfl": /* no output */ :"g" (x):"memory")
#define __cli() __asm__ __volatile__("cli": : :"memory")
#define __sti() __asm__ __volatile__("sti": : :"memory")
/* For spinlocks etc */
#define local_irq_save(x) __asm__ __volatile__("pushfl ; popl %0 ; cli":"=g" (x): /* no input */ :"memory")
-#define local_irq_restore(x) __asm__ __volatile__("pushl %0 ; popfl": /* no output */ :"g" (x):"memory")
-#define local_irq_disable() __asm__ __volatile__("cli": : :"memory")
-#define local_irq_enable() __asm__ __volatile__("sti": : :"memory")
+#define local_irq_restore(x) __restore_flags(x)
+#define local_irq_disable() __cli()
+#define local_irq_enable() __sti()
#ifdef CONFIG_SMP
#define F_EXLCK 4 /* or 3 */
#define F_SHLCK 8 /* or 4 */
+/* for leases */
+#define F_INPROGRESS 16
+
/* operations for bsd flock(), also used by the kernel implementation */
#define LOCK_SH 1 /* shared lock */
#define LOCK_EX 2 /* exclusive lock */
blocking */
#define LOCK_UN 8 /* remove lock */
+#define LOCK_MAND 32 /* This is a mandatory flock */
+#define LOCK_READ 64 /* ... Which allows concurrent read operations */
+#define LOCK_WRITE 128 /* ... Which allows concurrent write operations */
+#define LOCK_RW 192 /* ... Which allows concurrent read & write ops */
+
struct flock {
short l_type;
short l_whence;
pid_t l_pid;
};
+#define F_LINUX_SPECIFIC_BASE 1024
#endif /* _ASM_IA64_FCNTL_H */
#define RLIMIT_NOFILE 7 /* max number of open files */
#define RLIMIT_MEMLOCK 8 /* max locked-in-memory address space */
#define RLIMIT_AS 9 /* address space limit */
+#define RLIMIT_LOCKS 10 /* maximum file locks held */
-#define RLIM_NLIMITS 10
+#define RLIM_NLIMITS 11
/*
* SuS says limits have to be unsigned.
{ INR_OPEN, INR_OPEN }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
+ { RLIM_INFINITY, RLIM_INFINITY }, \
}
# endif /* __KERNEL__ */
#define F_EXLCK 4 /* or 3 */
#define F_SHLCK 8 /* or 4 */
+/* for leases */
+#define F_INPROGRESS 16
+
/* operations for bsd flock(), also used by the kernel implementation */
#define LOCK_SH 1 /* shared lock */
#define LOCK_EX 2 /* exclusive lock */
blocking */
#define LOCK_UN 8 /* remove lock */
+#define LOCK_MAND 32 /* This is a mandatory flock */
+#define LOCK_READ 64 /* ... Which allows concurrent read operations */
+#define LOCK_WRITE 128 /* ... Which allows concurrent write operations */
+#define LOCK_RW 192 /* ... Which allows concurrent read & write ops */
+
struct flock {
short l_type;
short l_whence;
pid_t l_pid;
};
+#define F_LINUX_SPECIFIC_BASE 1024
#endif /* _M68K_FCNTL_H */
#define RLIMIT_NOFILE 7 /* max number of open files */
#define RLIMIT_MEMLOCK 8 /* max locked-in-memory address space*/
#define RLIMIT_AS 9 /* address space limit */
+#define RLIMIT_LOCKS 10 /* maximum file locks held */
-#define RLIM_NLIMITS 10
+#define RLIM_NLIMITS 11
/*
* SuS says limits have to be unsigned.
{0, 0}, \
{INR_OPEN, INR_OPEN}, \
{LONG_MAX, LONG_MAX}, \
+ {LONG_MAX, LONG_MAX}, \
{LONG_MAX, LONG_MAX} \
}
#define F_EXLCK 4 /* or 3 */
#define F_SHLCK 8 /* or 4 */
+/* for leases */
+#define F_INPROGRESS 16
+
/* operations for bsd flock(), also used by the kernel implementation */
#define LOCK_SH 1 /* shared lock */
#define LOCK_EX 2 /* exclusive lock */
blocking */
#define LOCK_UN 8 /* remove lock */
+#define LOCK_MAND 32 /* This is a mandatory flock */
+#define LOCK_READ 64 /* ... Which allows concurrent read operations */
+#define LOCK_WRITE 128 /* ... Which allows concurrent write operations */
+#define LOCK_RW 192 /* ... Which allows concurrent read & write ops */
+
typedef struct flock {
short l_type;
short l_whence;
long pad[4]; /* ZZZZZZZZZZZZZZZZZZZZZZZZZZ */
} flock_t;
+#define F_LINUX_SPECIFIC_BASE 1024
#endif /* __ASM_MIPS_FCNTL_H */
#define RLIMIT_RSS 7 /* max resident set size */
#define RLIMIT_NPROC 8 /* max number of processes */
#define RLIMIT_MEMLOCK 9 /* max locked-in-memory address space */
+#define RLIMIT_LOCKS 10 /* maximum file locks held */
-#define RLIM_NLIMITS 10 /* Number of limit flavors. */
+#define RLIM_NLIMITS 11 /* Number of limit flavors. */
/*
* SuS says limits have to be unsigned.
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ 0, 0 }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
+ { RLIM_INFINITY, RLIM_INFINITY }, \
}
#endif /* __KERNEL__ */
#define F_EXLCK 4 /* or 3 */
#define F_SHLCK 8 /* or 4 */
+/* for leases */
+#define F_INPROGRESS 16
+
/* operations for bsd flock(), also used by the kernel implementation */
#define LOCK_SH 1 /* shared lock */
#define LOCK_EX 2 /* exclusive lock */
blocking */
#define LOCK_UN 8 /* remove lock */
+#define LOCK_MAND 32 /* This is a mandatory flock */
+#define LOCK_READ 64 /* ... Which allows concurrent read operations */
+#define LOCK_WRITE 128 /* ... Which allows concurrent write operations */
+#define LOCK_RW 192 /* ... Which allows concurrent read & write ops */
+
typedef struct flock {
short l_type;
short l_whence;
long pad[4]; /* ZZZZZZZZZZZZZZZZZZZZZZZZZZ */
} flock_t;
+#define F_LINUX_SPECIFIC_BASE 1024
#endif /* _ASM_FCNTL_H */
#define RLIMIT_RSS 7 /* max resident set size */
#define RLIMIT_NPROC 8 /* max number of processes */
#define RLIMIT_MEMLOCK 9 /* max locked-in-memory address space */
+#define RLIMIT_LOCKS 10 /* maximum file locks held */
-#define RLIM_NLIMITS 10 /* Number of limit flavors. */
+#define RLIM_NLIMITS 11 /* Number of limit flavors. */
/*
* SuS says limits have to be unsigned.
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ 0, 0 }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
+ { RLIM_INFINITY, RLIM_INFINITY }, \
}
#endif /* __KERNEL__ */
#define F_EXLCK 4 /* or 3 */
#define F_SHLCK 8 /* or 4 */
+/* for leases */
+#define F_INPROGRESS 16
+
/* operations for bsd flock(), also used by the kernel implementation */
#define LOCK_SH 1 /* shared lock */
#define LOCK_EX 2 /* exclusive lock */
blocking */
#define LOCK_UN 8 /* remove lock */
+#define LOCK_MAND 32 /* This is a mandatory flock */
+#define LOCK_READ 64 /* ... Which allows concurrent read operations */
+#define LOCK_WRITE 128 /* ... Which allows concurrent write operations */
+#define LOCK_RW 192 /* ... Which allows concurrent read & write ops */
+
#ifdef __KERNEL__
#define F_POSIX 1
#define F_FLOCK 2
pid_t l_pid;
};
+#define F_LINUX_SPECIFIC_BASE 1024
#endif
#define RLIMIT_NOFILE 7 /* max number of open files */
#define RLIMIT_MEMLOCK 8 /* max locked-in-memory address space */
#define RLIMIT_AS 9 /* address space limit(?) */
+#define RLIMIT_LOCKS 10 /* maximum file locks held */
-#define RLIM_NLIMITS 10
+#define RLIM_NLIMITS 11
#ifdef __KERNEL__
{ INR_OPEN, INR_OPEN }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
+ { RLIM_INFINITY, RLIM_INFINITY }, \
}
#endif /* __KERNEL__ */
#define F_EXLCK 4 /* or 3 */
#define F_SHLCK 8 /* or 4 */
+/* for leases */
+#define F_INPROGRESS 16
+
/* operations for bsd flock(), also used by the kernel implementation */
#define LOCK_SH 1 /* shared lock */
#define LOCK_EX 2 /* exclusive lock */
blocking */
#define LOCK_UN 8 /* remove lock */
+#define LOCK_MAND 32 /* This is a mandatory flock */
+#define LOCK_READ 64 /* ... Which allows concurrent read operations */
+#define LOCK_WRITE 128 /* ... Which allows concurrent write operations */
+#define LOCK_RW 192 /* ... Which allows concurrent read & write ops */
+
struct flock {
short l_type;
short l_whence;
pid_t l_pid;
};
+#define F_LINUX_SPECIFIC_BASE 1024
#endif
#define RLIMIT_NOFILE 7 /* max number of open files */
#define RLIMIT_MEMLOCK 8 /* max locked-in-memory address space */
#define RLIMIT_AS 9 /* address space limit */
+#define RLIMIT_AS 10 /* maximum file locks held */
-#define RLIM_NLIMITS 10
+#define RLIM_NLIMITS 11
/*
* SuS says limits have to be unsigned.
{ INR_OPEN, INR_OPEN }, \
{ LONG_MAX, LONG_MAX }, \
{ LONG_MAX, LONG_MAX }, \
+ { LONG_MAX, LONG_MAX }, \
}
#endif /* __KERNEL__ */
#define F_EXLCK 4 /* or 3 */
#define F_SHLCK 8 /* or 4 */
+/* for leases */
+#define F_INPROGRESS 16
+
/* operations for bsd flock(), also used by the kernel implementation */
#define LOCK_SH 1 /* shared lock */
#define LOCK_EX 2 /* exclusive lock */
blocking */
#define LOCK_UN 8 /* remove lock */
+#define LOCK_MAND 32 /* This is a mandatory flock */
+#define LOCK_READ 64 /* ... Which allows concurrent read operations */
+#define LOCK_WRITE 128 /* ... Which allows concurrent write operations */
+#define LOCK_RW 192 /* ... Which allows concurrent read & write ops */
+
struct flock {
short l_type;
short l_whence;
pid_t l_pid;
};
+#define F_LINUX_SPECIFIC_BASE 1024
#endif /* __ASM_SH_FCNTL_H */
#define RLIMIT_NOFILE 7 /* max number of open files */
#define RLIMIT_MEMLOCK 8 /* max locked-in-memory address space */
#define RLIMIT_AS 9 /* address space limit */
+#define RLIMIT_LOCKS 10 /* maximum file locks held */
-#define RLIM_NLIMITS 10
+#define RLIM_NLIMITS 11
#ifdef __KERNEL__
{ INR_OPEN, INR_OPEN }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
{ RLIM_INFINITY, RLIM_INFINITY }, \
+ { RLIM_INFINITY, RLIM_INFINITY }, \
}
#endif /* __KERNEL__ */
#define F_EXLCK 4 /* or 3 */
#define F_SHLCK 8 /* or 4 */
+/* for leases */
+#define F_INPROGRESS 16
+
/* operations for bsd flock(), also used by the kernel implementation */
#define LOCK_SH 1 /* shared lock */
#define LOCK_EX 2 /* exclusive lock */
blocking */
#define LOCK_UN 8 /* remove lock */
+#define LOCK_MAND 32 /* This is a mandatory flock */
+#define LOCK_READ 64 /* ... Which allows concurrent read operations */
+#define LOCK_WRITE 128 /* ... Which allows concurrent write operations */
+#define LOCK_RW 192 /* ... Which allows concurrent read & write ops */
+
struct flock {
short l_type;
short l_whence;
short __unused;
};
+#define F_LINUX_SPECIFIC_BASE 1024
#endif
#define RLIMIT_NPROC 7 /* max number of processes */
#define RLIMIT_MEMLOCK 8 /* max locked-in-memory address space */
#define RLIMIT_AS 9 /* address space limit */
+#define RLIMIT_LOCKS 10 /* maximum file locks held */
-#define RLIM_NLIMITS 10
+#define RLIM_NLIMITS 11
/*
* SuS says limits have to be unsigned.
{RLIM_INFINITY, RLIM_INFINITY}, \
{INR_OPEN, INR_OPEN}, {0, 0}, \
{RLIM_INFINITY, RLIM_INFINITY}, \
+ {RLIM_INFINITY, RLIM_INFINITY}, \
{RLIM_INFINITY, RLIM_INFINITY} \
}
#define F_EXLCK 4 /* or 3 */
#define F_SHLCK 8 /* or 4 */
+/* for leases */
+#define F_INPROGRESS 16
+
/* operations for bsd flock(), also used by the kernel implementation */
#define LOCK_SH 1 /* shared lock */
#define LOCK_EX 2 /* exclusive lock */
blocking */
#define LOCK_UN 8 /* remove lock */
+#define LOCK_MAND 32 /* This is a mandatory flock */
+#define LOCK_READ 64 /* ... Which allows concurrent read operations */
+#define LOCK_WRITE 128 /* ... Which allows concurrent write operations */
+#define LOCK_RW 192 /* ... Which allows concurrent read & write ops */
+
struct flock {
short l_type;
short l_whence;
#define flock64 flock
#endif
+#define F_LINUX_SPECIFIC_BASE 1024
#endif /* !(_SPARC64_FCNTL_H) */
#define RLIMIT_NPROC 7 /* max number of processes */
#define RLIMIT_MEMLOCK 8 /* max locked-in-memory address space */
#define RLIMIT_AS 9 /* address space limit */
+#define RLIMIT_LOCKS 10 /* maximum file locks held */
-#define RLIM_NLIMITS 10
+#define RLIM_NLIMITS 11
/*
* SuS says limits have to be unsigned.
{RLIM_INFINITY, RLIM_INFINITY}, \
{INR_OPEN, INR_OPEN}, {0, 0}, \
{RLIM_INFINITY, RLIM_INFINITY}, \
+ {RLIM_INFINITY, RLIM_INFINITY}, \
{RLIM_INFINITY, RLIM_INFINITY} \
}
#define nop() __asm__ __volatile__ ("nop")
#define membar(type) __asm__ __volatile__ ("membar " type : : : "memory");
-#define rmb() membar("#LoadLoad | #LoadStore")
-#define wmb() membar("#StoreLoad | #StoreStore")
+#define rmb() membar("#LoadLoad")
+#define wmb() membar("#StoreStore")
#define set_mb(__var, __value) \
do { __var = __value; membar("#StoreLoad | #StoreStore"); } while(0)
#define set_wmb(__var, __value) \
* appear to be correct if the sector contains all zeros, so also check that
* the disk size is non-zero!!!
*/
-extern inline int adfs_checkbblk(unsigned char *ptr)
+static inline int adfs_checkbblk(unsigned char *ptr)
{
unsigned int result = 0;
unsigned char *p = ptr + 511;
* Give a device find its atif control structure
*/
-extern __inline__ struct atalk_iface *atalk_find_dev(struct net_device *dev)
+static inline struct atalk_iface *atalk_find_dev(struct net_device *dev)
{
return dev->atalk_ptr;
}
lock = &__br_write_locks[idx].lock;
again:
(*ctr)++;
- rmb();
+ mb();
if (spin_is_locked(lock)) {
(*ctr)--;
- rmb();
+ wmb(); /*
+ * The release of the ctr must become visible
+ * to the other cpus eventually thus wmb(),
+ * we don't care if spin_is_locked is reordered
+ * before the releasing of the ctr.
+ * However IMHO this wmb() is superflous even in theory.
+ * It would not be superflous only if on the
+ * other CPUs doing a ldl_l instead of an ldl
+ * would make a difference and I don't think this is
+ * the case.
+ * I'd like to clarify this issue further
+ * but for now this is a slow path so adding the
+ * wmb() will keep us on the safe side.
+ */
while (spin_is_locked(lock))
barrier();
goto again;
#define CAP_MKNOD 27
+/* Allow taking of leases on files */
+
+#define CAP_LEASE 28
+
#ifdef __KERNEL__
/*
* Bounding set
#endif
#ifndef BUILDING_DEVPTS
-extern inline void
+static inline void
devpts_pty_new(int line, kdev_t device)
{
if ( devpts_upcall_new )
return devpts_upcall_new(line,device);
}
-extern inline void
+static inline void
devpts_pty_kill(int line)
{
if ( devpts_upcall_kill )
#else /* No /dev/pts filesystem at all */
-extern inline void
+static inline void
devpts_pty_new(int line, kdev_t device) { }
-extern inline void
+static inline void
devpts_pty_kill(int line) { }
#endif
--- /dev/null
+/*
+ * Directory notification for Linux
+ *
+ * Copyright 2000 (C) Stephen Rothwell
+ */
+
+struct dnotify_struct {
+ struct dnotify_struct * dn_next;
+ int dn_magic;
+ unsigned long dn_mask; /* Events to be notified
+ see linux/fcntl.h */
+ int dn_fd;
+ struct file * dn_filp;
+};
+
+#define DNOTIFY_MAGIC 0x444E4F54
+
+extern void __inode_dir_notify(struct inode *, unsigned long);
+extern int fcntl_dirnotify(int, struct file *, unsigned long);
+
+static inline void inode_dir_notify(struct inode *inode, unsigned long event)
+{
+ if ((inode)->i_dnotify_mask & (event))
+ __inode_dir_notify(inode, event);
+}
#include <asm/fcntl.h>
+#define F_SETLEASE (F_LINUX_SPECIFIC_BASE+0)
+#define F_GETLEASE (F_LINUX_SPECIFIC_BASE+1)
+
+/*
+ * Request nofications on a directory.
+ * See below for events that may be notified.
+ */
+#define F_NOTIFY (F_LINUX_SPECIFIC_BASE+2)
+
+/*
+ * Types of directory notifications that may be requested.
+ */
+#define DN_ACCESS 0x00000001 /* File accessed */
+#define DN_MODIFY 0x00000002 /* File modified */
+#define DN_CREATE 0x00000004 /* File created */
+#define DN_DELETE 0x00000008 /* File removed */
+#define DN_RENAME 0x00000010 /* File renamed */
+#define DN_ATTRIB 0x00000020 /* File changed attibutes */
+#define DN_MULTISHOT 0x80000000 /* Don't remove notifier */
+
#endif
};
extern struct files_stat_struct files_stat;
extern int max_super_blocks, nr_super_blocks;
+extern int leases_enable, dir_notify_enable, lease_break_time;
#define NR_FILE 8192 /* this can well be larger on a larger system */
#define NR_RESERVED_FILES 10 /* reserved for root */
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
+ unsigned long i_dnotify_mask; /* Directory notify events */
+ struct dnotify_struct *i_dnotify; /* for directory notifications */
+
unsigned long i_state;
unsigned int i_flags;
#define FL_BROKEN 4 /* broken flock() emulation */
#define FL_ACCESS 8 /* for processes suspended by mandatory locking */
#define FL_LOCKD 16 /* lock held by rpc.lockd */
+#define FL_LEASE 32 /* lease held on this file */
/*
* The POSIX file lock owner is determined by
struct file_lock *fl_next; /* singly linked list for this inode */
struct list_head fl_link; /* doubly linked list of all locks */
struct list_head fl_block; /* circular list of blocked processes */
+ struct list_head fl_list; /* block list member */
fl_owner_t fl_owner;
unsigned int fl_pid;
wait_queue_head_t fl_wait;
void (*fl_insert)(struct file_lock *); /* lock insertion callback */
void (*fl_remove)(struct file_lock *); /* lock removal callback */
+ struct fasync_struct * fl_fasync; /* for lease break notifications */
+
union {
struct nfs_lock_info nfs_fl;
} fl_u;
#endif
extern struct list_head file_lock_list;
+extern struct semaphore file_lock_sem;
#include <linux/fcntl.h>
extern int fcntl_setlk64(unsigned int, unsigned int, struct flock64 *);
/* fs/locks.c */
+extern void locks_init_lock(struct file_lock *);
+extern void locks_copy_lock(struct file_lock *, struct file_lock *);
extern void locks_remove_posix(struct file *, fl_owner_t);
extern void locks_remove_flock(struct file *);
extern struct file_lock *posix_test_lock(struct file *, struct file_lock *);
extern int posix_lock_file(struct file *, struct file_lock *, unsigned int);
extern void posix_block_lock(struct file_lock *, struct file_lock *);
extern void posix_unblock_lock(struct file_lock *);
+extern int __get_lease(struct inode *inode, unsigned int flags);
+extern time_t lease_get_mtime(struct inode *);
+extern int lock_may_read(struct inode *, loff_t start, unsigned long count);
+extern int lock_may_write(struct inode *, loff_t start, unsigned long count);
struct fasync_struct {
int magic;
return 0;
}
+extern inline int get_lease(struct inode *inode, unsigned int mode)
+{
+ if (inode->i_flock && (inode->i_flock->fl_flags & FL_LEASE))
+ return __get_lease(inode, mode);
+ return 0;
+}
/* fs/open.c */
* Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
*/
+#include <linux/sched.h>
#include <asm/io.h>
struct gameport;
unsigned char buffer[HDLCDRV_BITBUFFER];
};
-extern inline void hdlcdrv_add_bitbuffer(struct hdlcdrv_bitbuffer *buf,
+static inline void hdlcdrv_add_bitbuffer(struct hdlcdrv_bitbuffer *buf,
unsigned int bit)
{
unsigned char new;
}
}
-extern inline void hdlcdrv_add_bitbuffer_word(struct hdlcdrv_bitbuffer *buf,
+static inline void hdlcdrv_add_bitbuffer_word(struct hdlcdrv_bitbuffer *buf,
unsigned int bits)
{
buf->buffer[buf->wr] = bits & 0xff;
/* -------------------------------------------------------------------- */
-extern inline int hdlcdrv_hbuf_full(struct hdlcdrv_hdlcbuffer *hb)
+static inline int hdlcdrv_hbuf_full(struct hdlcdrv_hdlcbuffer *hb)
{
unsigned long flags;
int ret;
/* -------------------------------------------------------------------- */
-extern inline int hdlcdrv_hbuf_empty(struct hdlcdrv_hdlcbuffer *hb)
+static inline int hdlcdrv_hbuf_empty(struct hdlcdrv_hdlcbuffer *hb)
{
unsigned long flags;
int ret;
/* -------------------------------------------------------------------- */
-extern inline unsigned short hdlcdrv_hbuf_get(struct hdlcdrv_hdlcbuffer *hb)
+static inline unsigned short hdlcdrv_hbuf_get(struct hdlcdrv_hdlcbuffer *hb)
{
unsigned long flags;
unsigned short val;
/* -------------------------------------------------------------------- */
-extern inline void hdlcdrv_hbuf_put(struct hdlcdrv_hdlcbuffer *hb,
+static inline void hdlcdrv_hbuf_put(struct hdlcdrv_hdlcbuffer *hb,
unsigned short val)
{
unsigned newp;
/* -------------------------------------------------------------------- */
-extern inline void hdlcdrv_putbits(struct hdlcdrv_state *s, unsigned int bits)
+static inline void hdlcdrv_putbits(struct hdlcdrv_state *s, unsigned int bits)
{
hdlcdrv_hbuf_put(&s->hdlcrx.hbuf, bits);
}
-extern inline unsigned int hdlcdrv_getbits(struct hdlcdrv_state *s)
+static inline unsigned int hdlcdrv_getbits(struct hdlcdrv_state *s)
{
unsigned int ret;
return ret;
}
-extern inline void hdlcdrv_channelbit(struct hdlcdrv_state *s, unsigned int bit)
+static inline void hdlcdrv_channelbit(struct hdlcdrv_state *s, unsigned int bit)
{
#ifdef HDLCDRV_DEBUG
hdlcdrv_add_bitbuffer(&s->bitbuf_channel, bit);
#endif /* HDLCDRV_DEBUG */
}
-extern inline void hdlcdrv_setdcd(struct hdlcdrv_state *s, int dcd)
+static inline void hdlcdrv_setdcd(struct hdlcdrv_state *s, int dcd)
{
s->hdlcrx.dcd = !!dcd;
}
-extern inline int hdlcdrv_ptt(struct hdlcdrv_state *s)
+static inline int hdlcdrv_ptt(struct hdlcdrv_state *s)
{
return s->hdlctx.ptt || (s->hdlctx.calibrate > 0);
}
#define HFS_I(X) (&((X)->u.hfs_i))
#define HFS_SB(X) (&((X)->u.hfs_sb))
-extern __inline__ void hfs_nameout(struct inode *dir, struct hfs_name *out,
+static inline void hfs_nameout(struct inode *dir, struct hfs_name *out,
const char *in, int len) {
HFS_SB(dir->i_sb)->s_nameout(out, in, len);
}
-extern __inline__ int hfs_namein(struct inode *dir, char *out,
+static inline int hfs_namein(struct inode *dir, char *out,
const struct hfs_name *in) {
int len = HFS_SB(dir->i_sb)->s_namein(out, in);
if (HFS_SB(dir->i_sb)->s_lowercase) {
extern long int hfs_alloc;
#endif
-extern inline void *hfs_malloc(unsigned int size) {
+static inline void *hfs_malloc(unsigned int size) {
#if defined(DEBUG_ALL) || defined(DEBUG_MEM)
hfs_warn("%ld bytes allocation at %s:%u\n",
(hfs_alloc += size), __FILE__, __LINE__);
return kmalloc(size, GFP_KERNEL);
}
-extern inline void hfs_free(void *ptr, unsigned int size) {
+static inline void hfs_free(void *ptr, unsigned int size) {
kfree(ptr);
#if defined(DEBUG_ALL) || defined(DEBUG_MEM)
hfs_warn("%ld bytes allocation at %s:%u\n",
* not a good thing to do. instead, we depend upon tz_minuteswest
* having the correct daylight savings correction.
*/
-extern inline hfs_u32 hfs_from_utc(hfs_s32 time)
+static inline hfs_u32 hfs_from_utc(hfs_s32 time)
{
return time - sys_tz.tz_minuteswest*60;
}
-extern inline hfs_s32 hfs_to_utc(hfs_u32 time)
+static inline hfs_s32 hfs_to_utc(hfs_u32 time)
{
return time + sys_tz.tz_minuteswest*60;
}
-extern inline hfs_u32 hfs_time(void) {
+static inline hfs_u32 hfs_time(void) {
return htonl(hfs_from_utc(CURRENT_TIME)+2082844800U);
}
*/
typedef wait_queue_head_t hfs_wait_queue;
-extern inline void hfs_init_waitqueue(hfs_wait_queue *queue) {
+static inline void hfs_init_waitqueue(hfs_wait_queue *queue) {
init_waitqueue_head(queue);
}
-extern inline void hfs_sleep_on(hfs_wait_queue *queue) {
+static inline void hfs_sleep_on(hfs_wait_queue *queue) {
sleep_on(queue);
}
-extern inline void hfs_wake_up(hfs_wait_queue *queue) {
+static inline void hfs_wake_up(hfs_wait_queue *queue) {
wake_up(queue);
}
-extern inline void hfs_relinquish(void) {
+static inline void hfs_relinquish(void) {
schedule();
}
*/
typedef struct super_block *hfs_sysmdb;
-extern inline void hfs_mdb_dirty(hfs_sysmdb sys_mdb) {
+static inline void hfs_mdb_dirty(hfs_sysmdb sys_mdb) {
sys_mdb->s_dirt = 1;
}
-extern inline const char *hfs_mdb_name(hfs_sysmdb sys_mdb) {
+static inline const char *hfs_mdb_name(hfs_sysmdb sys_mdb) {
return kdevname(sys_mdb->s_dev);
}
/* In sysdep.c, since it needs HFS_SECTOR_SIZE */
extern hfs_buffer hfs_buffer_get(hfs_sysmdb, int, int);
-extern inline int hfs_buffer_ok(hfs_buffer buffer) {
+static inline int hfs_buffer_ok(hfs_buffer buffer) {
return (buffer != NULL);
}
-extern inline void hfs_buffer_put(hfs_buffer buffer) {
+static inline void hfs_buffer_put(hfs_buffer buffer) {
brelse(buffer);
}
-extern inline void hfs_buffer_dirty(hfs_buffer buffer) {
+static inline void hfs_buffer_dirty(hfs_buffer buffer) {
mark_buffer_dirty(buffer);
}
-extern inline void hfs_buffer_sync(hfs_buffer buffer) {
+static inline void hfs_buffer_sync(hfs_buffer buffer) {
while (buffer_locked(buffer)) {
wait_on_buffer(buffer);
}
}
}
-extern inline void *hfs_buffer_data(const hfs_buffer buffer) {
+static inline void *hfs_buffer_data(const hfs_buffer buffer) {
return buffer->b_data;
}
# error "Don't know if bytes are big- or little-endian!"
#endif
-extern inline int hfs_clear_bit(int bitnr, hfs_u32 *lword) {
+static inline int hfs_clear_bit(int bitnr, hfs_u32 *lword) {
return test_and_clear_bit(BITNR(bitnr), lword);
}
-extern inline int hfs_set_bit(int bitnr, hfs_u32 *lword) {
+static inline int hfs_set_bit(int bitnr, hfs_u32 *lword) {
return test_and_set_bit(BITNR(bitnr), lword);
}
-extern inline int hfs_test_bit(int bitnr, const hfs_u32 *lword) {
+static inline int hfs_test_bit(int bitnr, const hfs_u32 *lword) {
/* the kernel should declare the second arg of test_bit as const */
return test_bit(BITNR(bitnr), (void *)lword);
}
#else /* CONFIG_HIGHMEM */
-extern inline unsigned int nr_free_highpages(void) { return 0; }
+static inline unsigned int nr_free_highpages(void) { return 0; }
#define prepare_highmem_swapout(page) page
#define replace_with_highmem(page) page
extern struct ppp_channel_ops pppoe_chan_ops;
-extern void pppox_proto_init(struct net_proto *np);
+extern int pppox_proto_init(struct net_proto *np);
#endif /* __KERNEL__ */
#include <stdarg.h>
#include <linux/linkage.h>
+#include <linux/stddef.h>
/* Optimization barrier */
/* The "volatile" is due to gcc bugs */
extern inline void unlock_buffer(struct buffer_head *bh)
{
clear_bit(BH_Lock, &bh->b_state);
- wake_up(&bh->b_wait);
+ smp_mb__after_clear_bit();
+ if (waitqueue_active(&bh->b_wait))
+ wake_up(&bh->b_wait);
}
/*
extern inline void unlock_super(struct super_block * sb)
{
sb->s_lock = 0;
- wake_up(&sb->s_wait);
+ /*
+ * No need of any barrier, we're protected by
+ * the big kernel lock here... unfortunately :)
+ */
+ if (waitqueue_active(&sb->s_wait))
+ wake_up(&sb->s_wait);
}
#endif /* _LINUX_LOCKS_H */
#define USB_ACM_AUX_MAJOR 167
#define USB_CHAR_MAJOR 180
-#define TUN_MAJOR 195
-
#define UNIX98_PTY_MASTER_MAJOR 128
#define UNIX98_PTY_MAJOR_COUNT 8
#define UNIX98_PTY_SLAVE_MAJOR (UNIX98_PTY_MASTER_MAJOR+UNIX98_PTY_MAJOR_COUNT)
/* drivers/sgi/char/usema.c */
#define SGI_USEMACLONE 151
+#define TUN_MINOR 200
+
extern int misc_init(void);
struct miscdevice
#define PageLocked(page) test_bit(PG_locked, &(page)->flags)
#define LockPage(page) set_bit(PG_locked, &(page)->flags)
#define TryLockPage(page) test_and_set_bit(PG_locked, &(page)->flags)
+/*
+ * The first mb is necessary to safely close the critical section opened by the
+ * TryLockPage(), the second mb is necessary to enforce ordering between
+ * the clear_bit and the read of the waitqueue (to avoid SMP races with a
+ * parallel wait_on_page).
+ */
#define UnlockPage(page) do { \
+ smp_mb__before_clear_bit(); \
clear_bit(PG_locked, &(page)->flags); \
- wake_up(&page->wait); \
+ smp_mb__after_clear_bit(); \
+ if (waitqueue_active(&page->wait)) \
+ wake_up(&page->wait); \
} while (0)
#define PageError(page) test_bit(PG_error, &(page)->flags)
#define SetPageError(page) set_bit(PG_error, &(page)->flags)
#define NFSEXP_CROSSMNT 0x0200
#define NFSEXP_NOSUBTREECHECK 0x0400
#define NFSEXP_NOAUTHNLM 0x0800 /* Don't authenticate NLM requests - just trust */
-#define NFSEXP_ALLFLAGS 0x0FFF
+#define NFSEXP_MSNFS 0x1000 /* do silly things that MS clients expect */
+#define NFSEXP_ALLFLAGS 0x1FFF
#ifdef __KERNEL__
#define DEVICE_COUNT_DMA 2
#define DEVICE_COUNT_RESOURCE 12
+#define PCI_ANY_ID (~0)
+
+#define pci_present pcibios_present
+
+#define pci_for_each_dev(dev) \
+ for(dev = pci_dev_g(pci_devices.next); dev != pci_dev_g(&pci_devices); dev = pci_dev_g(dev->global_list.next))
+
+#define pci_for_each_dev_reverse(dev) \
+ for(dev = pci_dev_g(pci_devices.prev); dev != pci_dev_g(&pci_devices); dev = pci_dev_g(dev->global_list.prev))
+
/*
* The pci_dev structure is used to describe both PCI and ISAPnP devices.
*/
unsigned long mem_start, mem_end;
};
+struct pci_device_id {
+ unsigned int vendor, device; /* Vendor and device ID or PCI_ANY_ID */
+ unsigned int subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */
+ unsigned int class, class_mask; /* (class,subclass,prog-if) triplet */
+ unsigned long driver_data; /* Data private to the driver */
+};
+
+struct pci_driver {
+ struct list_head node;
+ char *name;
+ const struct pci_device_id *id_table; /* NULL if wants all devices */
+ int (*probe)(struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */
+ void (*remove)(struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */
+ void (*suspend)(struct pci_dev *dev); /* Device suspended */
+ void (*resume)(struct pci_dev *dev); /* Device woken up */
+};
+
+
+/* these external functions are only available when PCI support is enabled */
+#ifdef CONFIG_PCI
+
void pcibios_init(void);
void pcibios_fixup_bus(struct pci_bus *);
int pcibios_enable_device(struct pci_dev *);
/* Backward compatibility, don't use in new code! */
int pcibios_present(void);
-#define pci_present pcibios_present
int pcibios_read_config_byte (unsigned char bus, unsigned char dev_fn,
unsigned char where, unsigned char *val);
int pcibios_read_config_word (unsigned char bus, unsigned char dev_fn,
struct pci_dev *pci_find_slot (unsigned int bus, unsigned int devfn);
int pci_find_capability (struct pci_dev *dev, int cap);
-#define PCI_ANY_ID (~0)
-
int pci_read_config_byte(struct pci_dev *dev, int where, u8 *val);
int pci_read_config_word(struct pci_dev *dev, int where, u16 *val);
int pci_read_config_dword(struct pci_dev *dev, int where, u32 *val);
int pci_set_power_state(struct pci_dev *dev, int state);
int pci_assign_resource(struct pci_dev *dev, int i);
-#define pci_for_each_dev(dev) \
- for(dev = pci_dev_g(pci_devices.next); dev != pci_dev_g(&pci_devices); dev = pci_dev_g(dev->global_list.next))
-
-#define pci_for_each_dev_reverse(dev) \
- for(dev = pci_dev_g(pci_devices.prev); dev != pci_dev_g(&pci_devices); dev = pci_dev_g(dev->global_list.prev))
-
/* Helper functions for low-level code (drivers/pci/setup.c) */
int pci_claim_resource(struct pci_dev *, int);
int (*)(struct pci_dev *, u8, u8));
/* New-style probing supporting hot-pluggable devices */
-
-struct pci_device_id {
- unsigned int vendor, device; /* Vendor and device ID or PCI_ANY_ID */
- unsigned int subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */
- unsigned int class, class_mask; /* (class,subclass,prog-if) triplet */
- unsigned long driver_data; /* Data private to the driver */
-};
-
-struct pci_driver {
- struct list_head node;
- char *name;
- const struct pci_device_id *id_table; /* NULL if wants all devices */
- int (*probe)(struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */
- void (*remove)(struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */
- void (*suspend)(struct pci_dev *dev); /* Device suspended */
- void (*resume)(struct pci_dev *dev); /* Device woken up */
-};
-
int pci_register_driver(struct pci_driver *);
void pci_unregister_driver(struct pci_driver *);
void pci_insert_device(struct pci_dev *, struct pci_bus *);
struct pci_driver *pci_dev_driver(const struct pci_dev *);
const struct pci_device_id *pci_match_device(const struct pci_device_id *ids, const struct pci_dev *dev);
+#endif /* CONFIG_PCI */
+
/* Include architecture-dependent settings and functions */
#include <asm/pci.h>
#define PCI_DEVICE_ID_SERVERWORKS_CMIC_HE 0x0011
#define PCI_DEVICE_ID_SERVERWORKS_CSB5 0x0201
+#define PCI_VENDOR_ID_SBE 0x1176
+#define PCI_DEVICE_ID_SBE_WANXL100 0x0301
+#define PCI_DEVICE_ID_SBE_WANXL200 0x0302
+#define PCI_DEVICE_ID_SBE_WANXL400 0x0104
+
#define PCI_VENDOR_ID_TOSHIBA 0x1179
#define PCI_DEVICE_ID_TOSHIBA_601 0x0601
#define PCI_DEVICE_ID_TOSHIBA_TOPIC95 0x060a
#define PCI_DEVICE_ID_CYCLOM_8Y_Hi 0x0105
#define PCI_DEVICE_ID_CYCLOM_Z_Lo 0x0200
#define PCI_DEVICE_ID_CYCLOM_Z_Hi 0x0201
+#define PCI_DEVICE_ID_PC300_RX_2 0x0300
+#define PCI_DEVICE_ID_PC300_RX_1 0x0301
+#define PCI_DEVICE_ID_PC300_TE_2 0x0310
+#define PCI_DEVICE_ID_PC300_TE_1 0x0311
#define PCI_VENDOR_ID_ESSENTIAL 0x120f
#define PCI_DEVICE_ID_ESSENTIAL_ROADRUNNER 0x0001
* that's just fine.)
*/
struct list_head run_list;
+ unsigned long sleep_time;
struct task_struct *next_task, *prev_task;
struct mm_struct *active_mm;
/* file system info */
int link_count;
struct tty_struct *tty; /* NULL if no tty */
+ unsigned int locks; /* How many file locks are being held */
/* ipc stuff */
struct sem_undo *semundo;
struct sem_queue *semsleeping;
static inline void del_from_runqueue(struct task_struct * p)
{
nr_running--;
+ p->sleep_time = jiffies;
list_del(&p->run_list);
p->run_list.next = NULL;
}
FASTCALL(unsigned int nr_free_pages(void));
FASTCALL(unsigned int nr_inactive_clean_pages(void));
FASTCALL(unsigned int nr_free_buffer_pages(void));
-FASTCALL(unsigned int nr_free_highpages(void));
extern int nr_active_pages;
extern int nr_inactive_dirty_pages;
extern atomic_t nr_async_pages;
NET_CORE_MSG_COST=8,
NET_CORE_MSG_BURST=9,
NET_CORE_OPTMEM_MAX=10,
- NET_CORE_HOT_LIST_LENGTH=11
+ NET_CORE_HOT_LIST_LENGTH=11,
+ NET_CORE_DIVERT_VERSION=12
};
/* /proc/sys/net/ethernet */
FS_NRSUPER=9, /* int:current number of allocated super_blocks */
FS_MAXSUPER=10, /* int:maximum number of super_blocks that can be allocated */
FS_OVERFLOWUID=11, /* int: overflow UID */
- FS_OVERFLOWGID=12 /* int: overflow GID */
+ FS_OVERFLOWGID=12, /* int: overflow GID */
+ FS_LEASES=13, /* int: leases enabled */
+ FS_DIR_NOTIFY=14, /* int: directory notification enabled */
+ FS_LEASE_TIME=15, /* int: maximum time to wait for a lease break */
};
/* CTL_DEBUG names: */
#ifndef _LINUX_TIMEX_H
#define _LINUX_TIMEX_H
+#include <asm/param.h>
+
/*
* The following defines establish the engineering parameters of the PLL
* model. The HZ variable establishes the timer interrupt frequency, 100 Hz
* OSF/1 kernel. The SHIFT_HZ define expresses the same value as the
* nearest power of two in order to avoid hardware multiply operations.
*/
-#ifdef __alpha__
-# define SHIFT_HZ 10 /* log2(HZ) */
+#if HZ >= 24 && HZ < 48
+# define SHIFT_HZ 5
+#elif HZ >= 48 && HZ < 96
+# define SHIFT_HZ 6
+#elif HZ >= 96 && HZ < 192
+# define SHIFT_HZ 7
+#elif HZ >= 192 && HZ < 384
+# define SHIFT_HZ 8
+#elif HZ >= 384 && HZ < 768
+# define SHIFT_HZ 9
+#elif HZ >= 768 && HZ < 1536
+# define SHIFT_HZ 10
#else
-# define SHIFT_HZ 7 /* log2(HZ) */
+# error You lose.
#endif
/*
f = p -> routine;
save_p = p;
p = p -> next;
- mb();
+ smp_mb();
save_p -> sync = 0;
if (f)
(*f)(arg);
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
+ * $Id: snmp.h,v 1.17 2000/09/21 01:31:50 davem Exp $
+ *
*/
#ifndef _SNMP_H
#define _SNMP_H
+
+#include <linux/cache.h>
/*
* We use all unsigned longs. Linux will soon be so reliable that even these
* will rapidly get too small 8-). Seriously consider the IpInReceives count
* on the 20Gb/s + networks people expect in a few years time!
*/
-
+
+/*
+ * The rule for padding:
+ * Best is power of two because then the right structure can be found by a simple
+ * shift. The structure should be always cache line aligned.
+ * gcc needs n=alignto(cachelinesize, popcnt(sizeof(bla_mib))) shift/add instructions
+ * to emulate multiply in case it is not power-of-two. Currently n is always <=3 for
+ * all sizes so simple cache line alignment is enough.
+ *
+ * The best solution would be a global CPU local area , especially on 64 and 128byte
+ * cacheline machine it makes a *lot* of sense -AK
+ */
+
+
struct ip_mib
{
unsigned long IpInReceives;
unsigned long IpFragOKs;
unsigned long IpFragFails;
unsigned long IpFragCreates;
- unsigned long __pad[32-19];
-};
+ unsigned long __pad[0];
+} ____cacheline_aligned;
struct ipv6_mib
{
unsigned long Ip6FragCreates;
unsigned long Ip6InMcastPkts;
unsigned long Ip6OutMcastPkts;
- unsigned long __pad[32-22];
-};
+ unsigned long __pad[0];
+} ____cacheline_aligned;
struct icmp_mib
{
unsigned long IcmpOutTimestampReps;
unsigned long IcmpOutAddrMasks;
unsigned long IcmpOutAddrMaskReps;
- unsigned long __pad[32-26];
-};
+ unsigned long __pad[0];
+} ____cacheline_aligned;
struct icmpv6_mib
{
unsigned long Icmp6OutRedirects;
unsigned long Icmp6OutGroupMembResponses;
unsigned long Icmp6OutGroupMembReductions;
- unsigned long __pad[32-28];
-};
+ unsigned long __pad[0];
+} ____cacheline_aligned;
struct tcp_mib
{
unsigned long TcpRetransSegs;
unsigned long TcpInErrs;
unsigned long TcpOutRsts;
- unsigned long __pad[16-14];
-};
+ unsigned long __pad[0];
+} ____cacheline_aligned;
struct udp_mib
{
unsigned long UdpNoPorts;
unsigned long UdpInErrors;
unsigned long UdpOutDatagrams;
- unsigned long __pad[0];
-};
+ unsigned long __pad[0];
+} ____cacheline_aligned;
struct linux_mib
{
unsigned long TCPAbortOnLinger;
unsigned long TCPAbortFailed;
unsigned long TCPMemoryPressures;
- unsigned long __pad[64-64];
-};
+ unsigned long __pad[0];
+} ____cacheline_aligned;
+
+/*
+ * FIXME: On x86 and some other CPUs the split into user and softirq parts is not needed because
+ * addl $1,memory is atomic against interrupts (but atomic_inc would be overkill because of the lock
+ * cycles). Wants new nonlocked_atomic_inc() primitives -AK
+ */
#define SNMP_INC_STATS(mib, field) ((mib)[2*smp_processor_id()+!in_softirq()].field++)
#define SNMP_INC_STATS_BH(mib, field) ((mib)[2*smp_processor_id()].field++)
#define SNMP_INC_STATS_USER(mib, field) ((mib)[2*smp_processor_id()+1].field++)
Version 2 and 3 extensions to driver:
* Copyright (C) 1998 - 2000 Douglas Gilbert
- Version: 3.1.16 (20000716)
- This version is for 2.3/2.4 series kernels.
+ Version: 3.1.17 (20000921)
+ This version is for 2.4 series kernels.
+ Changes since 3.1.16 (20000716)
+ - changes for new scsi subsystem initialization
+ - change Scsi_Cmnd usage to Scsi_Request
+ - cleanup for no procfs
Changes since 3.1.15 (20000528)
- further (scatter gather) buffer length changes
Changes since 3.1.14 (20000503)
while (pid != wait(&i));
if (MAJOR(real_root_dev) != RAMDISK_MAJOR
|| MINOR(real_root_dev) != 0) {
-#ifdef CONFIG_BLK_DEV_MD
- md_run_setup();
-#endif
error = change_root(real_root_dev,"/initrd");
if (error)
printk(KERN_ERR "Change root to /initrd: "
EXPORT_SYMBOL(page_hash_bits);
EXPORT_SYMBOL(page_hash_table);
EXPORT_SYMBOL(file_lock_list);
+EXPORT_SYMBOL(file_lock_sem);
EXPORT_SYMBOL(posix_lock_file);
EXPORT_SYMBOL(posix_test_lock);
EXPORT_SYMBOL(posix_block_lock);
EXPORT_SYMBOL(page_symlink_inode_operations);
EXPORT_SYMBOL(block_symlink);
EXPORT_SYMBOL(vfs_readdir);
+EXPORT_SYMBOL(__get_lease);
+EXPORT_SYMBOL(lease_get_mtime);
+EXPORT_SYMBOL(lock_may_read);
+EXPORT_SYMBOL(lock_may_write);
EXPORT_SYMBOL(dcache_readdir);
/* for stackable file systems (lofs, wrapfs, cryptfs, etc.) */
spin_lock_irq(¤t->sigmask_lock);
sig = dequeue_signal(&these, &info);
if (!sig) {
- /* None ready -- temporarily unblock those we're interested
- in so that we'll be awakened when they arrive. */
- sigset_t oldblocked = current->blocked;
- sigandsets(¤t->blocked, ¤t->blocked, &these);
- recalc_sigpending(current);
- spin_unlock_irq(¤t->sigmask_lock);
-
timeout = MAX_SCHEDULE_TIMEOUT;
if (uts)
timeout = (timespec_to_jiffies(&ts)
+ (ts.tv_sec || ts.tv_nsec));
- current->state = TASK_INTERRUPTIBLE;
- timeout = schedule_timeout(timeout);
+ if (timeout) {
+ /* None ready -- temporarily unblock those we're
+ * interested while we are sleeping in so that we'll
+ * be awakened when they arrive. */
+ sigset_t oldblocked = current->blocked;
+ sigandsets(¤t->blocked, ¤t->blocked, &these);
+ recalc_sigpending(current);
+ spin_unlock_irq(¤t->sigmask_lock);
- spin_lock_irq(¤t->sigmask_lock);
- sig = dequeue_signal(&these, &info);
- current->blocked = oldblocked;
- recalc_sigpending(current);
+ current->state = TASK_INTERRUPTIBLE;
+ timeout = schedule_timeout(timeout);
+
+ spin_lock_irq(¤t->sigmask_lock);
+ sig = dequeue_signal(&these, &info);
+ current->blocked = oldblocked;
+ recalc_sigpending(current);
+ }
}
spin_unlock_irq(¤t->sigmask_lock);
irq_cpustat_t irq_stat[NR_CPUS];
#endif /* CONFIG_ARCH_S390 */
-static struct softirq_action softirq_vec[32];
+static struct softirq_action softirq_vec[32] __cacheline_aligned;
asmlinkage void do_softirq()
{
clear_bit(TASKLET_STATE_SCHED, &t->state);
t->func(t->data);
+ /*
+ * talklet_trylock() uses test_and_set_bit that imply
+ * an mb when it returns zero, thus we need the explicit
+ * mb only here: while closing the critical section.
+ */
+#ifdef CONFIG_SMP
+ smp_mb__before_clear_bit();
+#endif
tasklet_unlock(t);
continue;
}
{FS_OVERFLOWGID, "overflowgid", &fs_overflowgid, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL,
&minolduid, &maxolduid},
+ {FS_LEASES, "leases-enable", &leases_enable, sizeof(int),
+ 0644, NULL, &proc_dointvec},
+ {FS_DIR_NOTIFY, "dir-notify-enable", &dir_notify_enable,
+ sizeof(int), 0644, NULL, &proc_dointvec},
+ {FS_LEASE_TIME, "lease-break-time", &lease_break_time, sizeof(int),
+ 0644, NULL, &proc_dointvec},
{0}
};
* up kswapd.
*/
age_page_up(page);
- if (inactive_shortage() > (inactive_target * 3) / 4)
+ if (inactive_shortage() > inactive_target / 2 && free_shortage())
wakeup_kswapd(0);
not_found:
return page;
* processes, etc).
*/
if (gfp_mask & __GFP_WAIT) {
- wakeup_kswapd(1);
+ try_to_free_pages(gfp_mask);
+ memory_pressure++;
goto try_again;
}
}
spin_lock(&pagemap_lru_lock);
if (!PageLocked(page))
BUG();
- /*
- * Heisenbug Compensator(tm)
- * This bug shouldn't trigger, but for unknown reasons it
- * sometimes does. If there are no signs of list corruption,
- * we ignore the problem. Else we BUG()...
- */
- if (PageActive(page) || PageInactiveDirty(page) ||
- PageInactiveClean(page)) {
- struct list_head * page_lru = &page->lru;
- if (page_lru->next->prev != page_lru) {
- printk("VM: lru_cache_add, bit or list corruption..\n");
- BUG();
- }
- printk("VM: lru_cache_add, page already in list!\n");
- goto page_already_on_list;
- }
+ DEBUG_ADD_PAGE
add_page_to_active_list(page);
/* This should be relatively rare */
if (!page->age)
deactivate_page_nolock(page);
-page_already_on_list:
spin_unlock(&pagemap_lru_lock);
}
#define SWAP_SHIFT 5
#define SWAP_MIN 8
-static int swap_out(unsigned int priority, int gfp_mask)
+static int swap_out(unsigned int priority, int gfp_mask, unsigned long idle_time)
{
struct task_struct * p;
int counter;
struct mm_struct *best = NULL;
int pid = 0;
int assign = 0;
+ int found_task = 0;
select:
read_lock(&tasklist_lock);
p = init_task.next_task;
continue;
if (mm->rss <= 0)
continue;
+ /* Skip tasks which haven't slept long enough yet when idle-swapping. */
+ if (idle_time && !assign && (!(p->state & TASK_INTERRUPTIBLE) ||
+ time_before(p->sleep_time + idle_time * HZ, jiffies)))
+ continue;
+ found_task++;
/* Refresh swap_cnt? */
if (assign == 1) {
mm->swap_cnt = (mm->rss >> SWAP_SHIFT);
}
read_unlock(&tasklist_lock);
if (!best) {
- if (!assign) {
+ if (!assign && found_task > 0) {
assign = 1;
goto select;
}
* Now the page is really freeable, so we
* move it to the inactive_clean list.
*/
- UnlockPage(page);
del_page_from_inactive_dirty_list(page);
add_page_to_inactive_clean_list(page);
+ UnlockPage(page);
cleaned_pages++;
} else {
/*
* It's no use keeping it here, so we move it to
* the active list.
*/
- UnlockPage(page);
del_page_from_inactive_dirty_list(page);
add_page_to_active_list(page);
+ UnlockPage(page);
}
}
spin_unlock(&pagemap_lru_lock);
static int refill_inactive(unsigned int gfp_mask, int user)
{
int priority, count, start_count, made_progress;
+ unsigned long idle_time;
count = inactive_shortage() + free_shortage();
if (user)
/* Always trim SLAB caches when memory gets low. */
kmem_cache_reap(gfp_mask);
+ /*
+ * Calculate the minimum time (in seconds) a process must
+ * have slept before we consider it for idle swapping.
+ * This must be the number of seconds it takes to go through
+ * all of the cache. Doing this idle swapping makes the VM
+ * smoother once we start hitting swap.
+ */
+ idle_time = atomic_read(&page_cache_size);
+ idle_time += atomic_read(&buffermem_pages);
+ idle_time /= (inactive_target + 1);
+
priority = 6;
do {
made_progress = 0;
- if (current->need_resched) {
+ if (current->need_resched && (gfp_mask & __GFP_IO)) {
__set_current_state(TASK_RUNNING);
schedule();
}
- while (refill_inactive_scan(priority, 1)) {
+ while (refill_inactive_scan(priority, 1) ||
+ swap_out(priority, gfp_mask, idle_time)) {
made_progress = 1;
if (!--count)
goto done;
/*
* Then, try to page stuff out..
*/
- while (swap_out(priority, gfp_mask)) {
+ while (swap_out(priority, gfp_mask, 0)) {
made_progress = 1;
if (!--count)
goto done;
* before we get around to moving them to the other
* list, so this is a relatively cheap operation.
*/
- if (free_shortage())
+ if (free_shortage() || nr_inactive_dirty_pages > nr_free_pages() +
+ nr_inactive_clean_pages())
ret += page_launder(gfp_mask, user);
/*
run_task_queue(&tq_disk);
/*
- * If we've either completely gotten rid of the
- * free page shortage or the inactive page shortage
- * is getting low, then stop eating CPU time.
+ * We go to sleep if either the free page shortage
+ * or the inactive page shortage is gone. We do this
+ * because:
+ * 1) we need no more free pages or
+ * 2) the inactive pages need to be flushed to disk,
+ * it wouldn't help to eat CPU time now ...
*
* We go to sleep for one second, but if it's needed
* we'll be woken up earlier...
struct net_device *dev = head;
head = head->next_sched;
+ smp_mb__before_clear_bit();
clear_bit(__LINK_STATE_SCHED, &dev->state);
if (spin_trylock(&dev->queue_lock)) {
*
* Implementation of the Transmission Control Protocol(TCP).
*
- * Version: $Id: tcp_input.c,v 1.201 2000/09/18 05:59:48 davem Exp $
+ * Version: $Id: tcp_input.c,v 1.202 2000/09/21 01:05:38 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
*
* The User Datagram Protocol (UDP).
*
- * Version: $Id: udp.c,v 1.86 2000/09/18 05:59:48 davem Exp $
+ * Version: $Id: udp.c,v 1.87 2000/09/20 02:11:34 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
if (usin->sin_family != AF_INET) {
if (usin->sin_family != AF_UNSPEC)
return -EINVAL;
- if (net_ratelimit())
- printk("Remind Kuznetsov, he has to repair %s eventually\n", current->comm);
}
ufh.daddr = usin->sin_addr.s_addr;
if (flags & MSG_ERRQUEUE)
return ip_recv_error(sk, msg, len);
+
+ retry:
/*
* From here the generic datagram does a lot of the work. Come
* the finished NET3, it will do _ALL_ the work!
csum_copy_err:
UDP_INC_STATS_BH(UdpInErrors);
- /* Clear queue. */
- if (flags&MSG_PEEK) {
- int clear = 0;
+ if (flags&(MSG_PEEK|MSG_DONTWAIT)) {
+ struct sk_buff *skb2;
+
spin_lock_irq(&sk->receive_queue.lock);
- if (skb == skb_peek(&sk->receive_queue)) {
+ skb2 = skb_peek(&sk->receive_queue);
+ if ((flags & MSG_PEEK) && skb == skb2) {
__skb_unlink(skb, &sk->receive_queue);
- clear = 1;
}
spin_unlock_irq(&sk->receive_queue.lock);
- if (clear)
- kfree_skb(skb);
- }
-
- skb_free_datagram(sk, skb);
-
- /*
- * Error for blocking case is chosen to masquerade
- * as some normal condition.
- */
- return (flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH;
+ skb_free_datagram(sk, skb);
+ if ((flags & MSG_DONTWAIT) && !skb2)
+ return -EAGAIN;
+ } else
+ skb_free_datagram(sk, skb);
+ goto retry;
}
int udp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
* Check protocol is in range
*/
if(family<0 || family>=NPROTO)
- return -EINVAL;
+ return -EAFNOSUPPORT;
/* Compatibility.
case RPC_AUTH_TOOWEAK:
printk(KERN_NOTICE "call_verify: server requires stronger "
"authentication.\n");
+ break;
default:
printk(KERN_WARNING "call_verify: unknown auth error: %x\n", n);
error = -EIO;
if [ ! -f .hdepend -o "$CONFIG_MODVERSIONS" = "y" ] ; then
echo "*** Next, you must run 'make dep'."
else
- echo "*** Next, you may run 'make zImage', 'make zdisk', or 'make zlilo'."
+ echo "*** Next, you may run 'make bzImage', 'make bzdisk', or 'make install'."
fi
echo