summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-03-23 02:25:38 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-03-23 02:25:38 +0000
commit16b5d462f73eb29d1f67fa01cc1ea66afdc72569 (patch)
tree5407bd573f4840e473ea27cbe61e5c7a07131fcd
parentce8a076e11e7e5ee36007f9a3eee5bb3744cb8f6 (diff)
Merge with Linux 2.3.99-pre2.
-rw-r--r--CREDITS21
-rw-r--r--Documentation/Changes16
-rw-r--r--Documentation/Configure.help321
-rw-r--r--Documentation/DocBook/Makefile20
-rw-r--r--Documentation/DocBook/kernel-api.tmpl92
-rw-r--r--Documentation/DocBook/videobook.tmpl2
-rw-r--r--Documentation/i2c/dev-interface22
-rw-r--r--Documentation/scsi-generic.txt210
-rw-r--r--Documentation/sysrq.txt36
-rw-r--r--Documentation/usb/usb-serial.txt2
-rw-r--r--MAINTAINERS2
-rw-r--r--Makefile103
-rw-r--r--arch/alpha/config.in1
-rw-r--r--arch/alpha/kernel/Makefile7
-rw-r--r--arch/alpha/kernel/alpha_ksyms.c4
-rw-r--r--arch/alpha/kernel/core_cia.c609
-rw-r--r--arch/alpha/kernel/core_pyxis.c644
-rw-r--r--arch/alpha/kernel/core_tsunami.c2
-rw-r--r--arch/alpha/kernel/entry.S7
-rw-r--r--arch/alpha/kernel/irq.c67
-rw-r--r--arch/alpha/kernel/irq_alpha.c17
-rw-r--r--arch/alpha/kernel/irq_pyxis.c127
-rw-r--r--arch/alpha/kernel/irq_srm.c3
-rw-r--r--arch/alpha/kernel/machvec_impl.h12
-rw-r--r--arch/alpha/kernel/osf_sys.c14
-rw-r--r--arch/alpha/kernel/pci_impl.h1
-rw-r--r--arch/alpha/kernel/pci_iommu.c61
-rw-r--r--arch/alpha/kernel/proto.h8
-rw-r--r--arch/alpha/kernel/setup.c25
-rw-r--r--arch/alpha/kernel/smp.c12
-rw-r--r--arch/alpha/kernel/sys_alcor.c4
-rw-r--r--arch/alpha/kernel/sys_cabriolet.c17
-rw-r--r--arch/alpha/kernel/sys_dp264.c76
-rw-r--r--arch/alpha/kernel/sys_eiger.c4
-rw-r--r--arch/alpha/kernel/sys_miata.c10
-rw-r--r--arch/alpha/kernel/sys_mikasa.c2
-rw-r--r--arch/alpha/kernel/sys_noritake.c2
-rw-r--r--arch/alpha/kernel/sys_ruffian.c10
-rw-r--r--arch/alpha/kernel/sys_sx164.c12
-rw-r--r--arch/alpha/kernel/sys_takara.c2
-rw-r--r--arch/alpha/kernel/time.c6
-rw-r--r--arch/alpha/kernel/traps.c4
-rw-r--r--arch/arm/Makefile3
-rw-r--r--arch/arm/config.in10
-rw-r--r--arch/arm/kernel/arch.c89
-rw-r--r--arch/arm/kernel/bios32.c69
-rw-r--r--arch/arm/kernel/bios32.h24
-rw-r--r--arch/arm/kernel/dec21285.c30
-rw-r--r--arch/arm/kernel/hw-footbridge.c4
-rw-r--r--arch/arm/kernel/process.c20
-rw-r--r--arch/arm/mm/fault-armv.c3
-rw-r--r--arch/arm/mm/mm-armo.c12
-rw-r--r--arch/arm/mm/mm-armv.c21
-rw-r--r--arch/arm/mm/proc-arm6,7.S14
-rw-r--r--arch/arm/mm/proc-sa110.S31
-rw-r--r--arch/i386/Makefile5
-rw-r--r--arch/i386/boot/compressed/head.S8
-rw-r--r--arch/i386/defconfig2
-rw-r--r--arch/i386/kernel/acpi.c30
-rw-r--r--arch/i386/kernel/head.S10
-rw-r--r--arch/i386/kernel/i386_ksyms.c3
-rw-r--r--arch/i386/kernel/pci-i386.c14
-rw-r--r--arch/i386/kernel/pci-pc.c38
-rw-r--r--arch/i386/kernel/setup.c44
-rw-r--r--arch/i386/kernel/time.c22
-rw-r--r--arch/mips/defconfig-decstation19
-rw-r--r--arch/mips/kernel/irixelf.c27
-rw-r--r--arch/mips64/defconfig-ip2221
-rw-r--r--arch/ppc/kernel/setup.c2
-rw-r--r--arch/sparc/Makefile2
-rw-r--r--arch/sparc/config.in63
-rw-r--r--arch/sparc/defconfig80
-rw-r--r--arch/sparc/kernel/ioport.c6
-rw-r--r--arch/sparc/kernel/setup.c39
-rw-r--r--arch/sparc/kernel/sparc_ksyms.c10
-rw-r--r--arch/sparc/kernel/sys_solaris.c1
-rw-r--r--arch/sparc/kernel/sys_sunos.c32
-rw-r--r--arch/sparc/kernel/systbls.S6
-rw-r--r--arch/sparc/kernel/unaligned.c40
-rw-r--r--arch/sparc/lib/Makefile4
-rw-r--r--arch/sparc/lib/muldi3.S76
-rw-r--r--arch/sparc/mm/init.c91
-rw-r--r--arch/sparc64/config.in31
-rw-r--r--arch/sparc64/defconfig74
-rw-r--r--arch/sparc64/kernel/binfmt_aout32.c28
-rw-r--r--arch/sparc64/kernel/ioctl32.c425
-rw-r--r--arch/sparc64/kernel/setup.c27
-rw-r--r--arch/sparc64/kernel/smp.c17
-rw-r--r--arch/sparc64/kernel/sparc64_ksyms.c12
-rw-r--r--arch/sparc64/kernel/sys_sparc.c7
-rw-r--r--arch/sparc64/kernel/sys_sparc32.c99
-rw-r--r--arch/sparc64/kernel/sys_sunos32.c29
-rw-r--r--arch/sparc64/kernel/systbls.S10
-rw-r--r--arch/sparc64/kernel/unaligned.c18
-rw-r--r--arch/sparc64/lib/VIScopy.S8
-rw-r--r--arch/sparc64/lib/atomic.S26
-rw-r--r--arch/sparc64/lib/blockops.S85
-rw-r--r--arch/sparc64/lib/rwlock.S38
-rw-r--r--arch/sparc64/mm/fault.c4
-rw-r--r--arch/sparc64/mm/init.c80
-rw-r--r--drivers/block/Config.in2
-rw-r--r--drivers/block/floppy.c52
-rw-r--r--drivers/block/ll_rw_blk.c4
-rw-r--r--drivers/block/lvm.c8
-rw-r--r--drivers/block/nbd.c2
-rw-r--r--drivers/cdrom/cm206.c2
-rw-r--r--drivers/char/amiserial.c2
-rw-r--r--drivers/char/drm/bufs.c1
-rw-r--r--drivers/char/drm/drmP.h1
-rw-r--r--drivers/char/drm/gamma_drv.c1
-rw-r--r--drivers/char/drm/tdfx_drv.c1
-rw-r--r--drivers/char/dtlk.c100
-rw-r--r--drivers/char/pc_keyb.c5
-rw-r--r--drivers/char/raw.c2
-rw-r--r--drivers/char/rtc.c13
-rw-r--r--drivers/char/serial.c3
-rw-r--r--drivers/char/sysrq.c4
-rw-r--r--drivers/i2c/i2c-algo-pcf.c147
-rw-r--r--drivers/i2c/i2c-core.c13
-rw-r--r--drivers/i2c/i2c-dev.c73
-rw-r--r--drivers/i2c/i2c-elektor.c26
-rw-r--r--drivers/ide/Config.in23
-rw-r--r--drivers/ide/cmd64x.c38
-rw-r--r--drivers/ide/ide-geometry.c4
-rw-r--r--drivers/ide/ide-probe.c2
-rw-r--r--drivers/ide/piix.c24
-rw-r--r--drivers/ide/via82cxxx.c3
-rw-r--r--drivers/net/8390.c16
-rw-r--r--drivers/net/Makefile4
-rw-r--r--drivers/net/net_init.c30
-rw-r--r--drivers/net/pcnet32.c6
-rw-r--r--drivers/net/ppp_async.c2
-rw-r--r--drivers/net/ppp_generic.c8
-rw-r--r--drivers/net/rcpci45.c2
-rw-r--r--drivers/net/setup.c6
-rw-r--r--drivers/net/shaper.c1
-rw-r--r--drivers/net/sis900.c216
-rw-r--r--drivers/net/sunhme.c4
-rw-r--r--drivers/net/tokenring/olympic.c37
-rw-r--r--drivers/net/tokenring/olympic.h2
-rw-r--r--drivers/net/tulip/interrupt.c70
-rw-r--r--drivers/net/tulip/tulip.h15
-rw-r--r--drivers/net/tulip/tulip_core.c83
-rw-r--r--drivers/net/via-rhine.c2
-rw-r--r--drivers/net/wan/Makefile8
-rw-r--r--drivers/net/wan/comx.c12
-rw-r--r--drivers/net/yellowfin.c2
-rw-r--r--drivers/parport/ChangeLog25
-rw-r--r--drivers/parport/parport_pc.c69
-rw-r--r--drivers/pci/pci.c9
-rw-r--r--drivers/pci/pci.ids7
-rw-r--r--drivers/pcmcia/ti113x.h45
-rw-r--r--drivers/pcmcia/yenta.c11
-rw-r--r--drivers/pnp/quirks.c51
-rw-r--r--drivers/sbus/sbus.c13
-rw-r--r--drivers/scsi/53c7,8xx.c27
-rw-r--r--drivers/scsi/Makefile6
-rw-r--r--drivers/scsi/imm.c2
-rw-r--r--drivers/scsi/scsi_scan.c4
-rw-r--r--drivers/sound/Makefile2
-rw-r--r--drivers/sound/ac97_codec.c59
-rw-r--r--drivers/sound/ad1816.c412
-rw-r--r--drivers/sound/bin2hex.c2
-rw-r--r--drivers/sound/dev_table.c1
-rw-r--r--drivers/sound/sb_common.c3
-rw-r--r--drivers/sound/trident.c158
-rw-r--r--drivers/sound/trident.h27
-rw-r--r--drivers/usb/Config.in23
-rw-r--r--drivers/usb/Makefile2
-rw-r--r--drivers/usb/dc2xx.c23
-rw-r--r--drivers/usb/drivers.c6
-rw-r--r--drivers/usb/dsbr100.c6
-rw-r--r--drivers/usb/hub.c104
-rw-r--r--drivers/usb/hub.h13
-rw-r--r--drivers/usb/joydev.c4
-rw-r--r--drivers/usb/serial/Makefile2
-rw-r--r--drivers/usb/serial/usb-serial.c37
-rw-r--r--drivers/usb/serial/usb-serial.h1
-rw-r--r--drivers/usb/uhci.c34
-rw-r--r--drivers/usb/usb-ohci.c25
-rw-r--r--drivers/usb/usb-storage.c514
-rw-r--r--drivers/usb/usb-uhci.c1
-rw-r--r--drivers/video/aty128fb.c127
-rw-r--r--drivers/video/cyber2000fb.c9
-rw-r--r--drivers/video/cyber2000fb.h4
-rw-r--r--fs/autofs/inode.c1
-rw-r--r--fs/binfmt_aout.c28
-rw-r--r--fs/binfmt_elf.c28
-rw-r--r--fs/binfmt_em86.c4
-rw-r--r--fs/binfmt_misc.c2
-rw-r--r--fs/binfmt_script.c2
-rw-r--r--fs/buffer.c12
-rw-r--r--fs/dquot.c230
-rw-r--r--fs/exec.c14
-rw-r--r--fs/ext2/dir.c4
-rw-r--r--fs/namei.c87
-rw-r--r--fs/nfsd/nfs3xdr.c39
-rw-r--r--fs/nfsd/nfsctl.c4
-rw-r--r--fs/nfsd/nfsfh.c6
-rw-r--r--fs/nfsd/nfsproc.c34
-rw-r--r--fs/nfsd/vfs.c270
-rw-r--r--fs/open.c6
-rw-r--r--fs/partitions/acorn.c4
-rw-r--r--fs/partitions/msdos.c12
-rw-r--r--fs/smbfs/inode.c2
-rw-r--r--fs/super.c107
-rw-r--r--fs/ufs/dir.c4
-rw-r--r--include/asm-alpha/core_apecs.h47
-rw-r--r--include/asm-alpha/core_cia.h489
-rw-r--r--include/asm-alpha/core_irongate.h63
-rw-r--r--include/asm-alpha/core_lca.h47
-rw-r--r--include/asm-alpha/core_mcpcia.h47
-rw-r--r--include/asm-alpha/core_polaris.h63
-rw-r--r--include/asm-alpha/core_pyxis.h444
-rw-r--r--include/asm-alpha/core_t2.h37
-rw-r--r--include/asm-alpha/core_tsunami.h63
-rw-r--r--include/asm-alpha/delay.h69
-rw-r--r--include/asm-alpha/io.h36
-rw-r--r--include/asm-alpha/mman.h7
-rw-r--r--include/asm-alpha/mmu_context.h11
-rw-r--r--include/asm-alpha/page.h4
-rw-r--r--include/asm-alpha/pci.h5
-rw-r--r--include/asm-alpha/smp.h1
-rw-r--r--include/asm-alpha/unistd.h2
-rw-r--r--include/asm-alpha/vga.h4
-rw-r--r--include/asm-arm/arch-cl7500/system.h16
-rw-r--r--include/asm-arm/arch-ebsa110/system.h11
-rw-r--r--include/asm-arm/arch-ebsa285/system.h8
-rw-r--r--include/asm-arm/arch-rpc/system.h8
-rw-r--r--include/asm-arm/arch-sa1100/system.h6
-rw-r--r--include/asm-arm/cpu-multi32.h4
-rw-r--r--include/asm-arm/cpu-single.h2
-rw-r--r--include/asm-arm/mman.h6
-rw-r--r--include/asm-arm/page.h3
-rw-r--r--include/asm-arm/pci.h7
-rw-r--r--include/asm-arm/system.h4
-rw-r--r--include/asm-i386/hw_irq.h25
-rw-r--r--include/asm-i386/page.h3
-rw-r--r--include/asm-i386/pci.h5
-rw-r--r--include/asm-i386/processor.h4
-rw-r--r--include/asm-ia64/page.h3
-rw-r--r--include/asm-ia64/pci.h5
-rw-r--r--include/asm-m68k/page.h3
-rw-r--r--include/asm-m68k/pci.h5
-rw-r--r--include/asm-mips/page.h4
-rw-r--r--include/asm-mips/pci.h9
-rw-r--r--include/asm-mips64/page.h4
-rw-r--r--include/asm-mips64/pci.h9
-rw-r--r--include/asm-ppc/page.h2
-rw-r--r--include/asm-ppc/pci.h5
-rw-r--r--include/asm-sh/page.h2
-rw-r--r--include/asm-sh/pci.h7
-rw-r--r--include/asm-sparc/mman.h9
-rw-r--r--include/asm-sparc/page.h13
-rw-r--r--include/asm-sparc/pci.h7
-rw-r--r--include/asm-sparc64/atomic.h61
-rw-r--r--include/asm-sparc64/mman.h9
-rw-r--r--include/asm-sparc64/page.h4
-rw-r--r--include/asm-sparc64/parport.h3
-rw-r--r--include/asm-sparc64/pci.h7
-rw-r--r--include/asm-sparc64/semaphore.h16
-rw-r--r--include/asm-sparc64/spinlock.h44
-rw-r--r--include/linux/ac97_codec.h18
-rw-r--r--include/linux/binfmts.h2
-rw-r--r--include/linux/fs.h22
-rw-r--r--include/linux/hdreg.h4
-rw-r--r--include/linux/highmem.h20
-rw-r--r--include/linux/i2c-algo-pcf.h4
-rw-r--r--include/linux/i2c-dev.h8
-rw-r--r--include/linux/i2c-id.h7
-rw-r--r--include/linux/i2c.h4
-rw-r--r--include/linux/icmp.h21
-rw-r--r--include/linux/kernel.h2
-rw-r--r--include/linux/module.h6
-rw-r--r--include/linux/mount.h4
-rw-r--r--include/linux/netfilter.h15
-rw-r--r--include/linux/netfilter_ipv4.h10
-rw-r--r--include/linux/netfilter_ipv4/compat_firewall.h45
-rw-r--r--include/linux/netfilter_ipv4/ip_conntrack.h177
-rw-r--r--include/linux/netfilter_ipv4/ip_conntrack_core.h39
-rw-r--r--include/linux/netfilter_ipv4/ip_conntrack_ftp.h41
-rw-r--r--include/linux/netfilter_ipv4/ip_conntrack_helper.h30
-rw-r--r--include/linux/netfilter_ipv4/ip_conntrack_protocol.h58
-rw-r--r--include/linux/netfilter_ipv4/ip_conntrack_tuple.h105
-rw-r--r--include/linux/netfilter_ipv4/ip_nat.h117
-rw-r--r--include/linux/netfilter_ipv4/ip_nat_core.h33
-rw-r--r--include/linux/netfilter_ipv4/ip_nat_ftp.h21
-rw-r--r--include/linux/netfilter_ipv4/ip_nat_helper.h30
-rw-r--r--include/linux/netfilter_ipv4/ip_nat_protocol.h57
-rw-r--r--include/linux/netfilter_ipv4/ip_nat_rule.h35
-rw-r--r--include/linux/netfilter_ipv4/ip_queue.h86
-rw-r--r--include/linux/netfilter_ipv4/ip_tables.h420
-rw-r--r--include/linux/netfilter_ipv4/ipchains_core.h193
-rw-r--r--include/linux/netfilter_ipv4/ipfwadm_core.h256
-rw-r--r--include/linux/netfilter_ipv4/ipt_LOG.h15
-rw-r--r--include/linux/netfilter_ipv4/ipt_MARK.h8
-rw-r--r--include/linux/netfilter_ipv4/ipt_REJECT.h17
-rw-r--r--include/linux/netfilter_ipv4/ipt_TOS.h12
-rw-r--r--include/linux/netfilter_ipv4/ipt_limit.h21
-rw-r--r--include/linux/netfilter_ipv4/ipt_mac.h8
-rw-r--r--include/linux/netfilter_ipv4/ipt_mark.h9
-rw-r--r--include/linux/netfilter_ipv4/ipt_multiport.h21
-rw-r--r--include/linux/netfilter_ipv4/ipt_owner.h18
-rw-r--r--include/linux/netfilter_ipv4/ipt_state.h12
-rw-r--r--include/linux/netfilter_ipv4/ipt_tos.h13
-rw-r--r--include/linux/netfilter_ipv4/listhelp.h115
-rw-r--r--include/linux/netfilter_ipv4/lockhelp.h129
-rw-r--r--include/linux/nfsd/nfsd.h3
-rw-r--r--include/linux/pci.h7
-rw-r--r--include/linux/ppp_channel.h4
-rw-r--r--include/linux/prctl.h4
-rw-r--r--include/linux/quota.h2
-rw-r--r--include/linux/quotaops.h6
-rw-r--r--include/linux/sched.h2
-rw-r--r--include/linux/shm.h4
-rw-r--r--include/linux/skbuff.h2
-rw-r--r--include/linux/soundcard.h19
-rw-r--r--include/net/tcp.h2
-rw-r--r--ipc/shm.c203
-rw-r--r--kernel/acct.c2
-rw-r--r--kernel/ksyms.c7
-rw-r--r--kernel/sys.c24
-rw-r--r--kernel/sysctl.c3
-rw-r--r--mm/filemap.c7
-rw-r--r--mm/memory.c6
-rw-r--r--mm/page_alloc.c108
-rw-r--r--net/Config.in6
-rw-r--r--net/Makefile6
-rw-r--r--net/core/netfilter.c63
-rw-r--r--net/core/skbuff.c5
-rw-r--r--net/decnet/dn_route.c9
-rw-r--r--net/ipv4/Config.in5
-rw-r--r--net/ipv4/icmp.c18
-rw-r--r--net/ipv4/ip_gre.c9
-rw-r--r--net/ipv4/ip_output.c5
-rw-r--r--net/ipv4/ipip.c12
-rw-r--r--net/ipv4/ipmr.c14
-rw-r--r--net/ipv4/netfilter/Config.in64
-rw-r--r--net/ipv4/netfilter/Makefile234
-rw-r--r--net/ipv4/netfilter/ip_conntrack_core.c891
-rw-r--r--net/ipv4/netfilter/ip_conntrack_ftp.c251
-rw-r--r--net/ipv4/netfilter/ip_conntrack_proto_generic.c60
-rw-r--r--net/ipv4/netfilter/ip_conntrack_proto_icmp.c111
-rw-r--r--net/ipv4/netfilter/ip_conntrack_proto_tcp.c227
-rw-r--r--net/ipv4/netfilter/ip_conntrack_proto_udp.c65
-rw-r--r--net/ipv4/netfilter/ip_conntrack_standalone.c297
-rw-r--r--net/ipv4/netfilter/ip_fw_compat.c238
-rw-r--r--net/ipv4/netfilter/ip_fw_compat_masq.c288
-rw-r--r--net/ipv4/netfilter/ip_fw_compat_redir.c284
-rw-r--r--net/ipv4/netfilter/ip_nat_core.c855
-rw-r--r--net/ipv4/netfilter/ip_nat_ftp.c403
-rw-r--r--net/ipv4/netfilter/ip_nat_proto_icmp.c97
-rw-r--r--net/ipv4/netfilter/ip_nat_proto_tcp.c143
-rw-r--r--net/ipv4/netfilter/ip_nat_proto_udp.c141
-rw-r--r--net/ipv4/netfilter/ip_nat_proto_unknown.c61
-rw-r--r--net/ipv4/netfilter/ip_nat_rule.c327
-rw-r--r--net/ipv4/netfilter/ip_nat_standalone.c273
-rw-r--r--net/ipv4/netfilter/ip_queue.c752
-rw-r--r--net/ipv4/netfilter/ip_tables.c1664
-rw-r--r--net/ipv4/netfilter/ipchains_core.c1768
-rw-r--r--net/ipv4/netfilter/ipfwadm_core.c1410
-rw-r--r--net/ipv4/netfilter/ipt_LOG.c368
-rw-r--r--net/ipv4/netfilter/ipt_MARK.c68
-rw-r--r--net/ipv4/netfilter/ipt_MASQUERADE.c171
-rw-r--r--net/ipv4/netfilter/ipt_MIRROR.c131
-rw-r--r--net/ipv4/netfilter/ipt_REDIRECT.c104
-rw-r--r--net/ipv4/netfilter/ipt_REJECT.c145
-rw-r--r--net/ipv4/netfilter/ipt_TOS.c87
-rw-r--r--net/ipv4/netfilter/ipt_limit.c144
-rw-r--r--net/ipv4/netfilter/ipt_mac.c63
-rw-r--r--net/ipv4/netfilter/ipt_mark.c52
-rw-r--r--net/ipv4/netfilter/ipt_multiport.c102
-rw-r--r--net/ipv4/netfilter/ipt_owner.c136
-rw-r--r--net/ipv4/netfilter/ipt_state.c61
-rw-r--r--net/ipv4/netfilter/ipt_tos.c53
-rw-r--r--net/ipv4/netfilter/ipt_unclean.c576
-rw-r--r--net/ipv4/netfilter/iptable_filter.c181
-rw-r--r--net/ipv4/netfilter/iptable_mangle.c152
-rw-r--r--net/ipv4/route.c26
-rw-r--r--net/ipv4/tcp_ipv4.c6
-rw-r--r--net/ipv6/sit.c12
-rw-r--r--net/khttpd/security.c2
-rw-r--r--net/netsyms.c2
-rw-r--r--net/sched/cls_fw.c2
-rw-r--r--net/sched/sch_ingress.c4
-rw-r--r--net/sunrpc/svcsock.c2
-rw-r--r--net/unix/af_unix.c4
-rw-r--r--scripts/Makefile2
388 files changed, 22048 insertions, 4659 deletions
diff --git a/CREDITS b/CREDITS
index b98bae810..e20a1f141 100644
--- a/CREDITS
+++ b/CREDITS
@@ -318,6 +318,15 @@ P: 1024/04880A44 72E5 7031 4414 2EB6 F6B4 4CBD 1181 7032 0488 0A44
D: IEEE 1394 subsystem rewrite and maintainer
D: Texas Instruments PCILynx IEEE 1394 driver
+N: Marc Boucher
+E: marc@mbsi.ca
+P: CA 67 A5 1A 38 CE B6 F2 D5 83 51 03 D2 9C 30 9E CE D2 DD 65
+D: Netfilter core
+D: IP policy routing by mark
+D: Various fixes (mostly networking)
+S: Montreal, Quebec
+S: Canada
+
N: Zoltán Böszörményi
E: zboszor@mail.externet.hu
D: MTRR emulation with Cyrix style ARR registers, Athlon MTRR support
@@ -1368,6 +1377,11 @@ E: lam836@cs.cuhk.hk
D: Author of the dialog utility, foundation
D: for Menuconfig's lxdialog.
+N: Paul Laufer
+E: pelaufer@csupomona.edu
+D: Soundblaster driver fixes, ISAPnP quirk
+S: California, USA
+
N: Tom Lees
E: tom@lpsg.demon.co.uk
W: http://www.lpsg.demon.co.uk/
@@ -2248,6 +2262,13 @@ S: 22 Irvington Cres.
S: Willowdale, Ontario
S: Canada M2N 2Z1
+N: Adam Sulmicki
+E: adam@cfar.umd.edu
+W: http://www.eax.com
+D: core networking fixes
+D: patch-kernel enhancements
+D: misc kernel fixes and updates
+
N: Adrian Sun
E: asun@cobaltnet.com
D: hfs support
diff --git a/Documentation/Changes b/Documentation/Changes
index 9be04dcd6..0a06bebdf 100644
--- a/Documentation/Changes
+++ b/Documentation/Changes
@@ -43,7 +43,7 @@ Current Minimal Requirements
encountered a bug! If you're unsure what version you're currently
running, the suggested command should tell you.
-- Kernel modutils 2.3.7 ; insmod -V
+- Kernel modutils 2.3.10 ; insmod -V
- Gnu C 2.7.2.3 ; gcc --version
- Binutils 2.9.1.0.7 ; ld -v
- Linux libc5 C Library 5.4.46 ; ls -l /lib/libc*
@@ -62,7 +62,7 @@ running, the suggested command should tell you.
- Bash 1.14.7 ; bash -version
- Ncpfs 2.2.0 ; ncpmount -v
- Pcmcia-cs 3.1.2 ; cardmgr -V
-- PPP 2.3.9 ; pppd --version
+- PPP 2.3.11 ; pppd --version
- Util-linux 2.9i ; chsh -v
- isdn4k-utils v3.1beta7 ; isdnctrl 2>&1|grep version
@@ -174,7 +174,7 @@ Modules
=======
You need to upgrade to the latest version of modutils for the Linux
-2.3 kernel. This version will also work with your 2.0 kernel.
+2.3 kernel. This version can also be built to work with your 2.0 kernel.
As of 2.1.90-pre1, kerneld has been replaced by a kernel thread,
kmod. See Documentation/kmod.txt for more information. The main
@@ -390,7 +390,7 @@ PPP
Due to changes in the PPP driver and routing code, those of you
using PPP networking will need to upgrade your pppd to at least
-version 2.3.9. See ftp://cs.anu.edu.au/pub/software/ppp/ for newest
+version 2.3.11. See ftp://cs.anu.edu.au/pub/software/ppp/ for newest
versions.
You must make sure that the special device file /dev/ppp exists.
@@ -586,8 +586,8 @@ ftp://metalab.unc.edu/pub/Linux/GCC/ld.so-1.9.9.tar.gz
Modules utilities
=================
-The 2.3.7 release:
-ftp://ftp.ocs.com.au/pub/modutils/v2.3/modutils-2.3.7.tar.gz
+The 2.3.10 release:
+ftp://ftp.ocs.com.au/pub/modutils/v2.3/modutils-2.3.10.tar.gz
Procps utilities
================
@@ -715,8 +715,8 @@ ftp://metalab.unc.edu/pub/Linux/system/serial/setserial-2.15.tar.gz
PPP
===
-The 2.3.9 release:
-ftp://cs.anu.edu.au/pub/software/ppp/ppp-2.3.9.tar.gz
+The 2.3.11 release:
+ftp://cs.anu.edu.au/pub/software/ppp/ppp-2.3.11.tar.gz
IP Chains
=========
diff --git a/Documentation/Configure.help b/Documentation/Configure.help
index 64bdd50f2..6d2a38183 100644
--- a/Documentation/Configure.help
+++ b/Documentation/Configure.help
@@ -320,6 +320,47 @@ CONFIG_BLK_DEV_NBD
If unsure, say N.
+ATA/IDE/MFM/RLL support
+CONFIG_IDE
+ If you say Y here, your kernel will be able to manage low cost mass storage
+ units such as ATA/(E)IDE and ATAPI units.
+
+ Integrated Disk Electronics (IDE aka ATA-1) is a connecting standard for
+ mass storage units such as hard disks. It was designed by Western Digital
+ and Compaq Computer in 1984. It was then named ST506.
+ Quite a number of disks use IDE interface. State of the art disks use SCSI
+ interface.
+
+ AT Atachment (ATA) is a subset of the IDE specifications.
+ ST506 was also called ATA-1.
+
+ Fast-IDE is ATA-2 (also named Fast ATA), Enhanced IDE (EIDE) is ATA-3. It
+ provides support for larger disks (up to 8.4GB by means of the LBA standard),
+ more disks (4 instead of 2) and for other mass storage units such as tapes
+ and cdrom.
+ UDMA/33 (aka UltraDMA/33) is ATA-4 and provides faster (and more CPU friendly)
+ transfer modes than previous PIO (Programmed processor Input/Ouput) from
+ previous ATA/IDE standards by means of fast DMA controlers.
+
+ ATA Packet Interface (ATAPI) is a protocol used by EIDE tape and CDROM
+ drives, similar in many respects to the SCSI protocol.
+
+ SMART IDE (Self Monitoring, Analysis and Reporting Technology) was designed
+ in order to prevent data corruption and disk crash by detecting pre hardware
+ faillure conditions (heat, access time, and the like...). Disks builded since
+ june 1995 may follow this standard. The kernel itself don't manage this;
+ however there are quite a number of user programs such as smart that can
+ query the status of SMART parameters disk.
+
+ If you want to compile this driver 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. The module will be
+ called ide.o.
+
+ For further information, please read Documentation/ide.txt.
+
+ If unsure, say Y.
+
Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support
CONFIG_BLK_DEV_IDE
If you say Y here, you will use the full-featured IDE driver to
@@ -1657,6 +1698,230 @@ CONFIG_NETFILTER
Chances are that you should say Y here if you compile a kernel which
will run as a router and N for regular hosts. If unsure, say N.
+IP: connection tracking (required for masq/NAT)
+CONFIG_IP_NF_CONNTRACK
+ Connection tracking keeps a record of what packets have passed
+ through your machine, in order to figure out how they are related
+ into connections.
+
+ This is required to do Masquerading or other kinds of Network
+ Address Translation (except for Fast NAT). It can also be used to
+ enhance packet filtering (see `Connection state match support'
+ below).
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `N'.
+
+FTP protocol support
+CONFIG_IP_NF_FTP
+ Tracking FTP connections is problematic: special helpers are
+ required for tracking them, and doing masquerading and other forms
+ of Network Address Translation on them.
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `Y'.
+
+IP: userspace queueing via NETLINK (EXPERIMENTAL)
+CONFIG_IP_NF_QUEUE
+ Netfilter has the ability to queue packets to userspace: the netlink
+ device can be used to access them using this driver.
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `N'.
+
+IP: ip tables support (required for filtering/masq/NAT)
+CONFIG_IP_NF_IPTABLES
+ iptables is a general, extensible packet identification framework.
+ The packet filtering and full NAT (masquerading, port forwarding,
+ etc) subsystems now use this: say `Y' or `M' here if you want to use
+ either of those.
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `N'.
+
+limit match support
+CONFIG_IP_NF_MATCH_LIMIT
+ limit matching allows you to control the rate at which a rule can be
+ matched: mainly useful in combination with the LOG target.
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `N'.
+
+MAC address match support
+CONFIG_IP_NF_MATCH_MAC
+ mac matching allows you to match packets based on the source
+ ethernet address of the packet.
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `N'.
+
+netfilter mark match support
+CONFIG_IP_NF_MATCH_MARK
+ Netfilter mark matching allows you to match packets based on the
+ `nfmark' value in the packet. This can be set by the MARK target
+ (see below).
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `N'.
+
+Multiple port match support
+CONFIG_IP_NF_MATCH_MULTIPORT
+ Multiport matching allows you to match TCP or UDP packets based on
+ a series of source or destination ports: normally a rule can only
+ match a single range of ports.
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `N'.
+
+TOS match support
+CONFIG_IP_NF_MATCH_TOS
+ TOS matching allows you to match packets based on the Type Of
+ Service fields of the IP packet.
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `N'.
+
+Connection state match support
+CONFIG_IP_NF_MATCH_STATE
+ Connection state matching allows you to match packets based on their
+ relationship to a tracked connection (ie. previous packets). This
+ is a powerful tool for packet classification.
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `N'.
+
+Unclean match support (EXPERIMENTAL)
+CONFIG_IP_NF_MATCH_UNCLEAN
+ Unclean packet matching matches any strange or invalid packets, by
+ looking at a series of fields in the IP, TCP, UDP and ICMP headers.
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `N'.
+
+Owner match support (EXPERIMENTAL)
+CONFIG_IP_NF_MATCH_OWNER
+
+ Packet owner matching allows you to match locally-generated packets
+ based on who created them: the user, group, process or session.
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `N'.
+
+Packet filtering
+CONFIG_IP_NF_FILTER
+ Packet filtering defines a table `filter', which has a series of
+ rules for simple packet filtering at local input, forwarding and
+ local output. See the man page for iptables(8).
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `N'.
+
+REJECT target support
+CONFIG_IP_NF_TARGET_REJECT
+ The REJECT target allows a filtering rule to specify that an ICMP
+ error should be issued in response to an incoming packet, rather
+ than silently being dropped.
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `N'.
+
+MIRROR target support (EXPERIMENTAL)
+CONFIG_IP_NF_TARGET_MIRROR
+ The MIRROR target allows a filtering rule to specify that an
+ incoming packet should be bounced back to the sender.
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `N'.
+
+Full NAT
+CONFIG_IP_NF_NAT
+ The Full NAT option allows masquerading, port forwarding and other
+ forms of full Network Address Port Translation. It is controlled by
+ the `nat' table in iptables: see the man page for iptables(8).
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `N'.
+
+MASQUERADE target support
+CONFIG_IP_NF_TARGET_MASQUERADE
+ Masquerading is a special case of NAT: all outgoing connections are
+ changed to seem to come from a particular interface's address, and
+ if the interface goes down, those connections are lost. This is
+ only useful for dialup accounts with dynamic IP address (ie. your IP
+ address will be different on next dialup).
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `N'.
+
+REDIRECT target support
+CONFIG_IP_NF_TARGET_REDIRECT
+ REDIRECT is a special case of NAT: all incoming connections are
+ mapped onto the incoming interface's address, causing the packets to
+ come to the local machine instead of passing through. This is
+ useful for tranparent proxies.
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `N'.
+
+Packet mangling
+CONFIG_IP_NF_MANGLE
+ This option adds a `mangle' table to iptables: see the man page for
+ iptables(8). This table is used for various packet alterations
+ which can effect how the packet is routed.
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `N'.
+
+TOS target support
+CONFIG_IP_NF_TARGET_TOS
+ This option adds a `TOS' target, which allows you to create rules in
+ the `mangle' table which alter the Type Of Service field of an IP
+ packet prior to routing.
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `N'.
+
+MARK target support
+CONFIG_IP_NF_TARGET_MARK
+ This option adds a `MARK' target, which allows you to create rules in
+ the `mangle' table which alter the netfilter mark (nfmark) field
+ associated with the packet packet prior to routing. This can change
+ the routing method (see `IP: use netfilter MARK value as routing key')
+ and can also be used by other subsystems to change their behavior.
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `N'.
+
+LOG target support
+CONFIG_IP_NF_TARGET_LOG
+ This option adds a `LOG' target, which allows you to create rules in
+ any iptables table which records the packet header to the syslog.
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `N'.
+
+ipchains (2.2-style) support
+CONFIG_IP_NF_COMPAT_IPCHAINS
+ This option places ipchains (with masquerading and redirection
+ support) back into the kernel, using the new netfilter
+ infrastructure. It is not recommended for new installations (see
+ `Packet filtering'). With this enabled, you should be able to use
+ the ipchains tool exactly as in 2.2 kernels.
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `N'.
+
+ipfwadm (2.0-style) support
+CONFIG_IP_NF_COMPAT_IPFWADM
+ This option places ipfwadm (with masquerading and redirection
+ support) back into the kernel, using the new netfilter
+ infrastructure. It is not recommended for new installations (see
+ `Packet filtering'). With this enabled, you should be able to use
+ the ipfwadm tool exactly as in 2.0 kernels.
+
+ If you want to compile it as a module, say M here and read
+ Documentation/modules.txt. If unsure, say `N'.
+
SYN flood protection
CONFIG_SYN_COOKIES
Normal TCP/IP networking is open to an attack known as "SYN
@@ -3098,17 +3363,16 @@ CONFIG_IP_ROUTE_MULTIPATH
IP: use TOS value as routing key
CONFIG_IP_ROUTE_TOS
- The header of every IP packet carries a TOS (Type of Service) value
+ The header of every IP packet carries a TOS (Type Of Service) value
with which the packet requests a certain treatment, e.g. low latency
(for interactive traffic), high throughput, or high reliability. If
you say Y here, you will be able to specify different routes for
packets with different TOS values.
-IP: use FWMARK value as routing key
+IP: use netfilter MARK value as routing key
CONFIG_IP_ROUTE_FWMARK
If you say Y here, you will be able to specify different routes for
- packets with different FWMARK ("firewalling mark") values
- (see ipchains(8), "-m" argument).
+ packets with different mark values (see iptables(8), MARK target).
IP: verbose route monitoring
CONFIG_IP_ROUTE_VERBOSE
@@ -5793,6 +6057,26 @@ CONFIG_NETDEVICES
Guide", to be found in ftp://metalab.unc.edu/pub/Linux/docs/LDP . If
unsure, say Y.
+Bonding driver support
+CONFIG_BONDING
+ Say 'Y' or 'M' if you wish to be able to 'bond' multiple Ethernet
+ Channels together. This is called 'Etherchannel' by Cisco, 'Trunking'
+ by Sun, and 'Bonding' in Linux.
+
+ If you have two ethernet connections to some other computer, you can
+ make them behave like one double speed connection using this driver.
+ Naturally, this has to be supported at the other end as well, either
+ with a similar Bonding Linux driver, a Cisco 5500 switch or a
+ SunTrunking SunSoft driver.
+
+ This is similar to the EQL driver, but it merge etherner segments instead
+ of serial lines.
+
+ 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. The module will be
+ called bonding.o.
+
Dummy net driver support
CONFIG_DUMMY
This is essentially a bit-bucket device (i.e. traffic you send to
@@ -7085,11 +7369,12 @@ CONFIG_NE2K_PCI
module, say M here and read Documentation/modules.txt as well as
Documentation/networking/net-modules.txt.
-PCI DM9102 support
+PCI DM9102(A)/DM9132/DM9801 support
CONFIG_DM9102
This driver is for DM9102 compatible PCI cards from Davicom
- (http://www.davicom.com.tw)
- If you have a PCI DM9102 network (Ethernet) card, say Y.
+ (http://www.davicom.com.tw) including models DM9132 & DM9801.
+ If you have a PCI DM9102, DM9132 or DM9801 network (Ethernet)
+ card, say Y.
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you want).
@@ -8221,6 +8506,14 @@ CONFIG_SKFP
say M here and read Documentation/modules.txt. This is recommended.
The module will be called skfp.o.
+Linux telephony support
+CONFIG_PHONE
+ This option enable the kernel to manage telephony cards.
+
+ There are very few supported cards at this moment.
+
+ If unsure, say N.
+
HIgh Performance Parallel Interface support (EXPERIMENTAL)
CONFIG_HIPPI
HIgh Performance Parallel Interface (HIPPI) is a 800Mbit/sec and
@@ -9454,7 +9747,7 @@ CONFIG_ROMFS_FS
answer N.
QNX4 file system support (read only) (EXPERIMENTAL)
-CONFIG_QNX4FS_FS
+CONFIG_QNX4FS_FS
This is the file system used by the operating system QNX 4. Say Y if
you intend to mount QNX hard disks or floppies. Unless you say Y to
"QNX4FS write support" below, you will only be able to read
@@ -11077,6 +11370,16 @@ CONFIG_APPLICOM
If unsure, say N.
+Power Management support
+CONFIG_PM
+ This enable the kernel to lower the requested computer power by making some
+ devices enter in lower power levels (standy, sleep, ... modes).
+ Basically, this let you save power.
+ Two majors interfaces exist between the hardware and the OS, the older
+ Advanced Power Management (APM) and the newer Advanced Configuration and
+ Power Interface (ACPI).
+ Both are supported by the Linux Kernel.
+
Enter S1 for sleep (EXPERIMENTAL)
CONFIG_ACPI_S1_SLEEP
This enable ACPI compliant devices to enter level 1 of ACPI saving
@@ -13908,7 +14211,7 @@ CONFIG_ARCH_NETWINDER
Say Y here if you intend to run this kernel on the NetWinder.
Include support for the Compaq Personal Server
-CONFIG_PERSONAL_SERVER
+CONFIG_ARCH_PERSONAL_SERVER
Say Y here if you intend to run this kernel on the Compaq
Personal Server.
diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile
index 6bc727e43..155133a0a 100644
--- a/Documentation/DocBook/Makefile
+++ b/Documentation/DocBook/Makefile
@@ -1,4 +1,4 @@
-BOOKS := wanbook.sgml z8530book.sgml mcabook.sgml videobook.sgml
+BOOKS := wanbook.sgml z8530book.sgml mcabook.sgml videobook.sgml kernel-api.sgml
books: docproc $(BOOKS)
@@ -10,7 +10,7 @@ wanbook.sgml: wanbook.tmpl
<wanbook.tmpl >wanbook.sgml
z8530book.sgml: z8530book.tmpl
- $(TOPDIR)/scripts/docgen $(TOPDIR)/drivers/net/wan/syncppp.c \
+ $(TOPDIR)/scripts/docgen $(TOPDIR)/drivers/net/wan/z85230.c \
<z8530book.tmpl >z8530book.sgml
mcabook.sgml: mcabook.tmpl
@@ -21,9 +21,23 @@ videobook.sgml: videobook.tmpl
$(TOPDIR)/scripts/docgen $(TOPDIR)/drivers/char/videodev.c \
<videobook.tmpl >videobook.sgml
+kernel-api.sgml: kernel-api.tmpl
+ $(TOPDIR)/scripts/docgen $(TOPDIR)/drivers/char/videodev.c \
+ $(TOPDIR)/arch/i386/kernel/mca.c \
+ $(TOPDIR)/arch/i386/kernel/mtrr.c \
+ $(TOPDIR)/drivers/char/misc.c \
+ $(TOPDIR)/drivers/char/serial.c \
+ $(TOPDIR)/drivers/char/videodev.c \
+ $(TOPDIR)/drivers/sound/sound_core.c \
+ $(TOPDIR)/drivers/sound/sound_firmware.c \
+ $(TOPDIR)/drivers/net/wan/syncppp.c \
+ $(TOPDIR)/drivers/net/wan/z85230.c \
+ $(TOPDIR)/kernel/pm.c \
+ <kernel-api.tmpl >kernel-api.sgml
+
clean:
rm -f core *~
- rm -r $(BOOKS)
+ rm -f $(BOOKS)
include $(TOPDIR)/Rules.make
diff --git a/Documentation/DocBook/kernel-api.tmpl b/Documentation/DocBook/kernel-api.tmpl
new file mode 100644
index 000000000..96dd9ecc0
--- /dev/null
+++ b/Documentation/DocBook/kernel-api.tmpl
@@ -0,0 +1,92 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]>
+<book id="LinuxKernelAPI">
+ <bookinfo>
+ <title>The Linux Kernel API</title>
+
+ <legalnotice>
+ <para>
+ This documentation 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.
+ </para>
+
+ <para>
+ 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.
+ </para>
+
+ <para>
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ MA 02111-1307 USA
+ </para>
+
+ <para>
+ For more details see the file COPYING in the source
+ distribution of Linux.
+ </para>
+ </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+ <chapter id="intfuncs">
+ <title>Interrupt Handling</title>
+!Iarch/i386/kernel/irq.c
+ </chapter>
+ <chapter id="mtrrfuncs">
+ <title>MTRR Handling</title>
+!Earch/i386/kernel/mtrr.c
+ </chapter>
+
+ <chapter id="pmfuncs">
+ <title>Power Management</title>
+!Ekernel/pm.c
+ </chapter>
+
+ <chapter id="miscdev">
+ <title>Miscellaneous Device</title>
+!Edrivers/char/misc.c
+ </chapter>
+
+ <chapter id="viddev">
+ <title>Video4Linux</title>
+!Edrivers/char/videodev.c
+ </chapter>
+
+ <chapter id="snddev">
+ <title>Sound Devices</title>
+!Edrivers/sound/sound_core.c
+!Idrivers/sound/sound_firmware.c
+ </chapter>
+
+ <chapter id="mcabus">
+ <title>MCA Architecture</title>
+ <sect1><title>MCA Device Functions</title>
+!Earch/i386/kernel/mca.c
+ </sect1>
+ <sect1><title>MCA Bus DMA</title>
+!Iinclude/asm-i386/mca_dma.h
+ </sect1>
+ </chapter>
+
+ <chapter id="syncppp">
+ <title>Synchronous PPP</title>
+!Edrivers/net/wan/syncppp.c
+ </chapter>
+
+ <chapter id="16x50">
+ <title>16x50 UART Driver</title>
+!Edrivers/char/serial.c
+ </chapter>
+
+ <chapter id="z85230">
+ <title>Z85230 Support Library</title>
+!Edrivers/net/wan/z85230.c
+ </chapter>
+
+</book>
diff --git a/Documentation/DocBook/videobook.tmpl b/Documentation/DocBook/videobook.tmpl
index c152abf31..597413bf9 100644
--- a/Documentation/DocBook/videobook.tmpl
+++ b/Documentation/DocBook/videobook.tmpl
@@ -1642,7 +1642,7 @@ static struct video_buffer capture_fb;
</para>
</listitem></varlistentry>
- <varlistentry><term>API Deficiences</term>
+ <varlistentry><term>API Deficiencies</term>
<listitem>
<para>
The existing API poorly reflects compression capable devices. There
diff --git a/Documentation/i2c/dev-interface b/Documentation/i2c/dev-interface
index 0d917a8be..3a0f73bf6 100644
--- a/Documentation/i2c/dev-interface
+++ b/Documentation/i2c/dev-interface
@@ -89,15 +89,29 @@ ioctl(file,I2C_TENBIT,long select)
ioctl(file,I2C_FUNCS,unsigned long *funcs)
Gets the adapter functionality and puts it in *funcs.
+ioctl(file,I2C_RDWR,struct i2c_ioctl_rdwr_data *msgset)
+
+ Do combined read/write transaction without stop in between.
+ The argument is a pointer to a struct i2c_ioctl_rdwr_data {
+
+ struct i2c_msg *msgs; /* ptr to array of simple messages */
+ int nmsgs; /* number of messages to exchange */
+ }
+
+ The msgs[] themselves contain further pointers into data buffers.
+ The function will write or read data to or from that buffers depending
+ on whether the I2C_M_RD flag is set in a particular message or not.
+ The slave address and whether to use ten bit address mode has to be
+ set in each message, overriding the values set with the above ioctl's.
+
+
Other values are NOT supported at this moment, except for I2C_SMBUS,
which you should never directly call; instead, use the access functions
below.
You can do plain i2c transactions by using read(2) and write(2) calls.
-Combined read/write transactions are not yet supported (they will in
-the future, through an ioctl). You do not need to pass the address
-byte; instead, set it through ioctl I2C_SLAVE before you try to
-access the device.
+You do not need to pass the address byte; instead, set it through
+ioctl I2C_SLAVE before you try to access the device.
You can do SMBus level transactions (see documentation file smbus-protocol
for details) through the following functions:
diff --git a/Documentation/scsi-generic.txt b/Documentation/scsi-generic.txt
index 2d26dec9d..48ec05c3c 100644
--- a/Documentation/scsi-generic.txt
+++ b/Documentation/scsi-generic.txt
@@ -1,17 +1,18 @@
- Notes on Linux's SG driver version 2.1.34
+ Notes on Linux's SG driver version 2.1.36
-----------------------------------------
- 990606
+ 20000110
Introduction
============
-Sg is one of the four "high level" SCSI device drivers along with
-sd, st and sr (disk, tape and CDROM respectively). Sg is more generalized
-(but lower level) than its siblings and tends to be used on SCSI devices
-that don't fit into the already serviced categories. Thus sg is used for
-scanners, cd writers and reading audio cds digitally amongst other things.
+The SCSI Generic driver (sg) is one of the four "high level" SCSI device
+drivers along with sd, st and sr (disk, tape and CDROM respectively). Sg
+is more generalized (but lower level) than its siblings and tends to be
+used on SCSI devices that don't fit into the already serviced categories.
+Thus sg is used for scanners, cd writers and reading audio cds digitally
+amongst other things.
These are notes on the Linux SCSI generic packet device driver (sg)
-describing version 2.1.34 . The original driver was written by Lawrence
+describing version 2.1.36 . The original driver was written by Lawrence
Foard and remained in place with minimal changes since circa 1992.
Version 2 of this driver remains backward compatible (binary and
source **) with the original. It adds scatter gather, command queuing,
@@ -27,12 +28,12 @@ by Heiko Eissfeldt in a HOWTO called SCSI-Programming-HOWTO. My copy
of the document is version 1.5 dated 7th May 1996. It can found at
ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO-SCSI-Programming-HOWTO .
A copy of this document can be found at:
-http://www.torque.net/sg/p/original/HOWTO-SCSI-Programming-HOWTO .
+http://www.torque.net/sg/p/original/HOWTO-SCSI-Programming-HOWTO.txt .
** It is possible to write applications that perform differently
depending on whether they are using the original or this version of
-the sg device driver. The author is not aware of any useful
-pre-existing applications that have problems with version 2 (yet).
+the sg device driver. The author is not aware of any useful
+pre-existing applications that have problems with version 2.
Architecture
@@ -52,12 +53,12 @@ Since sg is a character device it supports the traditional Unix
system calls of open(), close(), read(), write() and ioctl(). Two other
related system calls: poll() and fcntl() are added to this list and
how they interact with the sg device driver is documented later.
-
-An SG device is accessed by write()ing SCSI commands plus any associated
+
+An SG device is accessed by write()ing SCSI commands plus any associated
outgoing data to it; the resulting status codes and any incoming data are
then obtained by a read() call. The device can be opened O_NONBLOCK
(non-blocking) and poll() used to monitor its progress. The device may be
-opened O_EXCL which excludes other "sg" users from this device (but not
+opened O_EXCL which excludes other "sg" users from this device (but not
"sd", "st" or "sr" users). The buffer given to the write() call is made
up as follows:
- struct sg_header image (see below)
@@ -71,6 +72,12 @@ The buffer received from the corresponding read() call contains:
The given SCSI command has its LUN field overwritten by the LUN value of
the associated sg device that has been open()ed.
+SCSI commands are only attempted once (i.e. there are no internal
+retries). If appropriate (e.g. a SCSI READ) the data buffer is copied back
+to user space irrespective of the values of the various SCSI related
+error/status codes. [Some adapters that use an old error interface in
+the SCSI mid level ignore the retry count and retry certain errors.]
+
sg_header
=========
@@ -96,11 +103,11 @@ The 'pack_len' is bizarre and ends up having the 'reply_len' put in it
input variable, it is not read by sg internally (only written).
The 'reply_len' is the length of the data the corresponding read()
-will/should request (including the sg_header).
+will/should request (including the sg_header).
The 'pack_id' is not acted upon by the sg device driver but is conveyed
back to the corresponding read() so it can be used for sequencing by an
-application.
+application.
The 'result' is also bizarre, turning certain types of host codes to 0 (no
error), EBUSY or EIO. With better error reporting now available, the
@@ -138,7 +145,7 @@ struct sg_header
unsigned int driver_status:8; /* [o] driver status+suggestion */
unsigned int other_flags:10; /* unused */
unsigned char sense_buffer[SG_MAX_SENSE]; /* [o] Output in 3 cases:
- when target_status is CHECK_CONDITION or
+ when target_status is CHECK_CONDITION or
when target_status is COMMAND_TERMINATED or
when (driver_status & DRIVER_SENSE) is true. */
}; /* This structure is 36 bytes long on i386 */
@@ -164,7 +171,7 @@ The 'target_status' field is always output and is the (masked and shifted
values are (found in <scsi/scsi.h>):
/* N.B. 1 bit offset from usual SCSI status values */
#define GOOD 0x00
-#define CHECK_CONDITION 0x01
+#define CHECK_CONDITION 0x01
#define CONDITION_GOOD 0x02
#define BUSY 0x04
#define INTERMEDIATE_GOOD 0x08
@@ -175,7 +182,7 @@ values are (found in <scsi/scsi.h>):
When the 'target_status' is CHECK_CONDITION or COMMAND_TERMINATED the
'sense_buffer' is output. Note that when (driver_status & DRIVER_SENSE)
is true then the 'sense_buffer' is also output (this seems to occur when
-the ide-scsi emulation is used). When the 'sense_buffer' is output the
+the ide-scsi emulation is used). When the 'sense_buffer' is output the
SCSI Sense Key can be found at (sense_buffer[2] & 0x0f) .
The 'host_status' field is always output and has the following values
@@ -269,12 +276,12 @@ The reserved buffer will be used if:
Returns a file descriptor if >= 0 , otherwise -1 implies an error.
Error codes (value in 'errno' after -1 returned):
-EACCES Either the user doesn't have appropriate permissions on
+EACCES Either the user doesn't have appropriate permissions on
'filename' or attempted to use both O_RDONLY and O_EXCL
EBUSY O_NONBLOCK set and some user of this sg device has O_EXCL
set while someone is already using this device
EINTR while waiting for an "exclusive" lock to clear, a signal
- is received, just try again ...
+ is received, just try again ...
ENODEV sg not compiled into kernel or the kernel cannot find the
sg module (or it can't initialize itself (low memory??))
ENOENT given filename not found
@@ -320,12 +327,13 @@ EAGAIN SCSI mid-level out of command blocks (rare), try again.
This is more likely to happen when queuing commands,
so wait a bit (eg usleep(10000) ) before trying again
EDOM a) command queuing off: a packet is already queued
- b) command queuing on: too many packets queued
+ b) command queuing on: too many packets queued
(SG_MAX_QUEUE exceeded)
- c) SCSI command length given in SG_NEXT_CMD_LEN too long
EFAULT 'buffer' for 'count' bytes is an invalid memory range
-EIO incoming buffer too short. It should be at least (6 +
- sizeof(struct sg_header))==42 bytes long
+EIO a) incoming buffer too short. It should be at least
+ (6 + sizeof(struct sg_header))==42 bytes long
+ b) SCSI command length given in SG_NEXT_CMD_LEN too long
+ c) reply_len negative
ENOMEM can't get memory for DMA. Take evasive action ...
ENXIO either scsi sub-system is currently processing some error
(eg doing a device reset) or the sg driver/module removed
@@ -334,7 +342,7 @@ ENXIO either scsi sub-system is currently processing some error
read(int sg_fd, void * buffer, size_t count)
--------------------------------------------
-Read() is used to receive a packet containing 1 mandatory part and 1
+Read() is used to receive a packet containing 1 mandatory part and 1
optional part. The mandatory part is:
- a control block (an instance of struct sg_header)
The optional part is:
@@ -343,7 +351,7 @@ The buffer given to a read() and its corresponding count should be
sufficient to accommodate this packet to avoid truncation. Truncation occurs
if count < sg_header::replylen .
-By default, read() will return the oldest packet queued up. If the
+By default, read() will return the oldest packet queued up. If the
SG_SET_FORCE_PACK_ID,1 ioctl() is active then read() will attempt to
fetch the packet whose pack_id (given earlier to write()) matches the
sg_header::pack_id given to this read(). If not available it will either
@@ -356,12 +364,16 @@ same as the count argument. It is not the actual number of bytes
DMA-ed by the SCSI device. This driver is currently unable to provide
such an underrun indication.
+If the SCSI device reports an error then a REQUEST SENSE is automatically
+done and the output is placed in the sense_buffer array which is in the
+control block. This action is sometimes called "auto-sense".
+
Error codes (value in 'errno' after -1 returned):
EAGAIN either no waiting packet or requested packet is not
available while O_NONBLOCK flag was set
EFAULT 'buffer' for 'count' bytes is an invalid memory range
EINTR while waiting for a packet, a signal is received, just
- try again ...
+ try again ...
EIO if the 'count' given to read() is < sizeof(struct sg_header)
and the 'result' element in sg_header is non-zero. Not a
recommended error reporting technique
@@ -380,11 +392,11 @@ arrange for an orderly cleanup of those packets that are still "in
flight".
A process that has an open file descriptor to an sg device may be aborted
-(eg by a kill signal). In this case, the kernel automatically calls close
+(eg by a kill signal). In this case, the kernel automatically calls close
(which is called 'sg_release()' in the version 2 driver) to facilitate
the cleanup mentioned above.
-A problem persists in version 2.1.34 if the sg driver is a module and is
+A problem persists in version 2.1.36 if the sg driver is a module and is
removed while packets are still "in flight".
Returns 0 if successful, otherwise -1 implies an error.
@@ -394,7 +406,7 @@ ENXIO sg driver/module removed or corrupted
ioctl(int sg_fd, int command, ...) [sg specific]
-------------------------------------------------
-Ken Thompson (or perhaps some other Unix luminary) described ioctl() as
+Ken Thompson (or perhaps some other Unix luminary) described ioctl() as
the "garbage bin of Unix". This driver compounds the situation by adding
more ...
If a ioctl command is not recognized by sg (and the various lower levels
@@ -417,7 +429,8 @@ Assumes 3rd argument points to an int containing the new timeout value
for this file descriptor. The unit is a "jiffy". Packets that are
already "in flight" will not be affected. The default value is set
on open() and is SG_DEFAULT_TIMEOUT (defined in sg.h). This default is
-currently 1 minute and may not be long enough for formats.
+currently 1 minute and may not be long enough for formats. Negative
+values will yield an EIO error.
SG_EMULATED_HOST:
Assumes 3rd argument points to an int and outputs a flag indicating
@@ -426,33 +439,31 @@ emulated one (eg ide-scsi device driver). A value of 1 means emulated
while 0 is not.
SG_SET_TRANSFORM W:
-Third argument is ignored. Only is meaningful when SG_EMULATED host has
-yielded 1 (ie the low-level is the ide-scsi device driver); otherwise
-an EINVAL error occurs. The default state is to _not_ transform SCSI
-commands to the corresponding ATAPI commands but pass them straight
-through as is. [Only certain classes of SCSI commands need to be
-transformed to their ATAPI equivalents.] Making this ioctl command causes
-transforms to occur thereafter. Subsequent calls to this ioctl command
-have no additional effect. Beware, this state will affect all devices
-(and hence all related sg file descriptors) associated with this ide-scsi
-"bus".
-The author of ide-scsi has pointed out that this is not the intended
-behaviour which is a 3rd argument of 0 to disable transforms and 1 to
-enable transforms. Note the 3rd argument is an 'int' not a 'int *'.
-Perhaps the intended behaviour will be implemented soon.
+Only is meaningful when SG_EMULATED host has yielded 1 (i.e. the low-level
+is the ide-scsi device driver); otherwise an EINVAL error occurs. The
+default state is to _not_ transform SCSI commands to the corresponding
+ATAPI commands but pass them straight through as is. [Only certain classes
+of SCSI commands need to be transformed to their ATAPI equivalents.]
+The third argument is interpreted as an integer. When it is non-zero then
+a flag is set inside the ide-scsi driver that transforms subsequent
+commands sent to this driver. When zero is passed as the 3rd argument to
+this ioctl then the flag within the ide-scsi driver is cleared and
+subsequent commands are not transformed. Beware, this state will affect
+all devices (and hence all related sg file descriptors) associated with
+this ide-scsi "bus".
SG_GET_TRANSFORM:
Third argument is ignored. Only is meaningful when SG_EMULATED host has
yielded 1 (ie the low-level is the ide-scsi device driver); otherwise
an EINVAL error occurs. Returns 0 to indicate _not_ transforming SCSI
-to ATAPI commands (default). Returns 1 when it is transforming.
+to ATAPI commands (default). Returns 1 when it is transforming them.
SG_SET_FORCE_LOW_DMA +:
Assumes 3rd argument points to an int containing 0 or 1. 0 (default)
means sg decides whether to use memory above 16 Mbyte level (on i386)
based on the host adapter being used by this SCSI device. Typically
PCI SCSI adapters will indicate they can DMA to the whole 32 bit address
-space.
+space.
If 1 is given then the host adapter is overridden and only memory below
the 16MB level is used for DMA. A requirement for this should be
extremely rare. If the "reserved" buffer allocated on open() is not in
@@ -469,15 +480,21 @@ adapters setting has been overridden by SG_SET_FORCE_LOW_DMA,1 .
SG_GET_SCSI_ID +:
Assumes 3rd argument is pointing to an object of type Sg_scsi_id (see
-sg.h) and populates it. That structure contains ints for host_no,
-channel, scsi_id, lun and scsi_type. Most of this information is
-available from other sources (eg SCSI_IOCTL_GET_IDLUN and
-SCSI_IOCTL_GET_BUS_NUMBER) but tends to be awkward to collect.
+sg.h) and populates it. That structure contains ints for host_no,
+channel, scsi_id, lun, scsi_type, allowable commands per lun and
+queue_depth. Most of this information is available from other sources
+(eg SCSI_IOCTL_GET_IDLUN and SCSI_IOCTL_GET_BUS_NUMBER) but tends to be
+awkward to collect.
+Allowable commands per lun and queue_depth give an insight to the
+command queuing capabilities of the adapters and the device. The latter
+overrides the former (logically) and the former is only of interest
+if it is equal to queue_depth which probably indicates the device
+does not support queueing commands (e.g. most scanners).
SG_SET_FORCE_PACK_ID +:
Assumes 3rd argument is pointing to an int. 0 (default) instructs read()
to return the oldest (written) packet if multiple packets are
-waiting to be read (when command queuing is being used).
+waiting to be read (when command queuing is being used).
1 instructs read() to view the sg_header::pack_id as input and return the
oldest packet matching that pack_id or wait until it arrives (or yield
EAGAIN if O_NONBLOCK is in force). As a special case the pack_id of -1
@@ -524,7 +541,7 @@ state and there is one or more _other_ file descriptors using this sg
device then an EBUSY error occurs. Per device sequencing was the original
semantics and allowed, for example different processes to "share" the
device, one perhaps write()ing with the other one read()ing. This command
-is supplied if anyone needs those semantics. Per file descriptor
+is supplied if anyone needs those semantics. Per file descriptor
sequencing, perhaps with the use of the O_EXCL flag, seems more sensible.
SG_GET_MERGE_FD +:
@@ -548,7 +565,7 @@ queuing is on.
SG_SET_UNDERRUN_FLAG +:
Assumes 3rd argument is pointing to an int. 0 (current default, set by
SG_DEF_UNDERRUN_FLAG in sg.h) requests underruns be ignored. 1 requests
-that underruns be flagged. [The only low level driver that acts on this
+that underruns be flagged. [The only low level driver that acts on this
at the moment is the aic7xxx which yields a DID_ERROR error on underrun.]
Only the current file descriptor is affected by this command (unless
"per device" sequencing has been selected).
@@ -571,7 +588,13 @@ this ioctl().
SG_GET_VERSION_NUM +:
Assumes 3rd argument points to an int. The version number is then placed
-in that int. A sg version such as 2.1.34 will yield "20134" from this ioctl.
+in that int. A sg version such as 2.1.36 will yield "20136" from this ioctl.
+
+SG_SCSI_RESET +:
+Assumes 3rd argument points to an int. Unfortunately doesn't currently
+do much (may in the future after other issues are resolved). Yields an
+EBUSY error if the SCSI bus or the associated device is being reset
+when this ioctl() is called, otherwise returns 0.
SG_SET_DEBUG +:
Assumes 3rd argument is pointing to an int. 0 (default) turns debugging
@@ -641,13 +664,16 @@ fcntl(sg_fd, F_SETFL, flags | O_ASYNC)
Utility and Test Programs
=========================
-See the README file in the sg_utils<date>.tgz tarball. At the time of
-writing this was sg_utils990527.tgz .
+See the README file in the sg_utils<date>.tgz tarball. Look on the
+http://www.torque.net/sg website for the latest version.
Briefly, that tarball contains the following utilities:
sg_dd512 'dd' like program that assumes 512 byte blocks size
sg_dd2048 'dd' like program that assumes 2048 byte blocks size
+sg_dd2352 'dd' like program that assumes 2352 byte blocks size
sgq_dd512 like 'sg_dd512' but does command queuing on "if"
+sgp_dd probably the most flexible 'dd' variant. It uses POSIX
+ threads, block size set by "bs=..." plus other options.
sg_scan outputs information (optionally Inquiry) on SCSI devices
sg_rbuf tests SCSI bus transfer speed (without physical IO)
sg_whoami outputs info (optionally capacity) of given SCSI device
@@ -657,6 +683,9 @@ sginfo outputs "mode" information about SCSI devices (it is a
It also contains the following test programs:
sg_debug outputs sg driver state to console/log file
sg_poll tests asynchronous notification
+sg_runt_ex example run time selection program for application authors
+sg_simple1 example program first time users
+sg_simple2 like sg_simple1 but with more primitive error processing
sg_inquiry does a SCSI Inquiry command (from original HOWTO)
sg_tst_med checks presence of media (from original HOWTO)
@@ -664,36 +693,40 @@ There are also 2 source files (sg_err.[hc]) for outputting and categorizing
SCSI 2 errors and warnings. This code is used by most of the above
utility and test programs.
-The following programs: sg_dd512, sg_dd2048, sg_scan, sg_rbuf, sg_tst_med,
-sg_inquiry and sginfo, can be compiled either for this new sg driver _or_
-the original sg driver.
+The following programs: sg_dd512, sg_dd2048, sg_dd2352, sg_scan, sg_runt_ex,
+sg_rbuf, sg_tst_med, sg_inquiry and sginfo, can be compiled either for this
+new sg driver _or_ the original sg driver (in 2.0 or 2.2 series kernels).
+sg_runt_ex can be run on 2.0, 2.2 or 2.3 series kernels even if it is
+compiled on a different series (eg compiled on 2.0, run on 2.2).
Header files
============
User applications need to find the correct "sg.h" header file matching
-their kernel in order to write code using the sg device driver. This is
+their kernel in order to write code using the sg device driver. This is
sometimes more difficult than it should be. The correct "sg.h" will usually
-be found at /usr/src/linux/include/scsi/sg.h . Another important header
+be found at /usr/src/linux/include/scsi/sg.h . Another important header
file is "scsi.h" which will be in the same directory.
-Several distributions have taken their own copies of these files and placed
-them in /usr/include/scsi which is where "#include <scsi/sg.h>" would go
-looking. The directory /usr/include/scsi _should_ be a symbolic link to
-/usr/src/linux/include/scsi/ . It was is Redhat 5.1 and 5.2 but it is
-not is Redhat 6.0 . Some other distributions have the same problem. To
-solve this (as root) do the following:
+When "#include <scsi/sg.h>" is written in an application then this refers
+to the file /usr/include/scsi/sg.h . A problem sometimes arises because
+the files in the /usr/include/scsi directory are controlled by the GNU
+library people who maintain glibc. Unfortunately these 2 versions of
+the sg.h header file are not always in sync. [This was the case in Redhat
+6.0 and 6.1 .] Glibc 2.1.3 and later versions should get this right.
-# cd /usr/include
-# mv scsi scsi_orig
-# ln -s ../src/linux/include/scsi scsi
+If this is a problem, the user may need to copy sg.h (and scsi.h) from
+the kernel source includes to /usr/include scsi. If the user can change
+the effected source code then another approach is to rely on the fact that
+/usr/src/linux is a symbolic link to /usr/src/linux/include/linux and
+change the sg.h include to look like:
+ #include <linux/../scsi/sg.h>
+This solution is used by the author of cdparanoia (Monty) in his application.
-This doesn't seem to be a problem with /usr/include/linux (at least in
-Redhat where it is a symbolic link) so it is hard to understand why
-/usr/include/scsi is defined the way it is. The fact the
-/usr/include/linux is a symbolic link opens up the following solution
-proposed by the author of cdparanoia (Monty):
-#include <linux/../scsi/sg.h>
+[Former scsi generic documents suggested adding a symbolic link to
+bypass this problem but that is not popular with the glibc maintainers.
+I would like to thank Andreas Jaeger <aj@suse.de> for his contributions
+on this subject.]
Extra information in scsi-generic_long.txt
@@ -711,6 +744,27 @@ The longer document contains additional sections on:
- an appendix with some SCSI 2 information in it
+References
+==========
+http://www.t10.org Very important site for SCSI related information.
+ Contains SCSI 2 and 3 draft standards.
+http://www.andante.org/scsi.html
+ This is Eric Youngdale's site. Eric is primarily
+ responsible for the Linux SCSI architecture and
+ its mid-level implementation.
+http://www.kernel.dk Jens Axboe's site for Linux cdrom matters including
+ the SCSI "sr" driver.
+http://www.torque.net/sg
+ My site with sg related information.
+newsgroup:linux-scsi@vger.rutgers.edu
+ Newsgroup for Linux related SCSI matters
+/usr/src/linux/MAINTAINERS
+ This is a file in the Linux kernel source that
+ contains up to date information about who maintains
+ what and where information can be found. Links to
+ SCSI adapter information are also here.
+
+
Conclusion
==========
The SCSI generic packet device driver attempts to make as few assumptions
diff --git a/Documentation/sysrq.txt b/Documentation/sysrq.txt
index c17774865..49faf2dab 100644
--- a/Documentation/sysrq.txt
+++ b/Documentation/sysrq.txt
@@ -1,6 +1,7 @@
- MAGIC SYSRQ KEY DOCUMENTATION v1.2
+
+ MAGIC SYSRQ KEY DOCUMENTATION v1.31
------------------------------------
- [Sat May 16 01:09:21 EDT 1998]
+ [Mon Mar 13 21:45:48 EST 2000]
* What is the magic SysRQ key?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -11,7 +12,10 @@ regardless of whatever else it is doing, unless it is completely locked up.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You need to say "yes" to 'Magic SysRq key (CONFIG_MAGIC_SYSRQ)' when
configuring the kernel. This option is only available in 2.1.x or later
-kernels.
+kernels. Once you boot the new kernel, you need to enable it manually
+using following command:
+
+ echo "1" > /proc/sys/kernel/sysrq
* How do I use the magic SysRQ key?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -28,7 +32,8 @@ On other - If you know of the key combos for other architectures, please
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'r' - Turns off keyboard raw mode and sets it to XLATE.
-'k' - Kills all programs on the current virtual console.
+'k' - Secure Access Key (SAK) Kills all programs on the current virtual
+ console. NOTE: See important comments below in SAK section.
'b' - Will immediately reboot the system without syncing or unmounting
your disks.
@@ -58,12 +63,23 @@ On other - If you know of the key combos for other architectures, please
'l' - Send a SIGKILL to all processes, INCLUDING init. (Your system
will be non-functional after this.)
+'h' - Will display help ( actually any other key than those listed
+ above will display help. but 'h' is easy to remember :-)
+
* Okay, so what can I use them for?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Well, un'R'aw is very handy when your X server or a svgalib program crashes.
-sa'K' (system attention key) is useful when you want to exit a program
-that will not let you switch consoles. (For example, X or a svgalib program.)
+sa'K' (Secure Access Key) is usefull when you want to be sure there are no
+trojan program is running at console and which could grab your password
+when you would try to login. It will kill all programs on given console
+and thus letting you make sure that the login prompt you see is actually
+the one from init, not some trojan program.
+IMPORTANT:In its true form it is not a true SAK like the one in :IMPORTANT
+IMPORTATN:c2 compliant systems, and it should be mistook as such. :IMPORTANT
+ It seems other find it usefull as (System Attention Key) which is
+useful when you want to exit a program that will not let you switch consoles.
+(For example, X or a svgalib program.)
re'B'oot is good when you're unable to shut down. But you should also 'S'ync
and 'U'mount first.
@@ -108,6 +124,10 @@ you exit 'showkey' by not typing anything for ten seconds.
* I have more questions, who can I ask?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You may feel free to send email to myrdraal@deathsdoor.com, and I will
-respond as soon as possible. If that email address does not work, use
-myrdraal@jackalz.dyn.ml.org.
+respond as soon as possible.
-Myrdraal
+
+* Credits
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Written by Mydraal <myrdraal@deathsdoor.com>
+Updated by Adam Sulmicki <adam@cfar.umd.edu>
diff --git a/Documentation/usb/usb-serial.txt b/Documentation/usb/usb-serial.txt
index 58f3cf108..1ea46bbaa 100644
--- a/Documentation/usb/usb-serial.txt
+++ b/Documentation/usb/usb-serial.txt
@@ -90,8 +90,6 @@ Current status:
not all of the standard USB descriptors are handled: Get_Status, Set_Feature
O_NONBLOCK, select()
- The device usually appears at /dev/ttyUSB1 .
-
Generic Serial driver
diff --git a/MAINTAINERS b/MAINTAINERS
index 6762ce048..f03fbb625 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -689,6 +689,8 @@ S: Maintained
NETFILTER
P: Rusty Russell
M: Rusty.Russell@rustcorp.com.au
+P: Marc Boucher
+M: marc@mbsi.ca
W: http://www.samba.org/netfilter/
W: http://netfilter.kernelnotes.org
W: http://antarctica.penguincomputing.com/~netfilter/
diff --git a/Makefile b/Makefile
index 4f9affb7e..e3f5fff90 100644
--- a/Makefile
+++ b/Makefile
@@ -1,11 +1,11 @@
VERSION = 2
PATCHLEVEL = 3
SUBLEVEL = 99
-EXTRAVERSION = -pre1
+EXTRAVERSION = -pre2
-ARCH = mips
+KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
-.EXPORT_ALL_VARIABLES:
+ARCH = mips
CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
else if [ -x /bin/bash ]; then echo /bin/bash; \
@@ -16,21 +16,28 @@ HPATH = $(TOPDIR)/include
FINDHPATH = $(HPATH)/asm $(HPATH)/linux $(HPATH)/scsi $(HPATH)/net
HOSTCC = gcc
-HOSTCFLAGS = -O2
+HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
CROSS_COMPILE =
-AS =$(CROSS_COMPILE)as
-LD =$(CROSS_COMPILE)ld
-CC =$(CROSS_COMPILE)gcc
-CPP =$(CC) -E
-AR =$(CROSS_COMPILE)ar
-NM =$(CROSS_COMPILE)nm
-STRIP =$(CROSS_COMPILE)strip
-OBJCOPY =$(CROSS_COMPILE)objcopy
-OBJDUMP =$(CROSS_COMPILE)objdump
-MAKE =make
-GENKSYMS=/sbin/genksyms
+AS = $(CROSS_COMPILE)as
+LD = $(CROSS_COMPILE)ld
+CC = $(CROSS_COMPILE)gcc
+CPP = $(CC) -E
+AR = $(CROSS_COMPILE)ar
+NM = $(CROSS_COMPILE)nm
+STRIP = $(CROSS_COMPILE)strip
+OBJCOPY = $(CROSS_COMPILE)objcopy
+OBJDUMP = $(CROSS_COMPILE)objdump
+MAKE = make
+MAKEFILES = $(TOPDIR)/.config
+GENKSYMS = /sbin/genksyms
+MODFLAGS = -DMODULE
+PERL = perl
+
+export VERSION PATCHLEVEL SUBLEVEL EXTRAVERSION KERNELRELEASE ARCH \
+ CONFIG_SHELL TOPDIR HPATH HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC \
+ CPP AR NM STRIP OBJCOPY OBJDUMP MAKE MAKEFILES GENKSYMS MODFLAGS PERL
all: do-it-all
@@ -38,6 +45,7 @@ all: do-it-all
# Make "config" the default target if there is no configuration file or
# "depend" the target if there is no top-level dependency information.
#
+
ifeq (.config,$(wildcard .config))
include .config
ifeq (.depend,$(wildcard .depend))
@@ -53,20 +61,11 @@ do-it-all: config
endif
#
-# ROOT_DEV specifies the default root-device when making the image.
-# This can be either FLOPPY, CURRENT, /dev/xxxx or empty, in which case
-# the default of FLOPPY is used by 'build'.
-#
-
-ROOT_DEV = CURRENT
-
-KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
-
-#
# INSTALL_PATH specifies where to place the updated kernel and system map
# images. Uncomment if you want to place them anywhere other than root.
+#
-#INSTALL_PATH=/boot
+#export INSTALL_PATH=/boot
#
# INSTALL_MOD_PATH specifies a prefix to MODLIB for module directory
@@ -75,15 +74,6 @@ KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
#
#
-# If you want to preset the SVGA mode, uncomment the next line and
-# set SVGA_MODE to whatever number you want.
-# Set it to -DSVGA_MODE=NORMAL_VGA if you just want the EGA/VGA mode.
-# The number is the same as you would ordinarily press at bootup.
-#
-
-SVGA_MODE= -DSVGA_MODE=NORMAL_VGA
-
-#
# standard CFLAGS
#
@@ -96,15 +86,34 @@ endif
CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
AFLAGS := $(CPPFLAGS)
+export CPPFLAGS CFLAGS AFLAGS
+
#
-# if you want the RAM disk device, define this to be the
-# size in blocks.
+# ROOT_DEV specifies the default root-device when making the image.
+# This can be either FLOPPY, CURRENT, /dev/xxxx or empty, in which case
+# the default of FLOPPY is used by 'build'.
+# This is i386 specific.
+#
+
+export ROOT_DEV = CURRENT
+
+#
+# If you want to preset the SVGA mode, uncomment the next line and
+# set SVGA_MODE to whatever number you want.
+# Set it to -DSVGA_MODE=NORMAL_VGA if you just want the EGA/VGA mode.
+# The number is the same as you would ordinarily press at bootup.
+# This is i386 specific.
#
-#RAMDISK = -DRAMDISK=512
+export SVGA_MODE = -DSVGA_MODE=NORMAL_VGA
-# Include the make variables (CC, etc...)
#
+# if you want the RAM disk device, define this to be the
+# size in blocks.
+# This is i386 specific.
+#
+
+#export RAMDISK = -DRAMDISK=512
CORE_FILES =kernel/kernel.o mm/mm.o fs/fs.o ipc/ipc.o
NETWORKS =net/network.a
@@ -166,6 +175,9 @@ DRIVERS += $(DRIVERS-y)
include arch/$(ARCH)/Makefile
+export CORE_FILES NETWORKS DRIVERS LIBS HEAD LDFLAGS LIBS LINKFLAGS \
+ MAKEBOOT ASFLAGS
+
# use '-fno-strict-aliasing', but only if the compiler can take it
CFLAGS += $(shell if $(CC) -fno-strict-aliasing -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-fno-strict-aliasing"; fi)
@@ -276,7 +288,6 @@ tags: dummy
find include -type d \( -name "asm-*" -o -name config \) -prune -o -name '*.h' -print | xargs ctags $$CTAGSF -a && \
find $(SUBDIRS) init -name '*.c' | xargs ctags $$CTAGSF -a
-MODFLAGS += -DMODULE
ifdef CONFIG_MODULES
ifdef CONFIG_MODVERSIONS
MODFLAGS += -DMODVERSIONS -include $(HPATH)/linux/modversions.h
@@ -320,6 +331,7 @@ modules_install:
if [ -f SK98LIN_MODULES ]; then inst_mod SK98LIN_MODULES net; fi; \
if [ -f SKFP_MODULES ]; then inst_mod SKFP_MODULES net; fi; \
if [ -f USB_MODULES ]; then inst_mod USB_MODULES usb; fi; \
+ if [ -f USB_SERIAL_MODULES ]; then inst_mod USB_SERIAL_MODULES usb; fi; \
if [ -f IEEE1394_MODULES ]; then inst_mod IEEE1394_MODULES ieee1394; fi; \
if [ -f PCMCIA_MODULES ]; then inst_mod PCMCIA_MODULES pcmcia; fi; \
if [ -f PCMCIA_NET_MODULES ]; then inst_mod PCMCIA_NET_MODULES pcmcia; fi; \
@@ -380,7 +392,7 @@ mrproper: clean archmrproper
rm -rf include/config
rm -f .depend `find . -type f -name .depend -print`
rm -f core `find . -type f -size 0 -print`
- rm -f .hdepend scripts/mkdep scripts/split-include
+ rm -f .hdepend scripts/mkdep scripts/split-include scripts/docproc
rm -f $(TOPDIR)/include/linux/modversions.h
rm -rf $(TOPDIR)/include/linux/modules
@@ -405,20 +417,21 @@ dep-files: scripts/mkdep archdep include/linux/version.h
find $(FINDHPATH) -follow -name \*.h ! -name modversions.h -print | env -i PATH="$(PATH)" HPATH="$(HPATH)" xargs scripts/mkdep > .hdepend
$(MAKE) $(patsubst %,_sfdep_%,$(SUBDIRS)) _FASTDEP_ALL_SUB_DIRS="$(SUBDIRS)"
-MODVERFILE :=
-
ifdef CONFIG_MODVERSIONS
MODVERFILE := $(TOPDIR)/include/linux/modversions.h
+else
+MODVERFILE :=
endif
+export MODVERFILE
depend dep: dep-files $(MODVERFILE)
# make checkconfig: Prune 'scripts' directory to avoid "false positives".
checkconfig:
- find * -name '*.[hcS]' -type f -print | grep -v scripts/ | sort | xargs perl -w scripts/checkconfig.pl
+ find * -name '*.[hcS]' -type f -print | grep -v scripts/ | sort | xargs $(PERL) -w scripts/checkconfig.pl
checkhelp:
- perl -w scripts/checkhelp.pl `find * -name [cC]onfig.in -print`
+ $(PERL) -w scripts/checkhelp.pl `find * -name [cC]onfig.in -print`
ifdef CONFIGURATION
..$(CONFIGURATION):
diff --git a/arch/alpha/config.in b/arch/alpha/config.in
index df7ae01ca..d70d42421 100644
--- a/arch/alpha/config.in
+++ b/arch/alpha/config.in
@@ -126,6 +126,7 @@ if [ "$CONFIG_ALPHA_MIATA" = "y" -o "$CONFIG_ALPHA_LX164" = "y" \
then
define_bool CONFIG_PCI y
define_bool CONFIG_ALPHA_EV5 y
+ define_bool CONFIG_ALPHA_CIA y
define_bool CONFIG_ALPHA_PYXIS y
fi
if [ "$CONFIG_ALPHA_DP264" = "y" -o "$CONFIG_ALPHA_EIGER" = "y" ]
diff --git a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile
index 6c538f4ba..9d87b6e0c 100644
--- a/arch/alpha/kernel/Makefile
+++ b/arch/alpha/kernel/Makefile
@@ -18,7 +18,7 @@ O_OBJS := entry.o traps.o process.o osf_sys.o irq.o irq_alpha.o \
OX_OBJS := alpha_ksyms.o
L_TARGET := rest.a
-L_OBJS := irq_i8259.o irq_srm.o \
+L_OBJS := irq_i8259.o irq_srm.o irq_pyxis.o \
es1888.o smc37c669.o smc37c93x.o ns87312.o
ifdef CONFIG_SMP
@@ -32,7 +32,7 @@ endif
ifdef CONFIG_ALPHA_GENERIC
O_OBJS += core_apecs.o core_cia.o core_irongate.o core_lca.o core_mcpcia.o \
- core_polaris.o core_pyxis.o core_t2.o core_tsunami.o \
+ core_polaris.o core_t2.o core_tsunami.o \
sys_alcor.o sys_cabriolet.o sys_dp264.o sys_eb64p.o sys_eiger.o \
sys_jensen.o sys_miata.o sys_mikasa.o sys_nautilus.o \
sys_noritake.o sys_rawhide.o sys_ruffian.o sys_rx164.o \
@@ -56,9 +56,6 @@ endif
ifdef CONFIG_ALPHA_MCPCIA
O_OBJS += core_mcpcia.o
endif
-ifdef CONFIG_ALPHA_PYXIS
-O_OBJS += core_pyxis.o
-endif
ifdef CONFIG_ALPHA_T2
O_OBJS += core_t2.o
endif
diff --git a/arch/alpha/kernel/alpha_ksyms.c b/arch/alpha/kernel/alpha_ksyms.c
index 25d9583dd..0001799db 100644
--- a/arch/alpha/kernel/alpha_ksyms.c
+++ b/arch/alpha/kernel/alpha_ksyms.c
@@ -37,6 +37,7 @@ extern struct hwrpb_struct *hwrpb;
extern void dump_thread(struct pt_regs *, struct user *);
extern int dump_fpu(struct pt_regs *, elf_fpregset_t *);
extern spinlock_t kernel_flag;
+extern spinlock_t rtc_lock;
/* these are C runtime functions with special calling conventions: */
extern void __divl (void);
@@ -106,6 +107,7 @@ EXPORT_SYMBOL(pci_map_single);
EXPORT_SYMBOL(pci_unmap_single);
EXPORT_SYMBOL(pci_map_sg);
EXPORT_SYMBOL(pci_unmap_sg);
+EXPORT_SYMBOL(pci_dma_supported);
EXPORT_SYMBOL(dump_thread);
EXPORT_SYMBOL(dump_fpu);
@@ -202,6 +204,8 @@ EXPORT_SYMBOL(__local_bh_count);
EXPORT_SYMBOL(__local_irq_count);
#endif /* __SMP__ */
+EXPORT_SYMBOL(rtc_lock);
+
/*
* The following are special because they're not called
* explicitly (the C compiler or assembler generates them in
diff --git a/arch/alpha/kernel/core_cia.c b/arch/alpha/kernel/core_cia.c
index b8a7f18ee..481a682b1 100644
--- a/arch/alpha/kernel/core_cia.c
+++ b/arch/alpha/kernel/core_cia.c
@@ -6,7 +6,7 @@
*
* Copyright (C) 1995 David A Rusling
* Copyright (C) 1997, 1998 Jay Estabrook
- * Copyright (C) 1998, 1999 Richard Henderson
+ * Copyright (C) 1998, 1999, 2000 Richard Henderson
*
* Code common to all CIA core logic chips.
*/
@@ -25,6 +25,8 @@
#include <asm/core_cia.h>
#undef __EXTERN_INLINE
+#include <linux/bootmem.h>
+
#include "proto.h"
#include "pci_impl.h"
@@ -35,27 +37,20 @@
* handle the system transaction. Another involves timing. Ho hum.
*/
-/*
- * BIOS32-style PCI interface:
- */
-
#define DEBUG_CONFIG 0
-#define DEBUG_DUMP_REGS 0
-
#if DEBUG_CONFIG
# define DBGC(args) printk args
#else
# define DBGC(args)
#endif
-#define vuip volatile unsigned int *
+#define vip volatile int *
/*
* Given a bus, device, and function number, compute resulting
- * configuration space address and setup the CIA_HAXR2 register
- * accordingly. It is therefore not safe to have concurrent
- * invocations to configuration space access routines, but there
- * really shouldn't be any need for this.
+ * configuration space address. It is therefore not safe to have
+ * concurrent invocations to configuration space access routines, but
+ * there really shouldn't be any need for this.
*
* Type 0:
*
@@ -96,34 +91,16 @@ static int
mk_conf_addr(struct pci_dev *dev, int where, unsigned long *pci_addr,
unsigned char *type1)
{
- unsigned long addr;
u8 bus = dev->bus->number;
u8 device_fn = dev->devfn;
- DBGC(("mk_conf_addr(bus=%d, device_fn=0x%x, where=0x%x, "
- "pci_addr=0x%p, type1=0x%p)\n",
- bus, device_fn, where, pci_addr, type1));
+ *type1 = (bus != 0);
+ *pci_addr = (bus << 16) | (device_fn << 8) | where;
- if (bus == 0) {
- int device = device_fn >> 3;
+ DBGC(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x,"
+ " returning address 0x%p\n"
+ bus, device_fn, where, *pci_addr));
- /* Type 0 configuration cycle. */
-
- if (device > 20) {
- DBGC(("mk_conf_addr: device (%d) > 20, returning -1\n",
- device));
- return -1;
- }
-
- *type1 = 0;
- addr = (device_fn << 8) | (where);
- } else {
- /* Type 1 configuration cycle. */
- *type1 = 1;
- addr = (bus << 16) | (device_fn << 8) | (where);
- }
- *pci_addr = addr;
- DBGC(("mk_conf_addr: returning pci_addr 0x%lx\n", addr));
return 0;
}
@@ -131,43 +108,37 @@ static unsigned int
conf_read(unsigned long addr, unsigned char type1)
{
unsigned long flags;
- unsigned int stat0, value;
- unsigned int cia_cfg = 0;
+ int stat0, value;
+ int cia_cfg = 0;
- value = 0xffffffffU;
- mb();
-
- __save_and_cli(flags); /* avoid getting hit by machine check */
-
- DBGC(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1));
+ DBGC(("conf_read(addr=0x%lx, type1=%d) ", addr, type1));
+ __save_and_cli(flags);
/* Reset status register to avoid losing errors. */
- stat0 = *(vuip)CIA_IOC_CIA_ERR;
- *(vuip)CIA_IOC_CIA_ERR = stat0;
+ stat0 = *(vip)CIA_IOC_CIA_ERR;
+ *(vip)CIA_IOC_CIA_ERR = stat0;
mb();
- DBGC(("conf_read: CIA ERR was 0x%x\n", stat0));
/* If Type1 access, must set CIA CFG. */
if (type1) {
- cia_cfg = *(vuip)CIA_IOC_CFG;
- *(vuip)CIA_IOC_CFG = cia_cfg | 1;
+ cia_cfg = *(vip)CIA_IOC_CFG;
+ *(vip)CIA_IOC_CFG = (cia_cfg & ~3) | 1;
mb();
- DBGC(("conf_read: TYPE1 access\n"));
+ *(vip)CIA_IOC_CFG;
}
- mb();
draina();
mcheck_expected(0) = 1;
mcheck_taken(0) = 0;
mb();
/* Access configuration space. */
- value = *(vuip)addr;
+ value = *(vip)addr;
mb();
mb(); /* magic */
if (mcheck_taken(0)) {
mcheck_taken(0) = 0;
- value = 0xffffffffU;
+ value = 0xffffffff;
mb();
}
mcheck_expected(0) = 0;
@@ -175,13 +146,14 @@ conf_read(unsigned long addr, unsigned char type1)
/* If Type1 access, must reset IOC CFG so normal IO space ops work. */
if (type1) {
- *(vuip)CIA_IOC_CFG = cia_cfg & ~1;
+ *(vip)CIA_IOC_CFG = cia_cfg;
mb();
+ *(vip)CIA_IOC_CFG;
}
- DBGC(("conf_read(): finished\n"));
-
__restore_flags(flags);
+ DBGC(("done\n"));
+
return value;
}
@@ -189,31 +161,31 @@ static void
conf_write(unsigned long addr, unsigned int value, unsigned char type1)
{
unsigned long flags;
- unsigned int stat0;
- unsigned int cia_cfg = 0;
+ int stat0, cia_cfg = 0;
- __save_and_cli(flags); /* avoid getting hit by machine check */
+ DBGC(("conf_write(addr=0x%lx, type1=%d) ", addr, type1));
+ __save_and_cli(flags);
/* Reset status register to avoid losing errors. */
- stat0 = *(vuip)CIA_IOC_CIA_ERR;
- *(vuip)CIA_IOC_CIA_ERR = stat0;
+ stat0 = *(vip)CIA_IOC_CIA_ERR;
+ *(vip)CIA_IOC_CIA_ERR = stat0;
mb();
- DBGC(("conf_write: CIA ERR was 0x%x\n", stat0));
/* If Type1 access, must set CIA CFG. */
if (type1) {
- cia_cfg = *(vuip)CIA_IOC_CFG;
- *(vuip)CIA_IOC_CFG = cia_cfg | 1;
+ cia_cfg = *(vip)CIA_IOC_CFG;
+ *(vip)CIA_IOC_CFG = (cia_cfg & ~3) | 1;
mb();
- DBGC(("conf_write: TYPE1 access\n"));
+ *(vip)CIA_IOC_CFG;
}
draina();
mcheck_expected(0) = 1;
+ mcheck_taken(0) = 0;
mb();
/* Access configuration space. */
- *(vuip)addr = value;
+ *(vip)addr = value;
mb();
mb(); /* magic */
@@ -222,12 +194,13 @@ conf_write(unsigned long addr, unsigned int value, unsigned char type1)
/* If Type1 access, must reset IOC CFG so normal IO space ops work. */
if (type1) {
- *(vuip)CIA_IOC_CFG = cia_cfg & ~1;
+ *(vip)CIA_IOC_CFG = cia_cfg;
mb();
+ *(vip)CIA_IOC_CFG;
}
- DBGC(("conf_write(): finished\n"));
__restore_flags(flags);
+ DBGC(("done\n"));
}
static int
@@ -314,158 +287,442 @@ struct pci_ops cia_pci_ops =
write_dword: cia_write_config_dword
};
+/*
+ * CIA Pass 1 and PYXIS Pass 1 and 2 have a broken scatter-gather tlb.
+ * It cannot be invalidated. Rather than hard code the pass numbers,
+ * actually try the tbia to see if it works.
+ */
+
void
cia_pci_tbi(struct pci_controler *hose, dma_addr_t start, dma_addr_t end)
{
wmb();
*(vip)CIA_IOC_PCI_TBIA = 3; /* Flush all locked and unlocked. */
mb();
+ *(vip)CIA_IOC_PCI_TBIA;
+}
+
+/*
+ * Fixup attempt number 1.
+ *
+ * Write zeros directly into the tag registers.
+ */
+
+static void
+cia_pci_tbi_try1(struct pci_controler *hose,
+ dma_addr_t start, dma_addr_t end)
+{
+ wmb();
+ *(vip)CIA_IOC_TB_TAGn(0) = 0;
+ *(vip)CIA_IOC_TB_TAGn(1) = 0;
+ *(vip)CIA_IOC_TB_TAGn(2) = 0;
+ *(vip)CIA_IOC_TB_TAGn(3) = 0;
+ *(vip)CIA_IOC_TB_TAGn(4) = 0;
+ *(vip)CIA_IOC_TB_TAGn(5) = 0;
+ *(vip)CIA_IOC_TB_TAGn(6) = 0;
+ *(vip)CIA_IOC_TB_TAGn(7) = 0;
+ mb();
+ *(vip)CIA_IOC_TB_TAGn(0);
+}
+
+#if 0
+/*
+ * Fixup attempt number 2. This is the method NT and NetBSD use.
+ *
+ * Allocate mappings, and put the chip into DMA loopback mode to read a
+ * garbage page. This works by causing TLB misses, causing old entries to
+ * be purged to make room for the new entries coming in for the garbage page.
+ */
+
+#define CIA_BROKEN_TBI_TRY2_BASE 0xE0000000
+
+static void __init
+cia_enable_broken_tbi_try2(void)
+{
+ unsigned long *ppte, pte;
+ long i;
+
+ ppte = __alloc_bootmem(PAGE_SIZE, 32768, 0);
+ pte = (virt_to_phys(ppte) >> (PAGE_SHIFT - 1)) | 1;
+
+ for (i = 0; i < PAGE_SIZE / sizeof(unsigned long); ++i)
+ ppte[i] = pte;
+
+ *(vip)CIA_IOC_PCI_W3_BASE = CIA_BROKEN_TBI_TRY2_BASE | 3;
+ *(vip)CIA_IOC_PCI_W3_MASK = (PAGE_SIZE - 1) & 0xfff00000;
+ *(vip)CIA_IOC_PCI_T3_BASE = virt_to_phys(ppte) >> 2;
+}
+
+static void
+cia_pci_tbi_try2(struct pci_controler *hose,
+ dma_addr_t start, dma_addr_t end)
+{
+ unsigned long flags;
+ unsigned long bus_addr;
+ int ctrl;
+ long i;
+
+ __save_and_cli(flags);
+
+ /* Put the chip into PCI loopback mode. */
+ mb();
+ ctrl = *(vip)CIA_IOC_CIA_CTRL;
+ *(vip)CIA_IOC_CIA_CTRL = ctrl | CIA_CTRL_PCI_LOOP_EN;
+ mb();
+ *(vip)CIA_IOC_CIA_CTRL;
+ mb();
+
+ /* Read from PCI dense memory space at TBI_ADDR, skipping 32k on
+ each read. This forces SG TLB misses. NetBSD claims that the
+ TLB entries are not quite LRU, meaning that we need to read more
+ times than there are actual tags. The 2117x docs claim strict
+ round-robin. Oh well, we've come this far... */
+
+ bus_addr = cia_ioremap(CIA_BROKEN_TBI_TRY2_BASE);
+ for (i = 0; i < 12; ++i, bus_addr += 32768)
+ cia_readl(bus_addr);
+
+ /* Restore normal PCI operation. */
+ mb();
+ *(vip)CIA_IOC_CIA_CTRL = ctrl;
+ mb();
+ *(vip)CIA_IOC_CIA_CTRL;
+ mb();
+
+ __restore_flags(flags);
+}
+#endif
+
+static void __init
+verify_tb_operation(void)
+{
+ static int page[PAGE_SIZE/4]
+ __attribute__((aligned(PAGE_SIZE)))
+ __initlocaldata = { 0 };
+
+ struct pci_iommu_arena *arena = pci_isa_hose->sg_isa;
+ int ctrl, addr0, tag0, pte0, data0;
+ int temp;
+
+ /* Put the chip into PCI loopback mode. */
+ mb();
+ ctrl = *(vip)CIA_IOC_CIA_CTRL;
+ *(vip)CIA_IOC_CIA_CTRL = ctrl | CIA_CTRL_PCI_LOOP_EN;
+ mb();
+ *(vip)CIA_IOC_CIA_CTRL;
+ mb();
+
+ /* Write a valid entry directly into the TLB registers. */
+
+ addr0 = arena->dma_base;
+ tag0 = addr0 | 1;
+ pte0 = (virt_to_phys(page) >> (PAGE_SHIFT - 1)) | 1;
+
+ *(vip)CIA_IOC_TB_TAGn(0) = tag0;
+ *(vip)CIA_IOC_TB_TAGn(1) = 0;
+ *(vip)CIA_IOC_TB_TAGn(2) = 0;
+ *(vip)CIA_IOC_TB_TAGn(3) = 0;
+ *(vip)CIA_IOC_TB_TAGn(4) = 0;
+ *(vip)CIA_IOC_TB_TAGn(5) = 0;
+ *(vip)CIA_IOC_TB_TAGn(6) = 0;
+ *(vip)CIA_IOC_TB_TAGn(7) = 0;
+ *(vip)CIA_IOC_TBn_PAGEm(0,0) = pte0;
+ *(vip)CIA_IOC_TBn_PAGEm(0,1) = 0;
+ *(vip)CIA_IOC_TBn_PAGEm(0,2) = 0;
+ *(vip)CIA_IOC_TBn_PAGEm(0,3) = 0;
+ mb();
+
+ /* First, verify we can read back what we've written. If
+ this fails, we can't be sure of any of the other testing
+ we're going to do, so bail. */
+ /* ??? Actually, we could do the work with machine checks.
+ By passing this register update test, we pretty much
+ guarantee that cia_pci_tbi_try1 works. If this test
+ fails, cia_pci_tbi_try2 might still work. */
+
+ temp = *(vip)CIA_IOC_TB_TAGn(0);
+ if (temp != tag0) {
+ printk("pci: failed tb register update test "
+ "(tag0 %#x != %#x)\n", temp, tag0);
+ goto failed;
+ }
+ temp = *(vip)CIA_IOC_TB_TAGn(1);
+ if (temp != 0) {
+ printk("pci: failed tb register update test "
+ "(tag1 %#x != 0)\n", temp);
+ goto failed;
+ }
+ temp = *(vip)CIA_IOC_TBn_PAGEm(0,0);
+ if (temp != pte0) {
+ printk("pci: failed tb register update test "
+ "(pte0 %#x != %#x)\n", temp, pte0);
+ goto failed;
+ }
+ printk("pci: passed tb register update test\n");
+
+ /* Second, verify we can actually do I/O through this entry. */
+
+ data0 = 0xdeadbeef;
+ page[0] = data0;
+ mcheck_expected(0) = 1;
+ mcheck_taken(0) = 0;
+ mb();
+ temp = cia_readl(cia_ioremap(addr0));
+ mb();
+ mcheck_expected(0) = 0;
+ mb();
+ if (mcheck_taken(0)) {
+ printk("pci: failed sg loopback i/o read test (mcheck)\n");
+ goto failed;
+ }
+ if (temp != data0) {
+ printk("pci: failed sg loopback i/o read test "
+ "(%#x != %#x)\n", temp, data0);
+ goto failed;
+ }
+ printk("pci: passed sg loopback i/o read test\n");
+
+ /* Third, try to invalidate the TLB. */
+
+ cia_pci_tbi(arena->hose, 0, -1);
+ temp = *(vip)CIA_IOC_TB_TAGn(0);
+ if (temp & 1) {
+ cia_pci_tbi_try1(arena->hose, 0, -1);
+
+ temp = *(vip)CIA_IOC_TB_TAGn(0);
+ if (temp & 1) {
+ printk("pci: failed tbia test; "
+ "no usable workaround\n");
+ goto failed;
+ }
+
+ alpha_mv.mv_pci_tbi = cia_pci_tbi_try1;
+ printk("pci: failed tbia test; workaround 1 succeeded\n");
+ } else {
+ printk("pci: passed tbia test\n");
+ }
+
+ /* Fourth, verify the TLB snoops the EV5's caches when
+ doing a tlb fill. */
+
+ data0 = 0x5adda15e;
+ page[0] = data0;
+ arena->ptes[4] = pte0;
+ mcheck_expected(0) = 1;
+ mcheck_taken(0) = 0;
+ mb();
+ temp = cia_readl(cia_ioremap(addr0 + 4*PAGE_SIZE));
+ mb();
+ mcheck_expected(0) = 0;
+ mb();
+ if (mcheck_taken(0)) {
+ printk("pci: failed pte write cache snoop test (mcheck)\n");
+ goto failed;
+ }
+ if (temp != data0) {
+ printk("pci: failed pte write cache snoop test "
+ "(%#x != %#x)\n", temp, data0);
+ goto failed;
+ }
+ printk("pci: passed pte write cache snoop test\n");
+
+ /* Fifth, verify that a previously invalid PTE entry gets
+ filled from the page table. */
+
+ data0 = 0xabcdef123;
+ page[0] = data0;
+ arena->ptes[5] = pte0;
+ mcheck_expected(0) = 1;
+ mcheck_taken(0) = 0;
+ mb();
+ temp = cia_readl(cia_ioremap(addr0 + 5*PAGE_SIZE));
+ mb();
+ mcheck_expected(0) = 0;
+ mb();
+ if (mcheck_taken(0)) {
+ printk("pci: failed valid tag invalid pte reload test "
+ "(mcheck; workaround available)\n");
+ /* Work around this bug by aligning new allocations
+ on 4 page boundaries. */
+ arena->align_entry = 4;
+ } else if (temp != data0) {
+ printk("pci: failed valid tag invalid pte reload test "
+ "(%#x != %#x)\n", temp, data0);
+ goto failed;
+ } else {
+ printk("pci: passed valid tag invalid pte reload test\n");
+ }
+
+ /* Sixth, verify machine checks are working. Test invalid
+ pte under the same valid tag as we used above. */
+
+ mcheck_expected(0) = 1;
+ mcheck_taken(0) = 0;
+ mb();
+ temp = cia_readl(cia_ioremap(addr0 + 6*PAGE_SIZE));
+ mb();
+ mcheck_expected(0) = 0;
+ mb();
+ printk("pci: %s pci machine check test\n",
+ mcheck_taken(0) ? "passed" : "failed");
+
+ /* Clean up after the tests. */
+ arena->ptes[4] = 0;
+ arena->ptes[5] = 0;
+ alpha_mv.mv_pci_tbi(arena->hose, 0, -1);
+
+exit:
+ /* Restore normal PCI operation. */
+ mb();
+ *(vip)CIA_IOC_CIA_CTRL = ctrl;
+ mb();
+ *(vip)CIA_IOC_CIA_CTRL;
+ mb();
+ return;
+
+failed:
+ printk("pci: disabling sg translation window\n");
+ *(vip)CIA_IOC_PCI_W0_BASE = 0;
+ alpha_mv.mv_pci_tbi = NULL;
+ goto exit;
}
-void __init
-cia_init_arch(void)
+static void __init
+do_init_arch(int is_pyxis)
{
struct pci_controler *hose;
- struct resource *hae_mem;
- unsigned int temp;
-
-#if DEBUG_DUMP_REGS
- temp = *(vuip)CIA_IOC_CIA_REV; mb();
- printk("cia_init: CIA_REV was 0x%x\n", temp);
- temp = *(vuip)CIA_IOC_PCI_LAT; mb();
- printk("cia_init: CIA_PCI_LAT was 0x%x\n", temp);
- temp = *(vuip)CIA_IOC_CIA_CTRL; mb();
- printk("cia_init: CIA_CTRL was 0x%x\n", temp);
- temp = *(vuip)0xfffffc8740000140UL; mb();
- printk("cia_init: CIA_CTRL1 was 0x%x\n", temp);
- temp = *(vuip)CIA_IOC_HAE_MEM; mb();
- printk("cia_init: CIA_HAE_MEM was 0x%x\n", temp);
- temp = *(vuip)CIA_IOC_HAE_IO; mb();
- printk("cia_init: CIA_HAE_IO was 0x%x\n", temp);
- temp = *(vuip)CIA_IOC_CFG; mb();
- printk("cia_init: CIA_CFG was 0x%x\n", temp);
- temp = *(vuip)CIA_IOC_CACK_EN; mb();
- printk("cia_init: CIA_CACK_EN was 0x%x\n", temp);
- temp = *(vuip)CIA_IOC_CFG; mb();
- printk("cia_init: CIA_CFG was 0x%x\n", temp);
- temp = *(vuip)CIA_IOC_CIA_DIAG; mb();
- printk("cia_init: CIA_DIAG was 0x%x\n", temp);
- temp = *(vuip)CIA_IOC_DIAG_CHECK; mb();
- printk("cia_init: CIA_DIAG_CHECK was 0x%x\n", temp);
- temp = *(vuip)CIA_IOC_PERF_MONITOR; mb();
- printk("cia_init: CIA_PERF_MONITOR was 0x%x\n", temp);
- temp = *(vuip)CIA_IOC_PERF_CONTROL; mb();
- printk("cia_init: CIA_PERF_CONTROL was 0x%x\n", temp);
- temp = *(vuip)CIA_IOC_CIA_ERR; mb();
- printk("cia_init: CIA_ERR was 0x%x\n", temp);
- temp = *(vuip)CIA_IOC_CIA_STAT; mb();
- printk("cia_init: CIA_STAT was 0x%x\n", temp);
- temp = *(vuip)CIA_IOC_MCR; mb();
- printk("cia_init: CIA_MCR was 0x%x\n", temp);
- temp = *(vuip)CIA_IOC_CIA_CTRL; mb();
- printk("cia_init: CIA_CTRL was 0x%x\n", temp);
- temp = *(vuip)CIA_IOC_ERR_MASK; mb();
- printk("cia_init: CIA_ERR_MASK was 0x%x\n", temp);
- temp = *((vuip)CIA_IOC_PCI_W0_BASE); mb();
- printk("cia_init: W0_BASE was 0x%x\n", temp);
- temp = *((vuip)CIA_IOC_PCI_W1_BASE); mb();
- printk("cia_init: W1_BASE was 0x%x\n", temp);
- temp = *((vuip)CIA_IOC_PCI_W2_BASE); mb();
- printk("cia_init: W2_BASE was 0x%x\n", temp);
- temp = *((vuip)CIA_IOC_PCI_W3_BASE); mb();
- printk("cia_init: W3_BASE was 0x%x\n", temp);
-#endif /* DEBUG_DUMP_REGS */
+ int temp;
+ int cia_rev;
+
+ cia_rev = *(vip)CIA_IOC_CIA_REV & CIA_REV_MASK;
+ printk("pci: cia revision %d%s\n",
+ cia_rev, is_pyxis ? " (pyxis)" : "");
+
+ /* Set up error reporting. */
+ temp = *(vip)CIA_IOC_ERR_MASK;
+ temp &= ~(CIA_ERR_CPU_PE | CIA_ERR_MEM_NEM | CIA_ERR_PA_PTE_INV
+ | CIA_ERR_RCVD_MAS_ABT | CIA_ERR_RCVD_TAR_ABT);
+ *(vip)CIA_IOC_ERR_MASK = temp;
+
+ /* Clear all currently pending errors. */
+ *(vip)CIA_IOC_CIA_ERR = 0;
+
+ /* Turn on mchecks. */
+ temp = *(vip)CIA_IOC_CIA_CTRL;
+ temp |= CIA_CTRL_FILL_ERR_EN | CIA_CTRL_MCHK_ERR_EN;
+ *(vip)CIA_IOC_CIA_CTRL = temp;
+
+ /* Clear the CFG register, which gets used for PCI config space
+ accesses. That is the way we want to use it, and we do not
+ want to depend on what ARC or SRM might have left behind. */
+ *(vip)CIA_IOC_CFG = 0;
+
+ /* Zero the HAEs. */
+ *(vip)CIA_IOC_HAE_MEM = 0;
+ *(vip)CIA_IOC_HAE_IO = 0;
+
+ /* For PYXIS, we always use BWX bus and i/o accesses. To that end,
+ make sure they're enabled on the controler. */
+ if (is_pyxis) {
+ temp = *(vip)CIA_IOC_CIA_CNFG;
+ temp |= CIA_CNFG_IOA_BWEN;
+ *(vip)CIA_IOC_CIA_CNFG = temp;
+ }
+
+ /* Syncronize with all previous changes. */
+ mb();
+ *(vip)CIA_IOC_CIA_REV;
/*
* Create our single hose.
*/
pci_isa_hose = hose = alloc_pci_controler();
- hae_mem = alloc_resource();
-
hose->io_space = &ioport_resource;
- hose->mem_space = hae_mem;
+ hose->mem_space = &iomem_resource;
hose->config_space = CIA_CONF;
hose->index = 0;
- hae_mem->start = 0;
- hae_mem->end = CIA_MEM_R1_MASK;
- hae_mem->name = pci_hae0_name;
- hae_mem->flags = IORESOURCE_MEM;
+ if (! is_pyxis) {
+ struct resource *hae_mem = alloc_resource();
+ hose->mem_space = hae_mem;
+
+ hae_mem->start = 0;
+ hae_mem->end = CIA_MEM_R1_MASK;
+ hae_mem->name = pci_hae0_name;
+ hae_mem->flags = IORESOURCE_MEM;
- if (request_resource(&iomem_resource, hae_mem) < 0)
- printk(KERN_ERR "Failed to request HAE_MEM\n");
+ if (request_resource(&iomem_resource, hae_mem) < 0)
+ printk(KERN_ERR "Failed to request HAE_MEM\n");
+ }
/*
* Set up the PCI to main memory translation windows.
*
* Window 0 is scatter-gather 8MB at 8MB (for isa)
- * Window 1 is scatter-gather 128MB at 1GB
- * Window 2 is direct access 2GB at 2GB
- * ??? We ought to scale window 1 with memory.
+ * Window 1 is direct access 1GB at 1GB
+ * Window 2 is direct access 1GB at 2GB
+ *
+ * We must actually use 2 windows to direct-map the 2GB space,
+ * because of an idiot-syncrasy of the CYPRESS chip used on
+ * many PYXIS systems. It may respond to a PCI bus address in
+ * the last 1MB of the 4GB address range.
+ *
+ * ??? NetBSD hints that page tables must be aligned to 32K,
+ * possibly due to a hardware bug. This is over-aligned
+ * from the 8K alignment one would expect for an 8MB window.
+ * No description of what revisions affected.
*/
- /* ??? NetBSD hints that page tables must be aligned to 32K,
- possibly due to a hardware bug. This is over-aligned
- from the 8K alignment one would expect for an 8MB window.
- No description of what CIA revisions affected. */
- hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 0x8000);
- hose->sg_pci = iommu_arena_new(hose, 0x40000000, 0x08000000, 0);
- __direct_map_base = 0x80000000;
+ hose->sg_pci = NULL;
+ hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 32768);
+ __direct_map_base = 0x40000000;
__direct_map_size = 0x80000000;
- *(vuip)CIA_IOC_PCI_W0_BASE = hose->sg_isa->dma_base | 3;
- *(vuip)CIA_IOC_PCI_W0_MASK = (hose->sg_isa->size - 1) & 0xfff00000;
- *(vuip)CIA_IOC_PCI_T0_BASE = virt_to_phys(hose->sg_isa->ptes) >> 2;
-
- *(vuip)CIA_IOC_PCI_W1_BASE = hose->sg_pci->dma_base | 3;
- *(vuip)CIA_IOC_PCI_W1_MASK = (hose->sg_pci->size - 1) & 0xfff00000;
- *(vuip)CIA_IOC_PCI_T1_BASE = virt_to_phys(hose->sg_pci->ptes) >> 2;
+ *(vip)CIA_IOC_PCI_W0_BASE = hose->sg_isa->dma_base | 3;
+ *(vip)CIA_IOC_PCI_W0_MASK = (hose->sg_isa->size - 1) & 0xfff00000;
+ *(vip)CIA_IOC_PCI_T0_BASE = virt_to_phys(hose->sg_isa->ptes) >> 2;
- *(vuip)CIA_IOC_PCI_W2_BASE = __direct_map_base | 1;
- *(vuip)CIA_IOC_PCI_W2_MASK = (__direct_map_size - 1) & 0xfff00000;
- *(vuip)CIA_IOC_PCI_T2_BASE = 0;
+ *(vip)CIA_IOC_PCI_W1_BASE = 0x40000000 | 1;
+ *(vip)CIA_IOC_PCI_W1_MASK = (0x40000000 - 1) & 0xfff00000;
+ *(vip)CIA_IOC_PCI_T1_BASE = 0;
- *(vuip)CIA_IOC_PCI_W3_BASE = 0;
+ *(vip)CIA_IOC_PCI_W2_BASE = 0x80000000 | 1;
+ *(vip)CIA_IOC_PCI_W2_MASK = (0x40000000 - 1) & 0xfff00000;
+ *(vip)CIA_IOC_PCI_T2_BASE = 0x40000000;
- cia_pci_tbi(hose, 0, -1);
+ *(vip)CIA_IOC_PCI_W3_BASE = 0;
+}
- /*
- * Set up error reporting.
- */
- temp = *(vuip)CIA_IOC_CIA_ERR;
- temp |= 0x180; /* master, target abort */
- *(vuip)CIA_IOC_CIA_ERR = temp;
+void __init
+cia_init_arch(void)
+{
+ do_init_arch(0);
+}
- temp = *(vuip)CIA_IOC_CIA_CTRL;
- temp |= 0x400; /* turn on FILL_ERR to get mchecks */
- *(vuip)CIA_IOC_CIA_CTRL = temp;
+void __init
+pyxis_init_arch(void)
+{
+ do_init_arch(1);
+}
- /*
- * Next, clear the CIA_CFG register, which gets used
- * for PCI Config Space accesses. That is the way
- * we want to use it, and we do not want to depend on
- * what ARC or SRM might have left behind...
- */
- *(vuip)CIA_IOC_CFG = 0;
-
- /*
- * Zero the HAEs.
- */
- *(vuip)CIA_IOC_HAE_MEM = 0;
- *(vuip)CIA_IOC_HAE_IO = 0;
- mb();
+void __init
+cia_init_pci(void)
+{
+ /* Must delay this from init_arch, as we need machine checks. */
+ verify_tb_operation();
+ common_init_pci();
}
static inline void
cia_pci_clr_err(void)
{
- unsigned int jd;
+ int jd;
- jd = *(vuip)CIA_IOC_CIA_ERR;
- *(vuip)CIA_IOC_CIA_ERR = jd;
+ jd = *(vip)CIA_IOC_CIA_ERR;
+ *(vip)CIA_IOC_CIA_ERR = jd;
mb();
- *(vuip)CIA_IOC_CIA_ERR; /* re-read to force write. */
+ *(vip)CIA_IOC_CIA_ERR; /* re-read to force write. */
}
void
diff --git a/arch/alpha/kernel/core_pyxis.c b/arch/alpha/kernel/core_pyxis.c
deleted file mode 100644
index f6106c475..000000000
--- a/arch/alpha/kernel/core_pyxis.c
+++ /dev/null
@@ -1,644 +0,0 @@
-/*
- * linux/arch/alpha/kernel/core_pyxis.c
- *
- * Based on code written by David A Rusling (david.rusling@reo.mts.dec.com).
- *
- * Code common to all PYXIS core logic chips.
- */
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-
-#define __EXTERN_INLINE inline
-#include <asm/io.h>
-#include <asm/core_pyxis.h>
-#undef __EXTERN_INLINE
-
-#include <linux/pci.h>
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/bootmem.h>
-
-#include <asm/ptrace.h>
-#include <asm/system.h>
-
-#include "proto.h"
-#include "irq_impl.h"
-#include "pci_impl.h"
-
-
-/* NOTE: Herein are back-to-back mb instructions. They are magic.
- One plausible explanation is that the I/O controller does not properly
- handle the system transaction. Another involves timing. Ho hum. */
-
-/*
- * BIOS32-style PCI interface:
- */
-
-#define DEBUG_CONFIG 0
-#if DEBUG_CONFIG
-# define DBG_CNF(args) printk args
-#else
-# define DBG_CNF(args)
-#endif
-
-
-/*
- * Given a bus, device, and function number, compute resulting
- * configuration space address and setup the PYXIS_HAXR2 register
- * accordingly. It is therefore not safe to have concurrent
- * invocations to configuration space access routines, but there
- * really shouldn't be any need for this.
- *
- * Type 0:
- *
- * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1
- * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | | |D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|0|
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *
- * 31:11 Device select bit.
- * 10:8 Function number
- * 7:2 Register number
- *
- * Type 1:
- *
- * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1
- * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1|
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *
- * 31:24 reserved
- * 23:16 bus number (8 bits = 128 possible buses)
- * 15:11 Device number (5 bits)
- * 10:8 function number
- * 7:2 register number
- *
- * Notes:
- * The function number selects which function of a multi-function device
- * (e.g., SCSI and Ethernet).
- *
- * The register selects a DWORD (32 bit) register offset. Hence it
- * doesn't get shifted by 2 bits as we want to "drop" the bottom two
- * bits.
- */
-
-static int
-mk_conf_addr(struct pci_dev *dev, int where, unsigned long *pci_addr,
- unsigned char *type1)
-{
- u8 bus = dev->bus->number;
- u8 device_fn = dev->devfn;
-
- *type1 = (bus == 0) ? 0 : 1;
- *pci_addr = (bus << 16) | (device_fn << 8) | (where);
-
- DBG_CNF(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x,"
- " returning address 0x%p\n"
- bus, device_fn, where, *pci_addr));
-
- return 0;
-}
-
-static unsigned int
-conf_read(unsigned long addr, unsigned char type1)
-{
- unsigned long flags;
- unsigned int stat0, value, temp;
- unsigned int pyxis_cfg = 0;
-
- __save_and_cli(flags); /* avoid getting hit by machine check */
-
- /* Reset status register to avoid losing errors. */
- stat0 = *(vuip)PYXIS_ERR;
- *(vuip)PYXIS_ERR = stat0; mb();
- temp = *(vuip)PYXIS_ERR; /* re-read to force write */
-
- /* If Type1 access, must set PYXIS CFG. */
- if (type1) {
- pyxis_cfg = *(vuip)PYXIS_CFG;
- *(vuip)PYXIS_CFG = (pyxis_cfg & ~3L) | 1; mb();
- temp = *(vuip)PYXIS_CFG; /* re-read to force write */
- }
-
- mb();
- draina();
- mcheck_expected(0) = 1;
- mcheck_taken(0) = 0;
- mb();
-
- /* Access configuration space. */
- value = *(vuip)addr;
- mb();
- mb(); /* magic */
-
- if (mcheck_taken(0)) {
- mcheck_taken(0) = 0;
- value = 0xffffffffU;
- mb();
- }
- mcheck_expected(0) = 0;
- mb();
-
- /* If Type1 access, must reset IOC CFG so normal IO space ops work. */
- if (type1) {
- *(vuip)PYXIS_CFG = pyxis_cfg & ~3L; mb();
- temp = *(vuip)PYXIS_CFG; /* re-read to force write */
- }
-
- __restore_flags(flags);
-
- DBG_CNF(("conf_read(addr=0x%lx, type1=%d) = %#x\n",
- addr, type1, value));
-
- return value;
-}
-
-static void
-conf_write(unsigned long addr, unsigned int value, unsigned char type1)
-{
- unsigned long flags;
- unsigned int stat0, temp;
- unsigned int pyxis_cfg = 0;
-
- __save_and_cli(flags); /* avoid getting hit by machine check */
-
- /* Reset status register to avoid losing errors. */
- stat0 = *(vuip)PYXIS_ERR;
- *(vuip)PYXIS_ERR = stat0; mb();
- temp = *(vuip)PYXIS_ERR; /* re-read to force write */
-
- /* If Type1 access, must set PYXIS CFG. */
- if (type1) {
- pyxis_cfg = *(vuip)PYXIS_CFG;
- *(vuip)PYXIS_CFG = (pyxis_cfg & ~3L) | 1; mb();
- temp = *(vuip)PYXIS_CFG; /* re-read to force write */
- }
-
- mb();
- draina();
- mcheck_expected(0) = 1;
- mcheck_taken(0) = 0;
- mb();
-
- /* Access configuration space. */
- *(vuip)addr = value;
- mb();
- temp = *(vuip)addr; /* read back to force the write */
- mcheck_expected(0) = 0;
- mb();
-
- /* If Type1 access, must reset IOC CFG so normal IO space ops work. */
- if (type1) {
- *(vuip)PYXIS_CFG = pyxis_cfg & ~3L; mb();
- temp = *(vuip)PYXIS_CFG; /* re-read to force write */
- }
-
- __restore_flags(flags);
-
- DBG_CNF(("conf_write(addr=%#lx, value=%#x, type1=%d)\n",
- addr, value, type1));
-}
-
-static int
-pyxis_read_config_byte(struct pci_dev *dev, int where, u8 *value)
-{
- unsigned long addr, pci_addr;
- unsigned char type1;
-
- if (mk_conf_addr(dev, where, &pci_addr, &type1))
- return PCIBIOS_DEVICE_NOT_FOUND;
-
- addr = (pci_addr << 5) + 0x00 + PYXIS_CONF;
- *value = conf_read(addr, type1) >> ((where & 3) * 8);
- return PCIBIOS_SUCCESSFUL;
-}
-
-static int
-pyxis_read_config_word(struct pci_dev *dev, int where, u16 *value)
-{
- unsigned long addr, pci_addr;
- unsigned char type1;
-
- if (mk_conf_addr(dev, where, &pci_addr, &type1))
- return PCIBIOS_DEVICE_NOT_FOUND;
-
- addr = (pci_addr << 5) + 0x08 + PYXIS_CONF;
- *value = conf_read(addr, type1) >> ((where & 3) * 8);
- return PCIBIOS_SUCCESSFUL;
-}
-
-static int
-pyxis_read_config_dword(struct pci_dev *dev, int where, u32 *value)
-{
- unsigned long addr, pci_addr;
- unsigned char type1;
-
- if (mk_conf_addr(dev, where, &pci_addr, &type1))
- return PCIBIOS_DEVICE_NOT_FOUND;
-
- addr = (pci_addr << 5) + 0x18 + PYXIS_CONF;
- *value = conf_read(addr, type1);
- return PCIBIOS_SUCCESSFUL;
-}
-
-static int
-pyxis_write_config(struct pci_dev *dev, int where, u32 value, long mask)
-{
- unsigned long addr, pci_addr;
- unsigned char type1;
-
- if (mk_conf_addr(dev, where, &pci_addr, &type1))
- return PCIBIOS_DEVICE_NOT_FOUND;
-
- addr = (pci_addr << 5) + mask + PYXIS_CONF;
- conf_write(addr, value << ((where & 3) * 8), type1);
- return PCIBIOS_SUCCESSFUL;
-}
-
-static int
-pyxis_write_config_byte(struct pci_dev *dev, int where, u8 value)
-{
- return pyxis_write_config(dev, where, value, 0x00);
-}
-
-static int
-pyxis_write_config_word(struct pci_dev *dev, int where, u16 value)
-{
- return pyxis_write_config(dev, where, value, 0x08);
-}
-
-static int
-pyxis_write_config_dword(struct pci_dev *dev, int where, u32 value)
-{
- return pyxis_write_config(dev, where, value, 0x18);
-}
-
-struct pci_ops pyxis_pci_ops =
-{
- read_byte: pyxis_read_config_byte,
- read_word: pyxis_read_config_word,
- read_dword: pyxis_read_config_dword,
- write_byte: pyxis_write_config_byte,
- write_word: pyxis_write_config_word,
- write_dword: pyxis_write_config_dword
-};
-
-/* Note mask bit is true for ENABLED irqs. */
-static unsigned long cached_irq_mask;
-
-static inline void
-pyxis_update_irq_hw(unsigned long mask)
-{
- *(vulp)PYXIS_INT_MASK = mask;
- mb();
- *(vulp)PYXIS_INT_MASK;
-}
-
-static inline void
-pyxis_enable_irq(unsigned int irq)
-{
- pyxis_update_irq_hw(cached_irq_mask |= 1UL << (irq - 16));
-}
-
-static void
-pyxis_disable_irq(unsigned int irq)
-{
- pyxis_update_irq_hw(cached_irq_mask &= ~(1UL << (irq - 16)));
-}
-
-static unsigned int
-pyxis_startup_irq(unsigned int irq)
-{
- pyxis_enable_irq(irq);
- return 0;
-}
-
-static void
-pyxis_end_irq(unsigned int irq)
-{
- if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
- pyxis_enable_irq(irq);
-}
-
-static void
-pyxis_mask_and_ack_irq(unsigned int irq)
-{
- unsigned long bit = 1UL << (irq - 16);
- unsigned long mask = cached_irq_mask &= ~bit;
-
- /* Disable the interrupt. */
- *(vulp)PYXIS_INT_MASK = mask;
- wmb();
- /* Ack PYXIS PCI interrupt. */
- *(vulp)PYXIS_INT_REQ = bit;
- mb();
- /* Re-read to force both writes. */
- *(vulp)PYXIS_INT_MASK;
-}
-
-static struct hw_interrupt_type pyxis_irq_type = {
- typename: "PYXIS",
- startup: pyxis_startup_irq,
- shutdown: pyxis_disable_irq,
- enable: pyxis_enable_irq,
- disable: pyxis_disable_irq,
- ack: pyxis_mask_and_ack_irq,
- end: pyxis_end_irq,
-};
-
-void
-pyxis_device_interrupt(unsigned long vector, struct pt_regs *regs)
-{
- unsigned long pld;
- unsigned int i;
-
- /* Read the interrupt summary register of PYXIS */
- pld = *(vulp)PYXIS_INT_REQ;
- pld &= cached_irq_mask;
-
- /*
- * Now for every possible bit set, work through them and call
- * the appropriate interrupt handler.
- */
- while (pld) {
- i = ffz(~pld);
- pld &= pld - 1; /* clear least bit set */
- if (i == 7)
- isa_device_interrupt(vector, regs);
- else
- handle_irq(16+i, regs);
- }
-}
-
-void __init
-init_pyxis_irqs(unsigned long ignore_mask)
-{
- long i;
-
- *(vulp)PYXIS_INT_MASK = 0; /* disable all */
- *(vulp)PYXIS_INT_REQ = -1; /* flush all */
- mb();
-
- /* Send -INTA pulses to clear any pending interrupts ...*/
- *(vuip) PYXIS_IACK_SC;
-
- for (i = 16; i < 48; ++i) {
- if ((ignore_mask >> i) & 1)
- continue;
- irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL;
- irq_desc[i].handler = &pyxis_irq_type;
- }
-
- setup_irq(16+7, &isa_cascade_irqaction);
-}
-
-void
-pyxis_pci_tbi(struct pci_controler *hose, dma_addr_t start, dma_addr_t end)
-{
- wmb();
- *(vip)PYXIS_TBIA = 3; /* Flush all locked and unlocked. */
- mb();
-}
-
-/*
- * Pass 1 and 2 have a broken scatter-gather tlb -- it cannot be invalidated.
- * To work around this problem, we allocate mappings, and put the chip into
- * DMA loopback mode to read a garbage page. This works by causing TLB
- * misses, causing old entries to be purged to make room for the new entries
- * coming in for the garbage page.
- *
- * Thanks to NetBSD sources for pointing out this bug. What a pain.
- */
-
-static unsigned long broken_tbi_addr;
-
-#define BROKEN_TBI_READS 12
-
-static void
-pyxis_broken_pci_tbi(struct pci_controler *hose,
- dma_addr_t start, dma_addr_t end)
-{
- unsigned long flags;
- unsigned long bus_addr;
- unsigned int ctrl;
- long i;
-
- __save_and_cli(flags);
-
- /* Put the chip into PCI loopback mode. */
- mb();
- ctrl = *(vuip)PYXIS_CTRL;
- *(vuip)PYXIS_CTRL = ctrl | 4;
- mb();
- *(vuip)PYXIS_CTRL;
- mb();
-
- /* Read from PCI dense memory space at TBI_ADDR, skipping 64k
- on each read. This forces SG TLB misses. It appears that
- the TLB entries are "not quite LRU", meaning that we need
- to read more times than there are actual tags. */
-
- bus_addr = broken_tbi_addr;
- for (i = 0; i < BROKEN_TBI_READS; ++i, bus_addr += 64*1024)
- pyxis_readl(bus_addr);
-
- /* Restore normal PCI operation. */
- mb();
- *(vuip)PYXIS_CTRL = ctrl;
- mb();
- *(vuip)PYXIS_CTRL;
- mb();
-
- __restore_flags(flags);
-}
-
-static void __init
-pyxis_enable_broken_tbi(struct pci_iommu_arena *arena)
-{
- void *page;
- unsigned long *ppte, ofs, pte;
- long i, npages;
-
- page = alloc_bootmem_pages(PAGE_SIZE);
- pte = (virt_to_phys(page) >> (PAGE_SHIFT - 1)) | 1;
- npages = (BROKEN_TBI_READS + 1) * 64*1024 / PAGE_SIZE;
-
- ofs = iommu_arena_alloc(arena, npages);
- ppte = arena->ptes + ofs;
- for (i = 0; i < npages; ++i)
- ppte[i] = pte;
-
- broken_tbi_addr = pyxis_ioremap(arena->dma_base + ofs*PAGE_SIZE);
- alpha_mv.mv_pci_tbi = pyxis_broken_pci_tbi;
-
- printk("PYXIS: Enabling broken tbia workaround.\n");
-}
-
-void __init
-pyxis_init_arch(void)
-{
- struct pci_controler *hose;
- unsigned int temp;
-
- /* Set up error reporting. Make sure CPU_PE is OFF in the mask. */
- temp = *(vuip)PYXIS_ERR_MASK;
- *(vuip)PYXIS_ERR_MASK = temp & ~4;
-
- /* Enable master/target abort. */
- temp = *(vuip)PYXIS_ERR;
- *(vuip)PYXIS_ERR = temp | 0x180;
-
- /* Clear the PYXIS_CFG register, which gets used for PCI Config
- Space accesses. That is the way we want to use it, and we do
- not want to depend on what ARC or SRM might have left behind. */
- *(vuip)PYXIS_CFG = 0;
-
- /* Zero the HAEs. */
- *(vuip)PYXIS_HAE_MEM = 0;
- *(vuip)PYXIS_HAE_IO = 0;
-
- /* Finally, check that the PYXIS_CTRL1 has IOA_BEN set for
- enabling byte/word PCI bus space(s) access. */
- temp = *(vuip)PYXIS_CTRL1;
- *(vuip)PYXIS_CTRL1 = temp | 1;
-
- /* Syncronize with all previous changes. */
- mb();
- *(vuip)PYXIS_REV;
-
- /*
- * Create our single hose.
- */
-
- hose = alloc_pci_controler();
- hose->io_space = &ioport_resource;
- hose->mem_space = &iomem_resource;
- hose->config_space = PYXIS_CONF;
- hose->index = 0;
-
- /*
- * Set up the PCI to main memory translation windows.
- *
- * Window 0 is scatter-gather 8MB at 8MB (for isa)
- * Window 1 is scatter-gather 128MB at 3GB
- * Window 2 is direct access 1GB at 1GB
- * Window 3 is direct access 1GB at 2GB
- * ??? We ought to scale window 1 with memory.
- *
- * We must actually use 2 windows to direct-map the 2GB space,
- * because of an idiot-syncrasy of the CYPRESS chip. It may
- * respond to a PCI bus address in the last 1MB of the 4GB
- * address range.
- */
-
-#if 1
- /* ??? There's some bit of syncronization wrt writing new tlb
- entries that's missing. Sometimes it works, sometimes invalid
- tlb machine checks, sometimes hard lockup. And this just within
- the boot sequence.
-
- I've tried extra memory barriers, extra alignment, pyxis
- register reads, tlb flushes, and loopback tlb accesses.
-
- I guess the pyxis revision in the sx164 is just too buggy... */
-
- hose->sg_isa = hose->sg_pci = NULL;
- __direct_map_base = 0x40000000;
- __direct_map_size = 0x80000000;
-
- *(vuip)PYXIS_W0_BASE = 0x40000000 | 1;
- *(vuip)PYXIS_W0_MASK = (0x40000000 - 1) & 0xfff00000;
- *(vuip)PYXIS_T0_BASE = 0;
-
- *(vuip)PYXIS_W1_BASE = 0x80000000 | 1;
- *(vuip)PYXIS_W1_MASK = (0x40000000 - 1) & 0xfff00000;
- *(vuip)PYXIS_T1_BASE = 0;
-
- *(vuip)PYXIS_W2_BASE = 0;
- *(vuip)PYXIS_W3_BASE = 0;
-
- alpha_mv.mv_pci_tbi = NULL;
- mb();
-#else
- /* ??? NetBSD hints that page tables must be aligned to 32K,
- possibly due to a hardware bug. This is over-aligned
- from the 8K alignment one would expect for an 8MB window.
- No description of what CIA revisions affected. */
- hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000, 0x08000);
- hose->sg_pci = iommu_arena_new(hose, 0xc0000000, 0x08000000, 0x20000);
- __direct_map_base = 0x40000000;
- __direct_map_size = 0x80000000;
-
- *(vuip)PYXIS_W0_BASE = hose->sg_isa->dma_base | 3;
- *(vuip)PYXIS_W0_MASK = (hose->sg_isa->size - 1) & 0xfff00000;
- *(vuip)PYXIS_T0_BASE = virt_to_phys(hose->sg_isa->ptes) >> 2;
-
- *(vuip)PYXIS_W1_BASE = hose->sg_pci->dma_base | 3;
- *(vuip)PYXIS_W1_MASK = (hose->sg_pci->size - 1) & 0xfff00000;
- *(vuip)PYXIS_T1_BASE = virt_to_phys(hose->sg_pci->ptes) >> 2;
-
- *(vuip)PYXIS_W2_BASE = 0x40000000 | 1;
- *(vuip)PYXIS_W2_MASK = (0x40000000 - 1) & 0xfff00000;
- *(vuip)PYXIS_T2_BASE = 0;
-
- *(vuip)PYXIS_W3_BASE = 0x80000000 | 1;
- *(vuip)PYXIS_W3_MASK = (0x40000000 - 1) & 0xfff00000;
- *(vuip)PYXIS_T3_BASE = 0;
-
- /* Pass 1 and 2 (ie revision <= 1) have a broken TBIA. See the
- complete description next to pyxis_broken_pci_tbi for details. */
- if ((*(vuip)PYXIS_REV & 0xff) <= 1)
- pyxis_enable_broken_tbi(hose->sg_pci);
-
- alpha_mv.mv_pci_tbi(hose, 0, -1);
-#endif
-}
-
-static inline void
-pyxis_pci_clr_err(void)
-{
- unsigned int tmp;
-
- tmp = *(vuip)PYXIS_ERR;
- *(vuip)PYXIS_ERR = tmp;
- mb();
- *(vuip)PYXIS_ERR; /* re-read to force write */
-}
-
-void
-pyxis_machine_check(unsigned long vector, unsigned long la_ptr,
- struct pt_regs * regs)
-{
- int expected;
-
- /* Clear the error before reporting anything. */
- mb();
- mb(); /* magic */
- draina();
- pyxis_pci_clr_err();
- wrmces(0x7);
- mb();
-
- expected = mcheck_expected(0);
- if (!expected && vector == 0x660) {
- struct el_common *com;
- struct el_common_EV5_uncorrectable_mcheck *ev5;
- struct el_PYXIS_sysdata_mcheck *pyxis;
-
- com = (void *)la_ptr;
- ev5 = (void *)(la_ptr + com->proc_offset);
- pyxis = (void *)(la_ptr + com->sys_offset);
-
- if (com->code == 0x202) {
- printk(KERN_CRIT "PYXIS PCI machine check: err0=%08x "
- "err1=%08x err2=%08x\n",
- (int) pyxis->pci_err0, (int) pyxis->pci_err1,
- (int) pyxis->pci_err2);
- expected = 1;
- }
- }
- process_mcheck_info(vector, la_ptr, regs, "PYXIS", expected);
-}
diff --git a/arch/alpha/kernel/core_tsunami.c b/arch/alpha/kernel/core_tsunami.c
index 1fa5a8088..1ddcd36d1 100644
--- a/arch/alpha/kernel/core_tsunami.c
+++ b/arch/alpha/kernel/core_tsunami.c
@@ -363,7 +363,7 @@ tsunami_init_one_pchip(tsunami_pchip *pchip, int index)
pchip->wsba[3].csr = 0x80000000 | 1;
pchip->wsm[3].csr = (0x40000000 - 1) & 0xfff00000;
- pchip->tba[3].csr = 0;
+ pchip->tba[3].csr = 0x40000000;
tsunami_pci_tbi(hose, 0, -1);
}
diff --git a/arch/alpha/kernel/entry.S b/arch/alpha/kernel/entry.S
index 5f1a6f59a..0b9e1786d 100644
--- a/arch/alpha/kernel/entry.S
+++ b/arch/alpha/kernel/entry.S
@@ -8,7 +8,7 @@
#define SIGCHLD 20
-#define NR_SYSCALLS 374
+#define NR_SYSCALLS 376
/*
* These offsets must match with alpha_mv in <asm/machvec.h>.
@@ -991,7 +991,7 @@ sys_call_table:
.quad osf_shmat
.quad sys_shmctl /* 210 */
.quad sys_shmdt
- .quad osf_shmget
+ .quad sys_shmget
.quad alpha_ni_syscall
.quad alpha_ni_syscall
.quad alpha_ni_syscall /* 215 */
@@ -1154,4 +1154,5 @@ sys_call_table:
.quad sys_setresgid
.quad sys_getresgid
.quad sys_ni_syscall /* sys_dipc */
- .quad sys_shmget
+ .quad sys_pivot_root
+ .quad sys_mincore /* 375 */
diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c
index bc8ca101a..1f454cf48 100644
--- a/arch/alpha/kernel/irq.c
+++ b/arch/alpha/kernel/irq.c
@@ -219,15 +219,33 @@ setup_irq(unsigned int irq, struct irqaction * new)
}
spin_unlock_irqrestore(&desc->lock,flags);
- register_irq_proc(irq);
return 0;
}
static struct proc_dir_entry * root_irq_dir;
-static struct proc_dir_entry * irq_dir [NR_IRQS];
-static struct proc_dir_entry * smp_affinity_entry [NR_IRQS];
+static struct proc_dir_entry * irq_dir[NR_IRQS];
-static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL };
+#ifdef CONFIG_SMP
+static struct proc_dir_entry * smp_affinity_entry[NR_IRQS];
+static char irq_user_affinity[NR_IRQS];
+static unsigned long irq_affinity[NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL };
+
+static void
+select_smp_affinity(int irq)
+{
+ static int last_cpu;
+ int cpu = last_cpu + 1;
+
+ if (! irq_desc[irq].handler->set_affinity || irq_user_affinity[irq])
+ return;
+
+ while (((cpu_present_mask >> cpu) & 1) == 0)
+ cpu = (cpu < NR_CPUS ? cpu + 1 : 0);
+ last_cpu = cpu;
+
+ irq_affinity[irq] = 1UL << cpu;
+ irq_desc[irq].handler->set_affinity(irq, 1UL << cpu);
+}
#define HEX_DIGITS 16
@@ -290,18 +308,22 @@ irq_affinity_write_proc(struct file *file, const char *buffer,
err = parse_hex_value(buffer, count, &new_value);
-#if CONFIG_SMP
- /*
- * Do not allow disabling IRQs completely - it's a too easy
- * way to make the system unusable accidentally :-) At least
- * one online CPU still has to be targeted.
- */
- if (!(new_value & cpu_present_mask))
+ /* The special value 0 means release control of the
+ affinity to kernel. */
+ if (new_value == 0) {
+ irq_user_affinity[irq] = 0;
+ select_smp_affinity(irq);
+ }
+ /* Do not allow disabling IRQs completely - it's a too easy
+ way to make the system unusable accidentally :-) At least
+ one online CPU still has to be targeted. */
+ else if (!(new_value & cpu_present_mask))
return -EINVAL;
-#endif
-
- irq_affinity[irq] = new_value;
- irq_desc[irq].handler->set_affinity(irq, new_value);
+ else {
+ irq_affinity[irq] = new_value;
+ irq_user_affinity[irq] = 1;
+ irq_desc[irq].handler->set_affinity(irq, new_value);
+ }
return full_count;
}
@@ -313,7 +335,7 @@ prof_cpu_mask_read_proc(char *page, char **start, off_t off,
unsigned long *mask = (unsigned long *) data;
if (count < HEX_DIGITS+1)
return -EINVAL;
- return sprintf (page, "%08lx\n", *mask);
+ return sprintf (page, "%016lx\n", *mask);
}
static int
@@ -330,6 +352,7 @@ prof_cpu_mask_write_proc(struct file *file, const char *buffer,
*mask = new_value;
return full_count;
}
+#endif /* CONFIG_SMP */
#define MAX_NAMELEN 10
@@ -348,6 +371,7 @@ register_irq_proc (unsigned int irq)
/* create /proc/irq/1234 */
irq_dir[irq] = proc_mkdir(name, root_irq_dir);
+#ifdef CONFIG_SMP
/* create /proc/irq/1234/smp_affinity */
entry = create_proc_entry("smp_affinity", 0700, irq_dir[irq]);
@@ -357,6 +381,7 @@ register_irq_proc (unsigned int irq)
entry->write_proc = irq_affinity_write_proc;
smp_affinity_entry[irq] = entry;
+#endif
}
unsigned long prof_cpu_mask = ~0UL;
@@ -370,6 +395,7 @@ init_irq_proc (void)
/* create /proc/irq */
root_irq_dir = proc_mkdir("irq", 0);
+#ifdef CONFIG_SMP
/* create /proc/irq/prof_cpu_mask */
entry = create_proc_entry("prof_cpu_mask", 0700, root_irq_dir);
@@ -377,6 +403,7 @@ init_irq_proc (void)
entry->data = (void *)&prof_cpu_mask;
entry->read_proc = prof_cpu_mask_read_proc;
entry->write_proc = prof_cpu_mask_write_proc;
+#endif
/*
* Create entries for all existing IRQs.
@@ -426,6 +453,10 @@ request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
action->next = NULL;
action->dev_id = dev_id;
+#ifdef CONFIG_SMP
+ select_smp_affinity(irq);
+#endif
+
retval = setup_irq(irq, action);
if (retval)
kfree(action);
@@ -522,10 +553,10 @@ get_irq_list(char *buf)
*p++ = '\n';
}
#if CONFIG_SMP
- p += sprintf(p, "LOC: ");
+ p += sprintf(p, "IPI: ");
for (j = 0; j < smp_num_cpus; j++)
p += sprintf(p, "%10lu ",
- cpu_data[cpu_logical_map(j)].smp_local_irq_count);
+ cpu_data[cpu_logical_map(j)].ipi_count);
p += sprintf(p, "\n");
#endif
p += sprintf(p, "ERR: %10lu\n", irq_err_count);
diff --git a/arch/alpha/kernel/irq_alpha.c b/arch/alpha/kernel/irq_alpha.c
index 62ba2362f..774fcf8a6 100644
--- a/arch/alpha/kernel/irq_alpha.c
+++ b/arch/alpha/kernel/irq_alpha.c
@@ -6,6 +6,7 @@
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/irq.h>
+#include <linux/kernel_stat.h>
#include <asm/machvec.h>
#include <asm/dma.h>
@@ -61,14 +62,22 @@ do_entInt(unsigned long type, unsigned long vector, unsigned long la_ptr,
break;
case 1:
#ifdef CONFIG_SMP
- cpu_data[smp_processor_id()].smp_local_irq_count++;
+ {
+ long cpu;
smp_percpu_timer_interrupt(&regs);
- if (smp_processor_id() == boot_cpuid)
-#endif
+ cpu = smp_processor_id();
+ if (cpu != boot_cpuid) {
+ irq_attempt(cpu, RTC_IRQ)++;
+ kstat.irqs[cpu][RTC_IRQ]++;
+ } else {
handle_irq(RTC_IRQ, &regs);
+ }
+ }
+#else
+ handle_irq(RTC_IRQ, &regs);
+#endif
return;
case 2:
- irq_err_count++;
alpha_mv.machine_check(vector, la_ptr, &regs);
return;
case 3:
diff --git a/arch/alpha/kernel/irq_pyxis.c b/arch/alpha/kernel/irq_pyxis.c
new file mode 100644
index 000000000..65414a7f5
--- /dev/null
+++ b/arch/alpha/kernel/irq_pyxis.c
@@ -0,0 +1,127 @@
+/*
+ * linux/arch/alpha/kernel/irq_pyxis.c
+ *
+ * Based on code written by David A Rusling (david.rusling@reo.mts.dec.com).
+ *
+ * IRQ Code common to all PYXIS core logic chips.
+ */
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/irq.h>
+
+#include <asm/io.h>
+#include <asm/core_cia.h>
+
+#include "proto.h"
+#include "irq_impl.h"
+
+
+/* Note mask bit is true for ENABLED irqs. */
+static unsigned long cached_irq_mask;
+
+static inline void
+pyxis_update_irq_hw(unsigned long mask)
+{
+ *(vulp)PYXIS_INT_MASK = mask;
+ mb();
+ *(vulp)PYXIS_INT_MASK;
+}
+
+static inline void
+pyxis_enable_irq(unsigned int irq)
+{
+ pyxis_update_irq_hw(cached_irq_mask |= 1UL << (irq - 16));
+}
+
+static void
+pyxis_disable_irq(unsigned int irq)
+{
+ pyxis_update_irq_hw(cached_irq_mask &= ~(1UL << (irq - 16)));
+}
+
+static unsigned int
+pyxis_startup_irq(unsigned int irq)
+{
+ pyxis_enable_irq(irq);
+ return 0;
+}
+
+static void
+pyxis_end_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ pyxis_enable_irq(irq);
+}
+
+static void
+pyxis_mask_and_ack_irq(unsigned int irq)
+{
+ unsigned long bit = 1UL << (irq - 16);
+ unsigned long mask = cached_irq_mask &= ~bit;
+
+ /* Disable the interrupt. */
+ *(vulp)PYXIS_INT_MASK = mask;
+ wmb();
+ /* Ack PYXIS PCI interrupt. */
+ *(vulp)PYXIS_INT_REQ = bit;
+ mb();
+ /* Re-read to force both writes. */
+ *(vulp)PYXIS_INT_MASK;
+}
+
+static struct hw_interrupt_type pyxis_irq_type = {
+ typename: "PYXIS",
+ startup: pyxis_startup_irq,
+ shutdown: pyxis_disable_irq,
+ enable: pyxis_enable_irq,
+ disable: pyxis_disable_irq,
+ ack: pyxis_mask_and_ack_irq,
+ end: pyxis_end_irq,
+};
+
+void
+pyxis_device_interrupt(unsigned long vector, struct pt_regs *regs)
+{
+ unsigned long pld;
+ unsigned int i;
+
+ /* Read the interrupt summary register of PYXIS */
+ pld = *(vulp)PYXIS_INT_REQ;
+ pld &= cached_irq_mask;
+
+ /*
+ * Now for every possible bit set, work through them and call
+ * the appropriate interrupt handler.
+ */
+ while (pld) {
+ i = ffz(~pld);
+ pld &= pld - 1; /* clear least bit set */
+ if (i == 7)
+ isa_device_interrupt(vector, regs);
+ else
+ handle_irq(16+i, regs);
+ }
+}
+
+void __init
+init_pyxis_irqs(unsigned long ignore_mask)
+{
+ long i;
+
+ *(vulp)PYXIS_INT_MASK = 0; /* disable all */
+ *(vulp)PYXIS_INT_REQ = -1; /* flush all */
+ mb();
+
+ /* Send -INTA pulses to clear any pending interrupts ...*/
+ *(vuip) CIA_IACK_SC;
+
+ for (i = 16; i < 48; ++i) {
+ if ((ignore_mask >> i) & 1)
+ continue;
+ irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL;
+ irq_desc[i].handler = &pyxis_irq_type;
+ }
+
+ setup_irq(16+7, &isa_cascade_irqaction);
+}
diff --git a/arch/alpha/kernel/irq_srm.c b/arch/alpha/kernel/irq_srm.c
index 8b14a59fe..51806c996 100644
--- a/arch/alpha/kernel/irq_srm.c
+++ b/arch/alpha/kernel/irq_srm.c
@@ -6,9 +6,6 @@
#include <linux/sched.h>
#include <linux/irq.h>
-#include <asm/machvec.h>
-#include <asm/dma.h>
-
#include "proto.h"
#include "irq_impl.h"
diff --git a/arch/alpha/kernel/machvec_impl.h b/arch/alpha/kernel/machvec_impl.h
index cc55781cb..bc9c0d3de 100644
--- a/arch/alpha/kernel/machvec_impl.h
+++ b/arch/alpha/kernel/machvec_impl.h
@@ -77,27 +77,26 @@
mv_writew: CAT(low,_writew), \
mv_writel: CAT(low,_writel), \
mv_writeq: CAT(low,_writeq), \
- mv_ioremap: CAT(low,_ioremap), \
- mv_is_ioaddr: CAT(low,_is_ioaddr)
+ mv_ioremap: CAT(low,_ioremap) \
#define IO(UP,low) \
IO_LITE(UP,low), \
pci_ops: &CAT(low,_pci_ops)
-/* Any assembler that can generate a GENERIC kernel can generate BWX
- instructions. So always use them for PYXIS I/O. */
-
#define DO_APECS_IO IO(APECS,apecs)
#define DO_CIA_IO IO(CIA,cia)
#define DO_IRONGATE_IO IO(IRONGATE,irongate)
#define DO_LCA_IO IO(LCA,lca)
#define DO_MCPCIA_IO IO(MCPCIA,mcpcia)
#define DO_POLARIS_IO IO(POLARIS,polaris)
-#define DO_PYXIS_IO IO(PYXIS,pyxis)
#define DO_T2_IO IO(T2,t2)
#define DO_TSUNAMI_IO IO(TSUNAMI,tsunami)
+#define DO_PYXIS_IO IO_LITE(CIA,cia_bwx), \
+ pci_ops: &CAT(cia,_pci_ops)
+
#define BUS(which) \
+ mv_is_ioaddr: CAT(which,_is_ioaddr), \
mv_pci_tbi: CAT(which,_pci_tbi)
#define DO_APECS_BUS BUS(apecs)
@@ -105,7 +104,6 @@
#define DO_IRONGATE_BUS BUS(irongate)
#define DO_LCA_BUS BUS(lca)
#define DO_MCPCIA_BUS BUS(mcpcia)
-#define DO_PYXIS_BUS BUS(pyxis)
#define DO_POLARIS_BUS BUS(polaris)
#define DO_T2_BUS BUS(t2)
#define DO_TSUNAMI_BUS BUS(tsunami)
diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c
index 091edaf37..0395f87f1 100644
--- a/arch/alpha/kernel/osf_sys.c
+++ b/arch/alpha/kernel/osf_sys.c
@@ -189,15 +189,6 @@ asmlinkage int osf_getpriority(int which, int who, int a2, int a3, int a4,
return prio;
}
-
-/*
- * Heh. As documented by DEC..
- */
-asmlinkage unsigned long sys_madvise(void)
-{
- return 0;
-}
-
/*
* No need to acquire the kernel lock, we're local..
*/
@@ -1374,8 +1365,3 @@ asmlinkage int sys_old_adjtimex(struct timex32 *txc_p)
return ret;
}
-
-asmlinkage long osf_shmget (key_t key, int size, int flag)
-{
- return sys_shmget (key, size, flag);
-}
diff --git a/arch/alpha/kernel/pci_impl.h b/arch/alpha/kernel/pci_impl.h
index 3aee6ec34..f434b329d 100644
--- a/arch/alpha/kernel/pci_impl.h
+++ b/arch/alpha/kernel/pci_impl.h
@@ -138,6 +138,7 @@ struct pci_iommu_arena
dma_addr_t dma_base;
unsigned int size;
unsigned int next_entry;
+ unsigned int align_entry;
};
diff --git a/arch/alpha/kernel/pci_iommu.c b/arch/alpha/kernel/pci_iommu.c
index 9e7e13e9b..5a36aa578 100644
--- a/arch/alpha/kernel/pci_iommu.c
+++ b/arch/alpha/kernel/pci_iommu.c
@@ -67,6 +67,10 @@ iommu_arena_new(struct pci_controler *hose, dma_addr_t base,
arena->size = window_size;
arena->next_entry = 0;
+ /* Align allocations to a multiple of a page size. Not needed
+ unless there are chip bugs. */
+ arena->align_entry = 1;
+
return arena;
}
@@ -74,28 +78,36 @@ long
iommu_arena_alloc(struct pci_iommu_arena *arena, long n)
{
unsigned long flags;
- unsigned long *beg, *p, *end;
- long i;
+ unsigned long *ptes;
+ long i, p, nent, mask;
spin_lock_irqsave(&arena->lock, flags);
/* Search forward for the first sequence of N empty ptes. */
- beg = arena->ptes;
- end = beg + (arena->size >> PAGE_SHIFT);
- p = beg + arena->next_entry;
+ ptes = arena->ptes;
+ nent = arena->size >> PAGE_SHIFT;
+ mask = arena->align_entry - 1;
+ p = (arena->next_entry + mask) & ~mask;
i = 0;
- while (i < n && p < end)
- i = (*p++ == 0 ? i + 1 : 0);
+ while (i < n && p+i < nent) {
+ if (ptes[p+i])
+ p = (p + i + 1 + mask) & ~mask, i = 0;
+ else
+ i = i + 1;
+ }
if (i < n) {
/* Reached the end. Flush the TLB and restart the
search from the beginning. */
alpha_mv.mv_pci_tbi(arena->hose, 0, -1);
- p = beg;
- i = 0;
- while (i < n && p < end)
- i = (*p++ == 0 ? i + 1 : 0);
+ p = 0, i = 0;
+ while (i < n && p+i < nent) {
+ if (ptes[p+i])
+ p = (p + i + 1 + mask) & ~mask, i = 0;
+ else
+ i = i + 1;
+ }
if (i < n) {
spin_unlock_irqrestore(&arena->lock, flags);
@@ -107,13 +119,13 @@ iommu_arena_alloc(struct pci_iommu_arena *arena, long n)
bit zero is the valid bit, so write ~1 into everything.
The chip specific bits will fill this in with something
kosher when we return. */
- for (p = p - n, i = 0; i < n; ++i)
- p[i] = ~1UL;
+ for (i = 0; i < n; ++i)
+ ptes[p+i] = ~1UL;
- arena->next_entry = p - beg + n;
+ arena->next_entry = p + n;
spin_unlock_irqrestore(&arena->lock, flags);
- return p - beg;
+ return p;
}
static void
@@ -238,6 +250,12 @@ pci_unmap_single(struct pci_dev *pdev, dma_addr_t dma_addr, long size,
npages = calc_npages((dma_addr & ~PAGE_MASK) + size);
iommu_arena_free(arena, dma_ofs, npages);
+ /* If we're freeing ptes above the `next_entry' pointer, they
+ may have snuck back into the TLB since the last wrap flush.
+ We need to flush the TLB before reallocating these. */
+ if (dma_ofs >= arena->next_entry)
+ alpha_mv.mv_pci_tbi(hose, dma_addr, dma_addr + size - 1);
+
DBGA("pci_unmap_single: sg [%x,%lx] np %ld from %p\n",
dma_addr, size, npages, __builtin_return_address(0));
}
@@ -509,6 +527,7 @@ pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents,
struct pci_iommu_arena *arena;
struct scatterlist *end;
dma_addr_t max_dma;
+ dma_addr_t fbeg, fend;
if (direction == PCI_DMA_NONE)
BUG();
@@ -522,9 +541,11 @@ pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents,
if (!arena || arena->dma_base + arena->size > max_dma)
arena = hose->sg_isa;
+ fbeg = -1, fend = 0;
for (end = sg + nents; sg < end; ++sg) {
unsigned long addr, size;
long npages, ofs;
+ dma_addr_t tend;
addr = sg->dma_address;
size = sg->dma_length;
@@ -547,8 +568,18 @@ pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents,
npages = calc_npages((addr & ~PAGE_MASK) + size);
ofs = (addr - arena->dma_base) >> PAGE_SHIFT;
iommu_arena_free(arena, ofs, npages);
+
+ tend = addr + size - 1;
+ if (fbeg > addr) fbeg = addr;
+ if (fend < tend) fend = tend;
}
+ /* If we're freeing ptes above the `next_entry' pointer, they
+ may have snuck back into the TLB since the last wrap flush.
+ We need to flush the TLB before reallocating these. */
+ if ((fend - arena->dma_base) >> PAGE_SHIFT >= arena->next_entry)
+ alpha_mv.mv_pci_tbi(hose, fbeg, fend);
+
DBGA("pci_unmap_sg: %d entries\n", nents - (end - sg));
}
diff --git a/arch/alpha/kernel/proto.h b/arch/alpha/kernel/proto.h
index 8e7bbcfaf..f13edeebf 100644
--- a/arch/alpha/kernel/proto.h
+++ b/arch/alpha/kernel/proto.h
@@ -20,7 +20,9 @@ extern void apecs_pci_tbi(struct pci_controler *, dma_addr_t, dma_addr_t);
/* core_cia.c */
extern struct pci_ops cia_pci_ops;
+extern void cia_init_pci(void);
extern void cia_init_arch(void);
+extern void pyxis_init_arch(void);
extern void cia_machine_check(u64, u64, struct pt_regs *);
extern void cia_pci_tbi(struct pci_controler *, dma_addr_t, dma_addr_t);
@@ -52,12 +54,6 @@ extern void polaris_init_arch(void);
extern void polaris_machine_check(u64, u64, struct pt_regs *);
#define polaris_pci_tbi ((void *)0)
-/* core_pyxis.c */
-extern struct pci_ops pyxis_pci_ops;
-extern void pyxis_init_arch(void);
-extern void pyxis_machine_check(u64, u64, struct pt_regs *);
-extern void pyxis_pci_tbi(struct pci_controler *, dma_addr_t, dma_addr_t);
-
/* core_t2.c */
extern struct pci_ops t2_pci_ops;
extern void t2_init_arch(void);
diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c
index 615914535..49ddca2e4 100644
--- a/arch/alpha/kernel/setup.c
+++ b/arch/alpha/kernel/setup.c
@@ -846,6 +846,22 @@ platform_string(void)
}
}
+static int
+get_nr_processors(struct percpu_struct *cpubase, unsigned long num)
+{
+ struct percpu_struct *cpu;
+ int i, count = 0;
+
+ for (i = 0; i < num; i++) {
+ cpu = (struct percpu_struct *)
+ ((char *)cpubase + i*hwrpb->processor_size);
+ if ((cpu->flags & 0x1cc) == 0x1cc)
+ count++;
+ }
+ return count;
+}
+
+
/*
* BUFFER is PAGE_SIZE bytes long.
*/
@@ -865,7 +881,7 @@ int get_cpuinfo(char *buffer)
char *cpu_name;
char *systype_name;
char *sysvariation_name;
- int len;
+ int len, nr_processors;
cpu = (struct percpu_struct*)((char*)hwrpb + hwrpb->processor_offset);
cpu_index = (unsigned) (cpu->type - 1);
@@ -876,6 +892,8 @@ int get_cpuinfo(char *buffer)
get_sysnames(hwrpb->sys_type, hwrpb->sys_variation,
&systype_name, &sysvariation_name);
+ nr_processors = get_nr_processors(cpu, hwrpb->nr_processors);
+
len = sprintf(buffer,
"cpu\t\t\t: Alpha\n"
"cpu model\t\t: %s\n"
@@ -894,7 +912,8 @@ int get_cpuinfo(char *buffer)
"BogoMIPS\t\t: %lu.%02lu\n"
"kernel unaligned acc\t: %ld (pc=%lx,va=%lx)\n"
"user unaligned acc\t: %ld (pc=%lx,va=%lx)\n"
- "platform string\t\t: %s\n",
+ "platform string\t\t: %s\n"
+ "cpus detected\t\t: %d\n",
cpu_name, cpu->variation, cpu->revision,
(char*)cpu->serial_no,
systype_name, sysvariation_name, hwrpb->sys_revision,
@@ -909,7 +928,7 @@ int get_cpuinfo(char *buffer)
loops_per_sec / 500000, (loops_per_sec / 5000) % 100,
unaligned[0].count, unaligned[0].pc, unaligned[0].va,
unaligned[1].count, unaligned[1].pc, unaligned[1].va,
- platform_string());
+ platform_string(), nr_processors);
#ifdef __SMP__
len += smp_info(buffer+len);
diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c
index ab1fb9ab2..1ddaaf74d 100644
--- a/arch/alpha/kernel/smp.c
+++ b/arch/alpha/kernel/smp.c
@@ -1003,15 +1003,11 @@ flush_icache_page(struct vm_area_struct *vma, struct page *page)
int
smp_info(char *buffer)
{
- long i;
- unsigned long sum = 0;
- for (i = 0; i < NR_CPUS; i++)
- sum += cpu_data[i].ipi_count;
-
- return sprintf(buffer, "CPUs probed %d active %d map 0x%lx IPIs %ld\n",
- smp_num_probed, smp_num_cpus, cpu_present_mask, sum);
+ return sprintf(buffer,
+ "cpus active\t\t: %d\n"
+ "cpu active mask\t\t: %016lx\n",
+ smp_num_cpus, cpu_present_mask);
}
-
#if DEBUG_SPINLOCK
void
diff --git a/arch/alpha/kernel/sys_alcor.c b/arch/alpha/kernel/sys_alcor.c
index 867d762f8..656a0a8df 100644
--- a/arch/alpha/kernel/sys_alcor.c
+++ b/arch/alpha/kernel/sys_alcor.c
@@ -249,7 +249,7 @@ struct alpha_machine_vector alcor_mv __initmv = {
init_arch: cia_init_arch,
init_irq: alcor_init_irq,
init_rtc: common_init_rtc,
- init_pci: common_init_pci,
+ init_pci: cia_init_pci,
kill_arch: alcor_kill_arch,
pci_map_irq: alcor_map_irq,
pci_swizzle: common_swizzle,
@@ -279,7 +279,7 @@ struct alpha_machine_vector xlt_mv __initmv = {
init_arch: cia_init_arch,
init_irq: alcor_init_irq,
init_rtc: common_init_rtc,
- init_pci: common_init_pci,
+ init_pci: cia_init_pci,
kill_arch: alcor_kill_arch,
pci_map_irq: alcor_map_irq,
pci_swizzle: common_swizzle,
diff --git a/arch/alpha/kernel/sys_cabriolet.c b/arch/alpha/kernel/sys_cabriolet.c
index 8e016ada0..9b2b39d73 100644
--- a/arch/alpha/kernel/sys_cabriolet.c
+++ b/arch/alpha/kernel/sys_cabriolet.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 1995 David A Rusling
* Copyright (C) 1996 Jay A Estabrook
- * Copyright (C) 1998, 1999 Richard Henderson
+ * Copyright (C) 1998, 1999, 2000 Richard Henderson
*
* Code supporting the Cabriolet (AlphaPC64), EB66+, and EB164,
* PC164 and LX164.
@@ -28,7 +28,6 @@
#include <asm/core_apecs.h>
#include <asm/core_cia.h>
#include <asm/core_lca.h>
-#include <asm/core_pyxis.h>
#include "proto.h"
#include "irq_impl.h"
@@ -223,6 +222,12 @@ cabriolet_init_pci(void)
ns87312_enable_ide(0x398);
}
+static inline void __init
+cia_cab_init_pci(void)
+{
+ cia_init_pci();
+ ns87312_enable_ide(0x398);
+}
/*
* The PC164 and LX164 have 19 PCI interrupts, four from each of the four
@@ -286,7 +291,7 @@ alphapc164_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
static inline void __init
alphapc164_init_pci(void)
{
- common_init_pci();
+ cia_init_pci();
SMC93x_Init();
}
@@ -339,7 +344,7 @@ struct alpha_machine_vector eb164_mv __initmv = {
init_arch: cia_init_arch,
init_irq: cabriolet_init_irq,
init_rtc: common_init_rtc,
- init_pci: cabriolet_init_pci,
+ init_pci: cia_cab_init_pci,
pci_map_irq: cabriolet_map_irq,
pci_swizzle: common_swizzle,
};
@@ -377,8 +382,8 @@ struct alpha_machine_vector lx164_mv __initmv = {
DO_EV5_MMU,
DO_DEFAULT_RTC,
DO_PYXIS_IO,
- DO_PYXIS_BUS,
- machine_check: pyxis_machine_check,
+ DO_CIA_BUS,
+ machine_check: cia_machine_check,
max_dma_address: ALPHA_MAX_DMA_ADDRESS,
min_io_address: DEFAULT_IO_BASE,
min_mem_address: DEFAULT_MEM_BASE,
diff --git a/arch/alpha/kernel/sys_dp264.c b/arch/alpha/kernel/sys_dp264.c
index 63abc109f..c10018fff 100644
--- a/arch/alpha/kernel/sys_dp264.c
+++ b/arch/alpha/kernel/sys_dp264.c
@@ -36,32 +36,31 @@
/* Note mask bit is true for ENABLED irqs. */
static unsigned long cached_irq_mask;
/* dp264 boards handle at max four CPUs */
-static unsigned long cpu_irq_affinity[4];
+static unsigned long cpu_irq_affinity[4] = { ~0UL, ~0UL, ~0UL, ~0UL };
spinlock_t dp264_irq_lock = SPIN_LOCK_UNLOCKED;
static void
-tsunami_update_irq_hw(unsigned long mask, unsigned long isa_enable)
+tsunami_update_irq_hw(unsigned long mask)
{
register tsunami_cchip *cchip = TSUNAMI_cchip;
+ unsigned long isa_enable = 1UL << 55;
register int bcpu = boot_cpuid;
#ifdef CONFIG_SMP
register unsigned long cpm = cpu_present_mask;
volatile unsigned long *dim0, *dim1, *dim2, *dim3;
- unsigned long mask0, mask1, mask2, mask3, maskB, dummy;
+ unsigned long mask0, mask1, mask2, mask3, dummy;
- mask0 = mask1 = mask2 = mask3 = mask;
- maskB = mask | isa_enable;
- if (bcpu == 0) mask0 = maskB;
- else if (bcpu == 1) mask1 = maskB;
- else if (bcpu == 2) mask2 = maskB;
- else if (bcpu == 3) mask3 = maskB;
+ mask0 = mask & cpu_irq_affinity[0];
+ mask1 = mask & cpu_irq_affinity[1];
+ mask2 = mask & cpu_irq_affinity[2];
+ mask3 = mask & cpu_irq_affinity[3];
- mask0 &= cpu_irq_affinity[0];
- mask1 &= cpu_irq_affinity[1];
- mask2 &= cpu_irq_affinity[2];
- mask3 &= cpu_irq_affinity[3];
+ if (bcpu == 0) mask0 |= isa_enable;
+ else if (bcpu == 1) mask1 |= isa_enable;
+ else if (bcpu == 2) mask2 |= isa_enable;
+ else mask3 |= isa_enable;
dim0 = &cchip->dim0.csr;
dim1 = &cchip->dim1.csr;
@@ -86,7 +85,7 @@ tsunami_update_irq_hw(unsigned long mask, unsigned long isa_enable)
if (bcpu == 0) dimB = &cchip->dim0.csr;
else if (bcpu == 1) dimB = &cchip->dim1.csr;
else if (bcpu == 2) dimB = &cchip->dim2.csr;
- else if (bcpu == 3) dimB = &cchip->dim3.csr;
+ else dimB = &cchip->dim3.csr;
*dimB = mask | isa_enable;
mb();
@@ -94,24 +93,12 @@ tsunami_update_irq_hw(unsigned long mask, unsigned long isa_enable)
#endif
}
-static inline void
-dp264_update_irq_hw(unsigned long mask)
-{
- tsunami_update_irq_hw(mask, (1UL << 55) | 0xffff);
-}
-
-static inline void
-clipper_update_irq_hw(unsigned long mask)
-{
- tsunami_update_irq_hw(mask, 1UL << 55);
-}
-
static void
dp264_enable_irq(unsigned int irq)
{
spin_lock(&dp264_irq_lock);
cached_irq_mask |= 1UL << irq;
- dp264_update_irq_hw(cached_irq_mask);
+ tsunami_update_irq_hw(cached_irq_mask);
spin_unlock(&dp264_irq_lock);
}
@@ -120,7 +107,7 @@ dp264_disable_irq(unsigned int irq)
{
spin_lock(&dp264_irq_lock);
cached_irq_mask &= ~(1UL << irq);
- dp264_update_irq_hw(cached_irq_mask);
+ tsunami_update_irq_hw(cached_irq_mask);
spin_unlock(&dp264_irq_lock);
}
@@ -142,8 +129,8 @@ static void
clipper_enable_irq(unsigned int irq)
{
spin_lock(&dp264_irq_lock);
- cached_irq_mask |= 1UL << irq;
- clipper_update_irq_hw(cached_irq_mask);
+ cached_irq_mask |= 1UL << (irq - 16);
+ tsunami_update_irq_hw(cached_irq_mask);
spin_unlock(&dp264_irq_lock);
}
@@ -151,8 +138,8 @@ static void
clipper_disable_irq(unsigned int irq)
{
spin_lock(&dp264_irq_lock);
- cached_irq_mask &= ~(1UL << irq);
- clipper_update_irq_hw(cached_irq_mask);
+ cached_irq_mask &= ~(1UL << (irq - 16));
+ tsunami_update_irq_hw(cached_irq_mask);
spin_unlock(&dp264_irq_lock);
}
@@ -191,7 +178,7 @@ dp264_set_affinity(unsigned int irq, unsigned long affinity)
{
spin_lock(&dp264_irq_lock);
cpu_set_irq_affinity(irq, affinity);
- dp264_update_irq_hw(cached_irq_mask);
+ tsunami_update_irq_hw(cached_irq_mask);
spin_unlock(&dp264_irq_lock);
}
@@ -199,8 +186,8 @@ static void
clipper_set_affinity(unsigned int irq, unsigned long affinity)
{
spin_lock(&dp264_irq_lock);
- cpu_set_irq_affinity(irq, affinity);
- clipper_update_irq_hw(cached_irq_mask);
+ cpu_set_irq_affinity(irq - 16, affinity);
+ tsunami_update_irq_hw(cached_irq_mask);
spin_unlock(&dp264_irq_lock);
}
@@ -304,12 +291,10 @@ clipper_srm_device_interrupt(unsigned long vector, struct pt_regs * regs)
}
static void __init
-init_tsunami_irqs(struct hw_interrupt_type * ops)
+init_tsunami_irqs(struct hw_interrupt_type * ops, int imin, int imax)
{
long i;
-
- /* Only irqs between 16 and 47 are tsunami irqs. */
- for (i = 16; i < 48; ++i) {
+ for (i = imin; i <= imax; ++i) {
irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL;
irq_desc[i].handler = ops;
}
@@ -318,8 +303,6 @@ init_tsunami_irqs(struct hw_interrupt_type * ops)
static void __init
dp264_init_irq(void)
{
- int cpu;
-
outb(0, DMA1_RESET_REG);
outb(0, DMA2_RESET_REG);
outb(DMA_MODE_CASCADE, DMA2_MODE_REG);
@@ -328,13 +311,10 @@ dp264_init_irq(void)
if (alpha_using_srm)
alpha_mv.device_interrupt = dp264_srm_device_interrupt;
- /* this is single threaded by design so no need of any smp lock */
- for (cpu = 0; cpu < 4; cpu++)
- cpu_irq_affinity[cpu] = ~0UL;
- dp264_update_irq_hw(0UL);
+ tsunami_update_irq_hw(0);
init_i8259a_irqs();
- init_tsunami_irqs(&dp264_irq_type);
+ init_tsunami_irqs(&dp264_irq_type, 16, 47);
}
static void __init
@@ -348,10 +328,10 @@ clipper_init_irq(void)
if (alpha_using_srm)
alpha_mv.device_interrupt = clipper_srm_device_interrupt;
- clipper_update_irq_hw(0UL);
+ tsunami_update_irq_hw(0);
init_i8259a_irqs();
- init_tsunami_irqs(&clipper_irq_type);
+ init_tsunami_irqs(&clipper_irq_type, 24, 63);
}
diff --git a/arch/alpha/kernel/sys_eiger.c b/arch/alpha/kernel/sys_eiger.c
index 9e4fbe569..4545ff0e2 100644
--- a/arch/alpha/kernel/sys_eiger.c
+++ b/arch/alpha/kernel/sys_eiger.c
@@ -34,10 +34,6 @@
#include "machvec_impl.h"
-/*
- * HACK ALERT! only the boot cpu is used for interrupts.
- */
-
/* Note that this interrupt code is identical to TAKARA. */
/* Note mask bit is true for DISABLED irqs. */
diff --git a/arch/alpha/kernel/sys_miata.c b/arch/alpha/kernel/sys_miata.c
index 0191ef91b..f54de2c87 100644
--- a/arch/alpha/kernel/sys_miata.c
+++ b/arch/alpha/kernel/sys_miata.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 1995 David A Rusling
* Copyright (C) 1996 Jay A Estabrook
- * Copyright (C) 1998, 1999 Richard Henderson
+ * Copyright (C) 1998, 1999, 2000 Richard Henderson
*
* Code supporting the MIATA (EV56+PYXIS).
*/
@@ -22,7 +22,7 @@
#include <asm/mmu_context.h>
#include <asm/io.h>
#include <asm/pgtable.h>
-#include <asm/core_pyxis.h>
+#include <asm/core_cia.h>
#include "proto.h"
#include "irq_impl.h"
@@ -215,7 +215,7 @@ miata_swizzle(struct pci_dev *dev, u8 *pinp)
static void __init
miata_init_pci(void)
{
- common_init_pci();
+ cia_init_pci();
SMC669_Init(0); /* it might be a GL (fails harmlessly if not) */
es1888_init();
}
@@ -240,8 +240,8 @@ struct alpha_machine_vector miata_mv __initmv = {
DO_EV5_MMU,
DO_DEFAULT_RTC,
DO_PYXIS_IO,
- DO_PYXIS_BUS,
- machine_check: pyxis_machine_check,
+ DO_CIA_BUS,
+ machine_check: cia_machine_check,
max_dma_address: ALPHA_MAX_DMA_ADDRESS,
min_io_address: DEFAULT_IO_BASE,
min_mem_address: DEFAULT_MEM_BASE,
diff --git a/arch/alpha/kernel/sys_mikasa.c b/arch/alpha/kernel/sys_mikasa.c
index a66731154..f436b4902 100644
--- a/arch/alpha/kernel/sys_mikasa.c
+++ b/arch/alpha/kernel/sys_mikasa.c
@@ -258,7 +258,7 @@ struct alpha_machine_vector mikasa_primo_mv __initmv = {
init_arch: cia_init_arch,
init_irq: mikasa_init_irq,
init_rtc: common_init_rtc,
- init_pci: common_init_pci,
+ init_pci: cia_init_pci,
pci_map_irq: mikasa_map_irq,
pci_swizzle: common_swizzle,
};
diff --git a/arch/alpha/kernel/sys_noritake.c b/arch/alpha/kernel/sys_noritake.c
index 523d29853..8ef79db13 100644
--- a/arch/alpha/kernel/sys_noritake.c
+++ b/arch/alpha/kernel/sys_noritake.c
@@ -309,7 +309,7 @@ struct alpha_machine_vector noritake_primo_mv __initmv = {
init_arch: cia_init_arch,
init_irq: noritake_init_irq,
init_rtc: common_init_rtc,
- init_pci: common_init_pci,
+ init_pci: cia_init_pci,
pci_map_irq: noritake_map_irq,
pci_swizzle: noritake_swizzle,
};
diff --git a/arch/alpha/kernel/sys_ruffian.c b/arch/alpha/kernel/sys_ruffian.c
index 01d80ab97..23069e06d 100644
--- a/arch/alpha/kernel/sys_ruffian.c
+++ b/arch/alpha/kernel/sys_ruffian.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 1995 David A Rusling
* Copyright (C) 1996 Jay A Estabrook
- * Copyright (C) 1998, 1999 Richard Henderson
+ * Copyright (C) 1998, 1999, 2000 Richard Henderson
*
* Code supporting the RUFFIAN.
*/
@@ -23,7 +23,7 @@
#include <asm/mmu_context.h>
#include <asm/io.h>
#include <asm/pgtable.h>
-#include <asm/core_pyxis.h>
+#include <asm/core_cia.h>
#include "proto.h"
#include "irq_impl.h"
@@ -149,8 +149,8 @@ struct alpha_machine_vector ruffian_mv __initmv = {
DO_EV5_MMU,
DO_DEFAULT_RTC,
DO_PYXIS_IO,
- DO_PYXIS_BUS,
- machine_check: pyxis_machine_check,
+ DO_CIA_BUS,
+ machine_check: cia_machine_check,
max_dma_address: ALPHA_RUFFIAN_MAX_DMA_ADDRESS,
min_io_address: DEFAULT_IO_BASE,
min_mem_address: DEFAULT_MEM_BASE,
@@ -161,7 +161,7 @@ struct alpha_machine_vector ruffian_mv __initmv = {
init_arch: pyxis_init_arch,
init_irq: ruffian_init_irq,
init_rtc: ruffian_init_rtc,
- init_pci: common_init_pci,
+ init_pci: cia_init_pci,
kill_arch: ruffian_kill_arch,
pci_map_irq: ruffian_map_irq,
pci_swizzle: common_swizzle,
diff --git a/arch/alpha/kernel/sys_sx164.c b/arch/alpha/kernel/sys_sx164.c
index bad57d35e..b98e46028 100644
--- a/arch/alpha/kernel/sys_sx164.c
+++ b/arch/alpha/kernel/sys_sx164.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 1995 David A Rusling
* Copyright (C) 1996 Jay A Estabrook
- * Copyright (C) 1998, 1999 Richard Henderson
+ * Copyright (C) 1998, 1999, 2000 Richard Henderson
*
* Code supporting the SX164 (PCA56+PYXIS).
*/
@@ -23,7 +23,7 @@
#include <asm/mmu_context.h>
#include <asm/io.h>
#include <asm/pgtable.h>
-#include <asm/core_pyxis.h>
+#include <asm/core_cia.h>
#include "proto.h"
#include "irq_impl.h"
@@ -107,10 +107,10 @@ sx164_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
return COMMON_TABLE_LOOKUP;
}
-void __init
+static void __init
sx164_init_pci(void)
{
- common_init_pci();
+ cia_init_pci();
SMC669_Init(0);
}
@@ -124,8 +124,8 @@ struct alpha_machine_vector sx164_mv __initmv = {
DO_EV5_MMU,
DO_DEFAULT_RTC,
DO_PYXIS_IO,
- DO_PYXIS_BUS,
- machine_check: pyxis_machine_check,
+ DO_CIA_BUS,
+ machine_check: cia_machine_check,
max_dma_address: ALPHA_MAX_DMA_ADDRESS,
min_io_address: DEFAULT_IO_BASE,
min_mem_address: DEFAULT_MEM_BASE,
diff --git a/arch/alpha/kernel/sys_takara.c b/arch/alpha/kernel/sys_takara.c
index a0bc57125..9ebba3955 100644
--- a/arch/alpha/kernel/sys_takara.c
+++ b/arch/alpha/kernel/sys_takara.c
@@ -258,7 +258,7 @@ takara_init_pci(void)
if (alpha_using_srm)
alpha_mv.pci_map_irq = takara_map_irq_srm;
- common_init_pci();
+ cia_init_pci();
ns87312_enable_ide(0x26e);
}
diff --git a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c
index 291682e33..6119ea371 100644
--- a/arch/alpha/kernel/time.c
+++ b/arch/alpha/kernel/time.c
@@ -48,6 +48,7 @@ extern volatile unsigned long lost_ticks; /* kernel/sched.c */
static int set_rtc_mmss(unsigned long);
+spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
/*
* Shift amount by which scaled_ticks_per_cycle is scaled. Shifting
@@ -163,7 +164,7 @@ static inline unsigned long mktime(unsigned int year, unsigned int mon,
}
void
-common_init_rtc()
+common_init_rtc(void)
{
unsigned char x;
@@ -406,6 +407,8 @@ set_rtc_mmss(unsigned long nowtime)
int real_seconds, real_minutes, cmos_minutes;
unsigned char save_control, save_freq_select;
+ /* irq are locally disabled here */
+ spin_lock(&rtc_lock);
/* Tell the clock it's being set */
save_control = CMOS_READ(RTC_CONTROL);
CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
@@ -455,6 +458,7 @@ set_rtc_mmss(unsigned long nowtime)
*/
CMOS_WRITE(save_control, RTC_CONTROL);
CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
+ spin_unlock(&rtc_lock);
return retval;
}
diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c
index 828044b24..76504a1ef 100644
--- a/arch/alpha/kernel/traps.c
+++ b/arch/alpha/kernel/traps.c
@@ -215,10 +215,8 @@ do_entIF(unsigned long type, unsigned long a1,
/* EV4 does not implement anything except normal
rounding. Everything else will come here as
an illegal instruction. Emulate them. */
- if (alpha_fp_emul(regs.pc)) {
- regs.pc += 4;
+ if (alpha_fp_emul(regs.pc-4))
return;
- }
}
send_sig(SIGILL, current, 1);
break;
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index ad8e9423a..ff732b8fa 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -14,7 +14,6 @@
OBJCOPY := $(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S
CPP := $(CC) -E
-PERL := perl
LINKFLAGS := -p -X -T arch/arm/vmlinux.lds
ARCHCC := $(word 1,$(CC))
@@ -101,6 +100,8 @@ endif
LIBGCC := $(shell $(CC) $(CFLAGS) --print-libgcc-file-name)
+export LIBGCC
+
ifeq ($(CONFIG_ARCH_A5K),y)
MACHINE = a5k
ARCHDIR = arc
diff --git a/arch/arm/config.in b/arch/arm/config.in
index ff4f954df..2ff7f6414 100644
--- a/arch/arm/config.in
+++ b/arch/arm/config.in
@@ -35,9 +35,9 @@ fi
if [ "$CONFIG_HOST_FOOTBRIDGE" = "y" ]; then
bool ' Include support for EBSA285' CONFIG_ARCH_EBSA285
- bool ' Include support for CATS' CONFIG_CATS
+ bool ' Include support for CATS' CONFIG_ARCH_CATS
bool ' Include support for NetWinder' CONFIG_ARCH_NETWINDER
- bool ' Include support for Compaq Personal Server' CONFIG_PERSONAL_SERVER
+ bool ' Include support for Compaq Personal Server' CONFIG_ARCH_PERSONAL_SERVER
fi
if [ "$CONFIG_ADDIN_FOOTBRIDGE" = "y" ]; then
@@ -124,7 +124,7 @@ fi
#
# These machines have ISA-DMA
#
-if [ "$CONFIG_CATS" = "y" -o \
+if [ "$CONFIG_ARCH_CATS" = "y" -o \
"$CONFIG_ARCH_SHARK" = "y" -o \
"$CONFIG_ARCH_NETWINDER" = "y" ]; then
define_bool CONFIG_ISA_DMA y
@@ -171,8 +171,8 @@ source drivers/parport/Config.in
if [ "$CONFIG_ARCH_EBSA110" = "y" -o \
"$CONFIG_ARCH_SA1100" = "y" -o \
"$CONFIG_ARCH_NETWINDER" = "y" -o \
- "$CONFIG_PERSONAL_SERVER" = "y" -o \
- "$CONFIG_CATS" = "y" ]; then
+ "$CONFIG_ARCH_PERSONAL_SERVER" = "y" -o \
+ "$CONFIG_ARCH_CATS" = "y" ]; then
string 'Initial kernel command string' CONFIG_CMDLINE
fi
if [ "$CONFIG_ARCH_NETWINDER" = "y" -o \
diff --git a/arch/arm/kernel/arch.c b/arch/arm/kernel/arch.c
index 8645497ae..536e614ff 100644
--- a/arch/arm/kernel/arch.c
+++ b/arch/arm/kernel/arch.c
@@ -192,6 +192,7 @@ fixup_sa1100(struct machine_desc *desc, struct param_struct *params,
* in head-armv.S.
*/
static struct machine_desc machine_desc[] __attribute__ ((__section__ (".arch.info"))) = {
+#ifdef CONFIG_ARCH_EBSA110
{
MACH_TYPE_EBSA110,
"EBSA110", /* RMK */
@@ -199,119 +200,160 @@ static struct machine_desc machine_desc[] __attribute__ ((__section__ (".arch.in
NO_VIDEO,
1, 0, 1, 1, 1,
NULL
- }, {
+ },
+#endif
+#ifdef CONFIG_ARCH_RPC
+ {
MACH_TYPE_RISCPC,
"Acorn-RiscPC", /* RMK */
0x10000100,
NO_VIDEO,
1, 1, 0, 0, 0,
fixup_acorn
- }, {
- 2,
- "unknown",
- NO_PARAMS,
- NO_VIDEO,
- 0, 0, 0, 0, 0,
- NULL
- }, {
+ },
+#endif
+#ifdef CONFIG_ARCH_NEXUSPCI
+ {
MACH_TYPE_NEXUSPCI,
"FTV/PCI", /* Philip Blundell */
NO_PARAMS,
NO_VIDEO,
0, 0, 0, 0, 0,
NULL
- }, {
+ },
+#endif
+#ifdef CONFIG_ARCH_EBSA285
+ {
MACH_TYPE_EBSA285,
"EBSA285", /* RMK */
0x00000100,
0x000a0000, 0x000bffff,
0, 0, 0, 0, 0,
fixup_ebsa285
- }, {
+ },
+#endif
+#ifdef CONFIG_ARCH_NETWINDER
+ {
MACH_TYPE_NETWINDER,
"Rebel-NetWinder", /* RMK */
0x00000100,
0x000a0000, 0x000bffff,
1, 0, 1, 0, 0,
fixup_netwinder
- }, {
+ },
+#endif
+#ifdef CONFIG_ARCH_CATS
+ {
MACH_TYPE_CATS,
"Chalice-CATS", /* Philip Blundell */
NO_PARAMS,
0x000a0000, 0x000bffff,
0, 0, 0, 0, 1,
fixup_cats
- }, {
+ },
+#endif
+#ifdef CONFIG_ARCH_TBOX
+ {
MACH_TYPE_TBOX,
"unknown-TBOX", /* Philip Blundell */
NO_PARAMS,
NO_VIDEO,
0, 0, 0, 0, 0,
NULL
- }, {
+ },
+#endif
+#ifdef CONFIG_ARCH_CO285
+ {
MACH_TYPE_CO285,
"co-EBSA285", /* Mark van Doesburg */
NO_PARAMS,
NO_VIDEO,
0, 0, 0, 0, 0,
fixup_coebsa285
- }, {
+ },
+#endif
+#ifdef CONFIG_ARCH_CLPS7110
+ {
MACH_TYPE_CLPS7110,
"CL-PS7110", /* Werner Almesberger */
NO_PARAMS,
NO_VIDEO,
0, 0, 0, 0, 0,
NULL
- }, {
+ },
+#endif
+#ifdef CONFIG_ARCH_ARC
+ {
MACH_TYPE_ARCHIMEDES,
"Acorn-Archimedes",/* RMK/DAG */
0x0207c000,
NO_VIDEO,
0, 0, 0, 0, 0,
fixup_acorn
- }, {
+ },
+#endif
+#ifdef CONFIG_ARCH_A5K
+ {
MACH_TYPE_A5K,
"Acorn-A5000", /* RMK/PB */
0x0207c000,
NO_VIDEO,
0, 0, 0, 0, 0,
fixup_acorn
- }, {
+ },
+#endif
+#ifdef CONFIG_ARCH_ETOILE
+ {
MACH_TYPE_ETOILE,
"Etoile", /* Alex de Vries */
NO_PARAMS,
NO_VIDEO,
0, 0, 0, 0, 0,
NULL
- }, {
+ },
+#endif
+#ifdef CONFIG_ARCH_LACIE_NAS
+ {
MACH_TYPE_LACIE_NAS,
"LaCie_NAS", /* Benjamin Herrenschmidt */
NO_PARAMS,
NO_VIDEO,
0, 0, 0, 0, 0,
NULL
- }, {
+ },
+#endif
+#ifdef CONFIG_ARCH_CLPS7500
+ {
MACH_TYPE_CLPS7500,
"CL-PS7500", /* Philip Blundell */
NO_PARAMS,
NO_VIDEO,
0, 0, 0, 0, 0,
NULL
- }, {
+ },
+#endif
+#ifdef CONFIG_ARCH_SHARK
+ {
MACH_TYPE_SHARK,
"Shark", /* Alexander Schulz */
NO_PARAMS,
0x06000000, 0x06000000+0x001fffff,
0, 0, 0, 0, 0,
NULL
- }, {
+ },
+#endif
+#ifdef CONFIG_ARCH_SA1100
+ {
MACH_TYPE_SA1100,
"SA1100-based", /* Nicolas Pitre */
NO_PARAMS,
NO_VIDEO,
0, 0, 0, 0, 0,
fixup_sa1100
- }, {
+ },
+#endif
+#ifdef CONFIG_ARCH_PERSONAL_SERVER
+ {
MACH_TYPE_PERSONAL_SERVER,
"Compaq Personal Server",
NO_PARAMS,
@@ -319,4 +361,5 @@ static struct machine_desc machine_desc[] __attribute__ ((__section__ (".arch.in
0, 0, 0, 0, 0,
NULL
}
+#endif
};
diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c
index 1b0a064d0..109e2f96d 100644
--- a/arch/arm/kernel/bios32.c
+++ b/arch/arm/kernel/bios32.c
@@ -164,7 +164,52 @@ void __init pcibios_update_irq(struct pci_dev *dev, int irq)
void __init pcibios_fixup_bus(struct pci_bus *bus)
{
struct list_head *walk = &bus->devices;
+ struct arm_pci_sysdata *sysdata =
+ (struct arm_pci_sysdata *)bus->sysdata;
+ struct arm_bus_sysdata *busdata;
+ if (bus->number < MAX_NR_BUS)
+ busdata = sysdata->bus + bus->number;
+ else
+ BUG();
+
+ /*
+ * Walk the devices on this bus, working out what we can
+ * and can't support.
+ */
+ for (walk = walk->next; walk != &bus->devices; walk = walk->next) {
+ struct pci_dev *dev = pci_dev_b(walk);
+ u16 status;
+
+ pci_read_config_word(dev, PCI_STATUS, &status);
+
+ /*
+ * If this device does not support fast back to back
+ * transfers, the bus as a whole cannot support them.
+ */
+ if (!(status & PCI_STATUS_FAST_BACK))
+ busdata->features &= ~PCI_COMMAND_FAST_BACK;
+
+ /*
+ * Calculate the maximum devsel latency.
+ */
+ if (busdata->maxdevsel < (status & PCI_STATUS_DEVSEL_MASK))
+ busdata->maxdevsel = (status & PCI_STATUS_DEVSEL_MASK);
+
+ /*
+ * If this device is an ISA bridge, set the have_isa_bridge
+ * flag. We will then go looking for things like keyboard,
+ * etc
+ */
+ if (dev->class >> 8 == PCI_CLASS_BRIDGE_ISA ||
+ dev->class >> 8 == PCI_CLASS_BRIDGE_EISA)
+ have_isa_bridge = !0;
+ }
+
+ /*
+ * Now walk the devices again, this time setting them up.
+ */
+ walk = &bus->devices;
for (walk = walk->next; walk != &bus->devices; walk = walk->next) {
struct pci_dev *dev = pci_dev_b(walk);
u16 cmd;
@@ -182,25 +227,15 @@ void __init pcibios_fixup_bus(struct pci_bus *bus)
pci_write_config_dword(dev, 0x40, 0x80000000);
/*
- * If this device is an ISA bridge, set the have_isa_bridge
- * flag. We will then go looking for things like keyboard,
- * etc
- */
- if (dev->class >> 8 == PCI_CLASS_BRIDGE_ISA ||
- dev->class >> 8 == PCI_CLASS_BRIDGE_EISA)
- have_isa_bridge = !0;
-
- /*
* Set latency timer to 32, and a cache line size to 32 bytes.
- * Also, set system error enable, parity error enable, and
- * fast back to back transaction enable. Disable ROM.
+ * Also, set system error enable, parity error enable.
+ * Disable ROM.
*/
pci_write_config_byte(dev, PCI_LATENCY_TIMER, 32);
pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 8);
pci_read_config_word(dev, PCI_COMMAND, &cmd);
- cmd |= PCI_COMMAND_FAST_BACK | PCI_COMMAND_SERR |
- PCI_COMMAND_PARITY;
+ cmd |= busdata->features;
pci_write_config_word(dev, PCI_COMMAND, cmd);
pci_read_config_word(dev, PCI_COMMAND, &cmd);
@@ -254,7 +289,7 @@ static struct hw_pci ebsa285_pci __initdata = {
};
#endif
-#ifdef CONFIG_CATS
+#ifdef CONFIG_ARCH_CATS
/* cats host-specific stuff */
static int irqmap_cats[] __initdata = { IRQ_PCI, IRQ_IN0, IRQ_IN1, IRQ_IN3 };
@@ -323,7 +358,7 @@ static struct hw_pci netwinder_pci __initdata = {
};
#endif
-#ifdef CONFIG_PERSONAL_SERVER
+#ifdef CONFIG_ARCH_PERSONAL_SERVER
static int irqmap_personal_server[] __initdata = {
IRQ_IN0, IRQ_IN1, IRQ_IN2, IRQ_IN3, 0, 0, 0,
IRQ_DOORBELLHOST, IRQ_DMA1, IRQ_DMA2, IRQ_PCI
@@ -399,7 +434,7 @@ void __init pcibios_init(void)
break;
}
#endif
-#ifdef CONFIG_CATS
+#ifdef CONFIG_ARCH_CATS
if (machine_is_cats()) {
hw_pci = &cats_pci;
break;
@@ -411,7 +446,7 @@ void __init pcibios_init(void)
break;
}
#endif
-#ifdef CONFIG_PERSONAL_SERVER
+#ifdef CONFIG_ARCH_PERSONAL_SERVER
if (machine_is_personal_server()) {
hw_pci = &personal_server_pci;
break;
diff --git a/arch/arm/kernel/bios32.h b/arch/arm/kernel/bios32.h
index 9a4380732..525af165b 100644
--- a/arch/arm/kernel/bios32.h
+++ b/arch/arm/kernel/bios32.h
@@ -1,9 +1,27 @@
+#define MAX_NR_BUS 2
+
+struct arm_bus_sysdata {
+ /*
+ * bitmask of features we can turn.
+ * See PCI command register for more info.
+ */
+ u16 features;
+ /*
+ * Maximum devsel for this bus.
+ */
+ u16 maxdevsel;
+};
+
+struct arm_pci_sysdata {
+ struct arm_bus_sysdata bus[MAX_NR_BUS];
+};
+
struct hw_pci {
- void (*init)(void);
+ void (*init)(void);
unsigned long io_start;
unsigned long mem_start;
- u8 (*swizzle)(struct pci_dev *dev, u8 *pin);
- int (*map_irq)(struct pci_dev *dev, u8 slot, u8 pin);
+ u8 (*swizzle)(struct pci_dev *dev, u8 *pin);
+ int (*map_irq)(struct pci_dev *dev, u8 slot, u8 pin);
};
void __init dc21285_init(void);
diff --git a/arch/arm/kernel/dec21285.c b/arch/arm/kernel/dec21285.c
index 6158e995a..64c5861f0 100644
--- a/arch/arm/kernel/dec21285.c
+++ b/arch/arm/kernel/dec21285.c
@@ -206,8 +206,11 @@ static struct irqaction dc21285_error_action = {
void __init dc21285_init(void)
{
static struct resource csrmem, csrio;
- unsigned int mem_size;
+ struct arm_pci_sysdata sysdata;
unsigned long cntl;
+ unsigned int mem_size, pci_cmd = PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE;
+ int i;
mem_size = (unsigned int)high_memory - PAGE_OFFSET;
*CSR_SDRAMBASEMASK = (mem_size - 1) & 0x0ffc0000;
@@ -240,25 +243,34 @@ void __init dc21285_init(void)
*CSR_PCICSRIOBASE = csrio.start;
*CSR_PCISDRAMBASE = virt_to_bus((void *)PAGE_OFFSET);
*CSR_PCIROMBASE = 0;
- *CSR_PCICMD = PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
- PCI_COMMAND_MASTER | PCI_COMMAND_FAST_BACK |
- PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY |
+ *CSR_PCICMD = pci_cmd |
(1 << 31) | (1 << 29) | (1 << 28) | (1 << 24);
#endif
printk(KERN_DEBUG "PCI: DC21285 footbridge, revision %02lX\n",
*CSR_CLASSREV & 0xff);
- pci_scan_bus(0, &dc21285_ops, NULL);
+ for (i = 0; i < MAX_NR_BUS; i++) {
+ sysdata.bus[i].features = PCI_COMMAND_FAST_BACK |
+ PCI_COMMAND_SERR |
+ PCI_COMMAND_PARITY;
+ sysdata.bus[i].maxdevsel = PCI_STATUS_DEVSEL_FAST;
+ }
+
+ pci_scan_bus(0, &dc21285_ops, &sysdata);
+
+ pci_cmd |= sysdata.bus[0].features;
+
+ printk("Fast back to back PCI transfers %sabled\n",
+ (sysdata.bus[0].features & PCI_COMMAND_FAST_BACK) ? "en" : "dis");
/*
* Clear any existing errors - we aren't
* interested in historical data...
*/
- cntl = *CSR_SA110_CNTL & 0xffffde07;
- *CSR_SA110_CNTL = cntl | SA110_CNTL_RXSERR;
- cntl = *CSR_PCICMD & 0x0000ffff;
- *CSR_PCICMD = cntl | 1 << 31 | 1 << 29 | 1 << 28 | 1 << 24;
+ cntl = *CSR_SA110_CNTL & 0xffffde07;
+ *CSR_SA110_CNTL = cntl | SA110_CNTL_RXSERR;
+ *CSR_PCICMD = pci_cmd | 1 << 31 | 1 << 29 | 1 << 28 | 1 << 24;
/*
* Initialise PCI error IRQ after we've finished probing
diff --git a/arch/arm/kernel/hw-footbridge.c b/arch/arm/kernel/hw-footbridge.c
index 8dcad0b93..f47e169c5 100644
--- a/arch/arm/kernel/hw-footbridge.c
+++ b/arch/arm/kernel/hw-footbridge.c
@@ -613,7 +613,7 @@ EXPORT_SYMBOL(cpld_modify);
/*
* CATS stuff
*/
-#ifdef CONFIG_CATS
+#ifdef CONFIG_ARCH_CATS
#define CONFIG_PORT 0x370
#define INDEX_PORT (CONFIG_PORT)
@@ -698,7 +698,7 @@ void __init hw_init(void)
#endif
}
#endif
-#ifdef CONFIG_CATS
+#ifdef CONFIG_ARCH_CATS
if (machine_is_cats())
cats_hw_init();
#endif
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index 46afd5938..6ea7916ef 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -1,7 +1,7 @@
/*
* linux/arch/arm/kernel/process.c
*
- * Copyright (C) 1996-1999 Russell King - Converted to ARM.
+ * Copyright (C) 1996-2000 Russell King - Converted to ARM.
* Origional Copyright (C) 1995 Linus Torvalds
*/
@@ -32,6 +32,7 @@
#include <asm/io.h>
extern char *processor_modes[];
+extern void setup_mm_for_reboot(char mode);
asmlinkage void ret_from_sys_call(void) __asm__("ret_from_sys_call");
@@ -96,15 +97,28 @@ __setup("reboot=", reboot_setup);
void machine_restart(char * __unused)
{
/*
- * Turn off caches, interrupts, etc
+ * Clean and disable cache, and turn off interrupts
*/
cpu_proc_fin();
+ /*
+ * Tell the mm system that we are going to reboot -
+ * we may need it to insert some 1:1 mappings so that
+ * soft boot works.
+ */
+ setup_mm_for_reboot(reboot_mode);
+
+ /*
+ * Now call the architecture specific reboot code.
+ */
arch_reset(reboot_mode);
+ /*
+ * Whoops - the architecture was unable to reboot.
+ * Tell the user!
+ */
mdelay(1000);
printk("Reboot failed -- System halted\n");
- cli();
while (1);
}
diff --git a/arch/arm/mm/fault-armv.c b/arch/arm/mm/fault-armv.c
index 4a23d14bd..13336c008 100644
--- a/arch/arm/mm/fault-armv.c
+++ b/arch/arm/mm/fault-armv.c
@@ -379,7 +379,8 @@ static const struct fsr_info {
*/
#define BUG_PROC_MSG \
KERN_DEBUG "Weird data abort (%08X).\n" \
- KERN_DEBUG "Please see http://www.arm.linux.org.uk/state.html for more information"
+ KERN_DEBUG "Please see http://www.arm.linux.org.uk/state.html for " \
+ "more information\n"
asmlinkage void
do_DataAbort(unsigned long addr, int fsr, int error_code, struct pt_regs *regs)
diff --git a/arch/arm/mm/mm-armo.c b/arch/arm/mm/mm-armo.c
index a5d4cdfe8..f4bf7cad5 100644
--- a/arch/arm/mm/mm-armo.c
+++ b/arch/arm/mm/mm-armo.c
@@ -3,7 +3,7 @@
*
* Page table sludge for older ARM processor architectures.
*
- * Copyright (C) 1998-1999 Russell King
+ * Copyright (C) 1998-2000 Russell King
*/
#include <linux/sched.h>
#include <linux/mm.h>
@@ -135,6 +135,13 @@ pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset)
}
/*
+ * No special code is required here.
+ */
+void setup_mm_for_reboot(char mode)
+{
+}
+
+/*
* This contains the code to setup the memory map on an ARM2/ARM250/ARM3
* machine. This is both processor & architecture specific, and requires
* some more work to get it to fit into our separate processor and
@@ -155,6 +162,9 @@ void __init pagetable_init(void)
pgd_val(swapper_pg_dir[i]) = 0;
}
+/*
+ * We never have holes in the memmap
+ */
void __init create_memmap_holes(void)
{
}
diff --git a/arch/arm/mm/mm-armv.c b/arch/arm/mm/mm-armv.c
index ddc69089a..cb29618a3 100644
--- a/arch/arm/mm/mm-armv.c
+++ b/arch/arm/mm/mm-armv.c
@@ -3,7 +3,7 @@
*
* Page table sludge for ARM v3 and v4 processor architectures.
*
- * Copyright (C) 1998-1999 Russell King
+ * Copyright (C) 1998-2000 Russell King
*/
#include <linux/sched.h>
#include <linux/mm.h>
@@ -286,6 +286,25 @@ static void __init create_mapping(struct map_desc *md)
}
}
+/*
+ * In order to soft-boot, we need to insert a 1:1 mapping in place of
+ * the user-mode pages. This will then ensure that we have predictable
+ * results when turning the mmu off
+ */
+void setup_mm_for_reboot(char mode)
+{
+ pgd_t *pgd = current->mm->pgd;
+ pmd_t pmd;
+ int i;
+
+ for (i = 0; i < FIRST_USER_PGD_NR + USER_PTRS_PER_PGD; i++) {
+ pmd_val(pmd) = (i << PGDIR_SHIFT) |
+ PMD_SECT_AP_WRITE | PMD_SECT_AP_READ |
+ PMD_TYPE_SECT;
+ set_pmd(pmd_offset(pgd + i, i << PGDIR_SHIFT), pmd);
+ }
+}
+
void __init pagetable_init(void)
{
struct map_desc *init_maps, *p, *q;
diff --git a/arch/arm/mm/proc-arm6,7.S b/arch/arm/mm/proc-arm6,7.S
index 9814d2a9c..dd039f208 100644
--- a/arch/arm/mm/proc-arm6,7.S
+++ b/arch/arm/mm/proc-arm6,7.S
@@ -1,7 +1,7 @@
/*
* linux/arch/arm/mm/proc-arm6,7.S: MMU functions for ARM6
*
- * (C) 1997-1999 Russell King
+ * (C) 1997-2000 Russell King
*
* These are the low level assembler for performing cache and TLB
* functions on the ARM6 & ARM7.
@@ -342,15 +342,17 @@ ENTRY(cpu_arm7_set_pte)
/*
* Function: _arm6_7_reset
- *
+ * Params : r0 = address to jump to
* Notes : This sets up everything for a reset
*/
ENTRY(cpu_arm6_reset)
ENTRY(cpu_arm7_reset)
- mov r0, #0
- mcr p15, 0, r0, c7, c0, 0 @ flush cache
- mcr p15, 0, r0, c5, c0, 0 @ flush TLB
- mov pc, lr
+ mov r1, #0
+ mcr p15, 0, r1, c7, c0, 0 @ flush cache
+ mcr p15, 0, r1, c5, c0, 0 @ flush TLB
+ mov r1, #0x30
+ mcr p15, 0, r1, c1, c0, 0 @ turn off MMU etc
+ mov pc, r0
cpu_armvlsi_name:
.asciz "ARM/VLSI"
diff --git a/arch/arm/mm/proc-sa110.S b/arch/arm/mm/proc-sa110.S
index 933408451..9800643d9 100644
--- a/arch/arm/mm/proc-sa110.S
+++ b/arch/arm/mm/proc-sa110.S
@@ -1,7 +1,7 @@
/*
* linux/arch/arm/mm/proc-sa110.S: MMU functions for SA110
*
- * (C) 1997-1999 Russell King
+ * (C) 1997-2000 Russell King
*
* These are the low level assembler for performing cache and TLB
* functions on the StrongARM-110 and StrongARM-1100
@@ -225,9 +225,9 @@ ENTRY(cpu_sa1100_flush_ram_page)
.align 5
ENTRY(cpu_sa110_flush_tlb_all)
ENTRY(cpu_sa1100_flush_tlb_all)
- mov r0, #0
- mcr p15, 0, r0, c7, c10, 4 @ drain WB
- mcr p15, 0, r0, c8, c7, 0 @ flush I & D tlbs
+ mov ip, #0
+ mcr p15, 0, ip, c7, c10, 4 @ drain WB
+ mcr p15, 0, ip, c8, c7, 0 @ flush I & D tlbs
mov pc, lr
/*
@@ -421,16 +421,18 @@ ENTRY(cpu_sa1100_proc_init)
ENTRY(cpu_sa110_proc_fin)
ENTRY(cpu_sa1100_proc_fin)
+ stmfd sp!, {r1, lr}
mrs r0, cpsr
orr r0, r0, #F_BIT | I_BIT
msr cpsr, r0
+ bl cpu_sa110_flush_cache_all @ clean caches
mov r0, #0
mcr p15, 0, r0, c15, c2, 2 @ Disable clock switching
mrc p15, 0, r0, c1, c0, 0
- bic r0, r0, #0x1100 @ ...i...s........
+ bic r0, r0, #0x1000 @ ...i............
bic r0, r0, #0x000e @ ............wca.
mcr p15, 0, r0, c1, c0, 0 @ disable caches
- mov pc, lr
+ ldmfd sp!, {r1, pc}
.align 5
ENTRY(cpu_sa110_do_idle)
@@ -448,18 +450,21 @@ ENTRY(cpu_sa1100_do_idle)
/*
* Function: sa110_reset
+ * Params : r0 = address to jump to
* Notes : This sets up everything for a reset
*/
+ .align 5
ENTRY(cpu_sa110_reset)
ENTRY(cpu_sa1100_reset)
- stmfd sp!, {r1, lr}
- bl cpu_sa110_flush_cache_all
- bl cpu_sa110_flush_tlb_all
+ mov ip, #0
mcr p15, 0, ip, c7, c7, 0 @ flush I,D caches
- mrc p15, 0, r0, c1, c0, 0 @ ctrl register
- bic r0, r0, #0x000f @ ............wcam
- bic r0, r0, #0x1100 @ ...i...s........
- ldmfd sp!, {r1, pc}
+ mcr p15, 0, ip, c7, c10, 4 @ drain WB
+ mcr p15, 0, ip, c8, c7, 0 @ flush I & D tlbs
+ mrc p15, 0, ip, c1, c0, 0 @ ctrl register
+ bic ip, ip, #0x000f @ ............wcam
+ bic ip, ip, #0x1100 @ ...i...s........
+ mcr p15, 0, ip, c1, c0, 0 @ ctrl register
+ mov pc, r0
/*
* Purpose : Function pointers used to access above functions - all calls
* come through these
diff --git a/arch/i386/Makefile b/arch/i386/Makefile
index 2a84bf05f..0e16a45b6 100644
--- a/arch/i386/Makefile
+++ b/arch/i386/Makefile
@@ -30,7 +30,8 @@ CFLAGS += $(shell if ! $(CC) -march=i486 -S -o /dev/null -xc /dev/null >/dev/nul
CFLAGS += $(shell if $(CC) -mpreferred-stack-boundary=2 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-mpreferred-stack-boundary=2"; fi)
ifdef CONFIG_M386
-CFLAGS := $(CFLAGS) -m386 -DCPU=386
+CFLAGS := $(CFLAGS) -DCPU=386
+CFLAGS += $(shell if $(CC) -march=i386 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=i386"; else echo "-m386"; fi)
AFLAGS := $(AFLAGS) -DCPU=386
endif
@@ -65,7 +66,7 @@ AFLAGS := $(AFLAGS) -DCPU=586
endif
ifdef CONFIG_MK7
-CFLAGS := $(CFLAGS) -DCPU=686 -march=pentiumpro -mpentiumpro -malign-functions=4 -fschedule-insns2 -mwide-multiply -fexpensive-optimizations
+CFLAGS := $(CFLAGS) -malign-functions=4 -fschedule-insns2 -mwide-multiply -fexpensive-optimizations -DCPU=686
CFLAGS += $(shell if $(CC) -march=i686 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=i686"; fi)
AFLAGS := $(AFLAGS) -DCPU=686
endif
diff --git a/arch/i386/boot/compressed/head.S b/arch/i386/boot/compressed/head.S
index 51e0fc893..102850b74 100644
--- a/arch/i386/boot/compressed/head.S
+++ b/arch/i386/boot/compressed/head.S
@@ -33,10 +33,10 @@ startup_32:
cld
cli
movl $(__KERNEL_DS),%eax
- movl %ax,%ds
- movl %ax,%es
- movl %ax,%fs
- movl %ax,%gs
+ movl %eax,%ds
+ movl %eax,%es
+ movl %eax,%fs
+ movl %eax,%gs
lss SYMBOL_NAME(stack_start),%esp
xorl %eax,%eax
diff --git a/arch/i386/defconfig b/arch/i386/defconfig
index 0f3f5cb8a..33f2d3cb1 100644
--- a/arch/i386/defconfig
+++ b/arch/i386/defconfig
@@ -179,7 +179,6 @@ CONFIG_IDEPCI_SHARE_IRQ=y
# CONFIG_BLK_DEV_OFFBOARD is not set
# CONFIG_IDEDMA_PCI_AUTO is not set
# CONFIG_BLK_DEV_IDEDMA is not set
-# CONFIG_IDEDMA_AUTO is not set
# CONFIG_IDEDMA_PCI_EXPERIMENTAL is not set
# CONFIG_IDEDMA_PCI_WIP is not set
# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set
@@ -208,6 +207,7 @@ CONFIG_IDEPCI_SHARE_IRQ=y
# CONFIG_BLK_DEV_TRM290 is not set
# CONFIG_BLK_DEV_VIA82CXXX is not set
# CONFIG_IDE_CHIPSETS is not set
+# CONFIG_IDEDMA_AUTO is not set
CONFIG_BLK_DEV_IDE_MODES=y
#
diff --git a/arch/i386/kernel/acpi.c b/arch/i386/kernel/acpi.c
index 8efa2832a..f9410f3fd 100644
--- a/arch/i386/kernel/acpi.c
+++ b/arch/i386/kernel/acpi.c
@@ -488,13 +488,13 @@ static int __init acpi_find_tables(void)
if (!rsdt) {
printk(KERN_ERR "ACPI: missing RSDT at 0x%p\n",
(void*) rsdp->rsdt);
- return -ENODEV;
+ return -EINVAL;
}
else if (rsdt->signature != ACPI_RSDT_SIG) {
printk(KERN_ERR "ACPI: bad RSDT at 0x%p (%08x)\n",
(void*) rsdp->rsdt, (unsigned) rsdt->signature);
acpi_unmap_table(rsdt);
- return -ENODEV;
+ return -EINVAL;
}
// search RSDT for FACP
acpi_facp.table = NULL;
@@ -532,7 +532,7 @@ static int __init acpi_find_tables(void)
if (!acpi_facp.table) {
printk(KERN_ERR "ACPI: missing FACP\n");
- return -ENODEV;
+ return -EINVAL;
}
return 0;
}
@@ -1461,8 +1461,19 @@ static int __init acpi_init(void)
switch (acpi_enabled) {
case ACPI_ENABLED:
- if (acpi_find_tables() && acpi_find_chipset())
+ switch (acpi_find_tables()) {
+ case 0:
+ // found valid ACPI tables
+ break;
+ case -ENODEV:
+ // found no ACPI tables, try chipset-specific
+ if (acpi_find_chipset())
+ return -ENODEV;
+ break;
+ default:
+ // found broken ACPI tables
return -ENODEV;
+ }
break;
case ACPI_TABLES_ONLY:
if (acpi_find_tables())
@@ -1478,6 +1489,12 @@ static int __init acpi_init(void)
facp = (struct acpi_facp*) acpi_facp.table;
+ if (PM_IS_ACTIVE()) {
+ printk(KERN_NOTICE "acpi: APM is already active.\n");
+ goto err_out;
+ }
+ pm_active = 1;
+
/*
* Internally we always keep latencies in timer
* ticks, which is simpler and more consistent (what is
@@ -1516,8 +1533,6 @@ static int __init acpi_init(void)
pm_power_off = acpi_power_off;
- pm_active = 1;
-
/*
* Set up the ACPI idle function. Note that we can't really
* do this with multiple CPU's, we'd need a per-CPU ACPI
@@ -1549,7 +1564,6 @@ static void __exit acpi_exit(void)
struct acpi_facp *facp = (struct acpi_facp*) acpi_facp.table;
pm_idle = NULL;
- pm_active = 0;
pm_power_off = NULL;
unregister_sysctl_table(acpi_sysctl);
@@ -1563,6 +1577,8 @@ static void __exit acpi_exit(void)
if (pci_driver_registered)
pci_unregister_driver(&acpi_driver);
+
+ pm_active = 0;
}
/*
diff --git a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S
index 3340946be..7a8a17b63 100644
--- a/arch/i386/kernel/head.S
+++ b/arch/i386/kernel/head.S
@@ -58,10 +58,19 @@ startup_32:
* New page tables may be in 4Mbyte page mode and may
* be using the global pages.
*
+ * NOTE! If we are on a 486 we may have no cr4 at all!
+ * So we do not try to touch it unless we really have
+ * some bits in it to set. This won't work if the BSP
+ * implements cr4 but this AP does not -- very unlikely
+ * but be warned! The same applies to the pse feature
+ * if not equally supported. --macro
+ *
* NOTE! We have to correct for the fact that we're
* not yet offset PAGE_OFFSET..
*/
#define cr4_bits mmu_cr4_features-__PAGE_OFFSET
+ cmpl $0,cr4_bits
+ je 1f
movl %cr4,%eax # Turn on 4Mb pages
orl cr4_bits,%eax
movl %eax,%cr4
@@ -219,7 +228,6 @@ is386: pushl %ecx # restore original EFLAGS
orl $2,%eax # set MP
2: movl %eax,%cr0
call check_x87
-4:
#ifdef __SMP__
incb ready
#endif
diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c
index e21c33b6f..5327f24a4 100644
--- a/arch/i386/kernel/i386_ksyms.c
+++ b/arch/i386/kernel/i386_ksyms.c
@@ -25,6 +25,7 @@
extern void dump_thread(struct pt_regs *, struct user *);
extern int dump_fpu(elf_fpregset_t *);
+extern spinlock_t rtc_lock;
#ifdef CONFIG_SMP
extern void FASTCALL( __write_lock_failed(rwlock_t *rw));
@@ -131,3 +132,5 @@ EXPORT_SYMBOL(screen_info);
EXPORT_SYMBOL(get_wchan);
EXPORT_SYMBOL(irq_stat);
+
+EXPORT_SYMBOL(rtc_lock);
diff --git a/arch/i386/kernel/pci-i386.c b/arch/i386/kernel/pci-i386.c
index 6dd0a4306..942de9c79 100644
--- a/arch/i386/kernel/pci-i386.c
+++ b/arch/i386/kernel/pci-i386.c
@@ -323,3 +323,17 @@ int pcibios_enable_resources(struct pci_dev *dev)
}
return 0;
}
+
+/*
+ * If we set up a device for bus mastering, we need to check the latency
+ * timer as certain crappy BIOSes forget to set it properly.
+ */
+void pcibios_set_master(struct pci_dev *dev)
+{
+ u8 lat;
+ pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
+ if (lat < 16) {
+ printk("PCI: Increasing latency timer of device %s to 64\n", dev->slot_name);
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
+ }
+}
diff --git a/arch/i386/kernel/pci-pc.c b/arch/i386/kernel/pci-pc.c
index e51e96e43..b588642cc 100644
--- a/arch/i386/kernel/pci-pc.c
+++ b/arch/i386/kernel/pci-pc.c
@@ -1103,9 +1103,9 @@ static char *pcibios_lookup_irq(struct pci_dev *dev, struct irq_routing_table *r
{
struct irq_info *q;
struct pci_dev *router;
- int i, pirq, newirq;
+ int i, pirq, newirq, reg;
u32 rtrid, mask;
- u8 x;
+ u8 x, y;
char *msg = NULL;
pin--;
@@ -1169,14 +1169,44 @@ static char *pcibios_lookup_irq(struct pci_dev *dev, struct irq_routing_table *r
DBG(" -> [PIIX] set to %02x\n", newirq);
pci_write_config_byte(router, pirq, newirq);
msg = "PIIX-NEW";
- } else
- DBG(" -> [PIIX] sink\n");
+ } else DBG(" -> [PIIX] sink\n");
break;
case ID(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533):
newirq = ali_set_irq(router, pirq-1, newirq);
if (newirq)
msg = "ALI";
break;
+ case ID(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C596):
+ case ID(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686):
+ reg = 0x55 + (pirq >> 1);
+ pci_read_config_byte(router, reg, &x);
+ y = (pirq & 1) ? (x >> 4) : (x & 0x0f);
+ if (y) {
+ DBG(" -> [VIA] %02x\n", y);
+ newirq = y;
+ msg = "VIA";
+ } else if (newirq) {
+ DBG(" -> [VIA] set to %02x\n", newirq);
+ x = (pirq & 1) ? ((x & 0x0f) | (newirq << 4)) : ((x & 0xf0) | newirq);
+ pci_write_config_byte(router, reg, y);
+ msg = "VIA-NEW";
+ } else DBG(" -> [VIA] sink\n");
+ break;
+ case ID(PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C700):
+ reg = 0xb8 + (pirq >> 5);
+ pci_read_config_byte(router, reg, &x);
+ y = (pirq & 0x10) ? (x >> 4) : (x & 0x0f);
+ if (y) {
+ DBG(" -> [OPTI] %02x\n", y);
+ newirq = y;
+ msg = "OPTI";
+ } else if (newirq) {
+ DBG(" -> [OPTI] set to %02x\n", newirq);
+ x = (pirq & 0x10) ? ((x & 0x0f) | (newirq << 4)) : ((x & 0xf0) | newirq);
+ pci_write_config_byte(router, reg, y);
+ msg = "OPTI-NEW";
+ } else DBG(" -> [OPTI] sink\n");
+ break;
default:
DBG(" -> unknown router %04x/%04x\n", rt->rtr_vendor, rt->rtr_device);
if (newirq && mask == (1 << newirq)) {
diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c
index a12b6b73d..46292496d 100644
--- a/arch/i386/kernel/setup.c
+++ b/arch/i386/kernel/setup.c
@@ -793,7 +793,7 @@ static int __init get_model_name(struct cpuinfo_x86 *c)
unsigned int n, dummy, *v, ecx, edx;
/* Actually we must have cpuid or we could never have
- * figured out that this was AMD from the vendor info :-).
+ * figured out that this was AMD/Cyrix from the vendor info :-).
*/
cpuid(0x80000000, &n, &dummy, &dummy, &dummy);
@@ -806,23 +806,27 @@ static int __init get_model_name(struct cpuinfo_x86 *c)
cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]);
c->x86_model_id[48] = 0;
/* Set MTRR capability flag if appropriate */
- if(boot_cpu_data.x86 == 5) {
- if((boot_cpu_data.x86_model == 9) ||
- ((boot_cpu_data.x86_model == 8) &&
- (boot_cpu_data.x86_mask >= 8)))
- c->x86_capability |= X86_FEATURE_MTRR;
- }
-
- if (n >= 0x80000005){
- cpuid(0x80000005, &dummy, &dummy, &ecx, &edx);
- printk("CPU: L1 I Cache: %dK L1 D Cache: %dK\n",
- ecx>>24, edx>>24);
- c->x86_cache_size=(ecx>>24)+(edx>>24);
- }
- if (n >= 0x80000006){
- cpuid(0x80000006, &dummy, &dummy, &ecx, &edx);
- printk("CPU: L2 Cache: %dK\n", ecx>>16);
- c->x86_cache_size=(ecx>>16);
+
+ if(c->x86_vendor==X86_VENDOR_AMD)
+ {
+ if(boot_cpu_data.x86 == 5) {
+ if((boot_cpu_data.x86_model == 9) ||
+ ((boot_cpu_data.x86_model == 8) &&
+ (boot_cpu_data.x86_mask >= 8)))
+ c->x86_capability |= X86_FEATURE_MTRR;
+ }
+
+ if (n >= 0x80000005){
+ cpuid(0x80000005, &dummy, &dummy, &ecx, &edx);
+ printk("CPU: L1 I Cache: %dK L1 D Cache: %dK\n",
+ ecx>>24, edx>>24);
+ c->x86_cache_size=(ecx>>24)+(edx>>24);
+ }
+ if (n >= 0x80000006){
+ cpuid(0x80000006, &dummy, &dummy, &ecx, &edx);
+ printk("CPU: L2 Cache: %dK\n", ecx>>16);
+ c->x86_cache_size=(ecx>>16);
+ }
}
return 1;
}
@@ -1034,6 +1038,8 @@ static void __init cyrix_model(struct cpuinfo_x86 *c)
printk(KERN_INFO "Working around Cyrix MediaGX virtual DMA bugs.\n");
isa_dma_bridge_buggy = 2;
#endif
+ c->x86_cache_size=16; /* Yep 16K integrated cache thats it */
+
/* GXm supports extended cpuid levels 'ala' AMD */
if (c->cpuid_level == 2) {
get_model_name(c); /* get CPU marketing name */
@@ -1546,7 +1552,7 @@ void __init cpu_init (void)
cpus_initialized++;
printk("Initializing CPU#%d\n", nr);
- if (cpu_has_pse)
+ if (cpu_has_vme || cpu_has_tsc || cpu_has_de)
clear_in_cr4(X86_CR4_VME|X86_CR4_PVI|X86_CR4_TSD|X86_CR4_DE);
__asm__ __volatile__("lgdt %0": "=m" (gdt_descr));
diff --git a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c
index b4a7753b8..bcec04401 100644
--- a/arch/i386/kernel/time.c
+++ b/arch/i386/kernel/time.c
@@ -79,6 +79,9 @@ static unsigned long last_tsc_low; /* lsb 32 bits of Time Stamp Counter */
unsigned long fast_gettimeoffset_quotient=0;
extern rwlock_t xtime_lock;
+extern volatile unsigned long lost_ticks;
+
+spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
static inline unsigned long do_fast_gettimeoffset(void)
{
@@ -113,6 +116,8 @@ static inline unsigned long do_fast_gettimeoffset(void)
#ifndef CONFIG_X86_TSC
+spinlock_t i8253_lock = SPIN_LOCK_UNLOCKED;
+
/* This function must be called with interrupts disabled
* It was inspired by Steve McCanne's microtime-i386 for BSD. -- jrs
*
@@ -157,6 +162,8 @@ static unsigned long do_slow_gettimeoffset(void)
*/
unsigned long jiffies_t;
+ /* gets recalled with irq locally disabled */
+ spin_lock(&i8253_lock);
/* timer count may underflow right here */
outb_p(0x00, 0x43); /* latch the count ASAP */
@@ -215,6 +222,7 @@ static unsigned long do_slow_gettimeoffset(void)
}
} else
jiffies_p = jiffies_t;
+ spin_unlock(&i8253_lock);
count_p = count;
@@ -238,7 +246,6 @@ static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset;
*/
void do_gettimeofday(struct timeval *tv)
{
- extern volatile unsigned long lost_ticks;
unsigned long flags;
unsigned long usec, sec;
@@ -272,6 +279,7 @@ void do_settimeofday(struct timeval *tv)
* would have done, and then undo it!
*/
tv->tv_usec -= do_gettimeoffset();
+ tv->tv_usec -= lost_ticks * (1000000 / HZ);
while (tv->tv_usec < 0) {
tv->tv_usec += 1000000;
@@ -302,6 +310,8 @@ static int set_rtc_mmss(unsigned long nowtime)
int real_seconds, real_minutes, cmos_minutes;
unsigned char save_control, save_freq_select;
+ /* gets recalled with irq locally disabled */
+ spin_lock(&rtc_lock);
save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */
CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
@@ -347,6 +357,7 @@ static int set_rtc_mmss(unsigned long nowtime)
*/
CMOS_WRITE(save_control, RTC_CONTROL);
CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
+ spin_unlock(&rtc_lock);
return retval;
}
@@ -448,10 +459,19 @@ static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
rdtscl(last_tsc_low);
+#if 0 /*
+ * SUBTLE: this is not necessary from here because it's implicit in the
+ * write xtime_lock.
+ */
+ spin_lock(&i8253_lock);
+#endif
outb_p(0x00, 0x43); /* latch the count ASAP */
count = inb_p(0x40); /* read the latched count */
count |= inb(0x40) << 8;
+#if 0
+ spin_unlock(&i8253_lock);
+#endif
count = ((LATCH-1) - count) * TICK_SIZE;
delay_at_last_interrupt = (count + LATCH/2) / LATCH;
diff --git a/arch/mips/defconfig-decstation b/arch/mips/defconfig-decstation
index 0eda6e9fc..7bef250f8 100644
--- a/arch/mips/defconfig-decstation
+++ b/arch/mips/defconfig-decstation
@@ -76,12 +76,8 @@ CONFIG_TC=y
# Block devices
#
# CONFIG_BLK_DEV_FD is not set
-# CONFIG_BLK_DEV_IDE is not set
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
-# CONFIG_BLK_DEV_HD_ONLY is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
#
# Additional Block Devices
@@ -90,10 +86,6 @@ CONFIG_TC=y
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_MD is not set
# CONFIG_BLK_DEV_RAM is not set
-# CONFIG_BLK_DEV_XD is not set
-# CONFIG_PARIDE is not set
-# CONFIG_BLK_DEV_IDE_MODES is not set
-# CONFIG_BLK_DEV_HD is not set
#
# Networking options
@@ -151,6 +143,13 @@ CONFIG_IP_PNP_BOOTP=y
# CONFIG_PHONE_IXJ is not set
#
+# ATA/IDE/MFM/RLL support
+#
+# CONFIG_IDE is not set
+# CONFIG_BLK_DEV_IDE_MODES is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
# SCSI support
#
CONFIG_SCSI=y
diff --git a/arch/mips/kernel/irixelf.c b/arch/mips/kernel/irixelf.c
index 35055ac68..47947bbf0 100644
--- a/arch/mips/kernel/irixelf.c
+++ b/arch/mips/kernel/irixelf.c
@@ -1,4 +1,4 @@
-/* $Id: irixelf.c,v 1.26 2000/03/07 15:45:28 ralf Exp $
+/* $Id: irixelf.c,v 1.27 2000/03/19 01:28:43 ralf Exp $
*
* irixelf.c: Code to load IRIX ELF executables which conform to
* the MIPS ABI.
@@ -43,7 +43,7 @@
#undef DEBUG_ELF
static int load_irix_binary(struct linux_binprm * bprm, struct pt_regs * regs);
-static int load_irix_library(int fd);
+static int load_irix_library(struct file *);
static int irix_core_dump(long signr, struct pt_regs * regs,
struct file *file);
extern int dump_fpu (elf_fpregset_t *);
@@ -829,7 +829,7 @@ out_free_ph:
/* This is really simpleminded and specialized - we are loading an
* a.out library that is given an ELF header.
*/
-static inline int do_load_irix_library(struct file *file)
+static int load_irix_library(struct file *file)
{
struct elfhdr elf_ex;
struct elf_phdr *elf_phdata = NULL;
@@ -843,8 +843,6 @@ static inline int do_load_irix_library(struct file *file)
int i,j, k;
len = 0;
- if (!file->f_op)
- return -EACCES;
dentry = file->f_dentry;
inode = dentry->d_inode;
elf_bss = 0;
@@ -897,16 +895,18 @@ static inline int do_load_irix_library(struct file *file)
while(elf_phdata->p_type != PT_LOAD) elf_phdata++;
/* Now use mmap to map the library into memory. */
+ down(&current->mm->mmap_sem);
error = do_mmap(file,
elf_phdata->p_vaddr & 0xfffff000,
elf_phdata->p_filesz + (elf_phdata->p_vaddr & 0xfff),
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
elf_phdata->p_offset & 0xfffff000);
+ up(&current->mm->mmap_sem);
k = elf_phdata->p_vaddr + elf_phdata->p_filesz;
- if(k > elf_bss) elf_bss = k;
-
+ if (k > elf_bss) elf_bss = k;
+
if (error != (elf_phdata->p_vaddr & 0xfffff000)) {
kfree(elf_phdata);
return error;
@@ -921,19 +921,6 @@ static inline int do_load_irix_library(struct file *file)
kfree(elf_phdata);
return 0;
}
-
-static int load_irix_library(int fd)
-{
- int retval = -EACCES;
- struct file *file;
-
- file = fget(fd);
- if (file) {
- retval = do_load_irix_library(file);
- fput(file);
- }
- return retval;
-}
/* Called through irix_syssgi() to map an elf image given an FD,
* a phdr ptr USER_PHDRP in userspace, and a count CNT telling how many
diff --git a/arch/mips64/defconfig-ip22 b/arch/mips64/defconfig-ip22
index a5de95ca5..414d98df6 100644
--- a/arch/mips64/defconfig-ip22
+++ b/arch/mips64/defconfig-ip22
@@ -53,12 +53,8 @@ CONFIG_BINFMT_ELF=y
# Block devices
#
# CONFIG_BLK_DEV_FD is not set
-# CONFIG_BLK_DEV_IDE is not set
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
-# CONFIG_BLK_DEV_HD_ONLY is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
#
# Additional Block Devices
@@ -67,10 +63,6 @@ CONFIG_BINFMT_ELF=y
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_MD is not set
# CONFIG_BLK_DEV_RAM is not set
-# CONFIG_BLK_DEV_XD is not set
-# CONFIG_PARIDE is not set
-# CONFIG_BLK_DEV_IDE_MODES is not set
-# CONFIG_BLK_DEV_HD is not set
#
# Networking options
@@ -102,6 +94,7 @@ CONFIG_SKB_LARGE=y
#
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
+# CONFIG_DECNET is not set
#
# Telephony Support
@@ -110,6 +103,13 @@ CONFIG_SKB_LARGE=y
# CONFIG_PHONE_IXJ is not set
#
+# ATA/IDE/MFM/RLL support
+#
+# CONFIG_IDE is not set
+# CONFIG_BLK_DEV_IDE_MODES is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
# SCSI support
#
# CONFIG_SCSI is not set
@@ -225,7 +225,6 @@ CONFIG_VT_CONSOLE=y
# CONFIG_WATCHDOG is not set
# CONFIG_NVRAM is not set
# CONFIG_RTC is not set
-# CONFIG_EFI_RTC is not set
#
# Video For Linux
diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c
index 73a6c2bf7..30595e694 100644
--- a/arch/ppc/kernel/setup.c
+++ b/arch/ppc/kernel/setup.c
@@ -756,7 +756,7 @@ void ppc_generic_ide_fix_driveid(struct hd_driveid *id)
id->CurAPMvalues = __le16_to_cpu(id->CurAPMvalues);
id->word92 = __le16_to_cpu(id->word92);
id->hw_config = __le16_to_cpu(id->hw_config);
- for (i=0; i<34; i++)
+ for (i=0; i<32; i++)
id->words94_125[i] = __le16_to_cpu(id->words94_125[i]);
id->last_lun = __le16_to_cpu(id->last_lun);
id->word127 = __le16_to_cpu(id->word127);
diff --git a/arch/sparc/Makefile b/arch/sparc/Makefile
index dec2105d8..6725e8cfe 100644
--- a/arch/sparc/Makefile
+++ b/arch/sparc/Makefile
@@ -49,6 +49,8 @@ SUBDIRS += arch/sparc/boot
CORE_FILES_NO_BTFIX := $(CORE_FILES)
CORE_FILES += arch/sparc/boot/btfix.o
+export CORE_FILES_NO_BTFIX
+
archclean:
rm -f $(TOPDIR)/vmlinux.aout
-$(MAKE) -C arch/sparc/boot clean
diff --git a/arch/sparc/config.in b/arch/sparc/config.in
index adb0ebe4c..e67a43022 100644
--- a/arch/sparc/config.in
+++ b/arch/sparc/config.in
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.88 2000/03/13 03:40:27 davem Exp $
+# $Id: config.in,v 1.90 2000/03/17 05:18:02 anton Exp $
# For a description of the syntax of this configuration file,
# see the Configure script.
#
@@ -19,18 +19,6 @@ define_bool CONFIG_VT_CONSOLE y
bool 'Symmetric multi-processing support (does not work on sun4/sun4c)' CONFIG_SMP
-bool 'Support for SUN4 machines (disables SUN4[CDM] support)' CONFIG_SUN4
-if [ "$CONFIG_SUN4" != "y" ]; then
- bool 'Support for PCI and PS/2 keyboard/mouse' CONFIG_PCI
- source drivers/pci/Config.in
-fi
-
-mainmenu_option next_comment
-comment 'Console drivers'
-bool 'PROM console' CONFIG_PROM_CONSOLE
-source drivers/video/Config.in
-endmenu
-
# Global things across all Sun machines.
define_bool CONFIG_SBUS y
define_bool CONFIG_SBUSCHAR y
@@ -43,9 +31,11 @@ define_bool CONFIG_SUN_KEYBOARD y
define_bool CONFIG_SUN_CONSOLE y
define_bool CONFIG_SUN_AUXIO y
define_bool CONFIG_SUN_IO y
+
+bool 'Support for SUN4 machines (disables SUN4[CDM] support)' CONFIG_SUN4
if [ "$CONFIG_SUN4" != "y" ]; then
- source drivers/sbus/char/Config.in
- source drivers/sbus/audio/Config.in
+ bool 'Support for PCI and PS/2 keyboard/mouse' CONFIG_PCI
+ source drivers/pci/Config.in
fi
tristate 'Openprom tree appears in /proc/openprom' CONFIG_SUN_OPENPROMFS
@@ -74,15 +64,27 @@ fi
endmenu
mainmenu_option next_comment
-comment 'Floppy and other block devices'
+comment 'Console drivers'
+bool 'PROM console' CONFIG_PROM_CONSOLE
+source drivers/video/Config.in
+endmenu
+
+if [ "$CONFIG_SUN4" != "y" ]; then
+ source drivers/sbus/char/Config.in
+ source drivers/sbus/audio/Config.in
+fi
+
+mainmenu_option next_comment
+comment 'Block devices'
bool 'Normal floppy disk support' CONFIG_BLK_DEV_FD
+
bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD
if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then
tristate ' Linear (append) mode' CONFIG_MD_LINEAR
tristate ' RAID-0 (striping) mode' CONFIG_MD_STRIPED
- tristate ' RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING
- tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5
+# tristate ' RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING
+# tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5
fi
tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
@@ -99,16 +101,6 @@ if [ "$CONFIG_NET" = "y" ]; then
source net/Config.in
fi
-mainmenu_option next_comment
-comment 'ISDN subsystem'
-
-tristate 'ISDN support' CONFIG_ISDN
-if [ "$CONFIG_ISDN" != "n" ]; then
- source drivers/isdn/Config.in
-fi
-endmenu
-
-
define_bool CONFIG_IDE n
define_bool CONFIG_BLK_DEV_IDE_MODES n
define_bool CONFIG_BLK_DEV_HD n
@@ -127,6 +119,15 @@ define_bool CONFIG_BLK_DEV_HD n
# endmenu
mainmenu_option next_comment
+comment 'ISDN subsystem'
+
+tristate 'ISDN support' CONFIG_ISDN
+if [ "$CONFIG_ISDN" != "n" ]; then
+ source drivers/isdn/Config.in
+fi
+endmenu
+
+mainmenu_option next_comment
comment 'SCSI support'
tristate 'SCSI support' CONFIG_SCSI
@@ -196,12 +197,16 @@ if [ "$CONFIG_NET" = "y" ]; then
fi
tristate ' Sun LANCE support' CONFIG_SUNLANCE
tristate ' Sun Happy Meal 10/100baseT support' CONFIG_HAPPYMEAL
- tristate ' Sun BigMAC 10/100baseT support (EXPERIMENTAL)' CONFIG_SUNBMAC
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ tristate ' Sun BigMAC 10/100baseT support (EXPERIMENTAL)' CONFIG_SUNBMAC
+ fi
tristate ' Sun QuadEthernet support' CONFIG_SUNQE
tristate ' MyriCOM Gigabit Ethernet support' CONFIG_MYRI_SBUS
+
# bool ' FDDI driver support' CONFIG_FDDI
# if [ "$CONFIG_FDDI" = "y" ]; then
# fi
+
if [ "$CONFIG_ATM" = "y" ]; then
source drivers/atm/Config.in
fi
diff --git a/arch/sparc/defconfig b/arch/sparc/defconfig
index de483af6e..b39da1989 100644
--- a/arch/sparc/defconfig
+++ b/arch/sparc/defconfig
@@ -14,8 +14,42 @@ CONFIG_EXPERIMENTAL=y
CONFIG_VT=y
CONFIG_VT_CONSOLE=y
# CONFIG_SMP is not set
+CONFIG_SBUS=y
+CONFIG_SBUSCHAR=y
+CONFIG_BUSMOUSE=y
+CONFIG_SUN_MOUSE=y
+CONFIG_SERIAL=y
+CONFIG_SUN_SERIAL=y
+CONFIG_SERIAL_CONSOLE=y
+CONFIG_SUN_KEYBOARD=y
+CONFIG_SUN_CONSOLE=y
+CONFIG_SUN_AUXIO=y
+CONFIG_SUN_IO=y
# CONFIG_SUN4 is not set
# CONFIG_PCI is not set
+CONFIG_SUN_OPENPROMFS=m
+CONFIG_NET=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_SYSCTL=y
+CONFIG_KCORE_ELF=y
+CONFIG_BINFMT_AOUT=y
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_MISC=m
+CONFIG_SUNOS_EMUL=y
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+# CONFIG_PRINTER is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODVERSIONS=y
+CONFIG_KMOD=y
#
# Console drivers
@@ -42,17 +76,6 @@ CONFIG_FBCON_CFB8=y
CONFIG_FBCON_FONTWIDTH8_ONLY=y
CONFIG_FONT_SUN8x16=y
# CONFIG_FBCON_FONTS is not set
-CONFIG_SBUS=y
-CONFIG_SBUSCHAR=y
-CONFIG_BUSMOUSE=y
-CONFIG_SUN_MOUSE=y
-CONFIG_SERIAL=y
-CONFIG_SUN_SERIAL=y
-CONFIG_SERIAL_CONSOLE=y
-CONFIG_SUN_KEYBOARD=y
-CONFIG_SUN_CONSOLE=y
-CONFIG_SUN_AUXIO=y
-CONFIG_SUN_IO=y
#
# Misc Linux/SPARC drivers
@@ -73,39 +96,14 @@ CONFIG_SUN_AURORA=m
# CONFIG_SPARCAUDIO_CS4231 is not set
# CONFIG_SPARCAUDIO_DBRI is not set
# CONFIG_SPARCAUDIO_DUMMY is not set
-CONFIG_SUN_OPENPROMFS=m
-CONFIG_NET=y
-CONFIG_SYSVIPC=y
-# CONFIG_BSD_PROCESS_ACCT is not set
-CONFIG_SYSCTL=y
-CONFIG_KCORE_ELF=y
-CONFIG_BINFMT_AOUT=y
-CONFIG_BINFMT_ELF=y
-CONFIG_BINFMT_MISC=m
-CONFIG_SUNOS_EMUL=y
#
-# Parallel port support
-#
-# CONFIG_PARPORT is not set
-# CONFIG_PRINTER is not set
-
-#
-# Loadable module support
-#
-CONFIG_MODULES=y
-CONFIG_MODVERSIONS=y
-CONFIG_KMOD=y
-
-#
-# Floppy, IDE, and other block devices
+# Block devices
#
CONFIG_BLK_DEV_FD=y
CONFIG_BLK_DEV_MD=y
CONFIG_MD_LINEAR=m
CONFIG_MD_STRIPED=m
-CONFIG_MD_MIRRORING=m
-CONFIG_MD_RAID5=m
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_BLK_DEV_LOOP=m
@@ -162,6 +160,9 @@ CONFIG_DECNET_SIOCGIFCONF=y
# QoS and/or fair queueing
#
# CONFIG_NET_SCHED is not set
+# CONFIG_IDE is not set
+# CONFIG_BLK_DEV_IDE_MODES is not set
+# CONFIG_BLK_DEV_HD is not set
#
# ISDN subsystem
@@ -169,11 +170,6 @@ CONFIG_DECNET_SIOCGIFCONF=y
# CONFIG_ISDN is not set
#
-# ATA/IDE/MFM/RLL support
-#
-# CONFIG_IDE is not set
-
-#
# SCSI support
#
CONFIG_SCSI=y
diff --git a/arch/sparc/kernel/ioport.c b/arch/sparc/kernel/ioport.c
index e9b506e10..b1ed3c2af 100644
--- a/arch/sparc/kernel/ioport.c
+++ b/arch/sparc/kernel/ioport.c
@@ -1,4 +1,4 @@
-/* $Id: ioport.c,v 1.35 2000/02/27 08:16:25 davem Exp $
+/* $Id: ioport.c,v 1.36 2000/03/16 08:22:53 anton Exp $
* ioport.c: Simple io mapping allocator.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -745,6 +745,10 @@ void ioport_init(void)
halt();
};
+}
+
+void register_proc_sparc_ioport(void)
+{
#ifdef CONFIG_PROC_FS
create_proc_read_entry("io_map",0,0,_sparc_io_get_info,&sparc_iomap);
create_proc_read_entry("dvma_map",0,0,_sparc_io_get_info,&_sparc_dvma);
diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c
index d4ac34932..5966e04d7 100644
--- a/arch/sparc/kernel/setup.c
+++ b/arch/sparc/kernel/setup.c
@@ -1,4 +1,4 @@
-/* $Id: setup.c,v 1.115 2000/02/26 04:24:31 davem Exp $
+/* $Id: setup.c,v 1.116 2000/03/15 23:26:22 anton Exp $
* linux/arch/sparc/kernel/setup.c
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -268,8 +268,6 @@ extern void sun_serial_setup(void);
extern unsigned short root_flags;
extern unsigned short root_dev;
extern unsigned short ram_flags;
-extern unsigned sparc_ramdisk_image;
-extern unsigned sparc_ramdisk_size;
#define RAMDISK_IMAGE_START_MASK 0x07FF
#define RAMDISK_PROMPT_FLAG 0x8000
#define RAMDISK_LOAD_FLAG 0x4000
@@ -382,42 +380,9 @@ void __init setup_arch(char **cmdline_p)
rd_prompt = ((ram_flags & RAMDISK_PROMPT_FLAG) != 0);
rd_doload = ((ram_flags & RAMDISK_LOAD_FLAG) != 0);
#endif
-#ifdef CONFIG_BLK_DEV_INITRD
-// FIXME needs to do the new bootmem alloc stuff
- if (sparc_ramdisk_image) {
- initrd_start = sparc_ramdisk_image;
- if (initrd_start < KERNBASE) initrd_start += KERNBASE;
- initrd_end = initrd_start + sparc_ramdisk_size;
- if (initrd_end > *memory_end_p) {
- printk(KERN_CRIT "initrd extends beyond end of memory "
- "(0x%08lx > 0x%08lx)\ndisabling initrd\n",
- initrd_end,*memory_end_p);
- initrd_start = 0;
- }
- if (initrd_start >= *memory_start_p && initrd_start < *memory_start_p + 2 * PAGE_SIZE) {
- initrd_below_start_ok = 1;
- *memory_start_p = PAGE_ALIGN (initrd_end);
- } else if (initrd_start && sparc_ramdisk_image < KERNBASE) {
- switch (sparc_cpu_model) {
- case sun4m:
- case sun4d:
- initrd_start -= KERNBASE;
- initrd_end -= KERNBASE;
- break;
- default:
- break;
- }
- }
- }
-#endif
+
prom_setsync(prom_sync_me);
-#ifdef CONFIG_SUN_SERIAL
-#if 0
- /* XXX We can't do this until the bootmem allocator is working. */
- sun_serial_setup(); /* set this up ASAP */
-#endif
-#endif
{
#if !CONFIG_SUN_SERIAL
serial_console = 0;
diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c
index e0bb41045..7b7a0eb61 100644
--- a/arch/sparc/kernel/sparc_ksyms.c
+++ b/arch/sparc/kernel/sparc_ksyms.c
@@ -1,4 +1,4 @@
-/* $Id: sparc_ksyms.c,v 1.94 2000/02/28 04:00:53 anton Exp $
+/* $Id: sparc_ksyms.c,v 1.96 2000/03/16 09:12:49 jj Exp $
* arch/sparc/kernel/ksyms.c: Sparc specific ksyms support.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -68,6 +68,8 @@ extern void bcopy (const char *, char *, int);
extern int __ashrdi3(int, int);
extern int __ashldi3(int, int);
extern int __lshrdi3(int, int);
+extern int __muldi3(int, int);
+extern int __divdi3(int, int);
extern void dump_thread(struct pt_regs *, struct user *);
@@ -85,10 +87,10 @@ extern int __sparc_dot_ ## sym (int) __asm__("." #sym); \
__EXPORT_SYMBOL(__sparc_dot_ ## sym, "." #sym)
#define EXPORT_SYMBOL_PRIVATE(sym) \
-extern int __sparc_priv_ ## sym (int) __asm__("__" ## #sym); \
+extern int __sparc_priv_ ## sym (int) __asm__("__" #sym); \
const struct module_symbol __export_priv_##sym \
__attribute__((section("__ksymtab"))) = \
-{ (unsigned long) &__sparc_priv_ ## sym, "__" ## #sym }
+{ (unsigned long) &__sparc_priv_ ## sym, "__" #sym }
/* used by various drivers */
EXPORT_SYMBOL(sparc_cpu_model);
@@ -273,6 +275,8 @@ EXPORT_SYMBOL_NOVERS(memmove);
EXPORT_SYMBOL_NOVERS(__ashrdi3);
EXPORT_SYMBOL_NOVERS(__ashldi3);
EXPORT_SYMBOL_NOVERS(__lshrdi3);
+EXPORT_SYMBOL_NOVERS(__muldi3);
+EXPORT_SYMBOL_NOVERS(__divdi3);
EXPORT_SYMBOL_DOT(rem);
EXPORT_SYMBOL_DOT(urem);
diff --git a/arch/sparc/kernel/sys_solaris.c b/arch/sparc/kernel/sys_solaris.c
index 8a9d54913..eb79c344c 100644
--- a/arch/sparc/kernel/sys_solaris.c
+++ b/arch/sparc/kernel/sys_solaris.c
@@ -14,6 +14,7 @@
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
+#include <linux/module.h>
/* CHECKME: this stuff looks rather bogus */
asmlinkage int
diff --git a/arch/sparc/kernel/sys_sunos.c b/arch/sparc/kernel/sys_sunos.c
index 262f6afdd..0a8a5827c 100644
--- a/arch/sparc/kernel/sys_sunos.c
+++ b/arch/sparc/kernel/sys_sunos.c
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos.c,v 1.115 2000/03/13 21:57:23 davem Exp $
+/* $Id: sys_sunos.c,v 1.117 2000/03/15 02:43:32 davem Exp $
* sys_sunos.c: SunOS specific syscall compatibility support.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -267,35 +267,6 @@ asmlinkage void sunos_vadvise(unsigned long strategy)
unlock_kernel();
}
-/* Same as vadvise, and just as bogus, but for a range of virtual
- * process address space.
- */
-#define MADV_NORMAL 0 /* Nothing special... */
-#define MADV_RANDOM 1 /* I am emacs... */
-#define MADV_SEQUENTIAL 2 /* I am researcher code... */
-#define MADV_WILLNEED 3 /* Pages in this range will be needed */
-#define MADV_DONTNEED 4 /* Pages in this range won't be needed */
-
-static char *mstrings[] = {
- "MADV_NORMAL",
- "MADV_RANDOM",
- "MADV_SEQUENTIAL",
- "MADV_WILLNEED",
- "MADV_DONTNEED",
-};
-
-asmlinkage void sunos_madvise(unsigned long address, unsigned long len,
- unsigned long strategy)
-{
- /* I wanna see who uses this... */
- lock_kernel();
- printk("%s: Advises us to use %s paging strategy for addr<%08lx> len<%08lx>\n",
- current->comm,
- strategy <= 4 ? mstrings[strategy] : "BOGUS",
- address, len);
- unlock_kernel();
-}
-
/* This just wants the soft limit (ie. rlim_cur element) of the RLIMIT_NOFILE
* resource limit and is for backwards compatibility with older sunos
* revs.
@@ -733,7 +704,6 @@ static int get_default (int value, int def_value)
asmlinkage int sunos_nfs_mount(char *dir_name, int linux_flags, void *data)
{
- int ret = -ENODEV;
int server_fd;
char *the_name;
struct nfs_mount_data linux_nfs_mount;
diff --git a/arch/sparc/kernel/systbls.S b/arch/sparc/kernel/systbls.S
index 42c072164..431f03daf 100644
--- a/arch/sparc/kernel/systbls.S
+++ b/arch/sparc/kernel/systbls.S
@@ -1,4 +1,4 @@
-/* $Id: systbls.S,v 1.95 2000/03/13 21:57:23 davem Exp $
+/* $Id: systbls.S,v 1.96 2000/03/15 02:43:32 davem Exp $
* systbls.S: System call entry point tables for OS compatibility.
* The native Linux system call table lives here also.
*
@@ -33,7 +33,7 @@ sys_call_table:
/*60*/ .long sys_umask, sys_chroot, sys_newfstat, sys_fstat64, sys_getpagesize
/*65*/ .long sys_msync, sys_vfork, sys_pread, sys_pwrite, sys_geteuid
/*70*/ .long sys_getegid, sys_mmap, sys_setreuid, sys_munmap, sys_mprotect
-/*75*/ .long sys_nis_syscall, sys_vhangup, sys_truncate64, sys_mincore, sys_getgroups16
+/*75*/ .long sys_madvise, sys_vhangup, sys_truncate64, sys_mincore, sys_getgroups16
/*80*/ .long sys_setgroups16, sys_getpgrp, sys_setgroups, sys_setitimer, sys_ftruncate64
/*85*/ .long sys_swapon, sys_getitimer, sys_setuid, sys_sethostname, sys_setgid
/*90*/ .long sys_dup2, sys_setfsuid, sys_fcntl, sys_select, sys_setfsgid
@@ -103,7 +103,7 @@ sunos_sys_table:
.long sys_msync, sys_vfork, sunos_nosys
.long sunos_nosys, sunos_sbrk, sunos_sstk
.long sunos_mmap, sunos_vadvise, sys_munmap
- .long sys_mprotect, sunos_madvise, sys_vhangup
+ .long sys_mprotect, sys_madvise, sys_vhangup
.long sunos_nosys, sys_mincore, sys_getgroups16
.long sys_setgroups16, sys_getpgrp, sunos_setpgrp
.long sys_setitimer, sunos_nosys, sys_swapon
diff --git a/arch/sparc/kernel/unaligned.c b/arch/sparc/kernel/unaligned.c
index 592a2a4c0..2f051a6aa 100644
--- a/arch/sparc/kernel/unaligned.c
+++ b/arch/sparc/kernel/unaligned.c
@@ -1,4 +1,4 @@
-/* $Id: unaligned.c,v 1.20 2000/01/21 11:38:42 jj Exp $
+/* $Id: unaligned.c,v 1.21 2000/03/15 08:50:16 anton Exp $
* unaligned.c: Unaligned load/store trap handling with special
* cases for the kernel to do them more quickly.
*
@@ -106,6 +106,26 @@ static inline unsigned long fetch_reg(unsigned int reg, struct pt_regs *regs)
return win->locals[reg - 16]; /* yes, I know what this does... */
}
+static inline unsigned long safe_fetch_reg(unsigned int reg, struct pt_regs *regs)
+{
+ struct reg_window *win;
+ unsigned long ret;
+
+ if(reg < 16)
+ return (!reg ? 0 : regs->u_regs[reg]);
+
+ /* Ho hum, the slightly complicated case. */
+ win = (struct reg_window *) regs->u_regs[UREG_FP];
+
+ if ((unsigned long)win & 3)
+ return -1;
+
+ if (get_user(ret, &win->locals[reg - 16]))
+ return -1;
+
+ return ret;
+}
+
static inline unsigned long *fetch_reg_addr(unsigned int reg, struct pt_regs *regs)
{
struct reg_window *win;
@@ -132,6 +152,22 @@ static inline unsigned long compute_effective_address(struct pt_regs *regs,
}
}
+static inline unsigned long safe_compute_effective_address(struct pt_regs *regs,
+ unsigned int insn)
+{
+ unsigned int rs1 = (insn >> 14) & 0x1f;
+ unsigned int rs2 = insn & 0x1f;
+ unsigned int rd = (insn >> 25) & 0x1f;
+
+ if(insn & 0x2000) {
+ maybe_flush_windows(rs1, 0, rd);
+ return (safe_fetch_reg(rs1, regs) + sign_extend_imm13(insn));
+ } else {
+ maybe_flush_windows(rs1, rs2, rd);
+ return (safe_fetch_reg(rs1, regs) + safe_fetch_reg(rs2, regs));
+ }
+}
+
/* This is just to make gcc think panic does return... */
static void unaligned_panic(char *str)
{
@@ -427,7 +463,7 @@ void user_mna_trap_fault(struct pt_regs *regs, unsigned int insn)
info.si_signo = SIGBUS;
info.si_errno = 0;
info.si_code = BUS_ADRALN;
- info.si_addr = (void *)compute_effective_address(regs, insn);
+ info.si_addr = (void *)safe_compute_effective_address(regs, insn);
info.si_trapno = 0;
send_sig_info(SIGBUS, &info, current);
}
diff --git a/arch/sparc/lib/Makefile b/arch/sparc/lib/Makefile
index d269e148b..8c3e8e4a9 100644
--- a/arch/sparc/lib/Makefile
+++ b/arch/sparc/lib/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.32 2000/02/28 04:00:48 anton Exp $
+# $Id: Makefile,v 1.33 2000/03/16 00:52:07 anton Exp $
# Makefile for Sparc library files..
#
@@ -6,7 +6,7 @@ OBJS = mul.o rem.o sdiv.o udiv.o umul.o urem.o ashrdi3.o memcpy.o memset.o \
strlen.o checksum.o blockops.o memscan.o memcmp.o strncmp.o \
strncpy_from_user.o divdi3.o udivdi3.o strlen_user.o \
copy_user.o locks.o atomic.o bitops.o debuglocks.o lshrdi3.o \
- ashldi3.o rwsem.o
+ ashldi3.o rwsem.o muldi3.o
lib.a: $(OBJS)
$(AR) rcs lib.a $(OBJS)
diff --git a/arch/sparc/lib/muldi3.S b/arch/sparc/lib/muldi3.S
new file mode 100644
index 000000000..7f17872d0
--- /dev/null
+++ b/arch/sparc/lib/muldi3.S
@@ -0,0 +1,76 @@
+/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC 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.
+
+GNU CC 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. */
+
+ .text
+ .align 4
+ .globl __muldi3
+__muldi3:
+ save %sp, -104, %sp
+ wr %g0, %i1, %y
+ sra %i3, 0x1f, %g2
+ and %i1, %g2, %g2
+ andcc %g0, 0, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, %i3, %g1
+ mulscc %g1, 0, %g1
+ add %g1, %g2, %l2
+ rd %y, %o1
+ mov %o1, %l3
+ mov %i1, %o0
+ call .umul
+ mov %i2, %o1
+ mov %o0, %l0
+ mov %i0, %o0
+ call .umul
+ mov %i3, %o1
+ add %l0, %o0, %l0
+ mov %l2, %i0
+ add %l2, %l0, %i0
+ ret
+ restore %g0, %l3, %o1
diff --git a/arch/sparc/mm/init.c b/arch/sparc/mm/init.c
index d92fbbb0b..2fa3a2c56 100644
--- a/arch/sparc/mm/init.c
+++ b/arch/sparc/mm/init.c
@@ -1,4 +1,4 @@
-/* $Id: init.c,v 1.83 2000/03/07 23:12:35 anton Exp $
+/* $Id: init.c,v 1.84 2000/03/15 23:26:26 anton Exp $
* linux/arch/sparc/mm/init.c
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -43,7 +43,11 @@ unsigned long sparc_unmapped_base;
struct pgtable_cache_struct pgt_quicklists = { 0, 0, 0, 0, SPIN_LOCK_UNLOCKED, SPIN_LOCK_UNLOCKED };
/* References to section boundaries */
-extern char __init_begin, __init_end, _start, end, etext , edata;
+extern char __init_begin, __init_end, _start, _end, etext , edata;
+
+/* Initial ramdisk setup */
+extern unsigned int sparc_ramdisk_image;
+extern unsigned int sparc_ramdisk_size;
unsigned long totalram_pages = 0;
@@ -118,6 +122,7 @@ unsigned long __init bootmem_init(void)
{
unsigned long bootmap_size, start_pfn, end_pfn;
unsigned long end_of_phys_memory = 0UL;
+ unsigned long bootmap_pfn;
int i;
/* Limit maximum memory until we implement highmem for sparc */
@@ -160,7 +165,7 @@ unsigned long __init bootmem_init(void)
/* Start with page aligned address of last symbol in kernel
* image.
*/
- start_pfn = (unsigned long)__pa(PAGE_ALIGN((unsigned long) &end));
+ start_pfn = (unsigned long)__pa(PAGE_ALIGN((unsigned long) &_end));
/* Adjust up to the physical address where the kernel begins. */
start_pfn += phys_base;
@@ -168,14 +173,36 @@ unsigned long __init bootmem_init(void)
/* Now shift down to get the real physical page frame number. */
start_pfn >>= PAGE_SHIFT;
+ bootmap_pfn = start_pfn;
+
end_pfn = end_of_phys_memory >> PAGE_SHIFT;
+#ifdef CONFIG_BLK_DEV_INITRD
+ /* Now have to check initial ramdisk, so that bootmap does not overwrite it */
+ if (sparc_ramdisk_image) {
+ if (sparc_ramdisk_image >= (unsigned long)&_end - 2 * PAGE_SIZE)
+ sparc_ramdisk_image -= KERNBASE;
+ initrd_start = sparc_ramdisk_image + phys_base;
+ initrd_end = initrd_start + sparc_ramdisk_size;
+ if (initrd_end > end_of_phys_memory) {
+ printk(KERN_CRIT "initrd extends beyond end of memory "
+ "(0x%016lx > 0x%016lx)\ndisabling initrd\n",
+ initrd_end, end_of_phys_memory);
+ initrd_start = 0;
+ }
+ if (initrd_start) {
+ if (initrd_start >= (start_pfn << PAGE_SHIFT) &&
+ initrd_start < (start_pfn << PAGE_SHIFT) + 2 * PAGE_SIZE)
+ bootmap_pfn = PAGE_ALIGN (initrd_end) >> PAGE_SHIFT;
+ }
+ }
+#endif
/* Initialize the boot-time allocator. */
#ifdef DEBUG_BOOTMEM
- prom_printf("init_bootmem(spfn[%lx],epfn[%lx])\n",
- start_pfn, end_pfn);
+ prom_printf("init_bootmem(spfn[%lx],bpfn[%lx],epfn[%lx])\n",
+ start_pfn, bootmap_pfn, end_pfn);
#endif
- bootmap_size = init_bootmem(start_pfn, end_pfn);
+ bootmap_size = init_bootmem(bootmap_pfn, end_pfn);
/* Now register the available physical memory with the
* allocator.
@@ -190,15 +217,27 @@ unsigned long __init bootmem_init(void)
sp_banks[i].num_bytes);
}
- /* Reserve the kernel text/data/bss and the bootmem bitmap. */
+ /* Reserve the kernel text/data/bss, the bootmem bitmap and initrd. */
#ifdef DEBUG_BOOTMEM
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (initrd_start)
+ prom_printf("reserve_bootmem: base[%lx] size[%lx]\n",
+ initrd_start, initrd_end - initrd_start);
+#endif
prom_printf("reserve_bootmem: base[%lx] size[%lx]\n",
- phys_base,
- (start_pfn << PAGE_SHIFT) +
- bootmap_size + PAGE_SIZE-1 - phys_base);
+ phys_base, (start_pfn << PAGE_SHIFT) - phys_base);
+ prom_printf("reserve_bootmem: base[%lx] size[%lx]\n",
+ (bootmap_pfn << PAGE_SHIFT), bootmap_size);
#endif
- reserve_bootmem(phys_base, (start_pfn << PAGE_SHIFT) +
- bootmap_size + PAGE_SIZE-1 - phys_base);
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (initrd_start) {
+ reserve_bootmem(initrd_start, initrd_end - initrd_start);
+ initrd_start += PAGE_OFFSET;
+ initrd_end += PAGE_OFFSET;
+ }
+#endif
+ reserve_bootmem(phys_base, (start_pfn << PAGE_SHIFT) - phys_base);
+ reserve_bootmem((bootmap_pfn << PAGE_SHIFT), bootmap_size);
#ifdef DEBUG_BOOTMEM
prom_printf("init_bootmem: return end_pfn[%lx]\n", end_pfn);
@@ -371,18 +410,6 @@ void __init mem_init(void)
}
memset(sparc_valid_addr_bitmap, 0, i << 2);
- /* fix this */
-#ifdef CONFIG_BLK_DEV_INITRD
- addr = __va(phys_base);
- last = PAGE_ALIGN((unsigned long)&end) + phys_base;
- while(addr < last) {
- if (initrd_below_start_ok && addr >= initrd_start && addr < initrd_end)
- mem_map[MAP_NR(addr)].flags &= ~(1<<PG_reserved);
- else
- addr += PAGE_SIZE;
- }
-#endif
-
taint_real_pages();
max_mapnr = last_valid_pfn;
@@ -446,6 +473,22 @@ void free_initmem (void)
printk ("Freeing unused kernel memory: %dk freed\n", (&__init_end - &__init_begin) >> 10);
}
+#ifdef CONFIG_BLK_DEV_INITRD
+void free_initrd_mem(unsigned long start, unsigned long end)
+{
+ if (start < end)
+ printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10);
+ for (; start < end; start += PAGE_SIZE) {
+ struct page *p = mem_map + MAP_NR(start);
+
+ ClearPageReserved(p);
+ set_page_count(p, 1);
+ __free_page(p);
+ num_physpages++;
+ }
+}
+#endif
+
void si_meminfo(struct sysinfo *val)
{
val->totalram = totalram_pages;
diff --git a/arch/sparc64/config.in b/arch/sparc64/config.in
index a1fb222c1..b4d7e5da9 100644
--- a/arch/sparc64/config.in
+++ b/arch/sparc64/config.in
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.101 2000/03/13 05:49:55 jj Exp $
+# $Id: config.in,v 1.104 2000/03/15 15:02:28 jj Exp $
# For a description of the syntax of this configuration file,
# see the Configure script.
#
@@ -32,16 +32,6 @@ define_bool CONFIG_SUN_IO y
bool 'PCI support' CONFIG_PCI
source drivers/pci/Config.in
-mainmenu_option next_comment
-comment 'Console drivers'
-bool 'PROM console' CONFIG_PROM_CONSOLE
-bool 'Support Frame buffer devices' CONFIG_FB
-source drivers/video/Config.in
-endmenu
-
-source drivers/sbus/char/Config.in
-source drivers/sbus/audio/Config.in
-
tristate 'Openprom tree appears in /proc/openprom' CONFIG_SUN_OPENPROMFS
bool 'Networking support' CONFIG_NET
bool 'System V IPC' CONFIG_SYSVIPC
@@ -78,7 +68,17 @@ fi
endmenu
mainmenu_option next_comment
-comment 'Floppy, IDE, and other block devices'
+comment 'Console drivers'
+bool 'PROM console' CONFIG_PROM_CONSOLE
+bool 'Support Frame buffer devices' CONFIG_FB
+source drivers/video/Config.in
+endmenu
+
+source drivers/sbus/char/Config.in
+source drivers/sbus/audio/Config.in
+
+mainmenu_option next_comment
+comment 'Block devices'
bool 'Normal floppy disk support' CONFIG_BLK_DEV_FD
@@ -86,8 +86,8 @@ bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD
if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then
tristate ' Linear (append) mode' CONFIG_MD_LINEAR
tristate ' RAID-0 (striping) mode' CONFIG_MD_STRIPED
- tristate ' RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING
- tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5
+# tristate ' RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING
+# tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5
fi
tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
@@ -98,6 +98,8 @@ fi
tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP
tristate 'Network block device support' CONFIG_BLK_DEV_NBD
+endmenu
+
if [ "$CONFIG_NET" = "y" ]; then
source net/Config.in
fi
@@ -224,6 +226,7 @@ if [ "$CONFIG_NET" = "y" ]; then
tristate ' Sun QuadEthernet support' CONFIG_SUNQE
if [ "$CONFIG_PCI" = "y" ]; then
tristate 'Generic DECchip & DIGITAL EtherWORKS PCI/EISA' CONFIG_DE4X5
+ tristate 'DECchip Tulip (dc21x4x) PCI support' CONFIG_TULIP
tristate '3c590/3c900 series (592/595/597) "Vortex/Boomerang" support' CONFIG_VORTEX
tristate 'RealTek RTL-8139 support' CONFIG_8139TOO
tristate 'PCI NE2000 support' CONFIG_NE2K_PCI
diff --git a/arch/sparc64/defconfig b/arch/sparc64/defconfig
index db85cfe1e..2f1d42a19 100644
--- a/arch/sparc64/defconfig
+++ b/arch/sparc64/defconfig
@@ -26,6 +26,42 @@ CONFIG_SUN_AUXIO=y
CONFIG_SUN_IO=y
CONFIG_PCI=y
CONFIG_PCI_NAMES=y
+CONFIG_SUN_OPENPROMFS=m
+CONFIG_NET=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_SYSCTL=y
+CONFIG_KCORE_ELF=y
+CONFIG_SPARC32_COMPAT=y
+CONFIG_BINFMT_ELF32=y
+# CONFIG_BINFMT_AOUT32 is not set
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_MISC=m
+# CONFIG_SUNOS_EMUL is not set
+CONFIG_SOLARIS_EMUL=m
+
+#
+# Parallel port support
+#
+CONFIG_PARPORT=m
+CONFIG_PARPORT_PC=m
+CONFIG_PARPORT_PC_FIFO=y
+# CONFIG_PARPORT_PC_SUPERIO is not set
+# CONFIG_PARPORT_AMIGA is not set
+# CONFIG_PARPORT_MFC3 is not set
+# CONFIG_PARPORT_ATARI is not set
+# CONFIG_PARPORT_SUNBPP is not set
+# CONFIG_PARPORT_OTHER is not set
+CONFIG_PARPORT_1284=y
+CONFIG_PRINTER=m
+CONFIG_ENVCTRL=m
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODVERSIONS=y
+CONFIG_KMOD=y
#
# Console drivers
@@ -84,44 +120,9 @@ CONFIG_SPARCAUDIO=y
CONFIG_SPARCAUDIO_CS4231=y
# CONFIG_SPARCAUDIO_DBRI is not set
# CONFIG_SPARCAUDIO_DUMMY is not set
-CONFIG_SUN_OPENPROMFS=m
-CONFIG_NET=y
-CONFIG_SYSVIPC=y
-# CONFIG_BSD_PROCESS_ACCT is not set
-CONFIG_SYSCTL=y
-CONFIG_KCORE_ELF=y
-CONFIG_SPARC32_COMPAT=y
-CONFIG_BINFMT_ELF32=y
-# CONFIG_BINFMT_AOUT32 is not set
-CONFIG_BINFMT_ELF=y
-CONFIG_BINFMT_MISC=m
-# CONFIG_SUNOS_EMUL is not set
-CONFIG_SOLARIS_EMUL=m
-
-#
-# Parallel port support
-#
-CONFIG_PARPORT=m
-CONFIG_PARPORT_PC=m
-CONFIG_PARPORT_PC_FIFO=y
-# CONFIG_PARPORT_AMIGA is not set
-# CONFIG_PARPORT_MFC3 is not set
-# CONFIG_PARPORT_ATARI is not set
-# CONFIG_PARPORT_SUNBPP is not set
-# CONFIG_PARPORT_OTHER is not set
-CONFIG_PARPORT_1284=y
-CONFIG_PRINTER=m
-CONFIG_ENVCTRL=m
-
-#
-# Loadable module support
-#
-CONFIG_MODULES=y
-CONFIG_MODVERSIONS=y
-CONFIG_KMOD=y
#
-# Floppy, IDE, and other block devices
+# Block devices
#
CONFIG_BLK_DEV_FD=y
# CONFIG_BLK_DEV_MD is not set
@@ -331,6 +332,7 @@ CONFIG_HAPPYMEAL=y
CONFIG_SUNBMAC=m
CONFIG_SUNQE=m
CONFIG_DE4X5=m
+CONFIG_TULIP=m
CONFIG_VORTEX=m
CONFIG_8139TOO=m
CONFIG_NE2K_PCI=m
diff --git a/arch/sparc64/kernel/binfmt_aout32.c b/arch/sparc64/kernel/binfmt_aout32.c
index 3e95ed9cf..3504533d1 100644
--- a/arch/sparc64/kernel/binfmt_aout32.c
+++ b/arch/sparc64/kernel/binfmt_aout32.c
@@ -33,7 +33,7 @@
#include <asm/pgalloc.h>
static int load_aout32_binary(struct linux_binprm *, struct pt_regs * regs);
-static int load_aout32_library(int fd);
+static int load_aout32_library(struct file*);
static int aout32_core_dump(long signr, struct pt_regs * regs, struct file *file);
extern void dump_thread(struct pt_regs *, struct user *);
@@ -343,9 +343,8 @@ beyond_if:
}
/* N.B. Move to .h file and use code in fs/binfmt_aout.c? */
-static int load_aout32_library(int fd)
+static int load_aout32_library(struct file *file)
{
- struct file * file;
struct inode * inode;
unsigned long bss, start_addr, len;
unsigned long error;
@@ -353,12 +352,6 @@ static int load_aout32_library(int fd)
loff_t offset = 0;
struct exec ex;
- retval = -EACCES;
- file = fget(fd);
- if (!file)
- goto out;
- if (!file->f_op)
- goto out_putf;
inode = file->f_dentry->d_inode;
retval = -ENOEXEC;
@@ -367,23 +360,23 @@ static int load_aout32_library(int fd)
error = file->f_op->read(file, (char *) &ex, sizeof(ex), &offset);
set_fs(USER_DS);
if (error != sizeof(ex))
- goto out_putf;
+ goto out;
/* We come in here for the regular a.out style of shared libraries */
if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || N_TRSIZE(ex) ||
N_DRSIZE(ex) || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) ||
inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
- goto out_putf;
+ goto out;
}
if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) &&
(N_TXTOFF(ex) < inode->i_sb->s_blocksize)) {
printk("N_TXTOFF < BLOCK_SIZE. Please convert library\n");
- goto out_putf;
+ goto out;
}
if (N_FLAGS(ex))
- goto out_putf;
+ goto out;
/* For QMAGIC, the starting address is 0x20 into the page. We mask
this off to get the starting address for the page */
@@ -391,13 +384,15 @@ static int load_aout32_library(int fd)
start_addr = ex.a_entry & 0xfffff000;
/* Now use mmap to map the library into memory. */
+ down(&current->mm->mmap_sem);
error = do_mmap(file, start_addr, ex.a_text + ex.a_data,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
N_TXTOFF(ex));
+ up(&current->mm->mmap_sem);
retval = error;
if (error != start_addr)
- goto out_putf;
+ goto out;
len = PAGE_ALIGN(ex.a_text + ex.a_data);
bss = ex.a_text + ex.a_data + ex.a_bss;
@@ -405,12 +400,9 @@ static int load_aout32_library(int fd)
error = do_brk(start_addr + len, bss - len);
retval = error;
if (error != start_addr + len)
- goto out_putf;
+ goto out;
}
retval = 0;
-
-out_putf:
- fput(file);
out:
return retval;
}
diff --git a/arch/sparc64/kernel/ioctl32.c b/arch/sparc64/kernel/ioctl32.c
index d3a3814a8..0ba88adf6 100644
--- a/arch/sparc64/kernel/ioctl32.c
+++ b/arch/sparc64/kernel/ioctl32.c
@@ -1,4 +1,4 @@
-/* $Id: ioctl32.c,v 1.82 2000/03/13 21:57:27 davem Exp $
+/* $Id: ioctl32.c,v 1.83 2000/03/14 07:31:25 jj Exp $
* ioctl32.c: Conversion between 32bit and 64bit native ioctls.
*
* Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com)
@@ -8,6 +8,7 @@
* ioctls.
*/
+#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
@@ -44,6 +45,14 @@
#include <linux/raw.h>
#include <linux/smb_fs.h>
#include <linux/blkpg.h>
+#include <linux/blk.h>
+#include <linux/elevator.h>
+#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE)
+/* Ugh. This header really is not clean */
+#define min min
+#define max max
+#include <linux/lvm.h>
+#endif /* LVM */
#include <scsi/scsi.h>
/* Ugly hack. */
@@ -1980,6 +1989,353 @@ static int do_atm_ioctl(unsigned int fd, unsigned int cmd32, unsigned long arg)
return -EINVAL;
}
+#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE)
+/* Ugh, LVM. Pitty it was not cleaned up before accepted :((. */
+typedef struct {
+ uint8_t vg_name[NAME_LEN];
+ uint32_t vg_number;
+ uint32_t vg_access;
+ uint32_t vg_status;
+ uint32_t lv_max;
+ uint32_t lv_cur;
+ uint32_t lv_open;
+ uint32_t pv_max;
+ uint32_t pv_cur;
+ uint32_t pv_act;
+ uint32_t dummy;
+ uint32_t vgda;
+ uint32_t pe_size;
+ uint32_t pe_total;
+ uint32_t pe_allocated;
+ uint32_t pvg_total;
+ u32 proc;
+ u32 pv[ABS_MAX_PV + 1];
+ u32 lv[ABS_MAX_LV + 1];
+} vg32_t;
+
+typedef struct {
+ uint8_t id[2];
+ uint16_t version;
+ lvm_disk_data_t pv_on_disk;
+ lvm_disk_data_t vg_on_disk;
+ lvm_disk_data_t pv_namelist_on_disk;
+ lvm_disk_data_t lv_on_disk;
+ lvm_disk_data_t pe_on_disk;
+ uint8_t pv_name[NAME_LEN];
+ uint8_t vg_name[NAME_LEN];
+ uint8_t system_id[NAME_LEN];
+ kdev_t pv_dev;
+ uint32_t pv_number;
+ uint32_t pv_status;
+ uint32_t pv_allocatable;
+ uint32_t pv_size;
+ uint32_t lv_cur;
+ uint32_t pe_size;
+ uint32_t pe_total;
+ uint32_t pe_allocated;
+ uint32_t pe_stale;
+ u32 pe;
+ u32 inode;
+} pv32_t;
+
+typedef struct {
+ char lv_name[NAME_LEN];
+ u32 lv;
+} lv_req32_t;
+
+typedef struct {
+ u32 lv_index;
+ u32 lv;
+} lv_status_byindex_req32_t;
+
+typedef struct {
+ uint8_t lv_name[NAME_LEN];
+ kdev_t old_dev;
+ kdev_t new_dev;
+ u32 old_pe;
+ u32 new_pe;
+} le_remap_req32_t;
+
+typedef struct {
+ char pv_name[NAME_LEN];
+ u32 pv;
+} pv_status_req32_t;
+
+typedef struct {
+ uint8_t lv_name[NAME_LEN];
+ uint8_t vg_name[NAME_LEN];
+ uint32_t lv_access;
+ uint32_t lv_status;
+ uint32_t lv_open;
+ kdev_t lv_dev;
+ uint32_t lv_number;
+ uint32_t lv_mirror_copies;
+ uint32_t lv_recovery;
+ uint32_t lv_schedule;
+ uint32_t lv_size;
+ u32 lv_current_pe;
+ uint32_t lv_current_le;
+ uint32_t lv_allocated_le;
+ uint32_t lv_stripes;
+ uint32_t lv_stripesize;
+ uint32_t lv_badblock;
+ uint32_t lv_allocation;
+ uint32_t lv_io_timeout;
+ uint32_t lv_read_ahead;
+ /* delta to version 1 starts here */
+ u32 lv_snapshot_org;
+ u32 lv_snapshot_prev;
+ u32 lv_snapshot_next;
+ u32 lv_block_exception;
+ uint32_t lv_remap_ptr;
+ uint32_t lv_remap_end;
+ uint32_t lv_chunk_size;
+ uint32_t lv_snapshot_minor;
+ char dummy[200];
+} lv32_t;
+
+typedef struct {
+ u32 hash[2];
+ u32 rsector_org;
+ kdev_t rdev_org;
+ u32 rsector_new;
+ kdev_t rdev_new;
+} lv_block_exception32_t;
+
+static void put_lv_t(lv_t *l)
+{
+ if (l->lv_current_pe) vfree(l->lv_current_pe);
+ if (l->lv_block_exception) vfree(l->lv_block_exception);
+ kfree(l);
+}
+
+static lv_t *get_lv_t(u32 p, int *errp)
+{
+ int err, i;
+ u32 ptr1, ptr2;
+ size_t size;
+ lv_block_exception32_t *lbe32;
+ lv_block_exception_t *lbe;
+ lv32_t *ul = (lv32_t *)A(p);
+ lv_t *l = (lv_t *)kmalloc(sizeof(lv_t), GFP_KERNEL);
+ if (!l) {
+ *errp = -ENOMEM;
+ return NULL;
+ }
+ memset(l, 0, sizeof(lv_t));
+ err = copy_from_user(l, ul, (long)&((lv32_t *)0)->lv_current_pe);
+ err |= __copy_from_user(&l->lv_current_le, &ul->lv_current_le,
+ ((long)&ul->lv_snapshot_org) - ((long)&ul->lv_current_le));
+ err |= __copy_from_user(&l->lv_remap_ptr, &ul->lv_remap_ptr,
+ ((long)&ul->dummy[0]) - ((long)&ul->lv_remap_ptr));
+ err |= __get_user(ptr1, &ul->lv_current_pe);
+ err |= __get_user(ptr2, &ul->lv_block_exception);
+ if (err) {
+ kfree(l);
+ *errp = -EFAULT;
+ return NULL;
+ }
+ if (ptr1) {
+ size = l->lv_allocated_le * sizeof(pe_t);
+ l->lv_current_pe = vmalloc(size);
+ if (l->lv_current_pe)
+ err = copy_from_user(l->lv_current_pe, (void *)A(ptr1), size);
+ }
+ if (!err && ptr2) {
+ size = l->lv_remap_end * sizeof(lv_block_exception_t);
+ l->lv_block_exception = lbe = vmalloc(size);
+ if (l->lv_block_exception) {
+ lbe32 = (lv_block_exception32_t *)A(ptr2);
+ memset(lbe, 0, size);
+ for (i = 0; i < l->lv_remap_end; i++, lbe++, lbe32++) {
+ err |= get_user(lbe->rsector_org, &lbe32->rsector_org);
+ err |= __get_user(lbe->rdev_org, &lbe32->rdev_org);
+ err |= __get_user(lbe->rsector_new, &lbe32->rsector_new);
+ err |= __get_user(lbe->rdev_new, &lbe32->rdev_new);
+ }
+ }
+ }
+ if (err || (ptr1 && !l->lv_current_pe) || (ptr2 && !l->lv_block_exception)) {
+ if (!err)
+ *errp = -ENOMEM;
+ else
+ *errp = -EFAULT;
+ put_lv_t(l);
+ return NULL;
+ }
+ return l;
+}
+
+static int copy_lv_t(u32 ptr, lv_t *l)
+{
+ int err;
+ lv32_t *ul = (lv32_t *)A(ptr);
+ u32 ptr1;
+ size_t size;
+
+ err = get_user(ptr1, &ul->lv_current_pe);
+ if (err)
+ return -EFAULT;
+ err = copy_to_user(ul, l, (long)&((lv32_t *)0)->lv_current_pe);
+ err |= __copy_to_user(&ul->lv_current_le, &l->lv_current_le,
+ ((long)&ul->lv_snapshot_org) - ((long)&ul->lv_current_le));
+ err |= __copy_to_user(&ul->lv_remap_ptr, &l->lv_remap_ptr,
+ ((long)&ul->dummy[0]) - ((long)&ul->lv_remap_ptr));
+ size = l->lv_allocated_le * sizeof(pe_t);
+ err |= __copy_to_user((void *)A(ptr1), l->lv_current_pe, size);
+ return -EFAULT;
+}
+
+static int do_lvm_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+ vg_t *v;
+ union {
+ lv_req_t lv_req;
+ le_remap_req_t le_remap;
+ lv_status_byindex_req_t lv_byindex;
+ pv_status_req32_t pv_status;
+ } u;
+ pv_t p;
+ int err;
+ u32 ptr = 0;
+ int i;
+ mm_segment_t old_fs;
+ void *karg = &u;
+
+ switch (cmd) {
+ case VG_STATUS:
+ v = kmalloc(sizeof(vg_t), GFP_KERNEL);
+ if (!v) return -ENOMEM;
+ karg = v;
+ break;
+ case VG_CREATE:
+ v = kmalloc(sizeof(vg_t), GFP_KERNEL);
+ if (!v) return -ENOMEM;
+ if (copy_from_user(v, (void *)arg, (long)&((vg32_t *)0)->proc) ||
+ __get_user(v->proc, &((vg32_t *)arg)->proc)) {
+ kfree(v);
+ return -EFAULT;
+ }
+ karg = v;
+ memset(v->pv, 0, sizeof(v->pv) + sizeof(v->lv));
+ if (v->pv_max > ABS_MAX_PV || v->lv_max == ABS_MAX_LV) return -EPERM;
+ for (i = 0; i < v->pv_max; i++) {
+ err = __get_user(ptr, &((vg32_t *)arg)->pv[i]);
+ if (err) break;
+ if (ptr) {
+ v->pv[i] = kmalloc(sizeof(pv_t), GFP_KERNEL);
+ if (!v->pv[i]) {
+ err = -ENOMEM;
+ break;
+ }
+ err = copy_from_user(v->pv[i], (void *)A(ptr), sizeof(pv32_t) - 8);
+ if (err) {
+ err = -EFAULT;
+ break;
+ }
+ v->pv[i]->pe = NULL; v->pv[i]->inode = NULL;
+ }
+ }
+ if (!err) {
+ for (i = 0; i < v->lv_max; i++) {
+ err = __get_user(ptr, &((vg32_t *)arg)->lv[i]);
+ if (err) break;
+ if (ptr) {
+ v->lv[i] = get_lv_t(ptr, &err);
+ if (err) break;
+ }
+ }
+ }
+ break;
+ case LV_CREATE:
+ case LV_EXTEND:
+ case LV_REDUCE:
+ case LV_REMOVE:
+ case LV_STATUS_BYNAME:
+ err = copy_from_user(&u.pv_status, arg, sizeof(u.pv_status.pv_name));
+ if (err) return -EFAULT;
+ if (cmd != LV_REMOVE) {
+ err = __get_user(ptr, &((lv_req32_t *)arg)->lv);
+ if (err) return err;
+ u.lv_req.lv = get_lv_t(ptr, &err);
+ } else
+ u.lv_req.lv = NULL;
+ break;
+ case LV_STATUS_BYINDEX:
+ err = get_user(u.lv_byindex.lv_index, &((lv_status_byindex_req32_t *)arg)->lv_index);
+ err |= __get_user(ptr, &((lv_status_byindex_req32_t *)arg)->lv);
+ if (err) return err;
+ u.lv_byindex.lv = get_lv_t(ptr, &err);
+ break;
+ case VG_EXTEND:
+ err = copy_from_user(&p, (void *)arg, sizeof(pv32_t) - 8);
+ if (err) return -EFAULT;
+ p.pe = NULL; p.inode = NULL;
+ karg = &p;
+ break;
+ case LE_REMAP:
+ err = copy_from_user(&u.le_remap, (void *)arg, sizeof(le_remap_req32_t));
+ if (err) return -EFAULT;
+ u.le_remap.new_pe = ((le_remap_req32_t *)&u.le_remap)->new_pe;
+ u.le_remap.old_pe = ((le_remap_req32_t *)&u.le_remap)->old_pe;
+ break;
+ case PV_CHANGE:
+ case PV_STATUS:
+ err = copy_from_user(&u.pv_status, arg, sizeof(u.lv_req.lv_name));
+ if (err) return -EFAULT;
+ err = __get_user(ptr, &((pv_status_req32_t *)arg)->pv);
+ if (err) return err;
+ u.pv_status.pv = &p;
+ if (cmd == PV_CHANGE) {
+ err = copy_from_user(&p, (void *)A(ptr), sizeof(pv32_t) - 8);
+ if (err) return -EFAULT;
+ p.pe = NULL; p.inode = NULL;
+ }
+ break;
+ }
+ old_fs = get_fs(); set_fs (KERNEL_DS);
+ err = sys_ioctl (fd, cmd, (unsigned long)karg);
+ set_fs (old_fs);
+ switch (cmd) {
+ case VG_STATUS:
+ if (!err) {
+ if (copy_to_user((void *)arg, v, (long)&((vg32_t *)0)->proc) ||
+ clear_user(&((vg32_t *)arg)->proc, sizeof(vg32_t) - (long)&((vg32_t *)0)->proc))
+ err = -EFAULT;
+ }
+ kfree(v);
+ break;
+ case VG_CREATE:
+ for (i = 0; i < v->pv_max; i++)
+ if (v->pv[i]) kfree(v->pv[i]);
+ for (i = 0; i < v->lv_max; i++)
+ if (v->lv[i]) put_lv_t(v->lv[i]);
+ kfree(v);
+ break;
+ case LV_STATUS_BYNAME:
+ if (!err && u.lv_req.lv) err = copy_lv_t(ptr, u.lv_req.lv);
+ /* Fall through */
+ case LV_CREATE:
+ case LV_EXTEND:
+ case LV_REDUCE:
+ if (u.lv_req.lv) put_lv_t(u.lv_req.lv);
+ break;
+ case LV_STATUS_BYINDEX:
+ if (u.lv_byindex.lv) {
+ if (!err) err = copy_lv_t(ptr, u.lv_byindex.lv);
+ put_lv_t(u.lv_byindex.lv);
+ }
+ case PV_STATUS:
+ if (!err) {
+ err = copy_to_user((void *)A(ptr), &p, sizeof(pv32_t) - 8);
+ if (err) return -EFAULT;
+ }
+ break;
+ }
+ return err;
+}
+#endif
+
static int ret_einval(unsigned int fd, unsigned int cmd, unsigned long arg)
{
return -EINVAL;
@@ -2027,6 +2383,39 @@ static int blkpg_ioctl_trans(unsigned int fd, unsigned int cmd, struct blkpg_ioc
return err;
}
+typedef struct blkelv_ioctl32_arg_s {
+ u32 queue_ID;
+ int read_latency;
+ int write_latency;
+ int max_bomb_segments;
+} blkelv_ioctl32_arg_t;
+
+static int do_blkelv_ioctl(unsigned int fd, unsigned int cmd, blkelv_ioctl32_arg_t *arg)
+{
+ blkelv_ioctl_arg_t b;
+ int err;
+ mm_segment_t old_fs = get_fs();
+
+ if (cmd == BLKELVSET) {
+ err = get_user((long)b.queue_ID, &arg->queue_ID);
+ err |= __get_user(b.read_latency, &arg->read_latency);
+ err |= __get_user(b.write_latency, &arg->write_latency);
+ err |= __get_user(b.max_bomb_segments, &arg->max_bomb_segments);
+ if (err) return err;
+ }
+ set_fs (KERNEL_DS);
+ err = sys_ioctl(fd, cmd, (unsigned long)&b);
+ set_fs (old_fs);
+ if (cmd == BLKELVGET && !err) {
+ err = put_user((long)b.queue_ID, &arg->queue_ID);
+ err |= __put_user(b.read_latency, &arg->read_latency);
+ err |= __put_user(b.write_latency, &arg->write_latency);
+ err |= __put_user(b.max_bomb_segments, &arg->max_bomb_segments);
+ if (err) return err;
+ }
+ return err;
+}
+
static int ioc_settimeout(unsigned int fd, unsigned int cmd, unsigned long arg)
{
return rw_long(fd, AUTOFS_IOC_SETTIMEOUT, arg);
@@ -2548,6 +2937,24 @@ COMPATIBLE_IOCTL(ATMTCP_CREATE)
COMPATIBLE_IOCTL(ATMTCP_REMOVE)
COMPATIBLE_IOCTL(ATMMPC_CTRL)
COMPATIBLE_IOCTL(ATMMPC_DATA)
+#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE)
+/* 0xfe - lvm */
+COMPATIBLE_IOCTL(VG_SET_EXTENDABLE)
+COMPATIBLE_IOCTL(VG_STATUS_GET_COUNT)
+COMPATIBLE_IOCTL(VG_STATUS_GET_NAMELIST)
+COMPATIBLE_IOCTL(VG_REMOVE)
+COMPATIBLE_IOCTL(VG_REDUCE)
+COMPATIBLE_IOCTL(PE_LOCK_UNLOCK)
+COMPATIBLE_IOCTL(PV_FLUSH)
+COMPATIBLE_IOCTL(LVM_LOCK_LVM)
+COMPATIBLE_IOCTL(LVM_GET_IOP_VERSION)
+#ifdef LVM_TOTAL_RESET
+COMPATIBLE_IOCTL(LVM_RESET)
+#endif
+COMPATIBLE_IOCTL(LV_SET_ACCESS)
+COMPATIBLE_IOCTL(LV_SET_STATUS)
+COMPATIBLE_IOCTL(LV_SET_ALLOCATION)
+#endif /* LVM */
/* And these ioctls need translation */
HANDLE_IOCTL(SIOCGIFNAME, dev_ifname32)
HANDLE_IOCTL(SIOCGIFCONF, dev_ifconf)
@@ -2594,6 +3001,8 @@ HANDLE_IOCTL(0x1260, broken_blkgetsize)
HANDLE_IOCTL(BLKFRAGET, w_long)
HANDLE_IOCTL(BLKSECTGET, w_long)
HANDLE_IOCTL(BLKPG, blkpg_ioctl_trans)
+HANDLE_IOCTL(BLKELVGET, do_blkelv_ioctl)
+HANDLE_IOCTL(BLKELVSET, do_blkelv_ioctl)
HANDLE_IOCTL(FBIOPUTCMAP32, fbiogetputcmap)
HANDLE_IOCTL(FBIOGETCMAP32, fbiogetputcmap)
HANDLE_IOCTL(FBIOSCURSOR32, fbiogscursor)
@@ -2677,6 +3086,20 @@ HANDLE_IOCTL(SONET_CLRDIAG, do_atm_ioctl)
HANDLE_IOCTL(SONET_SETFRAMING, do_atm_ioctl)
HANDLE_IOCTL(SONET_GETFRAMING, do_atm_ioctl)
HANDLE_IOCTL(SONET_GETFRSENSE, do_atm_ioctl)
+#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE)
+HANDLE_IOCTL(VG_STATUS, do_lvm_ioctl)
+HANDLE_IOCTL(VG_CREATE, do_lvm_ioctl)
+HANDLE_IOCTL(VG_EXTEND, do_lvm_ioctl)
+HANDLE_IOCTL(LV_CREATE, do_lvm_ioctl)
+HANDLE_IOCTL(LV_REMOVE, do_lvm_ioctl)
+HANDLE_IOCTL(LV_EXTEND, do_lvm_ioctl)
+HANDLE_IOCTL(LV_REDUCE, do_lvm_ioctl)
+HANDLE_IOCTL(LV_STATUS_BYNAME, do_lvm_ioctl)
+HANDLE_IOCTL(LV_STATUS_BYINDEX, do_lvm_ioctl)
+HANDLE_IOCTL(LE_REMAP, do_lvm_ioctl)
+HANDLE_IOCTL(PV_CHANGE, do_lvm_ioctl)
+HANDLE_IOCTL(PV_STATUS, do_lvm_ioctl)
+#endif /* LVM */
IOCTL_TABLE_END
unsigned int ioctl32_hash_table[1024];
diff --git a/arch/sparc64/kernel/setup.c b/arch/sparc64/kernel/setup.c
index 0d1b968fa..f0c9460fa 100644
--- a/arch/sparc64/kernel/setup.c
+++ b/arch/sparc64/kernel/setup.c
@@ -1,4 +1,4 @@
-/* $Id: setup.c,v 1.52 2000/03/03 23:48:41 davem Exp $
+/* $Id: setup.c,v 1.53 2000/03/15 14:42:52 jj Exp $
* linux/arch/sparc64/kernel/setup.c
*
* Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu)
@@ -431,8 +431,6 @@ extern void panic_setup(char *, int *);
extern unsigned short root_flags;
extern unsigned short root_dev;
extern unsigned short ram_flags;
-extern unsigned int sparc_ramdisk_image;
-extern unsigned int sparc_ramdisk_size;
#define RAMDISK_IMAGE_START_MASK 0x07FF
#define RAMDISK_PROMPT_FLAG 0x8000
#define RAMDISK_LOAD_FLAG 0x4000
@@ -512,29 +510,6 @@ void __init setup_arch(char **cmdline_p)
rd_prompt = ((ram_flags & RAMDISK_PROMPT_FLAG) != 0);
rd_doload = ((ram_flags & RAMDISK_LOAD_FLAG) != 0);
#endif
-#ifdef CONFIG_BLK_DEV_INITRD
-// FIXME needs to do the new bootmem alloc stuff
- if (sparc_ramdisk_image) {
- unsigned long start = 0;
-
- if (sparc_ramdisk_image >= (unsigned long)&end - 2 * PAGE_SIZE)
- sparc_ramdisk_image -= KERNBASE;
- initrd_start = sparc_ramdisk_image + phys_base + PAGE_OFFSET;
- initrd_end = initrd_start + sparc_ramdisk_size;
- if (initrd_end > *memory_end_p) {
- printk(KERN_CRIT "initrd extends beyond end of memory "
- "(0x%016lx > 0x%016lx)\ndisabling initrd\n",
- initrd_end,*memory_end_p);
- initrd_start = 0;
- }
- if (initrd_start)
- start = sparc_ramdisk_image + KERNBASE;
- if (start >= *memory_start_p && start < *memory_start_p + 2 * PAGE_SIZE) {
- initrd_below_start_ok = 1;
- *memory_start_p = PAGE_ALIGN (start + sparc_ramdisk_size);
- }
- }
-#endif
/* Due to stack alignment restrictions and assumptions... */
init_mm.mmap->vm_page_prot = PAGE_SHARED;
diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c
index b4ee5625e..0d5f615cf 100644
--- a/arch/sparc64/kernel/smp.c
+++ b/arch/sparc64/kernel/smp.c
@@ -528,7 +528,7 @@ static unsigned long penguins_are_doing_time = 0;
void smp_capture(void)
{
if (smp_processors_ready) {
- int result = atomic_add_return(1, &smp_capture_depth);
+ int result = __atomic_add(1, &smp_capture_depth);
membar("#StoreStore | #LoadStore");
if(result == 1) {
@@ -596,18 +596,21 @@ void smp_promstop_others(void)
smp_cross_call(&xcall_promstop, 0, 0, 0);
}
-static inline void sparc64_do_profile(unsigned long pc, unsigned long g3)
+static inline void sparc64_do_profile(unsigned long pc, unsigned long o7)
{
if (prof_buffer && current->pid) {
extern int _stext;
extern int rwlock_impl_begin, rwlock_impl_end;
extern int atomic_impl_begin, atomic_impl_end;
+ extern int __memcpy_begin, __memcpy_end;
- if ((pc >= (unsigned long) &rwlock_impl_begin &&
+ if ((pc >= (unsigned long) &atomic_impl_begin &&
+ pc < (unsigned long) &atomic_impl_end) ||
+ (pc >= (unsigned long) &rwlock_impl_begin &&
pc < (unsigned long) &rwlock_impl_end) ||
- (pc >= (unsigned long) &atomic_impl_begin &&
- pc < (unsigned long) &atomic_impl_end))
- pc = g3;
+ (pc >= (unsigned long) &__memcpy_begin &&
+ pc < (unsigned long) &__memcpy_end))
+ pc = o7;
pc -= (unsigned long) &_stext;
pc >>= prof_shift;
@@ -646,7 +649,7 @@ void smp_percpu_timer_interrupt(struct pt_regs *regs)
clear_softint((1UL << 0));
do {
if(!user)
- sparc64_do_profile(regs->tpc, regs->u_regs[UREG_G3]);
+ sparc64_do_profile(regs->tpc, regs->u_regs[UREG_RETPC]);
if(!--prof_counter(cpu))
{
if (cpu == boot_cpu_id) {
diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c
index f798358ce..26e11085d 100644
--- a/arch/sparc64/kernel/sparc64_ksyms.c
+++ b/arch/sparc64/kernel/sparc64_ksyms.c
@@ -1,4 +1,4 @@
-/* $Id: sparc64_ksyms.c,v 1.75 2000/02/21 15:50:08 davem Exp $
+/* $Id: sparc64_ksyms.c,v 1.79 2000/03/17 14:41:18 davem Exp $
* arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -109,10 +109,10 @@ extern unsigned long phys_base;
*/
#define EXPORT_SYMBOL_PRIVATE(sym) \
-extern int __sparc_priv_ ## sym (int) __asm__("__" ## #sym); \
+extern int __sparc_priv_ ## sym (int) __asm__("__" #sym); \
const struct module_symbol __export_priv_##sym \
__attribute__((section("__ksymtab"))) = \
-{ (unsigned long) &__sparc_priv_ ## sym, "__" ## #sym }
+{ (unsigned long) &__sparc_priv_ ## sym, "__" #sym }
/* used by various drivers */
#ifdef __SMP__
@@ -142,6 +142,8 @@ EXPORT_SYMBOL(cpu_data);
/* Misc SMP information */
EXPORT_SYMBOL(smp_num_cpus);
+EXPORT_SYMBOL(__cpu_number_map);
+EXPORT_SYMBOL(__cpu_logical_map);
/* Spinlock debugging library, optional. */
#ifdef SPIN_LOCK_DEBUG
@@ -207,6 +209,7 @@ EXPORT_SYMBOL(pci_map_sg);
EXPORT_SYMBOL(pci_unmap_sg);
EXPORT_SYMBOL(pci_dma_sync_single);
EXPORT_SYMBOL(pci_dma_sync_sg);
+EXPORT_SYMBOL(pci_dma_supported);
#endif
/* Solaris/SunOS binary compatibility */
@@ -288,6 +291,9 @@ EXPORT_SYMBOL(move_addr_to_user);
EXPORT_SYMBOL(__memcpy);
EXPORT_SYMBOL(__memset);
EXPORT_SYMBOL(clear_page);
+EXPORT_SYMBOL(copy_page);
+EXPORT_SYMBOL(clear_user_page);
+EXPORT_SYMBOL(copy_user_page);
EXPORT_SYMBOL(__bzero);
EXPORT_SYMBOL(__memscan_zero);
EXPORT_SYMBOL(__memscan_generic);
diff --git a/arch/sparc64/kernel/sys_sparc.c b/arch/sparc64/kernel/sys_sparc.c
index 81b4c4de1..82aedbb08 100644
--- a/arch/sparc64/kernel/sys_sparc.c
+++ b/arch/sparc64/kernel/sys_sparc.c
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc.c,v 1.36 2000/02/16 07:31:35 davem Exp $
+/* $Id: sys_sparc.c,v 1.37 2000/03/17 05:48:46 anton Exp $
* linux/arch/sparc64/kernel/sys_sparc.c
*
* This file contains various random system calls that
@@ -348,9 +348,10 @@ asmlinkage int solaris_syscall(struct pt_regs *regs)
lock_kernel();
regs->tpc = regs->tnpc;
regs->tnpc += 4;
- if(++count <= 20)
+ if(++count <= 5) {
printk ("For Solaris binary emulation you need solaris module loaded\n");
- show_regs (regs);
+ show_regs (regs);
+ }
send_sig(SIGSEGV, current, 1);
unlock_kernel();
return -ENOSYS;
diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c
index d4ecb0f4f..763acdc0f 100644
--- a/arch/sparc64/kernel/sys_sparc32.c
+++ b/arch/sparc64/kernel/sys_sparc32.c
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc32.c,v 1.136 2000/03/13 21:57:29 davem Exp $
+/* $Id: sys_sparc32.c,v 1.139 2000/03/16 20:37:57 davem Exp $
* sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
*
* Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -1746,70 +1746,58 @@ static int copy_mount_stuff_to_kernel(const void *user, unsigned long *kernel)
return 0;
}
-extern asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
- unsigned long new_flags, void *data);
+extern long do_sys_mount(char * dev_name, char * dir_name, unsigned long type_page,
+ unsigned long new_flags, unsigned long data_page);
#define SMBFS_NAME "smbfs"
#define NCPFS_NAME "ncpfs"
asmlinkage int sys32_mount(char *dev_name, char *dir_name, char *type, unsigned long new_flags, u32 data)
{
- unsigned long type_page;
+ unsigned long type_page = 0;
+ unsigned long data_page = 0;
int err, is_smb, is_ncp;
- if(!capable(CAP_SYS_ADMIN))
- return -EPERM;
is_smb = is_ncp = 0;
+
+ lock_kernel();
err = copy_mount_stuff_to_kernel((const void *)type, &type_page);
- if(err)
- return err;
- if(type_page) {
- is_smb = !strcmp((char *)type_page, SMBFS_NAME);
- is_ncp = !strcmp((char *)type_page, NCPFS_NAME);
+ if (err)
+ goto out;
+
+ if (!type_page) {
+ err = -EINVAL;
+ goto out;
}
- if(!is_smb && !is_ncp) {
- if(type_page)
- free_page(type_page);
- return sys_mount(dev_name, dir_name, type, new_flags, (void *)AA(data));
- } else {
- unsigned long dev_page, dir_page, data_page;
- mm_segment_t old_fs;
- err = copy_mount_stuff_to_kernel((const void *)dev_name, &dev_page);
- if(err)
- goto out;
- err = copy_mount_stuff_to_kernel((const void *)dir_name, &dir_page);
- if(err)
- goto dev_out;
- err = copy_mount_stuff_to_kernel((const void *)AA(data), &data_page);
- if(err)
- goto dir_out;
- if(is_ncp)
+ is_smb = !strcmp((char *)type_page, SMBFS_NAME);
+ is_ncp = !strcmp((char *)type_page, NCPFS_NAME);
+
+ err = copy_mount_stuff_to_kernel((const void *)AA(data), &data_page);
+ if (err)
+ goto type_out;
+
+ if (!is_smb && !is_ncp) {
+ err = do_sys_mount(dev_name, dir_name, type_page, new_flags,
+ data_page);
+ } else {
+ if (is_ncp)
do_ncp_super_data_conv((void *)data_page);
- else if(is_smb)
- do_smb_super_data_conv((void *)data_page);
else
- panic("The problem is here...");
- old_fs = get_fs();
- set_fs(KERNEL_DS);
- err = sys_mount((char *)dev_page, (char *)dir_page,
- (char *)type_page, new_flags,
- (void *)data_page);
- set_fs(old_fs);
+ do_smb_super_data_conv((void *)data_page);
- if(data_page)
- free_page(data_page);
- dir_out:
- if(dir_page)
- free_page(dir_page);
- dev_out:
- if(dev_page)
- free_page(dev_page);
- out:
- if(type_page)
- free_page(type_page);
- return err;
+ err = do_sys_mount(dev_name, dir_name, type_page, new_flags,
+ data_page);
}
+
+ free_page(data_page);
+
+type_out:
+ free_page(type_page);
+
+out:
+ unlock_kernel();
+ return err;
}
struct rusage32 {
@@ -3071,7 +3059,7 @@ do_execve32(char * filename, u32 * argv, u32 * envp, struct pt_regs * regs)
memset(bprm.page, 0, MAX_ARG_PAGES * sizeof(bprm.page[0]));
lock_kernel();
- dentry = open_namei(filename, 0, 0);
+ dentry = open_namei(filename);
unlock_kernel();
retval = PTR_ERR(dentry);
@@ -4166,7 +4154,7 @@ asmlinkage long sparc32_open(const char * filename, int flags, int mode)
if (fd >= 0) {
struct file * f;
lock_kernel();
- f = filp_open(tmp, flags, mode);
+ f = filp_open(tmp, flags, mode, NULL);
unlock_kernel();
error = PTR_ERR(f);
if (IS_ERR(f))
@@ -4218,12 +4206,3 @@ out_sem:
out:
return ret;
}
-
-extern asmlinkage long sys_mincore(unsigned long start, size_t len, unsigned char *vec);
-
-asmlinkage long sys32_mincore(unsigned long start, u32 __len, unsigned char *vec)
-{
- size_t len = (size_t) __len;
-
- return sys_mincore(start, len, vec);
-}
diff --git a/arch/sparc64/kernel/sys_sunos32.c b/arch/sparc64/kernel/sys_sunos32.c
index f7f5964e9..a572e4976 100644
--- a/arch/sparc64/kernel/sys_sunos32.c
+++ b/arch/sparc64/kernel/sys_sunos32.c
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos32.c,v 1.41 2000/03/13 21:57:31 davem Exp $
+/* $Id: sys_sunos32.c,v 1.42 2000/03/15 02:43:35 davem Exp $
* sys_sunos32.c: SunOS binary compatability layer on sparc64.
*
* Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -225,33 +225,6 @@ asmlinkage void sunos_vadvise(u32 strategy)
unlock_kernel();
}
-/* Same as vadvise, and just as bogus, but for a range of virtual
- * process address space.
- */
-#define MADV_NORMAL 0 /* Nothing special... */
-#define MADV_RANDOM 1 /* I am emacs... */
-#define MADV_SEQUENTIAL 2 /* I am researcher code... */
-#define MADV_WILLNEED 3 /* Pages in this range will be needed */
-#define MADV_DONTNEED 4 /* Pages in this range won't be needed */
-
-static char *mstrings[] = {
- "MADV_NORMAL",
- "MADV_RANDOM",
- "MADV_SEQUENTIAL",
- "MADV_WILLNEED",
- "MADV_DONTNEED",
-};
-
-asmlinkage void sunos_madvise(u32 address, u32 len, u32 strategy)
-{
- /* I wanna see who uses this... */
- lock_kernel();
- printk("%s: Advises us to use %s paging strategy for addr<%08x> len<%08x>\n",
- current->comm, strategy <= 4 ? mstrings[strategy] : "BOGUS",
- address, len);
- unlock_kernel();
-}
-
/* This just wants the soft limit (ie. rlim_cur element) of the RLIMIT_NOFILE
* resource limit and is for backwards compatibility with older sunos
* revs.
diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc64/kernel/systbls.S
index 0a0edbf82..d86649bf7 100644
--- a/arch/sparc64/kernel/systbls.S
+++ b/arch/sparc64/kernel/systbls.S
@@ -1,4 +1,4 @@
-/* $Id: systbls.S,v 1.69 2000/03/13 21:57:28 davem Exp $
+/* $Id: systbls.S,v 1.71 2000/03/15 02:43:36 davem Exp $
* systbls.S: System call entry point tables for OS compatibility.
* The native Linux system call table lives here also.
*
@@ -34,7 +34,7 @@ sys_call_table32:
/*60*/ .word sys_umask, sys_chroot, sys32_newfstat, sys_fstat64, sys_getpagesize
.word sys_msync, sys_vfork, sys32_pread, sys32_pwrite, sys_geteuid
/*70*/ .word sys_getegid, sys32_mmap, sys_setreuid, sys_munmap, sys_mprotect
- .word sys_nis_syscall, sys_vhangup, sys32_truncate64, sys32_mincore, sys32_getgroups16
+ .word sys_madvise, sys_vhangup, sys32_truncate64, sys_mincore, sys32_getgroups16
/*80*/ .word sys32_setgroups16, sys_getpgrp, sys_setgroups, sys32_setitimer, sys32_ftruncate64
.word sys_swapon, sys32_getitimer, sys_setuid, sys_sethostname, sys_setgid
/*90*/ .word sys_dup2, sys_setfsuid, sys32_fcntl, sys32_select, sys_setfsgid
@@ -93,7 +93,7 @@ sys_call_table:
/*60*/ .word sys_umask, sys_chroot, sys_newfstat, sys_nis_syscall, sys_getpagesize
.word sys_msync, sys_vfork, sys_pread, sys_pwrite, sys_nis_syscall
/*70*/ .word sys_nis_syscall, sys_mmap, sys_nis_syscall, sys64_munmap, sys_mprotect
- .word sys_nis_syscall, sys_vhangup, sys_nis_syscall, sys_mincore, sys_getgroups
+ .word sys_madvise, sys_vhangup, sys_nis_syscall, sys_mincore, sys_getgroups
/*80*/ .word sys_setgroups, sys_getpgrp, sys_nis_syscall, sys_setitimer, sys_nis_syscall
.word sys_swapon, sys_getitimer, sys_nis_syscall, sys_sethostname, sys_nis_syscall
/*90*/ .word sys_dup2, sys_nis_syscall, sys_fcntl, sys_select, sys_nis_syscall
@@ -163,8 +163,8 @@ sunos_sys_table:
.word sys_msync, sys_vfork, sunos_nosys
.word sunos_nosys, sunos_sbrk, sunos_sstk
.word sunos_mmap, sunos_vadvise, sys_munmap
- .word sys_mprotect, sunos_madvise, sys_vhangup
- .word sunos_nosys, sys32_mincore, sys32_getgroups16
+ .word sys_mprotect, sys_madvise, sys_vhangup
+ .word sunos_nosys, sys_mincore, sys32_getgroups16
.word sys32_setgroups16, sys_getpgrp, sunos_setpgrp
.word sys32_setitimer, sunos_nosys, sys_swapon
.word sys32_getitimer, sys_gethostname, sys_sethostname
diff --git a/arch/sparc64/kernel/unaligned.c b/arch/sparc64/kernel/unaligned.c
index 792334336..989e965a9 100644
--- a/arch/sparc64/kernel/unaligned.c
+++ b/arch/sparc64/kernel/unaligned.c
@@ -1,4 +1,4 @@
-/* $Id: unaligned.c,v 1.18 1999/08/02 08:39:44 davem Exp $
+/* $Id: unaligned.c,v 1.19 2000/03/16 11:53:05 jj Exp $
* unaligned.c: Unaligned load/store trap handling with special
* cases for the kernel to do them more quickly.
*
@@ -590,9 +590,19 @@ void handle_ld_nf(u32 insn, struct pt_regs *regs)
maybe_flush_windows(0, 0, rd, from_kernel);
reg = fetch_reg_addr(rd, regs);
- if ((insn & 0x780000) == 0x180000)
- reg[1] = 0;
- reg[0] = 0;
+ if (from_kernel || rd < 16) {
+ reg[0] = 0;
+ if ((insn & 0x780000) == 0x180000)
+ reg[1] = 0;
+ } else if (current->thread.flags & SPARC_FLAG_32BIT) {
+ put_user(0, (int *)reg);
+ if ((insn & 0x780000) == 0x180000)
+ put_user(0, ((int *)reg) + 1);
+ } else {
+ put_user(0, reg);
+ if ((insn & 0x780000) == 0x180000)
+ put_user(0, reg + 1);
+ }
advance(regs);
}
diff --git a/arch/sparc64/lib/VIScopy.S b/arch/sparc64/lib/VIScopy.S
index 57cf6b0f1..e20118648 100644
--- a/arch/sparc64/lib/VIScopy.S
+++ b/arch/sparc64/lib/VIScopy.S
@@ -1,4 +1,4 @@
-/* $Id: VIScopy.S,v 1.21 1999/07/30 09:35:35 davem Exp $
+/* $Id: VIScopy.S,v 1.22 2000/03/16 16:44:38 davem Exp $
* VIScopy.S: High speed copy operations utilizing the UltraSparc
* Visual Instruction Set.
*
@@ -304,6 +304,9 @@
.type bcopy,@function
#ifdef __KERNEL__
+ .globl __memcpy_begin
+__memcpy_begin:
+
.globl __memcpy
.type __memcpy,@function
@@ -1001,6 +1004,9 @@ fpu_retl:
FPU_RETL
#ifdef __KERNEL__
+ .globl __memcpy_end
+__memcpy_end:
+
.section .fixup
.align 4
VIScopyfixup_reto2:
diff --git a/arch/sparc64/lib/atomic.S b/arch/sparc64/lib/atomic.S
index cac9d15dd..35a34ebbf 100644
--- a/arch/sparc64/lib/atomic.S
+++ b/arch/sparc64/lib/atomic.S
@@ -1,4 +1,4 @@
-/* $Id: atomic.S,v 1.2 1999/08/23 05:15:58 davem Exp $
+/* $Id: atomic.S,v 1.3 2000/03/16 16:44:37 davem Exp $
* atomic.S: These things are too big to do inline.
*
* Copyright (C) 1999 David S. Miller (davem@redhat.com)
@@ -13,24 +13,24 @@
.globl __atomic_add
atomic_impl_begin:
-__atomic_add:
- lduw [%g1], %g5
- add %g5, %g2, %g7
- cas [%g1], %g5, %g7
+__atomic_add: /* %o0 = increment, %o1 = atomic_ptr */
+ lduw [%o1], %g5
+ add %g5, %o0, %g7
+ cas [%o1], %g5, %g7
cmp %g5, %g7
bne,pn %icc, __atomic_add
nop
- jmpl %g3 + 8, %g0
- add %g7, %g2, %g2
+ retl
+ add %g7, %o0, %o0
.globl __atomic_sub
-__atomic_sub:
- lduw [%g1], %g5
- sub %g5, %g2, %g7
- cas [%g1], %g5, %g7
+__atomic_sub: /* %o0 = increment, %o1 = atomic_ptr */
+ lduw [%o1], %g5
+ sub %g5, %o0, %g7
+ cas [%o1], %g5, %g7
cmp %g5, %g7
bne,pn %icc, __atomic_sub
nop
- jmpl %g3 + 8, %g0
- sub %g7, %g2, %g2
+ retl
+ sub %g7, %o0, %o0
atomic_impl_end:
diff --git a/arch/sparc64/lib/blockops.S b/arch/sparc64/lib/blockops.S
index 1cdbf09da..b4a91b97c 100644
--- a/arch/sparc64/lib/blockops.S
+++ b/arch/sparc64/lib/blockops.S
@@ -1,8 +1,8 @@
-/* $Id: blockops.S,v 1.20 2000/03/03 23:48:38 davem Exp $
+/* $Id: blockops.S,v 1.22 2000/03/15 07:18:55 davem Exp $
* blockops.S: UltraSparc block zero optimized routines.
*
- * Copyright (C) 1996,1998 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996, 1998, 1999, 2000 David S. Miller (davem@redhat.com)
+ * Copyright (C) 1997 Jakub Jelinek (jakub@redhat.com)
*/
#include "VIS.h"
@@ -24,23 +24,63 @@
#define TLBTEMP_ENTSZ (1 << 3)
.text
+
.align 32
.globl copy_page
.type copy_page,@function
copy_page: /* %o0=dest, %o1=src */
VISEntry
- ldx [%g6 + AOFF_task_active_mm], %o2
+ membar #LoadStore | #StoreStore | #StoreLoad
+ ldda [%o1] ASI_BLK_P, %f0
+ add %o1, 0x40, %o1
+ ldda [%o1] ASI_BLK_P, %f16
+ add %o1, 0x40, %o1
+ sethi %hi(8192), %o2
+1: TOUCH(f0, f2, f4, f6, f8, f10, f12, f14)
+ ldda [%o1] ASI_BLK_P, %f32
+ add %o1, 0x40, %o1
+ sub %o2, 0x40, %o2
+ stda %f48, [%o0] ASI_BLK_P
+ add %o0, 0x40, %o0
+ TOUCH(f16, f18, f20, f22, f24, f26, f28, f30)
+ ldda [%o1] ASI_BLK_P, %f0
+ add %o1, 0x40, %o1
+ sub %o2, 0x40, %o2
+ stda %f48, [%o0] ASI_BLK_P
+ add %o0, 0x40, %o0
+ TOUCH(f32, f34, f36, f38, f40, f42, f44, f46)
+ ldda [%o1] ASI_BLK_P, %f16
+ add %o1, 0x40, %o1
+ sub %o2, 0x40, %o2
+ stda %f48, [%o0] ASI_BLK_P
+ cmp %o2, 0x80
+ bne,pt %xcc, 1b
+ add %o0, 0x40, %o0
+ membar #Sync
+ stda %f0, [%o0] ASI_BLK_P
+ add %o0, 0x40, %o0
+ stda %f16, [%o0] ASI_BLK_P
+ membar #Sync
+ VISExit
+ retl
+ nop
+
+ .globl copy_user_page
+ .type copy_user_page,@function
+copy_user_page: /* %o0=dest, %o1=src, %o2=vaddr */
+ VISEntry
+ sethi %hi(PAGE_SIZE), %g3
sub %o0, %g4, %g1
+ and %o2, %g3, %o0
+ sethi %hi(TLBTEMP_BASE), %o3
sethi %uhi(_PAGE_VALID), %g3
sub %o1, %g4, %g2
sllx %g3, 32, %g3
- ldx [%o2 + AOFF_mm_segments], %o0
+ mov TLB_TAG_ACCESS, %o2
or %g3, (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_L | _PAGE_W), %g3
+ sethi %hi(DCACHE_SIZE), %o1
or %g1, %g3, %g1
or %g2, %g3, %g2
- mov TLB_TAG_ACCESS, %o2
- sethi %hi(TLBTEMP_BASE), %o3
- sethi %hi(DCACHE_SIZE), %o1
add %o0, %o3, %o0
add %o0, %o1, %o1
sethi %hi(TLBTEMP_ENT1), %o3
@@ -129,17 +169,25 @@ copy_page: /* %o0=dest, %o1=src */
.align 32
.globl clear_page
.type clear_page,@function
-clear_page: /* %o0=dest */
+clear_page: /* %o0=dest */
VISEntryHalf
- ldx [%g6 + AOFF_task_active_mm], %o2
+ ba,pt %xcc, clear_page_common
+ clr %o4
+
+ .align 32
+ .globl clear_user_page
+ .type clear_user_page,@function
+clear_user_page: /* %o0=dest, %o1=vaddr */
+ VISEntryHalf
+ sethi %hi(PAGE_SIZE), %g3
sub %o0, %g4, %g1
+ and %o1, %g3, %o0
+ mov TLB_TAG_ACCESS, %o2
sethi %uhi(_PAGE_VALID), %g3
+ sethi %hi(TLBTEMP_BASE), %o3
sllx %g3, 32, %g3
or %g3, (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_L | _PAGE_W), %g3
- ldx [%o2 + AOFF_mm_segments], %o0
or %g1, %g3, %g1
- mov TLB_TAG_ACCESS, %o2
- sethi %hi(TLBTEMP_BASE), %o3
add %o0, %o3, %o0
sethi %hi(TLBTEMP_ENT2), %o3
rdpr %pstate, %g3
@@ -162,6 +210,9 @@ clear_page: /* %o0=dest */
stxa %g1, [%o3] ASI_DTLB_DATA_ACCESS
membar #Sync
+ mov 1, %o4
+
+clear_page_common:
fzero %f0 ! FPA Group
mov 32, %o1 ! IEU0
fzero %f2 ! FPA Group
@@ -187,7 +238,13 @@ clear_page: /* %o0=dest */
membar #Sync ! LSU Group
VISExitHalf
- stxa %g5, [%o2] ASI_DMMU
+ brnz,pt %o4, 1f
+ nop
+
+ retl
+ nop
+
+1: stxa %g5, [%o2] ASI_DMMU
stxa %g7, [%o3] ASI_DTLB_DATA_ACCESS
membar #Sync
jmpl %o7 + 0x8, %g0
diff --git a/arch/sparc64/lib/rwlock.S b/arch/sparc64/lib/rwlock.S
index 74360bf68..03f3bfe59 100644
--- a/arch/sparc64/lib/rwlock.S
+++ b/arch/sparc64/lib/rwlock.S
@@ -1,4 +1,4 @@
-/* $Id: rwlock.S,v 1.2 1999/08/23 05:15:58 davem Exp $
+/* $Id: rwlock.S,v 1.3 2000/03/16 16:44:38 davem Exp $
* rwlocks.S: These things are too big to do inline.
*
* Copyright (C) 1999 David S. Miller (davem@redhat.com)
@@ -12,34 +12,33 @@
/* The non-contention read lock usage is 2 cache lines. */
.globl __read_lock, __read_unlock
- /* g1=lock, g3=retpc, g5/g7=scratch */
rwlock_impl_begin:
-__read_lock:
- ldsw [%g1], %g5
+__read_lock: /* %o0 = lock_ptr */
+ ldsw [%o0], %g5
brlz,pn %g5, __read_wait_for_writer
4: add %g5, 1, %g7
- cas [%g1], %g5, %g7
+ cas [%o0], %g5, %g7
cmp %g5, %g7
bne,pn %icc, __read_lock
membar #StoreLoad | #StoreStore
-99: jmpl %g3 + 8, %g0
+99: retl
nop
-__read_unlock:
- lduw [%g1], %g5
+__read_unlock: /* %o0 = lock_ptr */
+ lduw [%o0], %g5
sub %g5, 1, %g7
- cas [%g1], %g5, %g7
+ cas [%o0], %g5, %g7
cmp %g5, %g7
be,pt %xcc, 99b
membar #StoreLoad | #StoreStore
b,a,pt %xcc, __read_unlock
__read_wait_for_writer:
- ldsw [%g1], %g5
+ ldsw [%o0], %g5
brlz,pt %g5, __read_wait_for_writer
membar #LoadLoad
b,a,pt %xcc, 4b
__write_wait_for_writer:
- ldsw [%g1], %g5
+ ldsw [%o0], %g5
brlz,pt %g5, __write_wait_for_writer
membar #LoadLoad
b,a,pt %xcc, 4f
@@ -48,24 +47,23 @@ __write_wait_for_writer:
.align 64
.globl __write_unlock
- /* g1=lock, g3=retpc, g2/g5/g7=scratch */
-__write_unlock:
+__write_unlock: /* %o0 = lock_ptr */
sethi %hi(0x80000000), %g2
-1: lduw [%g1], %g5
+1: lduw [%o0], %g5
andn %g5, %g2, %g7
- cas [%g1], %g5, %g7
+ cas [%o0], %g5, %g7
cmp %g5, %g7
be,pt %icc, 99b
membar #StoreLoad | #StoreStore
b,a,pt %xcc, 1b
.globl __write_lock
-__write_lock:
+__write_lock: /* %o0 = lock_ptr */
sethi %hi(0x80000000), %g2
-1: ldsw [%g1], %g5
+1: ldsw [%o0], %g5
4: brnz,pn %g5, 5f
or %g5, %g2, %g7
- cas [%g1], %g5, %g7
+ cas [%o0], %g5, %g7
cmp %g5, %g7
be,pt %icc, 99b
membar #StoreLoad | #StoreStore
@@ -73,10 +71,10 @@ __write_lock:
b,a,pt %xcc, 1b
5: brlz %g5, __write_wait_for_writer
or %g5, %g2, %g7
- cas [%g1], %g5, %g7
+ cas [%o0], %g5, %g7
cmp %g5, %g7
bne,pn %icc, 5b
-8: ldsw [%g1], %g5
+8: ldsw [%o0], %g5
cmp %g5, %g2
be,pn %icc, 99b
membar #LoadLoad
diff --git a/arch/sparc64/mm/fault.c b/arch/sparc64/mm/fault.c
index 226246980..6063d002e 100644
--- a/arch/sparc64/mm/fault.c
+++ b/arch/sparc64/mm/fault.c
@@ -1,4 +1,4 @@
-/* $Id: fault.c,v 1.42 2000/01/21 11:39:13 jj Exp $
+/* $Id: fault.c,v 1.43 2000/03/14 03:59:46 davem Exp $
* arch/sparc64/mm/fault.c: Page fault handlers for the 64-bit Sparc.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -245,7 +245,7 @@ good_area:
if(!(vma->vm_flags & (VM_READ | VM_EXEC)))
goto bad_area;
}
- current->mm->segments = (void *) (address & PAGE_SIZE);
+
{
int fault = handle_mm_fault(current, vma, address, write);
diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c
index d09ac451b..1d2049b5f 100644
--- a/arch/sparc64/mm/init.c
+++ b/arch/sparc64/mm/init.c
@@ -1,4 +1,4 @@
-/* $Id: init.c,v 1.148 2000/03/07 07:08:31 anton Exp $
+/* $Id: init.c,v 1.149 2000/03/15 14:42:58 jj Exp $
* arch/sparc64/mm/init.c
*
* Copyright (C) 1996-1999 David S. Miller (davem@caip.rutgers.edu)
@@ -48,6 +48,10 @@ unsigned long mmu_context_bmap[CTX_BMAP_SLOTS];
/* References to section boundaries */
extern char __init_begin, __init_end, _start, _end, etext, edata;
+/* Initial ramdisk setup */
+extern unsigned int sparc_ramdisk_image;
+extern unsigned int sparc_ramdisk_size;
+
int do_check_pgt_cache(int low, int high)
{
int freed = 0;
@@ -808,6 +812,7 @@ unsigned long __init bootmem_init(void)
{
unsigned long bootmap_size, start_pfn, end_pfn;
unsigned long end_of_phys_memory = 0UL;
+ unsigned long bootmap_pfn;
int i;
/* XXX It is a bit ambiguous here, whether we should
@@ -855,15 +860,37 @@ unsigned long __init bootmem_init(void)
/* Now shift down to get the real physical page frame number. */
start_pfn >>= PAGE_SHIFT;
+
+ bootmap_pfn = start_pfn;
end_pfn = end_of_phys_memory >> PAGE_SHIFT;
+#ifdef CONFIG_BLK_DEV_INITRD
+ /* Now have to check initial ramdisk, so that bootmap does not overwrite it */
+ if (sparc_ramdisk_image) {
+ if (sparc_ramdisk_image >= (unsigned long)&_end - 2 * PAGE_SIZE)
+ sparc_ramdisk_image -= KERNBASE;
+ initrd_start = sparc_ramdisk_image + phys_base;
+ initrd_end = initrd_start + sparc_ramdisk_size;
+ if (initrd_end > end_of_phys_memory) {
+ printk(KERN_CRIT "initrd extends beyond end of memory "
+ "(0x%016lx > 0x%016lx)\ndisabling initrd\n",
+ initrd_end, end_of_phys_memory);
+ initrd_start = 0;
+ }
+ if (initrd_start) {
+ if (initrd_start >= (start_pfn << PAGE_SHIFT) &&
+ initrd_start < (start_pfn << PAGE_SHIFT) + 2 * PAGE_SIZE)
+ bootmap_pfn = PAGE_ALIGN (initrd_end) >> PAGE_SHIFT;
+ }
+ }
+#endif
/* Initialize the boot-time allocator. */
#ifdef DEBUG_BOOTMEM
- prom_printf("init_bootmem(spfn[%lx],epfn[%lx])\n",
- start_pfn, end_pfn);
+ prom_printf("init_bootmem(spfn[%lx],bpfn[%lx],epfn[%lx])\n",
+ start_pfn, bootmap_pfn, end_pfn);
#endif
- bootmap_size = init_bootmem(start_pfn, end_pfn);
+ bootmap_size = init_bootmem(bootmap_pfn, end_pfn);
/* Now register the available physical memory with the
* allocator.
@@ -878,15 +905,27 @@ unsigned long __init bootmem_init(void)
sp_banks[i].num_bytes);
}
- /* Reserve the kernel text/data/bss and the bootmem bitmap. */
+ /* Reserve the kernel text/data/bss, the bootmem bootmap and initrd. */
#ifdef DEBUG_BOOTMEM
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (initrd_start)
+ prom_printf("reserve_bootmem: base[%lx] size[%lx]\n",
+ initrd_start, initrd_end - initrd_start);
+#endif
+ prom_printf("reserve_bootmem: base[%lx] size[%lx]\n",
+ phys_base, (start_pfn << PAGE_SHIFT) - phys_base);
prom_printf("reserve_bootmem: base[%lx] size[%lx]\n",
- phys_base,
- (((start_pfn << PAGE_SHIFT) +
- bootmap_size) - phys_base));
+ (bootmap_pfn << PAGE_SHIFT), bootmap_size);
+#endif
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (initrd_start) {
+ reserve_bootmem(initrd_start, initrd_end - initrd_start);
+ initrd_start += PAGE_OFFSET;
+ initrd_end += PAGE_OFFSET;
+ }
#endif
- reserve_bootmem(phys_base, (((start_pfn << PAGE_SHIFT) +
- bootmap_size) - phys_base));
+ reserve_bootmem(phys_base, (start_pfn << PAGE_SHIFT) - phys_base);
+ reserve_bootmem((bootmap_pfn << PAGE_SHIFT), bootmap_size);
#ifdef DEBUG_BOOTMEM
prom_printf("init_bootmem: return end_pfn[%lx]\n", end_pfn);
@@ -1234,11 +1273,6 @@ void __init mem_init(void)
((unsigned long) &empty_zero_page);
last += PAGE_OFFSET + phys_base;
while (addr < last) {
-#ifdef CONFIG_BLK_DEV_INITRD
-// FIXME to use bootmem scheme...
- if (initrd_below_start_ok && addr >= initrd_start && addr < initrd_end)
- mem_map[MAP_NR(addr)].flags &= ~(1<<PG_reserved);
-#endif
set_bit(__pa(addr) >> 22, sparc64_valid_addr_bitmap);
addr += PAGE_SIZE;
}
@@ -1319,6 +1353,22 @@ void free_initmem (void)
}
}
+#ifdef CONFIG_BLK_DEV_INITRD
+void free_initrd_mem(unsigned long start, unsigned long end)
+{
+ if (start < end)
+ printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10);
+ for (; start < end; start += PAGE_SIZE) {
+ struct page *p = mem_map + MAP_NR(start);
+
+ ClearPageReserved(p);
+ set_page_count(p, 1);
+ __free_page(p);
+ num_physpages++;
+ }
+}
+#endif
+
void si_meminfo(struct sysinfo *val)
{
val->totalram = num_physpages;
diff --git a/drivers/block/Config.in b/drivers/block/Config.in
index 51a6db89c..cadc7eca7 100644
--- a/drivers/block/Config.in
+++ b/drivers/block/Config.in
@@ -31,7 +31,7 @@ if [ "$CONFIG_ATARI" = "y" ]; then
fi
fi
tristate 'XT hard disk support' CONFIG_BLK_DEV_XD
-dep_tristate 'Parallel port IDE device support' CONFIG_PARIDE $CONFIG_PARIDE_PARPORT
+dep_tristate 'Parallel port IDE device support' CONFIG_PARIDE $CONFIG_PARPORT
if [ "$CONFIG_PARIDE" = "y" -o "$CONFIG_PARIDE" = "m" ]; then
source drivers/block/paride/Config.in
fi
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index be7e25879..06c088aa8 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -4378,60 +4378,12 @@ static void __init parse_floppy_cfg_string(char *cfg)
}
}
-static void __init mod_setup(char *pattern, int (*setup)(char *))
-{
- unsigned long i;
- char c;
- int j;
- int match;
- char buffer[100];
- int length = strlen(pattern)+1;
-
- match=0;
- j=1;
-
- for (i=current->mm->env_start; i< current->mm->env_end; i ++){
- get_user(c, (char *)i);
- if (match){
- if (j==99)
- c='\0';
- buffer[j] = c;
- if (!c || c == ' ' || c == '\t'){
- if (j){
- buffer[j] = '\0';
- setup(buffer);
- }
- j=0;
- } else
- j++;
- if (!c)
- break;
- continue;
- }
- if ((!j && !c) || (j && c == pattern[j-1]))
- j++;
- else
- j=0;
- if (j==length){
- match=1;
- j=0;
- }
- }
-}
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
int init_module(void)
{
printk(KERN_INFO "inserting floppy driver for " UTS_RELEASE "\n");
if(floppy)
parse_floppy_cfg_string(floppy);
- else
- mod_setup("floppy=", floppy_setup);
-
return floppy_init();
}
@@ -4453,10 +4405,6 @@ MODULE_PARM(FLOPPY_DMA,"i");
MODULE_AUTHOR("Alain L. Knaff");
MODULE_SUPPORTED_DEVICE("fd");
-#ifdef __cplusplus
-}
-#endif
-
#else
__setup ("floppy=", floppy_setup);
diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c
index 63c5e4b85..431860f44 100644
--- a/drivers/block/ll_rw_blk.c
+++ b/drivers/block/ll_rw_blk.c
@@ -319,9 +319,9 @@ static struct request * __get_request_wait(int n, kdev_t dev)
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
- add_wait_queue(&wait_for_request, &wait);
+ add_wait_queue_exclusive(&wait_for_request, &wait);
for (;;) {
- current->state = TASK_UNINTERRUPTIBLE;
+ __set_current_state(TASK_UNINTERRUPTIBLE|TASK_EXCLUSIVE);
spin_lock_irqsave(&io_request_lock,flags);
req = get_request(n, dev);
spin_unlock_irqrestore(&io_request_lock,flags);
diff --git a/drivers/block/lvm.c b/drivers/block/lvm.c
index 9f58a48d5..beb4f1364 100644
--- a/drivers/block/lvm.c
+++ b/drivers/block/lvm.c
@@ -892,8 +892,8 @@ static int lvm_blk_ioctl(struct inode *inode, struct file *file,
"%s -- lvm_blk_ioctl -- BLKGETSIZE: %u\n",
lvm_name, lv_ptr->lv_size);
#endif
- copy_to_user((long *) arg, &lv_ptr->lv_size,
- sizeof(lv_ptr->lv_size));
+ if (put_user(lv_ptr->lv_size, (long *)arg))
+ return -EFAULT;
break;
@@ -931,8 +931,8 @@ static int lvm_blk_ioctl(struct inode *inode, struct file *file,
printk(KERN_DEBUG
"%s -- lvm_blk_ioctl -- BLKRAGET\n", lvm_name);
#endif
- copy_to_user((long *) arg, &lv_ptr->lv_read_ahead,
- sizeof(lv_ptr->lv_read_ahead));
+ if (put_user(lv->lv_read_ahead, (long *)arg))
+ return -EFAULT;
break;
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index a0b63a8c8..0c3f5429f 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -299,7 +299,7 @@ void nbd_clear_que(struct nbd_device *lo)
static void do_nbd_request(request_queue_t * q)
{
struct request *req;
- int dev;
+ int dev = 0;
struct nbd_device *lo;
while (!QUEUE_EMPTY) {
diff --git a/drivers/cdrom/cm206.c b/drivers/cdrom/cm206.c
index 05aff4b59..27ea344b9 100644
--- a/drivers/cdrom/cm206.c
+++ b/drivers/cdrom/cm206.c
@@ -1483,6 +1483,6 @@ __setup("cm206=", cm206_setup);
#endif /* !MODULE */
/*
* Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -D__SMP__ -pipe -fno-strength-reduce -m486 -DCPU=486 -D__SMP__ -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h -c -o cm206.o cm206.c"
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -m486 -DCPU=486 -D__SMP__ -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h -c -o cm206.o cm206.c"
* End:
*/
diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c
index ea828cc24..969a75331 100644
--- a/drivers/char/amiserial.c
+++ b/drivers/char/amiserial.c
@@ -2259,6 +2259,6 @@ void cleanup_module(void)
/*
Local variables:
- compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -D__SMP__ -pipe -fno-strength-reduce -DCPU=686 -march=i686 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -DEXPORT_SYMTAB -c serial.c"
+ compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -D__SMP__ -pipe -fno-strength-reduce -DCPU=686 -march=i686 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -DEXPORT_SYMTAB -c amiserial.c"
End:
*/
diff --git a/drivers/char/drm/bufs.c b/drivers/char/drm/bufs.c
index 1bb7bd612..4e8c50c59 100644
--- a/drivers/char/drm/bufs.c
+++ b/drivers/char/drm/bufs.c
@@ -29,6 +29,7 @@
*/
#define __NO_VERSION__
+#include <linux/config.h>
#include "drmP.h"
#include "linux/un.h"
diff --git a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h
index 3a371c23d..4d003ba2c 100644
--- a/drivers/char/drm/drmP.h
+++ b/drivers/char/drm/drmP.h
@@ -50,7 +50,6 @@
#include <asm/mtrr.h>
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
-#include <asm/spinlock.h>
#include <linux/poll.h>
#endif
#include "drm.h"
diff --git a/drivers/char/drm/gamma_drv.c b/drivers/char/drm/gamma_drv.c
index 6df4440f3..7e0d6e53e 100644
--- a/drivers/char/drm/gamma_drv.c
+++ b/drivers/char/drm/gamma_drv.c
@@ -28,6 +28,7 @@
*
*/
+#include <linux/config.h>
#include "drmP.h"
#include "gamma_drv.h"
EXPORT_SYMBOL(gamma_init);
diff --git a/drivers/char/drm/tdfx_drv.c b/drivers/char/drm/tdfx_drv.c
index 82b2ac9a2..7934c66b9 100644
--- a/drivers/char/drm/tdfx_drv.c
+++ b/drivers/char/drm/tdfx_drv.c
@@ -29,6 +29,7 @@
*
*/
+#include <linux/config.h>
#include "drmP.h"
#include "tdfx_drv.h"
diff --git a/drivers/char/dtlk.c b/drivers/char/dtlk.c
index 58ce08712..bd832b9b1 100644
--- a/drivers/char/dtlk.c
+++ b/drivers/char/dtlk.c
@@ -1,10 +1,14 @@
/* -*- linux-c -*-
- * dtlk.c - DoubleTalk PC driver for Linux kernel 2.0.29
- *
- * $Id: dtlk.c,v 1.19 1999/02/28 12:13:13 jrv Exp jrv $
+ * dtlk.c - DoubleTalk PC driver for Linux
*
* Original author: Chris Pallotta <chris@allmedia.com>
* Current maintainer: Jim Van Zandt <jrv@vanzandt.mv.com>
+ *
+ * 2000-03-18 Jim Van Zandt: Fix polling.
+ * Eliminate dtlk_timer_active flag and separate dtlk_stop_timer
+ * function. Don't restart timer in dtlk_timer_tick. Restart timer
+ * in dtlk_poll after every poll. dtlk_poll returns mask (duh).
+ * Eliminate unused function dtlk_write_byte. Misc. code cleanups.
*/
/* This driver is for the DoubleTalk PC, a speech synthesizer
@@ -79,7 +83,6 @@ static int dtlk_major;
static int dtlk_port_lpc;
static int dtlk_port_tts;
static int dtlk_busy;
-static int dtlk_timer_active;
static int dtlk_has_indexing;
static unsigned int dtlk_portlist[] =
{0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0};
@@ -114,13 +117,11 @@ static struct dtlk_settings *dtlk_interrogate(void);
static int dtlk_readable(void);
static char dtlk_read_lpc(void);
static char dtlk_read_tts(void);
-static void dtlk_stop_timer(void);
static int dtlk_writeable(void);
static char dtlk_write_bytes(const char *buf, int n);
static char dtlk_write_tts(char);
/*
static void dtlk_handle_error(char, char, unsigned int);
- static char dtlk_write_byte(unsigned int, const char*);
*/
static void dtlk_timer_tick(unsigned long data);
@@ -250,43 +251,27 @@ static unsigned int dtlk_poll(struct file *file, poll_table * wait)
poll_wait(file, &dtlk_process_list, wait);
if (dtlk_has_indexing && dtlk_readable()) {
- dtlk_stop_timer();
+ del_timer(&dtlk_timer);
mask = POLLIN | POLLRDNORM;
}
if (dtlk_writeable()) {
- dtlk_stop_timer();
+ del_timer(&dtlk_timer);
mask |= POLLOUT | POLLWRNORM;
}
/* there are no exception conditions */
- if (mask == 0 && !dtlk_timer_active) {
- /* not ready just yet. There won't be any interrupts,
- so we set a timer instead. */
- dtlk_timer_active = 1;
- dtlk_timer.expires = jiffies + HZ / 100;
- add_timer(&dtlk_timer);
- }
- return 0;
-}
+ /* There won't be any interrupts, so we set a timer instead. */
+ del_timer(&dtlk_timer);
+ dtlk_timer.expires = jiffies + 3*HZ / 100;
+ add_timer(&dtlk_timer);
-static void dtlk_stop_timer()
-{
- if (dtlk_timer_active) {
- dtlk_timer_active = 0;
- del_timer(&dtlk_timer);
- }
+ return mask;
}
static void dtlk_timer_tick(unsigned long data)
{
-
+ TRACE_TEXT(" dtlk_timer_tick");
wake_up_interruptible(&dtlk_process_list);
-
- if (dtlk_timer_active) {
- del_timer(&dtlk_timer);
- dtlk_timer.expires = jiffies + HZ / 100;
- add_timer(&dtlk_timer);
- }
}
static int dtlk_ioctl(struct inode *inode,
@@ -348,7 +333,7 @@ static int dtlk_release(struct inode *inode, struct file *file)
}
TRACE_RET;
- dtlk_stop_timer();
+ del_timer(&dtlk_timer);
return 0;
}
@@ -360,7 +345,6 @@ static int __init dtlk_init(void)
dtlk_port_lpc = 0;
dtlk_port_tts = 0;
dtlk_busy = 0;
- dtlk_timer_active = 0;
dtlk_major = devfs_register_chrdev(0, "dtlk", &dtlk_fops);
if (dtlk_major == 0) {
printk(KERN_ERR "DoubleTalk PC - cannot register device\n");
@@ -410,15 +394,17 @@ static void dtlk_delay(int ms)
static int dtlk_readable(void)
{
- TRACE_TEXT(" dtlk_readable");
+#ifdef TRACING
+ printk(" dtlk_readable=%u@%u", inb_p(dtlk_port_lpc) != 0x7f, jiffies);
+#endif
return inb_p(dtlk_port_lpc) != 0x7f;
}
static int dtlk_writeable(void)
{
/* TRACE_TEXT(" dtlk_writeable"); */
-#ifdef TRACING
- printk(" dtlk_writeable(%02x)", inb_p(dtlk_port_tts));
+#ifdef TRACINGMORE
+ printk(" dtlk_writeable=%u", (inb_p(dtlk_port_tts) & TTS_WRITABLE)!=0);
#endif
return inb_p(dtlk_port_tts) & TTS_WRITABLE;
}
@@ -465,7 +451,9 @@ static int __init dtlk_dev_probe(void)
appears. */
dtlk_delay(100);
dtlk_has_indexing = dtlk_readable();
-
+#ifdef TRACING
+ printk(", indexing %d\n", dtlk_has_indexing);
+#endif
#ifdef INSCOPE
{
/* This macro records ten samples read from the LPC port, for later display */
@@ -479,17 +467,17 @@ for (i = 0; i < 10; i++) \
int b = 0, i, j;
LOOK
- outb_p(0xff, dtlk_port_lpc);
+ outb_p(0xff, dtlk_port_lpc);
buffer[b++] = 0;
LOOK
- dtlk_write_bytes("\0012I\r", 4);
+ dtlk_write_bytes("\0012I\r", 4);
buffer[b++] = 0;
__delay(50 * loops_per_sec / 1000);
outb_p(0xff, dtlk_port_lpc);
buffer[b++] = 0;
LOOK
- printk("\n");
+ printk("\n");
for (j = 0; j < b; j++)
printk(" %02x", buffer[j]);
printk("\n");
@@ -510,12 +498,12 @@ for (i = 0; i < 10; i++) \
__delay(loops_per_sec / 100); /* 10 ms */
LOOK
- outb_p(0x03, dtlk_port_tts);
+ outb_p(0x03, dtlk_port_tts);
buffer[b++] = 0;
LOOK
- LOOK
+ LOOK
- printk("\n");
+ printk("\n");
for (j = 0; j < b; j++)
printk(" %02x", buffer[j]);
printk("\n");
@@ -555,7 +543,7 @@ static struct dtlk_settings *dtlk_interrogate(void)
if (total > 2 && buf[total] == 0x7f)
break;
if (total < sizeof(struct dtlk_settings))
- total++;
+ total++;
}
/*
if (i==50) printk("interrogate() read overrun\n");
@@ -606,7 +594,8 @@ static char dtlk_read_tts(void)
/* verify DT is ready, read char, wait for ACK */
do {
portval = inb_p(dtlk_port_tts);
- } while ((portval & TTS_READABLE) == 0 && retries++ < DTLK_MAX_RETRIES);
+ } while ((portval & TTS_READABLE) == 0 &&
+ retries++ < DTLK_MAX_RETRIES);
if (retries == DTLK_MAX_RETRIES)
printk(KERN_ERR "dtlk_read_tts() timeout\n");
@@ -617,7 +606,8 @@ static char dtlk_read_tts(void)
retries = 0;
do {
portval = inb_p(dtlk_port_tts);
- } while ((portval & TTS_READABLE) != 0 && retries++ < DTLK_MAX_RETRIES);
+ } while ((portval & TTS_READABLE) != 0 &&
+ retries++ < DTLK_MAX_RETRIES);
if (retries == DTLK_MAX_RETRIES)
printk(KERN_ERR "dtlk_read_tts() timeout\n");
@@ -649,22 +639,6 @@ static char dtlk_read_lpc(void)
return ch;
}
-#ifdef NEVER
-static char dtlk_write_byte(unsigned int minor, const char *buf)
-{
- char ch;
- int err;
- /* TRACE_TEXT("(dtlk_write_byte"); */
- err = get_user(ch, buf);
- /* printk(" dtlk_write_byte(%d, 0x%02x)", minor, (int)ch); */
-
- ch = dtlk_write_tts(ch);
- /*
- TRACE_RET; */
- return ch;
-}
-#endif /* NEVER */
-
/* write n bytes to tts port */
static char dtlk_write_bytes(const char *buf, int n)
{
@@ -680,7 +654,7 @@ static char dtlk_write_bytes(const char *buf, int n)
static char dtlk_write_tts(char ch)
{
int retries = 0;
-#ifdef TRACING
+#ifdef TRACINGMORE
printk(" dtlk_write_tts(");
if (' ' <= ch && ch <= '~')
printk("'%c'", ch);
@@ -702,7 +676,7 @@ static char dtlk_write_tts(char ch)
if ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0)
break;
-#ifdef TRACING
+#ifdef TRACINGMORE
printk(")\n");
#endif
return 0;
diff --git a/drivers/char/pc_keyb.c b/drivers/char/pc_keyb.c
index 6ae0ab71c..68db019f8 100644
--- a/drivers/char/pc_keyb.c
+++ b/drivers/char/pc_keyb.c
@@ -442,8 +442,11 @@ static unsigned char handle_kbd_event(void)
scancode = kbd_read_input();
+#if 0
/* Ignore error bytes */
- if (!(status & (KBD_STAT_GTO | KBD_STAT_PERR))) {
+ if (!(status & (KBD_STAT_GTO | KBD_STAT_PERR)))
+#endif
+ {
if (status & KBD_STAT_MOUSE_OBF)
handle_mouse_event(scancode);
else
diff --git a/drivers/char/raw.c b/drivers/char/raw.c
index a7c1b0703..a3159c836 100644
--- a/drivers/char/raw.c
+++ b/drivers/char/raw.c
@@ -102,7 +102,7 @@ int raw_open(struct inode *inode, struct file *filp)
*/
sector_size = 512;
- if (lookup_vfsmnt(rdev) != NULL) {
+ if (get_super(rdev) != NULL) {
if (blksize_size[MAJOR(rdev)])
sector_size = blksize_size[MAJOR(rdev)][MINOR(rdev)];
} else {
diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c
index f1fdac924..97014d26a 100644
--- a/drivers/char/rtc.c
+++ b/drivers/char/rtc.c
@@ -87,11 +87,11 @@ static struct fasync_struct *rtc_async_queue;
static DECLARE_WAIT_QUEUE_HEAD(rtc_wait);
-static spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
+extern spinlock_t rtc_lock;
static struct timer_list rtc_irq_timer;
-static long long rtc_llseek(struct file *file, loff_t offset, int origin);
+static loff_t rtc_llseek(struct file *file, loff_t offset, int origin);
static ssize_t rtc_read(struct file *file, char *buf,
size_t count, loff_t *ppos);
@@ -141,8 +141,11 @@ static const unsigned char days_in_mo[] =
#if !defined(__alpha__) && !defined(CONFIG_DECSTATION)
/*
* A very tiny interrupt handler. It runs with SA_INTERRUPT set,
- * so that there is no possibility of conflicting with the
- * set_rtc_mmss() call that happens during some timer interrupts.
+ * but there is possibility of conflicting with the set_rtc_mmss()
+ * call (the rtc irq and the timer irq can easily run at the same
+ * time in two different CPUs). So we need to serializes
+ * accesses to the chip with the rtc_lock spinlock that each
+ * architecture should implement in the timer code.
* (See ./arch/XXXX/kernel/time.c for the set_rtc_mmss() function.)
*/
@@ -175,7 +178,7 @@ static void rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
* Now all the various file operations that we export.
*/
-static long long rtc_llseek(struct file *file, loff_t offset, int origin)
+static loff_t rtc_llseek(struct file *file, loff_t offset, int origin)
{
return -ESPIPE;
}
diff --git a/drivers/char/serial.c b/drivers/char/serial.c
index 2d160acfd..2c512ccbf 100644
--- a/drivers/char/serial.c
+++ b/drivers/char/serial.c
@@ -4315,6 +4315,9 @@ static struct pci_board pnp_devices[] __initdata = {
/* Best Data Products Inc. Smart One 336F PnP Modem */
{ ISAPNP_VENDOR('B', 'D', 'P'), ISAPNP_DEVICE(0x3336), 0, 0,
SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 },
+ /* SupraExpress 28.8 Data/Fax PnP modem */
+ { ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1310), 0, 0,
+ SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 },
/* These ID's are taken from M$ documentation */
/* Compaq 14400 Modem */
{ ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC000), 0, 0,
diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c
index 7fcbfbb52..48e426623 100644
--- a/drivers/char/sysrq.c
+++ b/drivers/char/sysrq.c
@@ -203,7 +203,7 @@ static void go_sync(kdev_t dev, int remount_flag)
file->f_mode &= ~2;
}
file_list_unlock();
- DQUOT_OFF(dev);
+ DQUOT_OFF(sb);
fsync_dev(dev);
flags = MS_RDONLY;
if (sb->s_op && sb->s_op->remount_fs) {
@@ -212,8 +212,6 @@ static void go_sync(kdev_t dev, int remount_flag)
printk("error %d\n", ret);
else {
sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) | (flags & MS_RMT_MASK);
- if ((vfsmnt = lookup_vfsmnt(sb->s_dev)))
- vfsmnt->mnt_flags = sb->s_flags;
printk("OK\n");
}
} else
diff --git a/drivers/i2c/i2c-algo-pcf.c b/drivers/i2c/i2c-algo-pcf.c
index 14401bacc..39a16825e 100644
--- a/drivers/i2c/i2c-algo-pcf.c
+++ b/drivers/i2c/i2c-algo-pcf.c
@@ -21,9 +21,10 @@
/* ------------------------------------------------------------------------- */
/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and
- Frodo Looijaard <frodol@dds.nl> */
+ Frodo Looijaard <frodol@dds.nl> ,and also from Martin Bailey
+ <mbailey@littlefeet-inc.com> */
-/* $Id: i2c-algo-pcf.c,v 1.20 2000/01/24 02:06:33 mds Exp $ */
+/* $Id: i2c-algo-pcf.c,v 1.21 2000/03/16 13:07:34 frodo Exp $ */
#include <linux/kernel.h>
#include <linux/module.h>
@@ -108,10 +109,12 @@ static int wait_for_bb(struct i2c_algo_pcf_data *adap) {
int status;
status = get_pcf(adap, 1);
+#ifndef STUB_I2C
while (timeout-- && !(status & I2C_PCF_BB)) {
udelay(1000); /* How much is this? */
status = get_pcf(adap, 1);
}
+#endif
if (timeout<=0)
printk("Timeout waiting for Bus Busy\n");
/*
@@ -132,10 +135,12 @@ static int wait_for_pin(struct i2c_algo_pcf_data *adap, int *status) {
int timeout = DEF_TIMEOUT;
*status = get_pcf(adap, 1);
+#ifndef STUB_I2C
while (timeout-- && (*status & I2C_PCF_PIN)) {
adap->waitforpin();
*status = get_pcf(adap, 1);
}
+#endif
if (timeout <= 0)
return(-1);
else
@@ -268,7 +273,7 @@ static inline int try_address(struct i2c_algo_pcf_data *adap,
i2c_start(adap);
status = get_pcf(adap, 1);
if (wait_for_pin(adap, &status) >= 0) {
- if ((status && I2C_PCF_LRB) == 0) {
+ if ((status & I2C_PCF_LRB) == 0) {
i2c_stop(adap);
break; /* success! */
}
@@ -287,25 +292,28 @@ static int pcf_sendbytes(struct i2c_adapter *i2c_adap,const char *buf,
{
struct i2c_algo_pcf_data *adap = i2c_adap->algo_data;
int wrcount, status, timeout;
-
+
for (wrcount=0; wrcount<count; ++wrcount) {
DEB2(printk("i2c-algo-pcf.o: %s i2c_write: writing %2.2X\n",
- i2c_adap->name, buf[wrcount]&0xff));
+ i2c_adap->name, buf[wrcount]&0xff));
i2c_outb(adap, buf[wrcount]);
timeout = wait_for_pin(adap, &status);
if (timeout) {
- printk("i2c-algo-pcf.o: %s i2c_write: error - timeout.\n",
- i2c_adap->name);
i2c_stop(adap);
+ printk("i2c-algo-pcf.o: %s i2c_write: "
+ "error - timeout.\n", i2c_adap->name);
return -EREMOTEIO; /* got a better one ?? */
}
+#ifndef STUB_I2C
if (status & I2C_PCF_LRB) {
- printk("i2c-algo-pcf.o: %s i2c_write: error - no ack.\n",
- i2c_adap->name);
- i2c_stop(adap);
- return -EREMOTEIO; /* got a better one ?? */
+ i2c_stop(adap);
+ printk("i2c-algo-pcf.o: %s i2c_write: "
+ "error - no ack.\n", i2c_adap->name);
+ return -EREMOTEIO; /* got a better one ?? */
}
+#endif
}
+ i2c_stop(adap);
return (wrcount);
}
@@ -314,34 +322,48 @@ static int pcf_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count)
{
int rdcount=0, i, status, timeout, dummy=1;
struct i2c_algo_pcf_data *adap = i2c_adap->algo_data;
-
+
for (i=0; i<count-1; ++i) {
- buf[rdcount] = i2c_inb(adap);
+ buf[rdcount] = i2c_inb(adap);
if (dummy) {
dummy = 0;
- } else
+ } else {
rdcount++;
+ }
timeout = wait_for_pin(adap, &status);
if (timeout) {
- printk("i2c-algo-pcf.o: i2c_read: i2c_inb timed out.\n");
+ i2c_stop(adap);
+ printk("i2c-algo-pcf.o: i2c_read: "
+ "i2c_inb timed out.\n");
return (-1);
}
+#ifndef STUB_I2C
if (status & I2C_PCF_LRB) {
+ i2c_stop(adap);
printk("i2c-algo-pcf.o: i2c_read: i2c_inb, No ack.\n");
return (-1);
}
+#endif
}
set_pcf(adap, 1, I2C_PCF_ESO);
buf[rdcount] = i2c_inb(adap);
if (dummy) {
dummy = 0;
- } else
+ } else {
rdcount++;
+ }
timeout = wait_for_pin(adap, &status);
if (timeout) {
+ i2c_stop(adap);
printk("i2c-algo-pcf.o: i2c_read: i2c_inb timed out.\n");
return (-1);
}
+
+ i2c_stop(adap);
+
+ /* Read final byte from S0 register */
+ buf[rdcount++] = i2c_inb(adap);
+
return (rdcount);
}
@@ -396,45 +418,76 @@ static int pcf_xfer(struct i2c_adapter *i2c_adap,
{
struct i2c_algo_pcf_data *adap = i2c_adap->algo_data;
struct i2c_msg *pmsg;
- int i, ret, timeout, status;
-
+ int i = 0;
+ int ret, timeout, status;
+
+ pmsg = &msgs[i];
+
+ /* Send address here if Read */
+ if (pmsg->flags & I2C_M_RD) {
+ ret = pcf_doAddress(adap, pmsg, i2c_adap->retries);
+ }
+
+ /* Check for bus busy */
timeout = wait_for_bb(adap);
if (timeout) {
- DEB2(printk("i2c-algo-pcf.o: Timeout waiting for BB in pcf_xfer\n");)
+ DEB2(printk("i2c-algo-pcf.o: "
+ "Timeout waiting for BB in pcf_xfer\n");)
return -EIO;
}
+
+ /* Send address here if Write */
+ if (!(pmsg->flags & I2C_M_RD)) {
+ ret = pcf_doAddress(adap, pmsg, i2c_adap->retries);
+ }
+ /* Send START */
i2c_start(adap);
-
- for (i=0; i<num; i++) {
- pmsg = &msgs[i];
- if (!(pmsg->flags & I2C_M_NOSTART)) {
- if (i)
- i2c_repstart(adap);
- ret = pcf_doAddress(adap, pmsg, i2c_adap->retries);
- timeout = wait_for_pin(adap, &status);
- if (timeout) {
- DEB2(printk("i2c-algo-pcf.o: Timeout waiting for PIN(1) in pcf_xfer\n");)
- return (-EREMOTEIO);
- }
- if (status & I2C_PCF_LRB) {
- i2c_stop(adap);
- DEB2(printk("i2c-algo-pcf.o: No LRB(1) in pcf_xfer\n");)
- return (-EREMOTEIO);
- }
- }
- DEB3(printk("i2c-algo-pcf.o: Msg %d, addr=0x%x, flags=0x%x, len=%d\n",
- i, msgs[i].addr, msgs[i].flags, msgs[i].len);)
- if (pmsg->flags & I2C_M_RD ) {
- /* read bytes into buffer*/
- ret = pcf_readbytes(i2c_adap, pmsg->buf, pmsg->len);
+
+ /* Wait for PIN (pending interrupt NOT) */
+ timeout = wait_for_pin(adap, &status);
+ if (timeout) {
+ i2c_stop(adap);
+ DEB2(printk("i2c-algo-pcf.o: Timeout waiting "
+ "for PIN(1) in pcf_xfer\n");)
+ return (-EREMOTEIO);
+ }
+
+#ifndef STUB_I2C
+ /* Check LRB (last rcvd bit - slave ack) */
+ if (status & I2C_PCF_LRB) {
+ i2c_stop(adap);
+ DEB2(printk("i2c-algo-pcf.o: No LRB(1) in pcf_xfer\n");)
+ return (-EREMOTEIO);
+ }
+#endif
+
+ DEB3(printk("i2c-algo-pcf.o: Msg %d, addr=0x%x, flags=0x%x, len=%d\n",
+ i, msgs[i].addr, msgs[i].flags, msgs[i].len);)
+
+ /* Read */
+ if (pmsg->flags & I2C_M_RD) {
+
+ /* read bytes into buffer*/
+ ret = pcf_readbytes(i2c_adap, pmsg->buf, pmsg->len);
+
+ if (ret != pmsg->len) {
+ DEB2(printk("i2c-algo-pcf.o: fail: "
+ "only read %d bytes.\n",ret));
+ } else {
DEB2(printk("i2c-algo-pcf.o: read %d bytes.\n",ret));
+ }
+ } else { /* Write */
+
+ /* Write bytes from buffer */
+ ret = pcf_sendbytes(i2c_adap, pmsg->buf, pmsg->len);
+
+ if (ret != pmsg->len) {
+ DEB2(printk("i2c-algo-pcf.o: fail: "
+ "only wrote %d bytes.\n",ret));
} else {
- /* write bytes from buffer */
- ret = pcf_sendbytes(i2c_adap, pmsg->buf, pmsg->len);
DEB2(printk("i2c-algo-pcf.o: wrote %d bytes.\n",ret));
}
}
- i2c_stop(adap);
return (num);
}
@@ -503,7 +556,7 @@ int i2c_pcf_add_bus(struct i2c_adapter *adap)
i2c_outb(pcf_adap, i);
i2c_start(pcf_adap);
if ((wait_for_pin(pcf_adap, &status) >= 0) &&
- ((status && I2C_PCF_LRB) == 0)) {
+ ((status & I2C_PCF_LRB) == 0)) {
printk("(%02x)",i>>1);
} else {
printk(".");
@@ -528,7 +581,7 @@ int i2c_pcf_del_bus(struct i2c_adapter *adap)
return 0;
}
-int __init i2c_algo_pcf_init (void)
+static int __init i2c_algo_pcf_init (void)
{
printk("i2c-algo-pcf.o: i2c pcf8584 algorithm module\n");
return 0;
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 493befb7d..97b8960c7 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -20,7 +20,7 @@
/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi>.
All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl> */
-/* $Id: i2c-core.c,v 1.50 2000/02/02 23:29:54 frodo Exp $ */
+/* $Id: i2c-core.c,v 1.52 2000/02/27 10:43:29 frodo Exp $ */
#include <linux/module.h>
#include <linux/kernel.h>
@@ -101,6 +101,12 @@ static struct file_operations i2cproc_operations = {
read: i2cproc_bus_read,
};
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,48))
+static struct inode_operations i2cproc_inode_operations = {
+ &i2cproc_operations
+};
+#endif
+
static int i2cproc_initialized = 0;
#else /* undef CONFIG_PROC_FS */
@@ -159,7 +165,12 @@ int i2c_add_adapter(struct i2c_adapter *adap)
name);
return -ENOENT;
}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,48))
proc_entry->proc_fops = &i2cproc_operations;
+#else
+ proc_entry->ops = &i2cproc_inode_operations;
+#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27))
proc_entry->owner = THIS_MODULE;
#else
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c
index d73a7ff66..e373ce2f3 100644
--- a/drivers/i2c/i2c-dev.c
+++ b/drivers/i2c/i2c-dev.c
@@ -23,7 +23,9 @@
But I have used so much of his original code and ideas that it seems
only fair to recognize him as co-author -- Frodo */
-/* $Id: i2c-dev.c,v 1.25 2000/01/26 14:14:20 frodo Exp $ */
+/* The I2C_RDWR ioctl code is written by Kolja Waschk <waschk@telos.de> */
+
+/* $Id: i2c-dev.c,v 1.30 2000/02/28 21:35:05 frodo Exp $ */
#include <linux/kernel.h>
#include <linux/module.h>
@@ -143,8 +145,8 @@ static ssize_t i2cdev_read (struct file *file, char *buf, size_t count,
#endif
ret = i2c_master_recv(client,tmp,count);
- if (! ret)
- ret = copy_to_user(buf,tmp,count)?-EFAULT:0;
+ if (ret >= 0)
+ ret = copy_to_user(buf,tmp,count)?-EFAULT:ret;
kfree(tmp);
return ret;
}
@@ -182,9 +184,11 @@ int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
struct i2c_client *client = (struct i2c_client *)file->private_data;
+ struct i2c_rdwr_ioctl_data rdwr_arg;
struct i2c_smbus_ioctl_data data_arg;
union i2c_smbus_data temp;
- int datasize,res;
+ struct i2c_msg *rdwr_pa;
+ int i,datasize,res;
unsigned long funcs;
#ifdef DEBUG
@@ -212,6 +216,67 @@ int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd,
funcs = i2c_get_functionality(client->adapter);
return (copy_to_user((unsigned long *)arg,&funcs,
sizeof(unsigned long)))?-EFAULT:0;
+
+ case I2C_RDWR:
+ copy_from_user_ret(&rdwr_arg,
+ (struct i2c_rdwr_ioctl_data *)arg,
+ sizeof(rdwr_arg),
+ -EFAULT);
+
+ rdwr_pa = (struct i2c_msg *)
+ kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg),
+ GFP_KERNEL);
+
+ if (rdwr_pa == NULL) return -ENOMEM;
+
+ res = 0;
+ for( i=0; i<rdwr_arg.nmsgs; i++ )
+ {
+ if(copy_from_user(&(rdwr_pa[i]),
+ &(rdwr_arg.msgs[i]),
+ sizeof(rdwr_pa[i])))
+ {
+ res = -EFAULT;
+ break;
+ }
+ rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL);
+ if(rdwr_pa[i].buf == NULL)
+ {
+ res = -ENOMEM;
+ break;
+ }
+ if(copy_from_user(rdwr_pa[i].buf,
+ rdwr_arg.msgs[i].buf,
+ rdwr_pa[i].len))
+ {
+ kfree(rdwr_pa[i].buf);
+ res = -EFAULT;
+ break;
+ }
+ }
+ if (!res)
+ {
+ res = i2c_transfer(client->adapter,
+ rdwr_pa,
+ rdwr_arg.nmsgs);
+ }
+ while(i-- > 0)
+ {
+ if( res>=0 && (rdwr_pa[i].flags & I2C_M_RD))
+ {
+ if(copy_to_user(
+ rdwr_arg.msgs[i].buf,
+ rdwr_pa[i].buf,
+ rdwr_pa[i].len))
+ {
+ res = -EFAULT;
+ }
+ }
+ kfree(rdwr_pa[i].buf);
+ }
+ kfree(rdwr_pa);
+ return res;
+
case I2C_SMBUS:
copy_from_user_ret(&data_arg,
(struct i2c_smbus_ioctl_data *) arg,
diff --git a/drivers/i2c/i2c-elektor.c b/drivers/i2c/i2c-elektor.c
index ad7b9d41c..6140f61cd 100644
--- a/drivers/i2c/i2c-elektor.c
+++ b/drivers/i2c/i2c-elektor.c
@@ -22,7 +22,7 @@
/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
Frodo Looijaard <frodol@dds.nl> */
-/* $Id: i2c-elektor.c,v 1.16 2000/01/24 02:06:33 mds Exp $ */
+/* $Id: i2c-elektor.c,v 1.17 2000/03/16 13:07:34 frodo Exp $ */
#include <linux/kernel.h>
#include <linux/ioport.h>
@@ -73,17 +73,22 @@ static int pcf_pending;
static void pcf_isa_setbyte(void *data, int ctl, int val)
{
- if (ctl) {
+ unsigned long j = jiffies + 10;
+
+ if (ctl) {
if (gpi.pi_irq > 0) {
- DEB3(printk("i2c-elektor.o: Write control 0x%x\n",
+ DEB3(printk("i2c-elektor.o: Write Ctrl 0x%02X\n",
val|I2C_PCF_ENI));
+ DEB3({while (jiffies < j) schedule();})
outb(val | I2C_PCF_ENI, CTRL);
} else {
- DEB3(printk("i2c-elektor.o: Write control 0x%x\n", val));
- outb(val, CTRL);
+ DEB3(printk("i2c-elektor.o: Write Ctrl 0x%02X\n", val|I2C_PCF_ENI));
+ DEB3({while (jiffies < j) schedule();})
+ outb(val|I2C_PCF_ENI, CTRL);
}
} else {
- DEB3(printk("i2c-elektor.o: Write data 0x%x\n", val));
+ DEB3(printk("i2c-elektor.o: Write Data 0x%02X\n", val&0xff));
+ DEB3({while (jiffies < j) schedule();})
outb(val, DATA);
}
}
@@ -94,10 +99,10 @@ static int pcf_isa_getbyte(void *data, int ctl)
if (ctl) {
val = inb(CTRL);
- DEB3(printk("i2c-elektor.o: Read control 0x%x\n", val));
+ DEB3(printk("i2c-elektor.o: Read Ctrl 0x%02X\n", val));
} else {
val = inb(DATA);
- DEB3(printk("i2c-elektor.o: Read data 0x%x\n", val));
+ DEB3(printk("i2c-elektor.o: Read Data 0x%02X\n", val));
}
return (val);
}
@@ -226,7 +231,7 @@ static struct i2c_adapter pcf_isa_ops = {
pcf_isa_unreg,
};
-int __init i2c_pcfisa_init(void)
+static int __init i2c_pcfisa_init(void)
{
struct i2c_pcf_isa *pisa = &gpi;
@@ -277,6 +282,7 @@ MODULE_PARM(base, "i");
MODULE_PARM(irq, "i");
MODULE_PARM(clock, "i");
MODULE_PARM(own, "i");
+MODULE_PARM(i2c_debug,"i");
int init_module(void)
{
@@ -290,5 +296,3 @@ void cleanup_module(void)
}
#endif
-
-
diff --git a/drivers/ide/Config.in b/drivers/ide/Config.in
index bcd17994b..2d4d2228c 100644
--- a/drivers/ide/Config.in
+++ b/drivers/ide/Config.in
@@ -32,7 +32,6 @@ if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then
bool ' Boot off-board chipsets first support' CONFIG_BLK_DEV_OFFBOARD
dep_bool ' Use PCI DMA by default when available' CONFIG_IDEDMA_PCI_AUTO $CONFIG_BLK_DEV_IDEDMA_PCI
define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_PCI
- define_bool CONFIG_IDEDMA_AUTO $CONFIG_IDEDMA_PCI_AUTO
define_bool CONFIG_IDEDMA_PCI_EXPERIMENTAL $CONFIG_EXPERIMENTAL
dep_bool ' ATA Work(s) In Progress (EXPERIMENTAL)' CONFIG_IDEDMA_PCI_WIP $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_EXPERIMENTAL
dep_bool ' Good-Bad DMA Model-Firmware (WIP)' CONFIG_IDEDMA_NEW_DRIVE_LISTINGS $CONFIG_IDEDMA_PCI_WIP
@@ -72,14 +71,12 @@ if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then
dep_bool ' PowerMac IDE DMA support' CONFIG_BLK_DEV_IDEDMA_PMAC $CONFIG_BLK_DEV_IDE_PMAC
dep_bool ' Use DMA by default' CONFIG_IDEDMA_PMAC_AUTO $CONFIG_BLK_DEV_IDEDMA_PMAC
define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_PMAC
- define_bool CONFIG_IDEDMA_AUTO $CONFIG_IDEDMA_PMAC_AUTO
fi
if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
dep_bool ' ICS IDE interface support' CONFIG_BLK_DEV_IDE_ICSIDE $CONFIG_ARCH_ACORN
dep_bool ' ICS DMA support' CONFIG_BLK_DEV_IDEDMA_ICS $CONFIG_BLK_DEV_IDE_ICSIDE
dep_bool ' Use ICS DMA by default' CONFIG_IDEDMA_ICS_AUTO $CONFIG_BLK_DEV_IDEDMA_ICS
define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_ICS
- define_bool CONFIG_IDEDMA_AUTO $CONFIG_IDEDMA_ICS_AUTO
dep_bool ' RapIDE interface support' CONFIG_BLK_DEV_IDE_RAPIDE $CONFIG_ARCH_ACORN
fi
if [ "$CONFIG_AMIGA" = "y" ]; then
@@ -115,19 +112,13 @@ else
define_bool CONFIG_BLK_DEV_HD $CONFIG_BLK_DEV_HD_ONLY
fi
-# if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" -o \
-# "$CONFIG_BLK_DEV_IDEDMA_PMAC" = "y" -o \
-# "$CONFIG_BLK_DEV_IDEDMA_ICS" = "y" ]; then
-# define_bool CONFIG_BLK_DEV_IDEDMA y
-# if [ "$CONFIG_IDEDMA_PCI_AUTO" = "y" -o \
-# "$CONFIG_IDEDMA_PMAC_AUTO" = "y" -o \
-# "$CONFIG_IDEDMA_ICS_AUTO" = "y" ]; then
-# define_bool CONFIG_IDEDMA_AUTO y
-# fi
-# else
-# define_bool CONFIG_BLK_DEV_IDEDMA n
-# define_bool CONFIG_IDEDMA_AUTO n
-# fi
+if [ "$CONFIG_IDEDMA_PCI_AUTO" = "y" -o \
+ "$CONFIG_IDEDMA_PMAC_AUTO" = "y" -o \
+ "$CONFIG_IDEDMA_ICS_AUTO" = "y" ]; then
+ define_bool CONFIG_IDEDMA_AUTO y
+else
+ define_bool CONFIG_IDEDMA_AUTO n
+fi
if [ "$CONFIG_IDE_CHIPSETS" = "y" -o \
"$CONFIG_BLK_DEV_AEC6210" = "y" -o \
diff --git a/drivers/ide/cmd64x.c b/drivers/ide/cmd64x.c
index 542ad44a1..b2ae2e6b8 100644
--- a/drivers/ide/cmd64x.c
+++ b/drivers/ide/cmd64x.c
@@ -68,7 +68,7 @@
#define UDIDETCR1 0x7B
#define DTPR1 0x7C
-#undef DISPLAY_CMD64X_TIMINGS
+#define DISPLAY_CMD64X_TIMINGS
#if defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS)
#include <linux/stat.h>
@@ -116,22 +116,22 @@ static int cmd64x_get_info (char *buffer, char **addr, off_t offset, int count)
p += sprintf(p, "--------------- Primary Channel ---------------- Secondary Channel -------------\n");
p += sprintf(p, " %sabled %sabled\n",
- (reg72&0x80) ? "dis" : " en", (reg7a&0x80) ? "dis" : " en");
+ (reg72&0x80) ? "dis" : " en", (reg7a&0x80) ? "dis" : " en");
p += sprintf(p, "--------------- drive0 --------- drive1 -------- drive0 ---------- drive1 ------\n");
p += sprintf(p, "DMA enabled: %s %s %s %s\n",
- (reg72&0x20) ? "yes" : "no ", (reg72&0x40) ? "yes" : "no ", (reg7a&0x20) ? "yes" : "no ", (reg7a&0x40) ? "yes" : "no " );
- p += sprintf(p, "UDMA enabled: %s %s %s %s\n",
- (reg73&0x01) ? "yes" : "no ", (reg73&0x02) ? "yes" : "no ", (reg7b&0x01) ? "yes" : "no ", (reg7b&0x02) ? "yes" : "no " );
- p += sprintf(p, "UDMA enabled: %s %s %s %s\n",
- (reg73&0x15) ? "4" : (reg73&0x25) ? "3" : (reg73&0x11) ? "2" : (reg73&0x21) ? "1" : (reg73&0x31) ? "0" : "X",
- (reg73&0x4A) ? "4" : (reg73&0x8A) ? "3" : (reg73&0x42) ? "2" : (reg73&0x82) ? "1" : (reg73&0xC2) ? "0" : "X",
- (reg7b&0x15) ? "4" : (reg7b&0x25) ? "3" : (reg7b&0x11) ? "2" : (reg7b&0x21) ? "1" : (reg7b&0x31) ? "0" : "X",
- (reg7b&0x4A) ? "4" : (reg7b&0x8A) ? "3" : (reg7b&0x42) ? "2" : (reg7b&0x82) ? "1" : (reg7b&0xC2) ? "0" : "X" );
- p += sprintf(p, "DMA enabled: %s %s %s %s\n",
- (reg73&0x10) ? "2" : (reg73&0x20) ? "1" : (reg73&0x30) ? "0" : "X",
- (reg73&0x40) ? "2" : (reg73&0x80) ? "1" : (reg73&0xC0) ? "0" : "X",
- (reg7b&0x10) ? "2" : (reg7b&0x20) ? "1" : (reg7b&0x30) ? "0" : "X",
- (reg7b&0x40) ? "2" : (reg7b&0x80) ? "1" : (reg7b&0xC0) ? "0" : "X" );
+ (reg72&0x20) ? "yes" : "no ", (reg72&0x40) ? "yes" : "no ", (reg7a&0x20) ? "yes" : "no ", (reg7a&0x40) ? "yes" : "no " );
+ p += sprintf(p, "DMA Mode: %s(%s) %s(%s) %s(%s) %s(%s)\n",
+ (reg72&0x20)?((reg73&0x01)?"UDMA":" DMA"):" PIO",
+ (reg72&0x20)?(((reg73&0x15)==0x15)?"4":((reg73&0x25)==0x25)?"3":((reg73&0x10)==0x10)?"2":((reg73&0x20)==0x20)?"1":((reg73&0x30)==0x30)?"0":"X"):"?",
+ (reg72&0x40)?((reg73&0x02)?"UDMA":" DMA"):" PIO",
+ (reg72&0x40)?(((reg73&0x4A)==0x4A)?"4":((reg73&0x8A)==0x8A)?"3":((reg73&0x40)==0x40)?"2":((reg73&0x80)==0x80)?"1":((reg73&0xC0)==0xC0)?"0":"X"):"?",
+ (reg7a&0x20)?((reg7b&0x01)?"UDMA":" DMA"):" PIO",
+ (reg7a&0x20)?(((reg7b&0x15)==0x15)?"4":((reg7b&0x25)==0x25)?"3":((reg7b&0x10)==0x10)?"2":((reg7b&0x20)==0x20)?"1":((reg7b&0x30)==0x30)?"0":"X"):"?",
+ (reg7a&0x40)?((reg7b&0x02)?"UDMA":" DMA"):" PIO",
+ (reg7a&0x40)?(((reg7b&0x4A)==0x4A)?"4":((reg7b&0x8A)==0x8A)?"3":((reg7b&0x40)==0x40)?"2":((reg7b&0x80)==0x80)?"1":((reg7b&0xC0)==0xC0)?"0":"X"):"?" );
+ p += sprintf(p, "PIO Mode: %s %s %s %s\n",
+ "?", "?", "?", "?");
+
p += sprintf(p, "PIO\n");
SPLIT_BYTE(reg53, hi_byte, lo_byte);
@@ -668,9 +668,11 @@ unsigned int __init pci_init_cmd64x (struct pci_dev *dev, const char *name)
(void) pci_write_config_byte(dev, DRWTIM3, 0x3f);
#if defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS)
- cmd64x_proc = 1;
- bmide_dev = dev;
- cmd64x_display_info = &cmd64x_get_info;
+ if (!cmd64x_proc) {
+ cmd64x_proc = 1;
+ bmide_dev = dev;
+ cmd64x_display_info = &cmd64x_get_info;
+ }
#endif /* DISPLAY_CMD64X_TIMINGS && CONFIG_PROC_FS */
return 0;
diff --git a/drivers/ide/ide-geometry.c b/drivers/ide/ide-geometry.c
index 6ebf20fe1..4c67d0911 100644
--- a/drivers/ide/ide-geometry.c
+++ b/drivers/ide/ide-geometry.c
@@ -3,7 +3,7 @@
*/
#include <linux/config.h>
-#ifdef CONFIG_BLK_DEV_IDE
+#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
#include <linux/ide.h>
#include <asm/io.h>
@@ -211,4 +211,4 @@ int ide_xlate_1024 (kdev_t i_rdev, int xparm, int ptheads, const char *msg)
drive->bios_cyl, drive->bios_head, drive->bios_sect);
return ret;
}
-#endif /* CONFIG_BLK_DEV_IDE */
+#endif /* (CONFIG_BLK_DEV_IDE) || (CONFIG_BLK_DEV_IDE_MODULE) */
diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
index 311bcfa25..0e617216d 100644
--- a/drivers/ide/ide-probe.c
+++ b/drivers/ide/ide-probe.c
@@ -470,11 +470,13 @@ static void probe_hwif (ide_hwif_t *hwif)
if (hwif->noprobe)
return;
+#ifdef CONFIG_BLK_DEV_IDE
if (hwif->io_ports[IDE_DATA_OFFSET] == HD_DATA) {
extern void probe_cmos_for_drives(ide_hwif_t *);
probe_cmos_for_drives (hwif);
}
+#endif
if ((hwif->chipset != ide_4drives || !hwif->mate->present) &&
#if CONFIG_BLK_DEV_PDC4030
diff --git a/drivers/ide/piix.c b/drivers/ide/piix.c
index 97e57fa55..93f20566d 100644
--- a/drivers/ide/piix.c
+++ b/drivers/ide/piix.c
@@ -417,18 +417,22 @@ unsigned int __init ata66_piix (ide_hwif_t *hwif)
void __init ide_init_piix (ide_hwif_t *hwif)
{
+ if (!hwif->irq)
+ hwif->irq = hwif->channel ? 15 : 14;
+
hwif->tuneproc = &piix_tune_drive;
+ hwif->drives[0].autotune = 1;
+ hwif->drives[1].autotune = 1;
+
+ if (!hwif->dma_base)
+ return;
- if (hwif->dma_base) {
#ifdef CONFIG_PIIX_TUNING
- hwif->dmaproc = &piix_dmaproc;
+ hwif->autodma = 1;
+ hwif->dmaproc = &piix_dmaproc;
+#else
+ if (hwif->autodma)
+ hwif->autodma = 0;
+
#endif /* CONFIG_PIIX_TUNING */
- hwif->drives[0].autotune = 0;
- hwif->drives[1].autotune = 0;
- } else {
- hwif->drives[0].autotune = 1;
- hwif->drives[1].autotune = 1;
- }
- if (!hwif->irq)
- hwif->irq = hwif->channel ? 15 : 14;
}
diff --git a/drivers/ide/via82cxxx.c b/drivers/ide/via82cxxx.c
index c2ec24d39..afa1cc5b6 100644
--- a/drivers/ide/via82cxxx.c
+++ b/drivers/ide/via82cxxx.c
@@ -104,6 +104,7 @@ static const struct {
{ "VT 82C595 Apollo VP2", PCI_DEVICE_ID_VIA_82C595, },
{ "VT 82C597 Apollo VP3", PCI_DEVICE_ID_VIA_82C597_0, },
{ "VT 82C598 Apollo MVP3", PCI_DEVICE_ID_VIA_82C598_0, },
+ { "VT 82C598 Apollo MVP3", PCI_DEVICE_ID_VIA_82C598_0, },
{ "VT 82C680 Apollo P6", PCI_DEVICE_ID_VIA_82C680, },
{ "VT 82C691 Apollo Pro", PCI_DEVICE_ID_VIA_82C691, },
{ "VT 82C693 Apollo Pro Plus", PCI_DEVICE_ID_VIA_82C693, },
@@ -127,7 +128,7 @@ static const struct {
{ PCI_DEVICE_ID_VIA_82C598_0, PCI_DEVICE_ID_VIA_82C586_1, VIA_FLAG_CHECK_REV },
{ PCI_DEVICE_ID_VIA_82C598_0, PCI_DEVICE_ID_VIA_82C596, 0 },
{ PCI_DEVICE_ID_VIA_82C680, PCI_DEVICE_ID_VIA_82C586_1, VIA_FLAG_CHECK_REV },
- { PCI_DEVICE_ID_VIA_82C691, PCI_DEVICE_ID_VIA_82C596, 0 },
+ { PCI_DEVICE_ID_VIA_82C691, PCI_DEVICE_ID_VIA_82C596, VIA_FLAG_ATA_66 },
{ PCI_DEVICE_ID_VIA_82C693, PCI_DEVICE_ID_VIA_82C596, 0 },
{ PCI_DEVICE_ID_VIA_8501_0, PCI_DEVICE_ID_VIA_82C686, VIA_FLAG_ATA_66 },
{ PCI_DEVICE_ID_VIA_8371_0, PCI_DEVICE_ID_VIA_82C686, VIA_FLAG_ATA_66 },
diff --git a/drivers/net/8390.c b/drivers/net/8390.c
index 641693f45..65246369c 100644
--- a/drivers/net/8390.c
+++ b/drivers/net/8390.c
@@ -478,7 +478,9 @@ void ei_interrupt(int irq, void *dev_id, struct pt_regs * regs)
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
if (nr_serviced >= MAX_SERVICE)
{
- printk(KERN_WARNING "%s: Too much work at interrupt, status %#2.2x\n",
+ /* 0xFF is valid for a card removal */
+ if(interrupts!=0xFF)
+ printk(KERN_WARNING "%s: Too much work at interrupt, status %#2.2x\n",
dev->name, interrupts);
outb_p(ENISR_ALL, e8390_base + EN0_ISR); /* Ack. most intrs. */
} else {
@@ -588,8 +590,8 @@ static void ei_tx_intr(struct net_device *dev)
else
ei_local->lasttx = 10, ei_local->txing = 0;
}
- else printk(KERN_WARNING "%s: unexpected TX-done interrupt, lasttx=%d.\n",
- dev->name, ei_local->lasttx);
+// else printk(KERN_WARNING "%s: unexpected TX-done interrupt, lasttx=%d.\n",
+// dev->name, ei_local->lasttx);
#else /* EI_PINGPONG */
/*
@@ -651,8 +653,12 @@ static void ei_receive(struct net_device *dev)
this_frame = ei_local->rx_start_page;
/* Someday we'll omit the previous, iff we never get this message.
- (There is at least one clone claimed to have a problem.) */
- if (ei_debug > 0 && this_frame != ei_local->current_page)
+ (There is at least one clone claimed to have a problem.)
+
+ Keep quiet if it looks like a card removal. One problem here
+ is that some clones crash in roughly the same way.
+ */
+ if (ei_debug > 0 && this_frame != ei_local->current_page && (this_frame!=0x0 || rxing_page!=0xFF))
printk(KERN_ERR "%s: mismatched read page pointers %2x vs %2x.\n",
dev->name, this_frame, ei_local->current_page);
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index fc4bf1922..c2eeb81ba 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -104,11 +104,11 @@ else
endif
endif
-ifeq ($(CONFIG_ATALK),y)
+ifeq ($(CONFIG_APPLETALK),y)
SUB_DIRS += appletalk
MOD_IN_SUB_DIRS += appletalk
else
- ifeq ($(CONFIG_ATALK),m)
+ ifeq ($(CONFIG_APPLETALK),m)
MOD_IN_SUB_DIRS += appletalk
endif
endif
diff --git a/drivers/net/net_init.c b/drivers/net/net_init.c
index 96959300f..f2976e562 100644
--- a/drivers/net/net_init.c
+++ b/drivers/net/net_init.c
@@ -70,10 +70,9 @@ static struct net_device *init_alloc_dev(int sizeof_priv)
{
struct net_device *dev;
int alloc_size;
-
- /* 32-byte alignment */
+
+ /* ensure 32-byte alignment of the private area */
alloc_size = sizeof (*dev) + IFNAMSIZ + sizeof_priv + 31;
- alloc_size &= ~31;
dev = (struct net_device *) kmalloc (alloc_size, GFP_KERNEL);
if (dev == NULL)
@@ -85,9 +84,9 @@ static struct net_device *init_alloc_dev(int sizeof_priv)
memset(dev, 0, alloc_size);
if (sizeof_priv)
- dev->priv = (void *) (dev + 1);
+ dev->priv = (void *) (((long)(dev + 1) + 31) & ~31);
- dev->name = sizeof_priv + (char *)(dev + 1);
+ dev->name = sizeof_priv + 31 + (char *)(dev + 1);
return dev;
}
@@ -395,6 +394,8 @@ int ether_config(struct net_device *dev, struct ifmap *map)
int register_netdev(struct net_device *dev)
{
+ int err;
+
rtnl_lock();
/*
@@ -404,8 +405,9 @@ int register_netdev(struct net_device *dev)
if (dev->name && strchr(dev->name, '%'))
{
+ err = -EBUSY;
if(dev_alloc_name(dev, dev->name)<0)
- return -EBUSY;
+ goto out;
}
/*
@@ -414,17 +416,21 @@ int register_netdev(struct net_device *dev)
if (dev->name && (dev->name[0]==0 || dev->name[0]==' '))
{
+ err = -EBUSY;
if(dev_alloc_name(dev, "eth%d")<0)
- return -EBUSY;
+ goto out;
}
- if (register_netdevice(dev)) {
- rtnl_unlock();
- return -EIO;
- }
+ err = -EIO;
+ if (register_netdevice(dev))
+ goto out;
+
+ err = 0;
+
+out:
rtnl_unlock();
- return 0;
+ return err;
}
void unregister_netdev(struct net_device *dev)
diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c
index 167d3cb12..9b16fd3c3 100644
--- a/drivers/net/pcnet32.c
+++ b/drivers/net/pcnet32.c
@@ -949,7 +949,7 @@ pcnet32_init_ring(struct net_device *dev)
lp->tx_ring[i].status = 0;
}
- lp->init_block.tlen_rlen = TX_RING_LEN_BITS | RX_RING_LEN_BITS;
+ lp->init_block.tlen_rlen = le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS);
for (i = 0; i < 6; i++)
lp->init_block.phys_addr[i] = dev->dev_addr[i];
lp->init_block.rx_ring = (u32)le32_to_cpu(virt_to_bus(lp->rx_ring));
@@ -1073,8 +1073,10 @@ pcnet32_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0)
netif_start_queue(dev);
- else
+ else {
lp->tx_full = 1;
+ netif_stop_queue(dev);
+ }
spin_unlock_irqrestore(&lp->lock, flags);
return 0;
}
diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c
index 3cbf0e5e4..e87de253d 100644
--- a/drivers/net/ppp_async.c
+++ b/drivers/net/ppp_async.c
@@ -220,7 +220,7 @@ ppp_asynctty_ioctl(struct tty_struct *tty, struct file *file,
if (ap == 0)
break;
err = -EFAULT;
- if (put_user(ppp_channel_index(&ap->chan), (int *) arg))
+ if (put_user(ppp_unit_number(&ap->chan), (int *) arg))
break;
err = 0;
break;
diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c
index 01a4e2f81..01b2cc4ea 100644
--- a/drivers/net/ppp_generic.c
+++ b/drivers/net/ppp_generic.c
@@ -1653,13 +1653,13 @@ ppp_register_channel(struct ppp_channel *chan)
}
/*
- * Return the index of a channel.
+ * Return the unit number associated with a channel.
*/
-int ppp_channel_index(struct ppp_channel *chan)
+int ppp_unit_number(struct ppp_channel *chan)
{
struct channel *pch = chan->ppp;
- return pch->file.index;
+ return pch->ppp->file.index;
}
/*
@@ -2319,7 +2319,7 @@ module_exit(ppp_cleanup);
EXPORT_SYMBOL(ppp_register_channel);
EXPORT_SYMBOL(ppp_unregister_channel);
-EXPORT_SYMBOL(ppp_channel_index);
+EXPORT_SYMBOL(ppp_unit_number);
EXPORT_SYMBOL(ppp_input);
EXPORT_SYMBOL(ppp_input_error);
EXPORT_SYMBOL(ppp_output_wakeup);
diff --git a/drivers/net/rcpci45.c b/drivers/net/rcpci45.c
index e19f1e0fd..36d734a73 100644
--- a/drivers/net/rcpci45.c
+++ b/drivers/net/rcpci45.c
@@ -1221,7 +1221,7 @@ static void __exit rcpci_cleanup_module (void)
}
module_init(rcpci_init_module);
-module_exit(rcpci_clenaup_module);
+module_exit(rcpci_cleanup_module);
static int
diff --git a/drivers/net/setup.c b/drivers/net/setup.c
index f8f59b9fb..12e51e458 100644
--- a/drivers/net/setup.c
+++ b/drivers/net/setup.c
@@ -9,7 +9,6 @@
#include <linux/netlink.h>
extern int mkiss_init_ctrl_dev(void);
-extern int ppp_init(void);
extern int slip_init_ctrl_dev(void);
extern int strip_init_ctrl_dev(void);
extern int x25_asy_init_ctrl_dev(void);
@@ -77,7 +76,7 @@ struct net_probe pci_probes[] __initdata = {
{cpm_enet_init, 0},
#endif
#if defined(CONFIG_COMX)
- {comx_init(), 0},
+ {comx_init, 0},
#endif /*
* SLHC if present needs attaching so other people see it
* even if not opened.
@@ -167,9 +166,6 @@ static void __init network_ldisc_init(void)
#if defined(CONFIG_STRIP)
strip_init_ctrl_dev();
#endif
-#if defined(CONFIG_PPP)
- ppp_init();
-#endif
}
diff --git a/drivers/net/shaper.c b/drivers/net/shaper.c
index 9190b5a87..c3a552b43 100644
--- a/drivers/net/shaper.c
+++ b/drivers/net/shaper.c
@@ -66,6 +66,7 @@
* 1999/06/18 Jordi Murgo <savage@apostols.org>
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c
index 2653270c3..ddbb4bf7a 100644
--- a/drivers/net/sis900.c
+++ b/drivers/net/sis900.c
@@ -52,34 +52,36 @@
#include "sis900.h"
static const char *version =
-"sis900.c: v1.06.04 02/11/2000\n";
+"sis900.c: v1.07 03/07/2000\n";
static int max_interrupt_work = 20;
+static int multicast_filter_limit = 128;
+
#define sis900_debug debug
static int sis900_debug = 0;
-static int multicast_filter_limit = 128;
-
/* Time in jiffies before concluding the transmitter is hung. */
#define TX_TIMEOUT (4*HZ)
-
-struct mac_chip_info {
- const char *name;
- u16 vendor_id, device_id, flags;
- int io_size;
- struct net_device *(*probe) (struct mac_chip_info *mac, struct pci_dev * pci_dev,
- struct net_device * net_dev);
+/* SiS 900 is capable of 32 bits BM DMA */
+#define SIS900_DMA_MASK 0xffffffff
+
+static struct net_device * sis900_mac_probe (struct pci_dev * pci_dev,
+ char *card_name);
+enum {
+ SIS_900 = 0,
+ SIS_7018
+};
+static char * card_names[] = {
+ "SiS 900 PCI Fast Ethernet",
+ "SiS 7016 PCI Fast Ethernet"
};
-static struct net_device * sis900_mac_probe (struct mac_chip_info * mac, struct pci_dev * pci_dev,
- struct net_device * net_dev);
-
-static struct mac_chip_info mac_chip_table[] = {
- { "SiS 900 PCI Fast Ethernet", PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_900,
- PCI_COMMAND_IO|PCI_COMMAND_MASTER, SIS900_TOTAL_SIZE, sis900_mac_probe},
- { "SiS 7016 PCI Fast Ethernet",PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7016,
- PCI_COMMAND_IO|PCI_COMMAND_MASTER, SIS900_TOTAL_SIZE, sis900_mac_probe},
- {0,}, /* 0 terminated list. */
+static struct pci_device_id sis900_pci_tbl [] __initdata = {
+ {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_900,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_900},
+ {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7016,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_7018}
};
+MODULE_DEVICE_TABLE (pci, sis900_pci_tbl);
static void sis900_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex);
static void amd79c901_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex);
@@ -111,24 +113,22 @@ typedef struct _BufferDesc {
} BufferDesc;
struct sis900_private {
- struct net_device *next_module;
struct net_device_stats stats;
struct pci_dev * pci_dev;
spinlock_t lock;
- struct mac_chip_info * mac;
struct mii_phy * mii;
unsigned int cur_phy;
struct timer_list timer; /* Link status detection timer. */
- unsigned int cur_rx, dirty_rx;
+ unsigned int cur_rx, dirty_rx;
unsigned int cur_tx, dirty_tx;
/* The saved address of a sent/receive-in-place packet buffer */
struct sk_buff *tx_skbuff[NUM_TX_DESC];
struct sk_buff *rx_skbuff[NUM_RX_DESC];
- BufferDesc tx_ring[NUM_TX_DESC];
+ BufferDesc tx_ring[NUM_TX_DESC];
BufferDesc rx_ring[NUM_RX_DESC];
unsigned int tx_full; /* The Tx queue is full. */
@@ -163,68 +163,47 @@ static u16 sis900_compute_hashtable_index(u8 *addr);
static void set_rx_mode(struct net_device *net_dev);
static void sis900_reset(struct net_device *net_dev);
-/* A list of all installed SiS900 devices, for removing the driver module. */
-static struct net_device *root_sis900_dev = NULL;
-
/* walk through every ethernet PCI devices to see if some of them are matched with our card list*/
-static int __init sis900_probe (void)
+static int __init sis900_probe (struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
{
- int found = 0;
- struct pci_dev * pci_dev = NULL;
-
- while ((pci_dev = pci_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, pci_dev)) != NULL) {
- /* pci_dev contains all ethernet devices */
- u32 pci_io_base;
- struct mac_chip_info * mac;
- struct net_device *net_dev = NULL;
-
- for (mac = mac_chip_table; mac->vendor_id; mac++) {
- /* try to match our card list */
- if (pci_dev->vendor == mac->vendor_id &&
- pci_dev->device == mac->device_id)
- break;
- }
-
- if (mac->vendor_id == 0)
- /* pci_dev does not match any of our cards */
- continue;
-
- /* now, pci_dev should be either 900 or 7016 */
- pci_io_base = pci_dev->resource[0].start;
- if ((mac->flags & PCI_COMMAND_IO ) &&
- check_region(pci_io_base, mac->io_size))
- continue;
-
- /* setup various bits in PCI command register */
- pci_enable_device (pci_dev);
- pci_set_master(pci_dev);
+ u32 pci_io_base;
- /* do the real low level jobs */
- net_dev = mac->probe(mac, pci_dev, net_dev);
-
- if (net_dev != NULL) {
- found++;
- }
- net_dev = NULL;
+ if (!pci_dma_supported(pci_dev, SIS900_DMA_MASK)) {
+ printk(KERN_ERR "sis900.c: architecture does not support "
+ "32bit PCI busmaster DMA\n");
+ return -ENODEV;
+ }
+
+ pci_io_base = pci_dev->resource[0].start;
+ if (check_region(pci_io_base, SIS900_TOTAL_SIZE)) {
+ printk(KERN_ERR "sis900.c: can't allocate I/O space at 0x%08x\n",
+ pci_io_base);
+ return -ENODEV;
}
- return found ? 0 : -ENODEV;
+
+ /* setup various bits in PCI command register */
+ pci_enable_device (pci_dev);
+ pci_set_master(pci_dev);
+
+ /* do the real low level jobs */
+ if (sis900_mac_probe(pci_dev, card_names[pci_id->driver_data]) == NULL)
+ return -1;
+
+ return 0;
}
-static struct net_device * sis900_mac_probe (struct mac_chip_info * mac, struct pci_dev * pci_dev,
- struct net_device * net_dev)
+static struct net_device * __init sis900_mac_probe (struct pci_dev * pci_dev, char * card_name)
{
struct sis900_private *sis_priv;
long ioaddr = pci_dev->resource[0].start;
+ struct net_device *net_dev = NULL;
int irq = pci_dev->irq;
- static int did_version = 0;
u16 signature;
int i;
- if (did_version++ == 0)
- printk(KERN_INFO "%s", version);
-
if ((net_dev = init_etherdev(net_dev, 0)) == NULL)
return NULL;
+
/* check to see if we have sane EEPROM */
signature = (u16) read_eeprom(ioaddr, EEPROMSignature);
if (signature == 0xffff || signature == 0x0000) {
@@ -233,8 +212,8 @@ static struct net_device * sis900_mac_probe (struct mac_chip_info * mac, struct
return NULL;
}
- printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ", net_dev->name, mac->name,
- ioaddr, irq);
+ printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ", net_dev->name,
+ card_name, ioaddr, irq);
/* get MAC address from EEPROM */
for (i = 0; i < 3; i++)
@@ -252,23 +231,22 @@ static struct net_device * sis900_mac_probe (struct mac_chip_info * mac, struct
memset(sis_priv, 0, sizeof(struct sis900_private));
/* We do a request_region() to register /proc/ioports info. */
- request_region(ioaddr, mac->io_size, net_dev->name);
+ request_region(ioaddr, SIS900_TOTAL_SIZE, net_dev->name);
net_dev->base_addr = ioaddr;
net_dev->irq = irq;
sis_priv->pci_dev = pci_dev;
- sis_priv->mac = mac;
spin_lock_init(&sis_priv->lock);
/* probe for mii transciver */
if (sis900_mii_probe(net_dev) == 0) {
unregister_netdev(net_dev);
kfree(sis_priv);
- release_region(ioaddr, mac->io_size);
+ release_region(ioaddr, SIS900_TOTAL_SIZE);
return NULL;
}
- sis_priv->next_module = root_sis900_dev;
- root_sis900_dev = net_dev;
+ pci_dev->driver_data = net_dev;
+ pci_dev->dma_mask = SIS900_DMA_MASK;
/* The SiS900-specific entries in the device structure. */
net_dev->open = &sis900_open;
@@ -283,7 +261,7 @@ static struct net_device * sis900_mac_probe (struct mac_chip_info * mac, struct
return net_dev;
}
-static int sis900_mii_probe (struct net_device * net_dev)
+static int __init sis900_mii_probe (struct net_device * net_dev)
{
struct sis900_private * sis_priv = (struct sis900_private *)net_dev->priv;
int phy_addr;
@@ -966,21 +944,42 @@ static int sis900_rx(struct net_device *net_dev)
net_dev->name);
break;
}
+
+ /* gvie the socket buffer to upper layers */
skb = sis_priv->rx_skbuff[entry];
- sis_priv->rx_skbuff[entry] = NULL;
- /* reset buffer descriptor state */
- sis_priv->rx_ring[entry].cmdsts = 0;
- sis_priv->rx_ring[entry].bufptr = 0;
-
skb_put(skb, rx_size);
skb->protocol = eth_type_trans(skb, net_dev);
netif_rx(skb);
-
+
+ /* some network statistics */
if ((rx_status & BCAST) == MCAST)
sis_priv->stats.multicast++;
net_dev->last_rx = jiffies;
sis_priv->stats.rx_bytes += rx_size;
sis_priv->stats.rx_packets++;
+
+ /* refill the Rx buffer, what if there is not enought memory for
+ new socket buffer ?? */
+ if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) {
+ /* not enough memory for skbuff, this makes a "hole"
+ on the buffer ring, it is not clear how the
+ hardware will react to this kind of degenerated
+ buffer */
+ printk(KERN_INFO "%s: Memory squeeze,"
+ "deferring packet.\n",
+ net_dev->name);
+ sis_priv->rx_skbuff[entry] = NULL;
+ /* reset buffer descriptor state */
+ sis_priv->rx_ring[entry].cmdsts = 0;
+ sis_priv->rx_ring[entry].bufptr = 0;
+ sis_priv->stats.rx_dropped++;
+ break;
+ }
+ skb->dev = net_dev;
+ sis_priv->rx_skbuff[entry] = skb;
+ sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE;
+ sis_priv->rx_ring[entry].bufptr = virt_to_bus(skb->tail);
+ sis_priv->dirty_rx++;
}
sis_priv->cur_rx++;
entry = sis_priv->cur_rx % NUM_RX_DESC;
@@ -1249,23 +1248,46 @@ static void sis900_reset(struct net_device *net_dev)
outl(PESEL, ioaddr + cfg);
}
-static void __exit sis900_cleanup_module(void)
+static void __exit sis900_remove(struct pci_dev *pci_dev)
{
- /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
- while (root_sis900_dev) {
- struct sis900_private *sis_priv =
- (struct sis900_private *)root_sis900_dev->priv;
- struct net_device *next_dev = sis_priv->next_module;
+ struct net_device *net_dev = pci_dev->driver_data;
+ struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
- unregister_netdev(root_sis900_dev);
- release_region(root_sis900_dev->base_addr,
- sis_priv->mac->io_size);
- kfree(sis_priv);
- kfree(root_sis900_dev);
+ unregister_netdev(net_dev);
+ release_region(net_dev->base_addr,
+ SIS900_TOTAL_SIZE);
+
+ kfree(sis_priv);
+ kfree(net_dev);
+}
- root_sis900_dev = next_dev;
+#define SIS900_MODULE_NAME "sis900"
+
+static struct pci_driver sis900_pci_driver = {
+ name: SIS900_MODULE_NAME,
+ id_table: sis900_pci_tbl,
+ probe: sis900_probe,
+ remove: sis900_remove,
+};
+
+static int __init sis900_init_module(void)
+{
+ if (!pci_present()) /* No PCI bus in this machine! */
+ return -ENODEV;
+
+ printk(KERN_INFO "%s", version);
+
+ if (!pci_register_driver(&sis900_pci_driver)) {
+ pci_unregister_driver(&sis900_pci_driver);
+ return -ENODEV;
}
+ return 0;
+}
+
+static void __exit sis900_cleanup_module(void)
+{
+ pci_unregister_driver(&sis900_pci_driver);
}
-module_init(sis900_probe);
+module_init(sis900_init_module);
module_exit(sis900_cleanup_module);
diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c
index 059d4a4ea..d4f6e5b19 100644
--- a/drivers/net/sunhme.c
+++ b/drivers/net/sunhme.c
@@ -1,4 +1,4 @@
-/* $Id: sunhme.c,v 1.93 2000/03/12 04:02:14 davem Exp $
+/* $Id: sunhme.c,v 1.94 2000/03/15 06:47:04 davem Exp $
* sunhme.c: Sparc HME/BigMac 10/100baseT half/full duplex auto switching,
* auto carrier detecting ethernet driver. Also known as the
* "Happy Meal Ethernet" found on SunSwift SBUS cards.
@@ -2953,7 +2953,7 @@ static int __init happy_meal_probe(void)
}
-static void __exit cleanup_module(void)
+static void __exit happy_meal_cleanup_module(void)
{
#ifdef MODULE
/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
diff --git a/drivers/net/tokenring/olympic.c b/drivers/net/tokenring/olympic.c
index d4b2e0380..0787d8d75 100644
--- a/drivers/net/tokenring/olympic.c
+++ b/drivers/net/tokenring/olympic.c
@@ -27,6 +27,8 @@
* the pci resource.
* 1/11/00 - Added spinlocks for smp
* 2/23/00 - Updated to dev_kfree_irq
+ * 3/10/00 - Fixed FDX enable which triggered other bugs also
+ * squashed.
*
* To Do:
*
@@ -83,11 +85,11 @@
* Version Number = a.b.c.d where a.b.c is the level of code and d is the latest author.
* So 0.0.1.pds = Peter, 0.0.1.mlp = Mike
*
- * Official releases will only have an a.b.c version number format.
+ * Official releases will only have an a.b.c version number format.
*/
static char *version =
-"Olympic.c v0.3.2 2/23/00 - Peter De Schrijver & Mike Phillips" ;
+"Olympic.c v0.5.0 3/10/00 - Peter De Schrijver & Mike Phillips" ;
static char *open_maj_error[] = {"No error", "Lobe Media Test", "Physical Insertion",
"Address Verification", "Neighbor Notification (Ring Poll)",
@@ -319,6 +321,14 @@ static int __init olympic_init(struct net_device *dev)
return -1;
}
+ if (olympic_priv->olympic_message_level) {
+ if ( readb(init_srb +2) & 0x40) {
+ printk(KERN_INFO "Olympic: Adapter is FDX capable.\n") ;
+ } else {
+ printk(KERN_INFO "Olympic: Adapter cannot do FDX.\n");
+ }
+ }
+
uaa_addr=ntohs(readw(init_srb+8));
#if OLYMPIC_DEBUG
@@ -401,7 +411,7 @@ static int olympic_open(struct net_device *dev)
#if OLYMPIC_NETWORK_MONITOR
writew(ntohs(OPEN_ADAPTER_ENABLE_FDX | OPEN_ADAPTER_PASS_ADC_MAC | OPEN_ADAPTER_PASS_ATT_MAC | OPEN_ADAPTER_PASS_BEACON),init_srb+8);
#else
- writew(OPEN_ADAPTER_ENABLE_FDX,init_srb+8);
+ writew(ntohs(OPEN_ADAPTER_ENABLE_FDX),init_srb+8);
#endif
if (olympic_priv->olympic_laa[0]) {
@@ -941,9 +951,9 @@ static void olympic_set_rx_mode(struct net_device *dev)
options = olympic_priv->olympic_copy_all_options;
if (dev->flags&IFF_PROMISC)
- options |= (3<<5) ; /* All LLC and MAC frames, all through the main rx channel */
+ options |= 0x61 ;
else
- options &= ~(3<<5) ;
+ options &= ~0x61 ;
/* Only issue the srb if there is a change in options */
@@ -1182,7 +1192,7 @@ static void olympic_arb_cmd(struct net_device *dev)
__u16 lan_status = 0, lan_status_diff ; /* Initialize to stop compiler warning */
__u8 fdx_prot_error ;
__u16 next_ptr;
-
+ int i ;
#if OLYMPIC_NETWORK_MONITOR
struct trh_hdr *mac_hdr ;
#endif
@@ -1242,7 +1252,7 @@ static void olympic_arb_cmd(struct net_device *dev)
/* Is the ASB free ? */
- if (!(readl(olympic_priv->olympic_mmio + SISR) & SISR_ASB_FREE)) {
+ if (readb(asb_block + 2) != 0xff) {
olympic_priv->asb_queued = 1 ;
writel(LISR_ASB_FREE_REQ,olympic_priv->olympic_mmio+LISR_SUM);
return ;
@@ -1261,7 +1271,7 @@ static void olympic_arb_cmd(struct net_device *dev)
return ;
} else if (readb(arb_block) == ARB_LAN_CHANGE_STATUS) { /* Lan.change.status */
- lan_status = readw(arb_block+6);
+ lan_status = ntohs(readw(arb_block+6));
fdx_prot_error = readb(arb_block+8) ;
/* Issue ARB Free */
@@ -1288,10 +1298,19 @@ static void olympic_arb_cmd(struct net_device *dev)
writel(readl(olympic_mmio+BCTL)&~(3<<13),olympic_mmio+BCTL);
netif_stop_queue(dev);
olympic_priv->srb = readw(olympic_priv->olympic_lap + LAPWWO) ;
+
+ olympic_priv->rx_status_last_received++;
+ olympic_priv->rx_status_last_received&=OLYMPIC_RX_RING_SIZE-1;
+ for(i=0;i<OLYMPIC_RX_RING_SIZE;i++) {
+ dev_kfree_skb(olympic_priv->rx_ring_skb[olympic_priv->rx_status_last_received]);
+ olympic_priv->rx_status_last_received++;
+ olympic_priv->rx_status_last_received&=OLYMPIC_RX_RING_SIZE-1;
+ }
+
free_irq(dev->irq,dev);
printk(KERN_WARNING "%s: Adapter has been closed \n", dev->name) ;
-
+ MOD_DEC_USE_COUNT ;
} /* If serious error */
if (olympic_priv->olympic_message_level) {
diff --git a/drivers/net/tokenring/olympic.h b/drivers/net/tokenring/olympic.h
index ba83366c7..5974e4e4f 100644
--- a/drivers/net/tokenring/olympic.h
+++ b/drivers/net/tokenring/olympic.h
@@ -282,7 +282,7 @@ struct olympic_parameters_table {
__u8 phys_addr[4] ;
__u8 up_node_addr[6] ;
- __u8 up_phys_addr[6] ;
+ __u8 up_phys_addr[4] ;
__u8 poll_addr[6] ;
__u16 reserved ;
__u16 acc_priority ;
diff --git a/drivers/net/tulip/interrupt.c b/drivers/net/tulip/interrupt.c
index 6d70ffd70..12b7af968 100644
--- a/drivers/net/tulip/interrupt.c
+++ b/drivers/net/tulip/interrupt.c
@@ -16,6 +16,7 @@
#include "tulip.h"
#include <asm/io.h>
#include <linux/etherdevice.h>
+#include <linux/pci.h>
int tulip_rx_copybreak;
@@ -32,13 +33,20 @@ static int tulip_refill_rx(struct net_device *dev)
/* Refill the Rx ring buffers. */
for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) {
entry = tp->dirty_rx % RX_RING_SIZE;
- if (tp->rx_skbuff[entry] == NULL) {
+ if (tp->rx_buffers[entry].skb == NULL) {
struct sk_buff *skb;
- skb = tp->rx_skbuff[entry] = dev_alloc_skb(PKT_BUF_SZ);
+ dma_addr_t mapping;
+
+ skb = tp->rx_buffers[entry].skb = dev_alloc_skb(PKT_BUF_SZ);
if (skb == NULL)
break;
+
+ mapping = pci_map_single(tp->pdev, skb->tail, PKT_BUF_SZ,
+ PCI_DMA_FROMDEVICE);
+ tp->rx_buffers[entry].mapping = mapping;
+
skb->dev = dev; /* Mark as being used by this device. */
- tp->rx_ring[entry].buffer1 = virt_to_le32desc(skb->tail);
+ tp->rx_ring[entry].buffer1 = cpu_to_le32(mapping);
refilled++;
}
tp->rx_ring[entry].status = cpu_to_le32(DescOwned);
@@ -106,24 +114,39 @@ static int tulip_rx(struct net_device *dev)
&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
skb->dev = dev;
skb_reserve(skb, 2); /* 16 byte align the IP header */
+ pci_dma_sync_single(tp->pdev,
+ tp->rx_buffers[entry].mapping,
+ pkt_len, PCI_DMA_FROMDEVICE);
#if ! defined(__alpha__)
- eth_copy_and_sum(skb, tp->rx_skbuff[entry]->tail, pkt_len, 0);
+ eth_copy_and_sum(skb, tp->rx_buffers[entry].skb->tail,
+ pkt_len, 0);
skb_put(skb, pkt_len);
#else
- memcpy(skb_put(skb, pkt_len), tp->rx_skbuff[entry]->tail,
- pkt_len);
+ memcpy(skb_put(skb, pkt_len),
+ tp->rx_buffers[entry].skb->tail,
+ pkt_len);
#endif
} else { /* Pass up the skb already on the Rx ring. */
- char *temp = skb_put(skb = tp->rx_skbuff[entry], pkt_len);
- tp->rx_skbuff[entry] = NULL;
+ char *temp = skb_put(skb = tp->rx_buffers[entry].skb,
+ pkt_len);
+
#ifndef final_version
- if (le32desc_to_virt(tp->rx_ring[entry].buffer1) != temp)
+ if (tp->rx_buffers[entry].mapping !=
+ le32_to_cpu(tp->rx_ring[entry].buffer1)) {
printk(KERN_ERR "%s: Internal fault: The skbuff addresses "
- "do not match in tulip_rx: %p vs. %p / %p.\n",
- dev->name,
- le32desc_to_virt(tp->rx_ring[entry].buffer1),
- skb->head, temp);
+ "do not match in tulip_rx: %08x vs. %08x %p / %p.\n",
+ dev->name,
+ le32_to_cpu(tp->rx_ring[entry].buffer1),
+ tp->rx_buffers[entry].mapping,
+ skb->head, temp);
+ }
#endif
+
+ pci_unmap_single(tp->pdev, tp->rx_buffers[entry].mapping,
+ PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
+
+ tp->rx_buffers[entry].skb = NULL;
+ tp->rx_buffers[entry].mapping = 0;
}
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
@@ -189,8 +212,13 @@ void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
if (status < 0)
break; /* It still has not been Txed */
/* Check for Rx filter setup frames. */
- if (tp->tx_skbuff[entry] == NULL)
+ if (tp->tx_buffers[entry].skb == NULL) {
+ pci_unmap_single(tp->pdev,
+ tp->tx_buffers[entry].mapping,
+ sizeof(tp->setup_frame),
+ PCI_DMA_TODEVICE);
continue;
+ }
if (status & 0x8000) {
/* There was an major error, log it. */
@@ -213,14 +241,20 @@ void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
#ifdef ETHER_STATS
if (status & 0x0001) tp->stats.tx_deferred++;
#endif
- tp->stats.tx_bytes += tp->tx_skbuff[entry]->len;
+ tp->stats.tx_bytes +=
+ tp->tx_buffers[entry].skb->len;
tp->stats.collisions += (status >> 3) & 15;
tp->stats.tx_packets++;
}
+ pci_unmap_single(tp->pdev, tp->tx_buffers[entry].mapping,
+ tp->tx_buffers[entry].skb->len,
+ PCI_DMA_TODEVICE);
+
/* Free the original skb. */
- dev_kfree_skb_irq(tp->tx_skbuff[entry]);
- tp->tx_skbuff[entry] = 0;
+ dev_kfree_skb_irq(tp->tx_buffers[entry].skb);
+ tp->tx_buffers[entry].skb = NULL;
+ tp->tx_buffers[entry].mapping = 0;
tx++;
}
@@ -311,7 +345,7 @@ void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
/* check if we card is in suspend mode */
entry = tp->dirty_rx % RX_RING_SIZE;
- if (tp->rx_skbuff[entry] == NULL) {
+ if (tp->rx_buffers[entry].skb == NULL) {
if (tulip_debug > 1)
printk(KERN_WARNING "%s: in rx suspend mode: (%lu) (tp->cur_rx = %u, ttimer = %d, rx = %d) go/stay in suspend mode\n", dev->name, tp->nir, tp->cur_rx, tp->ttimer, rx);
if (tp->ttimer == 0 || (inl(ioaddr + CSR11) & 0xffff) == 0) {
diff --git a/drivers/net/tulip/tulip.h b/drivers/net/tulip/tulip.h
index a5cdc6b8d..8196723ae 100644
--- a/drivers/net/tulip/tulip.h
+++ b/drivers/net/tulip/tulip.h
@@ -204,12 +204,6 @@ enum desc_status_bits {
#define get_u16(ptr) (((u8*)(ptr))[0] + (((u8*)(ptr))[1]<<8))
#endif
-
-/* 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))
-
-
struct medialeaf {
u8 type;
u8 media;
@@ -237,6 +231,10 @@ struct mediainfo {
unsigned char *info;
};
+struct ring_info {
+ struct sk_buff *skb;
+ dma_addr_t mapping;
+};
struct tulip_private {
const char *product_name;
@@ -246,10 +244,9 @@ struct tulip_private {
dma_addr_t rx_ring_dma;
dma_addr_t tx_ring_dma;
/* The saved address of a sent-in-place packet/buffer, for skfree(). */
- struct sk_buff *tx_skbuff[TX_RING_SIZE];
+ struct ring_info tx_buffers[TX_RING_SIZE];
/* The addresses of receive-in-place skbuffs. */
- struct sk_buff *rx_skbuff[RX_RING_SIZE];
- char *rx_buffs; /* Address of temporary Rx buffers. */
+ struct ring_info rx_buffers[RX_RING_SIZE];
u16 setup_frame[96]; /* Pseudo-Tx frame to init address table. */
int chip_id;
int revision;
diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c
index b82dbac51..6881093ea 100644
--- a/drivers/net/tulip/tulip_core.c
+++ b/drivers/net/tulip/tulip_core.c
@@ -51,7 +51,7 @@ const char * const medianame[] = {
};
/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */
-#if defined(__alpha__) || defined(__arm__)
+#if defined(__alpha__) || defined(__arm__) || defined(__sparc__)
static int rx_copybreak = 1518;
#else
static int rx_copybreak = 100;
@@ -72,8 +72,14 @@ static int rx_copybreak = 100;
#if defined(__alpha__)
static int csr0 = 0x01A00000 | 0xE000;
-#elif defined(__i386__) || defined(__powerpc__) || defined(__sparc__)
+#elif defined(__i386__) || defined(__powerpc__)
static int csr0 = 0x01A00000 | 0x8000;
+#elif defined(__sparc__)
+/* The UltraSparc PCI controllers will disconnect at every 64-byte
+ * crossing anyways so it makes no sense to tell Tulip to burst
+ * any more than that.
+ */
+static int csr0 = 0x01A00000 | 0x9000;
#elif defined(__arm__)
static int csr0 = 0x01A00000 | 0x4800;
#else
@@ -285,6 +291,7 @@ static void tulip_up(struct net_device *dev)
/* This is set_rx_mode(), but without starting the transmitter. */
u16 *eaddrs = (u16 *)dev->dev_addr;
u16 *setup_frm = &tp->setup_frame[15*6];
+ dma_addr_t mapping;
/* 21140 bug: you must add the broadcast address. */
memset(tp->setup_frame, 0xff, sizeof(tp->setup_frame));
@@ -292,9 +299,16 @@ static void tulip_up(struct net_device *dev)
*setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0];
*setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1];
*setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2];
+
+ mapping = pci_map_single(tp->pdev, tp->setup_frame,
+ sizeof(tp->setup_frame),
+ PCI_DMA_TODEVICE);
+ tp->tx_buffers[0].skb = NULL;
+ tp->tx_buffers[0].mapping = mapping;
+
/* Put the setup frame on the Tx list. */
tp->tx_ring[0].length = cpu_to_le32(0x08000000 | 192);
- tp->tx_ring[0].buffer1 = virt_to_le32desc(tp->setup_frame);
+ tp->tx_ring[0].buffer1 = cpu_to_le32(mapping);
tp->tx_ring[0].status = cpu_to_le32(DescOwned);
tp->cur_tx++;
@@ -569,30 +583,37 @@ static void tulip_init_ring(struct net_device *dev)
tp->rx_ring[i].status = 0x00000000;
tp->rx_ring[i].length = cpu_to_le32(PKT_BUF_SZ);
tp->rx_ring[i].buffer2 = cpu_to_le32(tp->rx_ring_dma + sizeof(struct tulip_rx_desc) * (i + 1));
- tp->rx_skbuff[i] = NULL;
+ tp->rx_buffers[i].skb = NULL;
+ tp->rx_buffers[i].mapping = 0;
}
/* Mark the last entry as wrapping the ring. */
tp->rx_ring[i-1].length = cpu_to_le32(PKT_BUF_SZ | DESC_RING_WRAP);
tp->rx_ring[i-1].buffer2 = cpu_to_le32(tp->rx_ring_dma);
for (i = 0; i < RX_RING_SIZE; i++) {
+ dma_addr_t mapping;
+
/* Note the receive buffer must be longword aligned.
dev_alloc_skb() provides 16 byte alignment. But do *not*
use skb_reserve() to align the IP header! */
struct sk_buff *skb = dev_alloc_skb(PKT_BUF_SZ);
- tp->rx_skbuff[i] = skb;
+ tp->rx_buffers[i].skb = skb;
if (skb == NULL)
break;
+ mapping = pci_map_single(tp->pdev, skb->tail,
+ PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
+ tp->rx_buffers[i].mapping = mapping;
skb->dev = dev; /* Mark as being used by this device. */
tp->rx_ring[i].status = cpu_to_le32(DescOwned); /* Owned by Tulip chip */
- tp->rx_ring[i].buffer1 = virt_to_le32desc(skb->tail);
+ tp->rx_ring[i].buffer1 = cpu_to_le32(mapping);
}
tp->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
/* The Tx buffer descriptor is filled in as needed, but we
do need to clear the ownership bit. */
for (i = 0; i < TX_RING_SIZE; i++) {
- tp->tx_skbuff[i] = 0;
+ tp->tx_buffers[i].skb = NULL;
+ tp->tx_buffers[i].mapping = 0;
tp->tx_ring[i].status = 0x00000000;
tp->tx_ring[i].buffer2 = cpu_to_le32(tp->tx_ring_dma + sizeof(struct tulip_tx_desc) * (i + 1));
}
@@ -605,6 +626,7 @@ tulip_start_xmit(struct sk_buff *skb, struct net_device *dev)
struct tulip_private *tp = (struct tulip_private *)dev->priv;
int entry;
u32 flag;
+ dma_addr_t mapping;
unsigned long cpuflags;
/* Caution: the write order is important here, set the field
@@ -615,8 +637,11 @@ tulip_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* Calculate the next Tx descriptor entry. */
entry = tp->cur_tx % TX_RING_SIZE;
- tp->tx_skbuff[entry] = skb;
- tp->tx_ring[entry].buffer1 = virt_to_le32desc(skb->data);
+ tp->tx_buffers[entry].skb = skb;
+ mapping = pci_map_single(tp->pdev, skb->data,
+ skb->len, PCI_DMA_TODEVICE);
+ tp->tx_buffers[entry].mapping = mapping;
+ tp->tx_ring[entry].buffer1 = cpu_to_le32(mapping);
if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */
flag = 0x60000000; /* No interrupt */
@@ -697,19 +722,31 @@ static int tulip_close (struct net_device *dev)
/* Free all the skbuffs in the Rx queue. */
for (i = 0; i < RX_RING_SIZE; i++) {
- struct sk_buff *skb = tp->rx_skbuff[i];
- tp->rx_skbuff[i] = 0;
+ struct sk_buff *skb = tp->rx_buffers[i].skb;
+ dma_addr_t mapping = tp->rx_buffers[i].mapping;
+
+ tp->rx_buffers[i].skb = NULL;
+ tp->rx_buffers[i].mapping = 0;
+
tp->rx_ring[i].status = 0; /* Not owned by Tulip chip. */
tp->rx_ring[i].length = 0;
tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */
if (skb) {
+ pci_unmap_single(tp->pdev, mapping, PKT_BUF_SZ,
+ PCI_DMA_FROMDEVICE);
dev_kfree_skb (skb);
}
}
for (i = 0; i < TX_RING_SIZE; i++) {
- if (tp->tx_skbuff[i])
- dev_kfree_skb (tp->tx_skbuff[i]);
- tp->tx_skbuff[i] = 0;
+ struct sk_buff *skb = tp->tx_buffers[i].skb;
+
+ if (skb != NULL) {
+ pci_unmap_single(tp->pdev, tp->tx_buffers[i].mapping,
+ skb->len, PCI_DMA_TODEVICE);
+ dev_kfree_skb (skb);
+ }
+ tp->tx_buffers[i].skb = NULL;
+ tp->tx_buffers[i].mapping = 0;
}
MOD_DEC_USE_COUNT;
@@ -937,7 +974,8 @@ static void set_rx_mode(struct net_device *dev)
if (entry != 0) {
/* Avoid a chip errata by prefixing a dummy entry. */
- tp->tx_skbuff[entry] = 0;
+ tp->tx_buffers[entry].skb = NULL;
+ tp->tx_buffers[entry].mapping = 0;
tp->tx_ring[entry].length =
(entry == TX_RING_SIZE-1) ? cpu_to_le32(DESC_RING_WRAP) : 0;
tp->tx_ring[entry].buffer1 = 0;
@@ -945,12 +983,17 @@ static void set_rx_mode(struct net_device *dev)
entry = tp->cur_tx++ % TX_RING_SIZE;
}
- tp->tx_skbuff[entry] = 0;
+ tp->tx_buffers[entry].skb = NULL;
+ tp->tx_buffers[entry].mapping =
+ pci_map_single(tp->pdev, tp->setup_frame,
+ sizeof(tp->setup_frame),
+ PCI_DMA_TODEVICE);
/* Put the setup frame on the Tx list. */
if (entry == TX_RING_SIZE-1)
tx_flags |= DESC_RING_WRAP; /* Wrap ring. */
tp->tx_ring[entry].length = cpu_to_le32(tx_flags);
- tp->tx_ring[entry].buffer1 = virt_to_le32desc(tp->setup_frame);
+ tp->tx_ring[entry].buffer1 =
+ cpu_to_le32(tp->tx_buffers[entry].mapping);
tp->tx_ring[entry].status = cpu_to_le32(DescOwned);
if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) {
netif_stop_queue(dev);
@@ -1163,8 +1206,10 @@ static int __devinit tulip_init_one (struct pci_dev *pdev,
And the ASIX must have a burst limit or horrible things happen. */
if (chip_idx == DC21143 && chip_rev == 65)
tp->csr0 &= ~0x01000000;
- else if (chip_idx == AX88140)
- tp->csr0 |= 0x2000;
+ else if (chip_idx == AX88140) {
+ if ((tp->csr0 & 0x3f00) == 0)
+ tp->csr0 |= 0x2000;
+ }
/* The lower four bits are the media type. */
if (board_idx >= 0 && board_idx < MAX_UNITS) {
diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c
index 19615012f..4c52f055c 100644
--- a/drivers/net/via-rhine.c
+++ b/drivers/net/via-rhine.c
@@ -420,7 +420,7 @@ static int __devinit via_rhine_init_one (struct pci_dev *pdev,
if (via_rhine_chip_info[chip_id].flags & PCI_USES_MASTER)
pci_set_master (pdev);
- dev = init_etherdev(NULL, sizeof (*np));
+ dev = init_etherdev(NULL, sizeof(*np));
if (dev == NULL) {
printk (KERN_ERR "init_ethernet failed for card #%d\n",
card_idx);
diff --git a/drivers/net/wan/Makefile b/drivers/net/wan/Makefile
index b2cd8aafb..dbb12c2bf 100644
--- a/drivers/net/wan/Makefile
+++ b/drivers/net/wan/Makefile
@@ -49,6 +49,14 @@ else
endif
endif
+ifeq ($(CONFIG_COMX),y)
+LX_OBJS += comx.o
+else
+ ifeq ($(CONFIG_COMX),m)
+ MX_OBJS += comx.o
+ endif
+endif
+
ifeq ($(CONFIG_COMX_HW_COMX),y)
L_OBJS += comx-hw-comx.o
else
diff --git a/drivers/net/wan/comx.c b/drivers/net/wan/comx.c
index d3ca69e86..1c59075be 100644
--- a/drivers/net/wan/comx.c
+++ b/drivers/net/wan/comx.c
@@ -432,7 +432,7 @@ static int comx_statistics(struct net_device *dev, char *page)
ch->line_status & PROTO_UP ? "UP" : "DOWN");
len += sprintf(page + len, "Modem status changes: %lu, Transmitter status "
"is %s, tbusy: %d\n", ch->current_stats->tx_carrier_errors, ch->HW_txe ?
- ch->HW_txe(dev) ? "IDLE" : "BUSY" : "NOT READY", (int)dev->tbusy);
+ ch->HW_txe(dev) ? "IDLE" : "BUSY" : "NOT READY", netif_running(dev));
len += sprintf(page + len, "Interface load (input): %d / %d / %d bits/s (",
LOADAVG(0,0), LOADAVG(1, 0), LOADAVG(2, 0));
tmpstr[0] = 0;
@@ -860,7 +860,7 @@ static int comx_mkdir(struct inode *dir, struct dentry *dentry, int mode)
return -EIO;
}
- new_dir->ops = &proc_dir_inode_operations; // ez egy normalis /proc konyvtar
+ new_dir->proc_iops = &proc_dir_inode_operations; // ez egy normalis /proc konyvtar
new_dir->nlink = 2;
new_dir->data = NULL; // ide jon majd a struct dev
@@ -884,7 +884,7 @@ static int comx_mkdir(struct inode *dir, struct dentry *dentry, int mode)
S_IFREG | 0644, new_dir)) == NULL) {
return -ENOMEM;
}
- debug_file->ops = &comx_debug_inode_ops;
+ debug_file->proc_iops = &comx_debug_inode_ops;
debug_file->data = (void *)debug_file;
debug_file->read_proc = NULL; // see below
debug_file->write_proc = &comx_write_proc;
@@ -1027,7 +1027,7 @@ static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode,
struct proc_dir_entry *new_file;
if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) {
- new_file->ops = &comx_normal_inode_ops;
+ new_file->proc_iops = &comx_normal_inode_ops;
new_file->data = (void *)new_file;
new_file->read_proc = &comx_read_proc;
new_file->write_proc = &comx_write_proc;
@@ -1129,7 +1129,7 @@ int comx_unregister_protocol(char *name)
#define comx_init init_module
#endif
-__initfunc(int comx_init(void))
+int __init comx_init(void)
{
struct proc_dir_entry *new_file;
@@ -1177,7 +1177,7 @@ __initfunc(int comx_init(void))
return -ENOMEM;
}
- new_file->ops = &comx_normal_inode_ops;
+ new_file->proc_iops = &comx_normal_inode_ops;
new_file->data = new_file;
new_file->read_proc = &comx_root_read_proc;
new_file->write_proc = NULL;
diff --git a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c
index f28feea43..a86c83b7d 100644
--- a/drivers/net/yellowfin.c
+++ b/drivers/net/yellowfin.c
@@ -1411,7 +1411,7 @@ static void __exit yellowfin_cleanup (void)
module_init(yellowfin_init);
-module_exit(yellowfin_exit);
+module_exit(yellowfin_cleanup);
/*
diff --git a/drivers/parport/ChangeLog b/drivers/parport/ChangeLog
index 8d01df21e..d2bb93c74 100644
--- a/drivers/parport/ChangeLog
+++ b/drivers/parport/ChangeLog
@@ -1,4 +1,23 @@
-2000-03-13 <twaugh@redhat.com>
+2000-03-16 Tim Waugh <twaugh@redhat.com>
+
+ * parport_pc.c (parport_ECP_supported): This seems to trigger on
+ machines that don't have an IRQ conflict; toned down the warning
+ message accordingly.
+
+2000-03-16 Gunther Mayer <gunther.mayer@braunschweig.netsurf.de>
+
+ * parport_pc.c (show_parconfig_smsc37c669): Fix typo.
+ (decode_winbond): More IDs.
+ (winbond_check): Protect against false positives.
+ (winbond_check2): Likewise.
+ (smsc_check): Likewise.
+
+2000-03-15 Tim Waugh <twaugh@redhat.com>
+
+ * parport_pc.c (cleanup_module): Don't call pci_unregister_driver
+ if we didn't call pci_register_driver first.
+
+2000-03-13 Tim Waugh <twaugh@redhat.com>
* parport_pc.c (parport_pc_init): Moved from asm/parport.h.
@@ -19,12 +38,12 @@
(parport_pc_find_ports): New function.
(init_module): Make superio a config option, not a parameter.
-2000-03-10 <twaugh@redhat.com>
+2000-03-10 Tim Waugh <twaugh@redhat.com>
* parport_pc.c (decode_winbond): Use correct 83877ATF chip ID.
(decode_winbond): Fix typo.
-2000-03-09 <twaugh@redhat.com>
+2000-03-09 Tim Waugh <twaugh@redhat.com>
* parport_pc.c: Integrate SuperIO PCI probe with normal PCI card
probe, so that the MODULE_DEVICE_TABLE is complete.
diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c
index 7117c847e..dbbd12864 100644
--- a/drivers/parport/parport_pc.c
+++ b/drivers/parport/parport_pc.c
@@ -1060,7 +1060,7 @@ static void __devinit show_parconfig_smsc37c669(int io, int key)
(cr23*4 >=0x100) ?"yes":"no", (cr1 & 4) ? "yes" : "no");
printk("SMSC LPT Config: Port mode=%s, EPP version =%s\n",
(cr1 & 0x08 ) ? "Standard mode only (SPP)" : modes[cr4 & 0x03],
- (cr4 & 40) ? "1.7" : "1.9");
+ (cr4 & 0x40) ? "1.7" : "1.9");
/* Heuristics ! BIOS setup for this mainboard device limits
the choices to standard settings, i.e. io-address and IRQ
@@ -1172,12 +1172,15 @@ static void __devinit decode_winbond(int efer, int key, int devid, int devrev, i
/* Values are from public data sheets pdf files, I can just
confirm 83977TF is correct :-) */
- if (id == 0x9773) type="83977TF";
+ if (id == 0x9771) type="83977F/AF";
+ else if (id == 0x9773) type="83977TF / SMSC 97w33x/97w34x";
else if (id == 0x9774) type="83977ATF";
else if ((id & ~0x0f) == 0x5270) type="83977CTF / SMSC 97w36x";
- else if ((id & ~0x0f) == 0x52f0) type="83977EF / SMSC 97x35x";
+ else if ((id & ~0x0f) == 0x52f0) type="83977EF / SMSC 97w35x";
else if ((id & ~0x0f) == 0x5210) type="83627";
else if ((id & ~0x0f) == 0x6010) type="83697HF";
+ else if ((oldid &0x0f ) == 0x0a) { type="83877F"; progif=1;}
+ else if ((oldid &0x0f ) == 0x0b) { type="83877AF"; progif=1;}
else if ((oldid &0x0f ) == 0x0c) { type="83877TF"; progif=1;}
else if ((oldid &0x0f ) == 0x0d) { type="83877ATF"; progif=1;}
else progif=0;
@@ -1225,7 +1228,15 @@ static void __devinit decode_smsc(int efer, int key, int devid, int devrev)
static void __devinit winbond_check(int io, int key)
{
- int devid,devrev,oldid;
+ int devid,devrev,oldid,x_devid,x_devrev,x_oldid;
+
+ /* First probe without key */
+ outb(0x20,io);
+ x_devid=inb(io+1);
+ outb(0x21,io);
+ x_devrev=inb(io+1);
+ outb(0x09,io);
+ x_oldid=inb(io+1);
outb(key,io);
outb(key,io); /* Write Magic Sequence to EFER, extended
@@ -1238,12 +1249,23 @@ static void __devinit winbond_check(int io, int key)
oldid=inb(io+1);
outb(0xaa,io); /* Magic Seal */
+ if ((x_devid == devid) && (x_devrev == devrev) && (x_oldid == oldid))
+ return; /* protection against false positives */
+
decode_winbond(io,key,devid,devrev,oldid);
}
static void __devinit winbond_check2(int io,int key)
{
- int devid,devrev,oldid;
+ int devid,devrev,oldid,x_devid,x_devrev,x_oldid;
+
+ /* First probe without the key */
+ outb(0x20,io+2);
+ x_devid=inb(io+2);
+ outb(0x21,io+1);
+ x_devrev=inb(io+2);
+ outb(0x09,io+1);
+ x_oldid=inb(io+2);
outb(key,io); /* Write Magic Byte to EFER, extended
funtion enable register */
@@ -1255,23 +1277,44 @@ static void __devinit winbond_check2(int io,int key)
oldid=inb(io+2);
outb(0xaa,io); /* Magic Seal */
+ if ((x_devid == devid) && (x_devrev == devrev) && (x_oldid == oldid))
+ return; /* protection against false positives */
+
decode_winbond(io,key,devid,devrev,oldid);
}
static void __devinit smsc_check(int io, int key)
{
- int devid,devrev;
+ int id,rev,oldid,oldrev,x_id,x_rev,x_oldid,x_oldrev;
+
+ /* First probe without the key */
+ outb(0x0d,io);
+ x_oldid=inb(io+1);
+ outb(0x0e,io);
+ x_oldrev=inb(io+1);
+ outb(0x20,io);
+ x_id=inb(io+1);
+ outb(0x21,io);
+ x_rev=inb(io+1);
outb(key,io);
outb(key,io); /* Write Magic Sequence to EFER, extended
funtion enable register */
outb(0x0d,io); /* Write EFIR, extended function index register */
- devid=inb(io+1); /* Read EFDR, extended function data register */
+ oldid=inb(io+1); /* Read EFDR, extended function data register */
outb(0x0e,io);
- devrev=inb(io+1);
+ oldrev=inb(io+1);
+ outb(0x20,io);
+ id=inb(io+1);
+ outb(0x21,io);
+ rev=inb(io+1);
outb(0xaa,io); /* Magic Seal */
- decode_smsc(io,key,devid,devrev);
+ if ((x_id == id) && (x_oldrev == oldrev) &&
+ (x_oldid == oldid) && (x_rev == rev))
+ return; /* protection against false positives */
+
+ decode_smsc(io,key,oldid,oldrev);
}
@@ -1584,7 +1627,8 @@ static int __devinit parport_ECP_supported(struct parport *pb)
configb = inb (CONFIGB (pb));
if (!(configb & 0x40)) {
- printk (KERN_WARNING "0x%lx: IRQ conflict!\n", pb->base);
+ printk (KERN_WARNING "0x%lx: possible IRQ conflict!\n",
+ pb->base);
pb->irq = PARPORT_IRQ_NONE;
}
printk (KERN_DEBUG "0x%lx: ECP port cfgA=0x%02x cfgB=0x%02x\n",
@@ -2487,7 +2531,10 @@ int init_module(void)
void cleanup_module(void)
{
struct parport *p = parport_enumerate(), *tmp;
- pci_unregister_driver (&parport_pc_pci_driver);
+
+ if (!user_specified)
+ pci_unregister_driver (&parport_pc_pci_driver);
+
while (p) {
tmp = p->next;
if (p->modes & PARPORT_MODE_PCSPP) {
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 1837c2467..0b6614fe1 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -384,19 +384,14 @@ void
pci_set_master(struct pci_dev *dev)
{
u16 cmd;
- u8 lat;
pci_read_config_word(dev, PCI_COMMAND, &cmd);
if (! (cmd & PCI_COMMAND_MASTER)) {
- printk("PCI: Enabling bus mastering for device %s\n", dev->slot_name);
+ DBG("PCI: Enabling bus mastering for device %s\n", dev->slot_name);
cmd |= PCI_COMMAND_MASTER;
pci_write_config_word(dev, PCI_COMMAND, cmd);
}
- pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
- if (lat < 16) {
- printk("PCI: Increasing latency timer of device %s to 64\n", dev->slot_name);
- pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
- }
+ pcibios_set_master(dev);
}
/*
diff --git a/drivers/pci/pci.ids b/drivers/pci/pci.ids
index dc7cfe9e1..9dae829b5 100644
--- a/drivers/pci/pci.ids
+++ b/drivers/pci/pci.ids
@@ -695,10 +695,13 @@
ac18 PCI1260
ac19 PCI1221
ac1a PCI1210
- ac1b PCI1221
+ ac1b PCI1450
ac1c PCI1225
- ac1d PCI1251
+ ac1d PCI1251A
+ ac1e PCI1211
+ ac1f PCI1251B
ac20 TI 2030
+ ac51 PCI1420
fe00 FireWire Host Controller
fe03 12C01A FireWire Host Controller
104d Sony Corporation
diff --git a/drivers/pcmcia/ti113x.h b/drivers/pcmcia/ti113x.h
index b8664c6b5..16c881f7d 100644
--- a/drivers/pcmcia/ti113x.h
+++ b/drivers/pcmcia/ti113x.h
@@ -136,6 +136,46 @@
#ifdef CONFIG_CARDBUS
+/*
+ * Generic TI open - TI has an extension for the
+ * INTCTL register that sets the PCI CSC interrupt.
+ * Make sure we set it correctly at open and init
+ * time.
+ */
+static int ti_open(pci_socket_t *socket)
+{
+ u8 new, reg = exca_readb(socket, I365_INTCTL);
+
+ new = reg & ~I365_INTR_ENA;
+ if (socket->cb_irq)
+ new |= I365_INTR_ENA;
+ if (new != reg)
+ exca_writeb(socket, I365_INTCTL, new);
+ return 0;
+}
+
+static int ti_init(pci_socket_t *socket)
+{
+ yenta_init(socket);
+ ti_open(socket);
+ return 0;
+}
+
+static struct pci_socket_ops ti_ops = {
+ ti_open,
+ yenta_close,
+ ti_init,
+ yenta_suspend,
+ yenta_get_status,
+ yenta_get_socket,
+ yenta_set_socket,
+ yenta_get_io_map,
+ yenta_set_io_map,
+ yenta_get_mem_map,
+ yenta_set_mem_map,
+ yenta_proc_setup
+};
+
#define ti_sysctl(socket) ((socket)->private[0])
#define ti_cardctl(socket) ((socket)->private[1])
#define ti_devctl(socket) ((socket)->private[2])
@@ -149,6 +189,7 @@ static int ti113x_open(pci_socket_t *socket)
ti_cardctl(socket) &= ~(TI113X_CCR_PCI_IRQ_ENA | TI113X_CCR_PCI_IREQ | TI113X_CCR_PCI_CSC);
if (socket->cb_irq)
ti_cardctl(socket) |= TI113X_CCR_PCI_IRQ_ENA | TI113X_CCR_PCI_CSC | TI113X_CCR_PCI_IREQ;
+ ti_open(socket);
return 0;
}
@@ -159,7 +200,7 @@ static int ti113x_init(pci_socket_t *socket)
config_writel(socket, TI113X_SYSTEM_CONTROL, ti_sysctl(socket));
config_writeb(socket, TI113X_CARD_CONTROL, ti_cardctl(socket));
config_writeb(socket, TI113X_DEVICE_CONTROL, ti_devctl(socket));
-
+ ti_open(socket);
return 0;
}
@@ -187,6 +228,7 @@ static int ti1250_open(pci_socket_t *socket)
ti_diag(socket) &= ~(TI1250_DIAG_PCI_CSC | TI1250_DIAG_PCI_IREQ);
if (socket->cb_irq)
ti_diag(socket) |= TI1250_DIAG_PCI_CSC | TI1250_DIAG_PCI_IREQ;
+ ti_open(socket);
return 0;
}
@@ -195,6 +237,7 @@ static int ti1250_init(pci_socket_t *socket)
yenta_init(socket);
config_writeb(socket, TI1250_DIAGNOSTIC, ti_diag(socket));
+ ti_open(socket);
return 0;
}
diff --git a/drivers/pcmcia/yenta.c b/drivers/pcmcia/yenta.c
index c57fdfcc6..a8b311780 100644
--- a/drivers/pcmcia/yenta.c
+++ b/drivers/pcmcia/yenta.c
@@ -528,6 +528,7 @@ static unsigned int yenta_probe_irq(pci_socket_t *socket, u32 isa_irq_mask)
cb_writel(socket, CB_SOCKET_EVENT, -1);
}
cb_writel(socket, CB_SOCKET_MASK, 0);
+ exca_writeb(socket, I365_CSCINT, 0);
mask = probe_irq_mask(val) & 0xffff;
@@ -739,8 +740,18 @@ static struct cardbus_override_struct {
struct pci_socket_ops *op;
} cardbus_override[] = {
{ PD(TI,1130), &ti113x_ops },
+ { PD(TI,1031), &ti_ops },
{ PD(TI,1131), &ti113x_ops },
{ PD(TI,1250), &ti1250_ops },
+ { PD(TI,1220), &ti_ops },
+ { PD(TI,1221), &ti_ops },
+ { PD(TI,1210), &ti_ops },
+ { PD(TI,1450), &ti_ops },
+ { PD(TI,1225), &ti_ops },
+ { PD(TI,1251A), &ti_ops },
+ { PD(TI,1211), &ti_ops },
+ { PD(TI,1251B), &ti_ops },
+ { PD(TI,1420), &ti_ops },
{ PD(RICOH,RL5C465), &ricoh_ops },
{ PD(RICOH,RL5C466), &ricoh_ops },
diff --git a/drivers/pnp/quirks.c b/drivers/pnp/quirks.c
index 12a82eba5..02766fa0d 100644
--- a/drivers/pnp/quirks.c
+++ b/drivers/pnp/quirks.c
@@ -18,7 +18,6 @@
#include <linux/isapnp.h>
#include <linux/string.h>
-
static void __init quirk_awe32_resources(struct pci_dev *dev)
{
struct isapnp_port *port, *port2, *port3;
@@ -67,6 +66,37 @@ static void __init quirk_cmi8330_resources(struct pci_dev *dev)
printk(KERN_INFO "isapnp: CMI8330 quirk - fixing interrupts and dma\n");
}
+static void __init quirk_sb16audio_resources(struct pci_dev *dev)
+{
+ struct isapnp_port *port;
+ struct isapnp_resources *res = dev->sysdata;
+ int changed = 0;
+
+ /*
+ * The default range on the mpu port for these devices is 0x388-0x388.
+ * Here we increase that range so that two such cards can be
+ * auto-configured.
+ */
+
+ for( ; res ; res = res->alt ) {
+ port = res->port;
+ if(!port)
+ continue;
+ port = port->next;
+ if(!port)
+ continue;
+ port = port->next;
+ if(!port)
+ continue;
+ if(port->min != port->max)
+ continue;
+ port->max += 0x70;
+ changed = 1;
+ }
+ if(changed)
+ printk(KERN_INFO "ISAPnP: SB audio device quirk - increasing port range\n");
+ return;
+}
/*
* ISAPnP Quirks
@@ -74,14 +104,31 @@ static void __init quirk_cmi8330_resources(struct pci_dev *dev)
*/
static struct isapnp_fixup isapnp_fixups[] __initdata = {
+ /* Soundblaster awe io port quirk */
{ ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0021),
quirk_awe32_resources },
{ ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0022),
quirk_awe32_resources },
{ ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0023),
quirk_awe32_resources },
- { ISAPNP_VENDOR('@','X','@'), ISAPNP_DEVICE(0x0001), // CMI8330
+ /* CMI 8330 interrupt and dma fix */
+ { ISAPNP_VENDOR('@','X','@'), ISAPNP_DEVICE(0x0001),
quirk_cmi8330_resources },
+ /* Soundblaster audio device io port range quirk */
+ { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0001),
+ quirk_sb16audio_resources },
+ { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0031),
+ quirk_sb16audio_resources },
+ { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0041),
+ quirk_sb16audio_resources },
+ { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0042),
+ quirk_sb16audio_resources },
+ { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0043),
+ quirk_sb16audio_resources },
+ { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0044),
+ quirk_sb16audio_resources },
+ { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0045),
+ quirk_sb16audio_resources },
{ 0 }
};
diff --git a/drivers/sbus/sbus.c b/drivers/sbus/sbus.c
index f29dc114e..35865dd70 100644
--- a/drivers/sbus/sbus.c
+++ b/drivers/sbus/sbus.c
@@ -1,4 +1,4 @@
-/* $Id: sbus.c,v 1.83 1999/10/18 01:47:01 zaitcev Exp $
+/* $Id: sbus.c,v 1.86 2000/03/16 09:23:57 jj Exp $
* sbus.c: SBus support routines.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -205,6 +205,7 @@ static void __init sbus_do_child_siblings(int start_node,
this_dev->child = kmalloc(sizeof(struct sbus_dev),
GFP_ATOMIC);
this_dev->child->bus = sbus;
+ this_dev->child->next = 0;
fill_sbus_device(prom_getchild(this_node), this_dev->child);
sbus_do_child_siblings(prom_getchild(this_node),
this_dev->child, this_dev, sbus);
@@ -303,6 +304,8 @@ static void __init sbus_fixup_all_regs(struct sbus_dev *first_sdev)
}
}
+extern void register_proc_sparc_ioport(void);
+
void __init sbus_init(void)
{
int nd, this_sbus, sbus_devs, topnd, iommund;
@@ -310,7 +313,11 @@ void __init sbus_init(void)
struct sbus_bus *sbus;
struct sbus_dev *this_dev;
int num_sbus = 0; /* How many did we find? */
-
+
+#ifndef __sparc_v9__
+ register_proc_sparc_ioport();
+#endif
+
#ifdef CONFIG_SUN4
return sun4_dvma_init();
#endif
@@ -424,6 +431,7 @@ void __init sbus_init(void)
GFP_ATOMIC);
/* Fill it */
this_dev->child->bus = sbus;
+ this_dev->child->next = 0;
fill_sbus_device(prom_getchild(sbus_devs),
this_dev->child);
sbus_do_child_siblings(prom_getchild(sbus_devs),
@@ -453,6 +461,7 @@ void __init sbus_init(void)
GFP_ATOMIC);
/* Fill it */
this_dev->child->bus = sbus;
+ this_dev->child->next = 0;
fill_sbus_device(prom_getchild(sbus_devs),
this_dev->child);
sbus_do_child_siblings(prom_getchild(sbus_devs),
diff --git a/drivers/scsi/53c7,8xx.c b/drivers/scsi/53c7,8xx.c
index 7049fa349..9c6754924 100644
--- a/drivers/scsi/53c7,8xx.c
+++ b/drivers/scsi/53c7,8xx.c
@@ -1396,7 +1396,7 @@ ncr_pci_init (Scsi_Host_Template *tpnt, int board, int chip,
int i, irq;
struct pci_dev *pdev = pci_find_slot(bus, device_fn);
- printk("scsi-ncr53c7,8xx : at PCI bus %d, device %d, function %d\n",
+ printk("scsi-ncr53c7,8xx : at PCI bus %d, device %d, function %d\n",
bus, (int) (device_fn & 0xf8) >> 3,
(int) device_fn & 7);
@@ -1406,10 +1406,8 @@ ncr_pci_init (Scsi_Host_Template *tpnt, int board, int chip,
return -1;
}
- if ((error = pcibios_read_config_word (bus, device_fn, PCI_COMMAND,
- &command)) ||
- (error = pcibios_read_config_byte (bus, device_fn, PCI_CLASS_REVISION,
- &revision))) {
+ if ((error = pci_read_config_word (pdev, PCI_COMMAND, &command)) ||
+ (error = pci_read_config_byte (pdev, PCI_CLASS_REVISION, &revision))) {
printk ("scsi-ncr53c7,8xx : error %d not initializing due to error reading configuration space\n"
" perhaps you specified an incorrect PCI bus, device, or function.\n", error);
return -1;
@@ -1451,24 +1449,21 @@ ncr_pci_init (Scsi_Host_Template *tpnt, int board, int chip,
*/
if (command & PCI_COMMAND_IO) {
- if ((io_port & 3) != 1) {
- printk ("scsi-ncr53c7,8xx : disabling I/O mapping since base address 0 (0x%x)\n"
- " bits 0..1 indicate a non-IO mapping\n",
- (unsigned) io_port);
+ if (!(pdev->resource[0].flags & IORESOURCE_IO)) {
+ printk ("scsi-ncr53c7,8xx : disabling I/O mapping since base "
+ "address 0\n contains a non-IO mapping\n");
io_port = 0;
- } else
- io_port &= PCI_BASE_ADDRESS_IO_MASK;
+ }
} else {
io_port = 0;
}
if (command & PCI_COMMAND_MEMORY) {
- if ((base & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) {
- printk("scsi-ncr53c7,8xx : disabling memory mapping since base address 1\n"
- " contains a non-memory mapping\n");
+ if (!(pdev->resource[1].flags & IORESOURCE_MEM)) {
+ printk("scsi-ncr53c7,8xx : disabling memory mapping since base "
+ "address 1\n contains a non-memory mapping\n");
base = 0;
- } else
- base &= PCI_BASE_ADDRESS_MEM_MASK;
+ }
} else {
base = 0;
}
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 16f26fbcd..9081a6d13 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -709,7 +709,7 @@ include $(TOPDIR)/Rules.make
53c8xx_d.h: 53c7,8xx.scr script_asm.pl
ln -sf 53c7,8xx.scr fake8.c
- $(CPP) $(CPPFLAGS) -traditional -DCHIP=810 fake8.c | grep -v '^#' | perl script_asm.pl
+ $(CPP) $(CPPFLAGS) -traditional -DCHIP=810 fake8.c | grep -v '^#' | $(PERL) script_asm.pl
mv script.h 53c8xx_d.h
mv scriptu.h 53c8xx_u.h
rm fake8.c
@@ -718,7 +718,7 @@ include $(TOPDIR)/Rules.make
53c7xx_d.h: 53c7xx.scr script_asm.pl
ln -sf 53c7xx.scr fake7.c
- $(CPP) $(CPPFLAGS) -traditional -DCHIP=710 fake7.c | grep -v '^#' | perl -s script_asm.pl -ncr7x0_family
+ $(CPP) $(CPPFLAGS) -traditional -DCHIP=710 fake7.c | grep -v '^#' | $(PERL) -s script_asm.pl -ncr7x0_family
mv script.h 53c7xx_d.h
mv scriptu.h 53c7xx_u.h
rm fake7.c
@@ -729,7 +729,7 @@ include $(TOPDIR)/Rules.make
sim710_d.h: sim710.scr script_asm.pl
ln -sf sim710.scr fake7.c
- $(CPP) $(CPPFLAGS) -traditional -DCHIP=710 fake7.c | grep -v '^#' | perl -s script_asm.pl -ncr7x0_family
+ $(CPP) $(CPPFLAGS) -traditional -DCHIP=710 fake7.c | grep -v '^#' | $(PERL) -s script_asm.pl -ncr7x0_family
mv script.h sim710_d.h
mv scriptu.h sim710_u.h
rm fake7.c
diff --git a/drivers/scsi/imm.c b/drivers/scsi/imm.c
index 7b2944b30..ab12ef01d 100644
--- a/drivers/scsi/imm.c
+++ b/drivers/scsi/imm.c
@@ -330,7 +330,7 @@ static unsigned char imm_wait(int host_no)
static int imm_negotiate(imm_struct * tmp)
{
/*
- * The following is supposedly the IEEE 1248-1994 negotiate
+ * The following is supposedly the IEEE 1284-1994 negotiate
* sequence. I have yet to obtain a copy of the above standard
* so this is a bit of a guess...
*
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index a43f2988c..0bd06ea0f 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -108,6 +108,8 @@ static struct dev_info device_list[] =
{"YAMAHA", "CDR100", "1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
{"YAMAHA", "CDR102", "1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0
* extra reset */
+ {"YAMAHA", "CRW8424S", "1.0", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+ {"YAMAHA", "CRW6416S", "1.0c", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
{"MITSUMI", "CD-R CR-2201CS", "6119", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
{"RELISYS", "Scorpio", "*", BLIST_NOLUN}, /* responds to all LUN */
{"MICROTEK", "ScanMaker II", "5.61", BLIST_NOLUN}, /* responds to all LUN */
@@ -137,10 +139,12 @@ static struct dev_info device_list[] =
{"iomega", "jaz 1GB", "J.86", BLIST_NOTQ | BLIST_NOLUN},
{"CREATIVE","DVD-RAM RAM","*", BLIST_GHOST},
{"MATSHITA","PD-2 LF-D100","*", BLIST_GHOST},
+ {"AOpen", "PD-2 DVD-520S", "*", BLIST_GHOST},
{"HITACHI", "GF-1050","*", BLIST_GHOST}, /* Hitachi SCSI DVD-RAM */
{"TOSHIBA","CDROM","*", BLIST_ISROM},
{"TOSHIBA","DVD-RAM SD-W1101","*", BLIST_GHOST},
{"TOSHIBA","DVD-RAM SD-W1111","*", BLIST_GHOST},
+ {"MegaRAID", "LD", "*", BLIST_FORCELUN},
/*
* Must be at end of list...
diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile
index 5b4d35c8c..046379d37 100644
--- a/drivers/sound/Makefile
+++ b/drivers/sound/Makefile
@@ -18,7 +18,7 @@ ALL_SUB_DIRS := $(SUB_DIRS)
export-objs := ad1848.o audio_syms.o midi_syms.o mpu401.o \
msnd.o opl3.o sb_common.o sequencer_syms.o \
- sound_core.o sound_syms.o uart401.o ad1816.o \
+ sound_core.o sound_syms.o uart401.o \
nm256_audio.o ac97.o ac97_codec.o
diff --git a/drivers/sound/ac97_codec.c b/drivers/sound/ac97_codec.c
index 32a3380e9..0a0e30775 100644
--- a/drivers/sound/ac97_codec.c
+++ b/drivers/sound/ac97_codec.c
@@ -1,5 +1,5 @@
/*
- * ac97_codec.c: Generic AC97 mixer module
+ * ac97_codec.c: Generic AC97 mixer/modem module
*
* Derived from ac97 mixer in maestro and trident driver.
*
@@ -20,6 +20,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* History
+ * v0.4 Mar 15 2000 Ollie Lho
+ * dual codec support verified with 4 channel output
* v0.3 Feb 22 2000 Ollie Lho
* bug fix for record mask setting
* v0.2 Feb 10 2000 Ollie Lho
@@ -42,7 +44,9 @@ static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, uns
static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask);
static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg);
-static int sigmatel_init(struct ac97_codec * codec);
+static int ac97_init_mixer(struct ac97_codec *codec);
+
+static int sigmatel_init(struct ac97_codec *codec);
#define arraysize(x) (sizeof(x)/sizeof((x)[0]))
@@ -59,6 +63,8 @@ static struct {
{0x43525923, "Cirrus Logic CS4298" , NULL},
{0x43525931, "Cirrus Logic CS4299" , NULL},
{0x4e534331, "National Semiconductor LM4549" , NULL},
+ {0x53494c22, "Silicon Laboratory Si3036" , NULL},
+ {0x53494c23, "Silicon Laboratory Si3038" , NULL},
{0x83847600, "SigmaTel STAC????" , NULL},
{0x83847604, "SigmaTel STAC9701/3/4/5", NULL},
{0x83847605, "SigmaTel STAC9704" , NULL},
@@ -323,7 +329,6 @@ static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask)
/* else, write the first set in the mask as the
output */
-
val = ffs(mask);
val = ac97_oss_rm[val-1];
val |= val << 8; /* set both channels */
@@ -503,14 +508,22 @@ int ac97_read_proc (char *page, char **start, off_t off,
int ac97_probe_codec(struct ac97_codec *codec)
{
- u16 id1, id2, cap;
+ u16 id1, id2;
+ u16 audio, modem;
int i;
/* probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should
be read zero. Probing of AC97 in this way is not reliable, it is not even SAFE !! */
codec->codec_write(codec, AC97_RESET, 0L);
- if ((cap = codec->codec_read(codec, AC97_RESET)) & 0x8000)
+ if ((audio = codec->codec_read(codec, AC97_RESET)) & 0x8000) {
+ printk(KERN_ERR "ac97_codec: %s ac97 codec not present\n",
+ codec->id ? "Secondary" : "Primary");
return 0;
+ }
+
+ /* probe for Modem Codec */
+ codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0L);
+ modem = codec->codec_read(codec, AC97_EXTENDED_MODEM_ID);
codec->name = NULL;
codec->codec_init = NULL;
@@ -526,9 +539,20 @@ int ac97_probe_codec(struct ac97_codec *codec)
}
if (codec->name == NULL)
codec->name = "Unknown";
- printk(KERN_INFO "ac97_codec: ac97 vendor id1: 0x%04x, id2: 0x%04x (%s)\n",
+ printk(KERN_INFO "ac97_codec: AC97 %s codec, vendor id1: 0x%04x, "
+ "id2: 0x%04x (%s)\n", audio ? "Audio" : (modem ? "Modem" : ""),
id1, id2, codec->name);
+ return ac97_init_mixer(codec);
+}
+
+static int ac97_init_mixer(struct ac97_codec *codec)
+{
+ u16 cap;
+ int i;
+
+ cap = codec->codec_read(codec, AC97_RESET);
+
/* mixer masks */
codec->supported_mixers = AC97_SUPPORTED_MASK;
codec->stereo_mixers = AC97_STEREO_MASK;
@@ -548,7 +572,7 @@ int ac97_probe_codec(struct ac97_codec *codec)
codec->codec_write(codec, AC97_MASTER_VOL_STEREO, 0L);
codec->codec_write(codec, AC97_PCMOUT_VOL, 0L);
- /* codec specific initialization for 4-6 channel output */
+ /* codec specific initialization for 4-6 channel output or secondary codec stuff */
if (codec->id != 0 && codec->codec_init != NULL) {
codec->codec_init(codec);
}
@@ -566,11 +590,28 @@ int ac97_probe_codec(struct ac97_codec *codec)
return 1;
}
+static int ac97_init_modem(struct ac97_codec *codec)
+{
+ return 0;
+}
+
static int sigmatel_init(struct ac97_codec * codec)
{
codec->codec_write(codec, AC97_SURROUND_MASTER, 0L);
- /* initialize SigmaTel STAC9721/23 */
- codec->codec_write(codec, 0x74, 0x01);
+
+ /* initialize SigmaTel STAC9721/23 as secondary codec, decoding AC link
+ sloc 3,4 = 0x01, slot 7,8 = 0x00, */
+ codec->codec_write(codec, 0x74, 0x00);
+
+ /* we don't have the crystal when we are on an AMR card, so use
+ BIT_CLK as our clock source. Write the magic word ABBA and read
+ back to enable register 0x78 */
+ codec->codec_write(codec, 0x76, 0xabba);
+ codec->codec_read(codec, 0x76);
+
+ /* sync all the clocks*/
+ codec->codec_write(codec, 0x78, 0x3802);
+
return 1;
}
diff --git a/drivers/sound/ad1816.c b/drivers/sound/ad1816.c
index 8bd68c4dc..e91d71909 100644
--- a/drivers/sound/ad1816.c
+++ b/drivers/sound/ad1816.c
@@ -6,16 +6,8 @@
*
* Based on the CS4232/AD1848 driver Copyright (C) by Hannu Savolainen 1993-1996
*
- * This software is still under development. New versions of the driver
- * are available from:
- * http://www.student.informatik.tu-darmstadt.de/~tek/projects/linux.html
- * or http://www.tu-darmstadt.de/~tek01/projects/linux.html
- *
- * Please report any bugs to: tek@rbg.informatik.tu-darmstadt.de
*
- *
- * version: 1.3
- * cvs: $Header: /home/tek/CVSROOT/sound22/ad1816.c,v 1.3 1999/04/18 16:41:41 tek Exp $
+ * version: 1.3.1
* status: experimental
* date: 1999/4/18
*
@@ -34,18 +26,23 @@
* required by some Aztech/Newcom SC-16 cards. 1999/04/18
*
* Christoph Hellwig: Adapted to module_init/module_exit. 2000/03/03
+ *
+ * Christoph Hellwig: Added isapnp support 2000/03/15
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/isapnp.h>
#include <linux/stddef.h>
+
#include "soundmodule.h"
#include "sound_config.h"
#define DEBUGNOISE(x)
-#define DEBUGINFO(x)
-#define DEBUGLOG(x) x
-#define DEBUGWARN(x) x
+#define DEBUGINFO(x)
+#define DEBUGLOG(x)
+#define DEBUGWARN(x)
#define CHECK_FOR_POWER { int timeout=100; \
while (timeout > 0 && (inb(devc->base)&0x80)!= 0x80) {\
@@ -356,7 +353,7 @@ static void ad1816_trigger (int dev, int state)
unsigned long flags;
ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc;
- DEBUGINFO (printk("ad1816: trigger called! (devc=%d,devc->base=%d\n",devc,devc->base));
+ DEBUGINFO (printk("ad1816: trigger called! (devc=%d,devc->base=%d\n", devc, devc->base));
/* mode may have changed */
@@ -427,70 +424,38 @@ static unsigned int ad1816_set_bits (int dev, unsigned int arg)
{
ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc;
- static struct format_tbl
- {
+ static struct format_tbl {
int format;
unsigned char bits;
- }
- format2bits[] =
- {
- {
- 0, 0
- }
- ,
- {
- AFMT_MU_LAW, 1
- }
- ,
- {
- AFMT_A_LAW, 3
- }
- ,
- {
- AFMT_IMA_ADPCM, 0
- }
- ,
- {
- AFMT_U8, 0
- }
- ,
- {
- AFMT_S16_LE, 2
- }
- ,
- {
- AFMT_S16_BE, 6
- }
- ,
- {
- AFMT_S8, 0
- }
- ,
- {
- AFMT_U16_LE, 0
- }
- ,
- {
- AFMT_U16_BE, 0
- }
- };
+ } format2bits[] = {
+ { 0, 0 },
+ { AFMT_MU_LAW, 1 },
+ { AFMT_A_LAW, 3 },
+ { AFMT_IMA_ADPCM, 0 },
+ { AFMT_U8, 0 },
+ { AFMT_S16_LE, 2 },
+ { AFMT_S16_BE, 6 },
+ { AFMT_S8, 0 },
+ { AFMT_U16_LE, 0 },
+ { AFMT_U16_BE, 0 }
+ };
+
int i, n = sizeof (format2bits) / sizeof (struct format_tbl);
/* return current format */
- if (arg == 0) {
+ if (arg == 0)
return devc->audio_format;
- }
devc->audio_format = arg;
/* search matching format bits */
- for (i = 0; i < n; i++) {
+ for (i = 0; i < n; i++)
if (format2bits[i].format == arg) {
devc->format_bits = format2bits[i].bits;
devc->audio_format = arg;
return arg;
}
- }
+
/* Still hanging here. Something must be terribly wrong */
devc->format_bits = 0;
return devc->audio_format = AFMT_U8;
@@ -500,9 +465,8 @@ static short ad1816_set_channels (int dev, short arg)
{
ad1816_info *devc = (ad1816_info *) audio_devs[dev]->devc;
- if (arg != 1 && arg != 2) {
+ if (arg != 1 && arg != 2)
return devc->channels;
- }
devc->channels = arg;
return arg;
@@ -515,9 +479,8 @@ static int ad1816_open (int dev, int mode)
unsigned long flags;
/* is device number valid ? */
- if (dev < 0 || dev >= num_audiodevs) {
+ if (dev < 0 || dev >= num_audiodevs)
return -(ENXIO);
- }
/* get device info of this dev */
devc = (ad1816_info *) audio_devs[dev]->devc;
@@ -600,10 +563,10 @@ static struct audio_driver ad1816_audio_driver =
static void ad1816_interrupt (int irq, void *dev_id, struct pt_regs *dummy)
{
- unsigned char status;
- ad1816_info *devc;
- int dev;
- unsigned long flags;
+ unsigned char status;
+ ad1816_info *devc;
+ int dev;
+ unsigned long flags;
if (irq < 0 || irq > 15) {
@@ -632,18 +595,15 @@ static void ad1816_interrupt (int irq, void *dev_id, struct pt_regs *dummy)
devc->irq_ok=1;
- if (status == 0) {
+ if (status == 0)
DEBUGWARN(printk ("ad1816: interrupt: Got interrupt, but no reason?\n"));
- }
- if (devc->opened && (devc->audio_mode & PCM_ENABLE_INPUT)
- && (status&64)){
+
+ if (devc->opened && (devc->audio_mode & PCM_ENABLE_INPUT) && (status&64))
DMAbuf_inputintr (dev);
- }
- if (devc->opened && (devc->audio_mode & PCM_ENABLE_OUTPUT) &&
- (status & 128)) {
+ if (devc->opened && (devc->audio_mode & PCM_ENABLE_OUTPUT) && (status & 128))
DMAbuf_outputintr (dev, 1);
- }
+
restore_flags(flags);
}
@@ -659,7 +619,7 @@ struct mixer_def {
};
static char mix_cvt[101] = {
- 0, 0,3,7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42,
+ 0, 0, 3, 7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42,
43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65,
65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79,
80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90,
@@ -705,23 +665,23 @@ MIX_ENT(SOUND_MIXER_LINE3, 39, 0, 9, 4, 39, 1, 0, 5)
static unsigned short default_mixer_levels[SOUND_MIXER_NRDEVICES] =
{
- 0x4343, /* Master Volume */
- 0x3232, /* Bass */
- 0x3232, /* Treble */
- 0x0000, /* FM */
- 0x4343, /* PCM */
- 0x0000, /* PC Speaker */
- 0x0000, /* Ext Line */
- 0x0000, /* Mic */
- 0x0000, /* CD */
- 0x0000, /* Recording monitor */
- 0x0000, /* SB PCM */
- 0x0000, /* Recording level */
- 0x0000, /* Input gain */
- 0x0000, /* Output gain */
- 0x0000, /* Line1 */
- 0x0000, /* Line2 */
- 0x0000 /* Line3 (usually line in)*/
+ 0x4343, /* Master Volume */
+ 0x3232, /* Bass */
+ 0x3232, /* Treble */
+ 0x0000, /* FM */
+ 0x4343, /* PCM */
+ 0x0000, /* PC Speaker */
+ 0x0000, /* Ext Line */
+ 0x0000, /* Mic */
+ 0x0000, /* CD */
+ 0x0000, /* Recording monitor */
+ 0x0000, /* SB PCM */
+ 0x0000, /* Recording level */
+ 0x0000, /* Input gain */
+ 0x0000, /* Output gain */
+ 0x0000, /* Line1 */
+ 0x0000, /* Line2 */
+ 0x0000 /* Line3 (usually line in)*/
};
#define LEFT_CHN 0
@@ -739,29 +699,24 @@ ad1816_set_recmask (ad1816_info * devc, int mask)
n = 0;
/* Count selected device bits */
- for (i = 0; i < 32; i++) {
- if (mask & (1 << i)) {
+ for (i = 0; i < 32; i++)
+ if (mask & (1 << i))
n++;
- }
- }
- if (n == 0) {
+ if (n == 0)
mask = SOUND_MASK_MIC;
- } else if (n != 1) { /* Too many devices selected */
+ else if (n != 1) { /* Too many devices selected */
/* Filter out active settings */
mask &= ~devc->recmask;
n = 0;
/* Count selected device bits */
- for (i = 0; i < 32; i++) {
- if (mask & (1 << i)) {
+ for (i = 0; i < 32; i++)
+ if (mask & (1 << i))
n++;
- }
- }
- if (n != 1) {
+ if (n != 1)
mask = SOUND_MASK_MIC;
- }
}
switch (mask) {
@@ -810,9 +765,8 @@ change_bits (int *regval, int dev, int chn, int newval)
/* Reverse polarity*/
- if (mix_devices[dev][chn].polarity == 1) {
+ if (mix_devices[dev][chn].polarity == 1)
newval = 100 - newval;
- }
mask = (1 << mix_devices[dev][chn].nbits) - 1;
shift = mix_devices[dev][chn].bitpos;
@@ -830,12 +784,10 @@ ad1816_mixer_get (ad1816_info * devc, int dev)
DEBUGINFO(printk("ad1816: mixer_get called!\n"));
/* range check + supported mixer check */
- if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES ) {
+ if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES )
return (-(EINVAL));
- }
- if (!((1 << dev) & devc->supported_devices)) {
+ if (!((1 << dev) & devc->supported_devices))
return -(EINVAL);
- }
return devc->levels[dev];
}
@@ -853,27 +805,21 @@ ad1816_mixer_set (ad1816_info * devc, int dev, int value)
DEBUGINFO(printk("ad1816: mixer_set called!\n"));
- if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES ) {
+ if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES )
return -(EINVAL);
- }
- if (left > 100) {
+ if (left > 100)
left = 100;
- }
- if (left < 0) {
+ if (left < 0)
left = 0;
- }
- if (right > 100) {
+ if (right > 100)
right = 100;
- }
- if (right < 0) {
+ if (right < 0)
right = 0;
- }
/* Mono control */
- if (mix_devices[dev][RIGHT_CHN].nbits == 0) {
+ if (mix_devices[dev][RIGHT_CHN].nbits == 0)
right = left;
- }
retvol = left | (right << 8);
/* Scale it */
@@ -882,14 +828,12 @@ ad1816_mixer_set (ad1816_info * devc, int dev, int value)
right = mix_cvt[right];
/* reject all mixers that are not supported */
- if (!(devc->supported_devices & (1 << dev))) {
+ if (!(devc->supported_devices & (1 << dev)))
return -(EINVAL);
- }
/* sanity check */
- if (mix_devices[dev][LEFT_CHN].nbits == 0) {
+ if (mix_devices[dev][LEFT_CHN].nbits == 0)
return -(EINVAL);
- }
/* keep precise volume internal */
devc->levels[dev] = retvol;
@@ -905,11 +849,10 @@ ad1816_mixer_set (ad1816_info * devc, int dev, int value)
if ( regoffs==5 || regoffs==14 || regoffs==15 ||
regoffs==16 || regoffs==17 || regoffs==18 ||
regoffs==19 || regoffs==39) {
- if (left==0) {
+ if (left==0)
valmute |= 0x8000;
- } else {
+ else
valmute &= ~0x8000;
- }
}
ad_write (devc, regoffs, valmute); /* mute */
@@ -918,9 +861,9 @@ ad1816_mixer_set (ad1816_info * devc, int dev, int value)
*/
/* Was just a mono channel */
- if (mix_devices[dev][RIGHT_CHN].nbits == 0) {
+ if (mix_devices[dev][RIGHT_CHN].nbits == 0)
return retvol;
- }
+
regoffs = mix_devices[dev][RIGHT_CHN].regno;
val = ad_read (devc, regoffs);
change_bits (&val, dev, RIGHT_CHN, right);
@@ -929,11 +872,10 @@ ad1816_mixer_set (ad1816_info * devc, int dev, int value)
if ( regoffs==5 || regoffs==14 || regoffs==15 ||
regoffs==16 || regoffs==17 || regoffs==18 ||
regoffs==19 || regoffs==39) {
- if (right==0) {
+ if (right==0)
valmute |= 0x80;
- } else {
+ else
valmute &= ~0x80;
- }
}
ad_write (devc, regoffs, valmute); /* mute */
@@ -968,11 +910,9 @@ ad1816_mixer_reset (ad1816_info * devc)
devc->supported_rec_devices = REC_DEVICES;
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
- if (devc->supported_devices & (1 << i)) {
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if (devc->supported_devices & (1 << i))
ad1816_mixer_set (devc, i, default_mixer_levels[i]);
- }
- }
ad1816_set_recmask (devc, SOUND_MASK_MIC);
}
@@ -992,22 +932,19 @@ ad1816_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
switch (cmd & 0xff){
case SOUND_MIXER_RECSRC:
- if (get_user(val, (int *)arg)) {
+ if (get_user(val, (int *)arg))
return -EFAULT;
- }
val=ad1816_set_recmask (devc, val);
- return put_user(val, (int *)arg);
+ return put_user(val, (int *)arg);
break;
default:
- if (get_user(val, (int *)arg)){
+ if (get_user(val, (int *)arg))
return -EFAULT;
- }
- if ((val=ad1816_mixer_set (devc, cmd & 0xff, val))<0) {
+ if ((val=ad1816_mixer_set (devc, cmd & 0xff, val))<0)
return val;
- } else {
+ else
return put_user(val, (int *)arg);
- }
}
} else {
/* read ioctl */
@@ -1039,25 +976,22 @@ ad1816_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
break;
default:
- if ((val=ad1816_mixer_get (devc, cmd & 0xff))<0) {
+ if ((val=ad1816_mixer_get (devc, cmd & 0xff))<0)
return val;
- } else {
+ else
return put_user(val, (int *)arg);
- }
}
}
- } else {
+ } else
/* not for mixer */
return -(EINVAL);
- }
}
/* ------------------------------------------------------------------- */
/* Mixer structure */
-static struct mixer_operations ad1816_mixer_operations =
-{
+static struct mixer_operations ad1816_mixer_operations = {
"AD1816",
"AD1816 Mixer",
ad1816_mixer_ioctl
@@ -1076,7 +1010,7 @@ static int __init probe_ad1816 ( struct address_info *hw_config )
int io_base=hw_config->io_base;
int *osp=hw_config->osp;
int tmp;
-
+
printk("ad1816: AD1816 sounddriver Copyright (C) 1998 by Thorsten Knabe\n");
printk("ad1816: io=0x%x, irq=%d, dma=%d, dma2=%d, clockfreq=%d, options=%d isadmabug=%d\n",
hw_config->io_base,
@@ -1098,7 +1032,7 @@ static int __init probe_ad1816 ( struct address_info *hw_config )
printk ("ad1816: detect error - step 0\n");
return 0;
}
-
+
devc->base = io_base;
devc->irq_ok = 0;
devc->irq = 0;
@@ -1126,7 +1060,8 @@ static int __init probe_ad1816 ( struct address_info *hw_config )
DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 2)\n"));
return(0);
}
-
+
+
/* writes to ireg 10 are copied to ireg 11 */
ad_write(devc,10,54321);
if (ad_read(devc,11)!=54321) {
@@ -1140,7 +1075,7 @@ static int __init probe_ad1816 ( struct address_info *hw_config )
DEBUGLOG (printk ("ad1816: Chip is not an AD1816 (Test 4)\n"));
return(0);
}
-
+
/* bit in base +1 cannot be set to 1 */
tmp=inb(devc->base+1);
outb(0xff,devc->base+1);
@@ -1152,7 +1087,7 @@ static int __init probe_ad1816 ( struct address_info *hw_config )
DEBUGLOG (printk ("ad1816: detect() - Detected OK\n"));
DEBUGLOG (printk ("ad1816: AD1816 Version: %d\n",ad_read(devc,45)));
-
+
/* detection was successful */
return 1;
}
@@ -1330,6 +1265,22 @@ static int __initdata irq = -1;
static int __initdata dma = -1;
static int __initdata dma2 = -1;
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+struct pci_dev *ad1816_dev = NULL,
+ *mpu_dev = NULL;
+
+static int activated = 1;
+
+static int isapnp = 1;
+static int isapnpjump = 0;
+
+MODULE_PARM(isapnp, "i");
+MODULE_PARM(isapnpjump, "i");
+
+#else
+static int isapnp = 0;
+#endif
+
MODULE_PARM(io,"i");
MODULE_PARM(irq,"i");
MODULE_PARM(dma,"i");
@@ -1337,12 +1288,132 @@ MODULE_PARM(dma2,"i");
MODULE_PARM(ad1816_clockfreq,"i");
MODULE_PARM(options,"i");
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+
+static struct pci_dev *activate_dev(char *devname, char *resname, struct pci_dev *dev)
+{
+ int err;
+
+ if(dev->active) {
+ activated = 0;
+ return(dev);
+ }
+
+ if((err = dev->activate(dev)) < 0) {
+ printk(KERN_ERR "ad1816: %s %s config failed (out of resources?)[%d]\n",
+ devname, resname, err);
+ dev->deactivate(dev);
+ return(NULL);
+ }
+
+ return(dev);
+}
+
+static struct pci_dev *ad1816_init_generic(struct pci_bus *bus, struct pci_dev *card,
+ struct address_info *hw_config)
+{
+ if((ad1816_dev = isapnp_find_dev(bus, card->vendor, card->device, NULL))) {
+ ad1816_dev->prepare(ad1816_dev);
+
+ if((ad1816_dev = activate_dev("Analog Devices 1816(A)", "ad1816", ad1816_dev))) {
+ hw_config->io_base = ad1816_dev->resource[2].start;
+ hw_config->irq = ad1816_dev->irq_resource[0].start;
+ hw_config->dma = ad1816_dev->dma_resource[0].start;
+ hw_config->dma2 = ad1816_dev->dma_resource[1].start;
+ }
+ }
+
+ return(ad1816_dev);
+}
+
+static struct {
+ unsigned short vendor;
+ unsigned short function;
+ struct pci_dev * (*initfunc)(struct pci_bus*, struct pci_dev *, struct address_info *);
+ char *name;
+} isapnp_ad1816_list[] __initdata = {
+ {ISAPNP_VENDOR('A','D','S'), ISAPNP_FUNCTION(0x7150), &ad1816_init_generic, "Analog Devices 1815" },
+ {ISAPNP_VENDOR('A','D','S'), ISAPNP_FUNCTION(0x7180), &ad1816_init_generic, "Analog Devices 1816A" },
+ {0}
+};
+
+static int __init ad1816_init_isapnp(struct address_info *hw_config,
+ struct pci_bus *bus, struct pci_dev *card, int slot)
+{
+ struct pci_dev *idev = NULL;
+
+ /* You missed the init func? That's bad. */
+ if(isapnp_ad1816_list[slot].initfunc) {
+ char *busname = bus->name[0] ? bus->name : isapnp_ad1816_list[slot].name;
+
+ printk(KERN_INFO "ad1816: %s detected\n", busname);
+
+ /* Initialize this baby. */
+ if((idev = isapnp_ad1816_list[slot].initfunc(bus, card, hw_config))) {
+ /* We got it. */
+
+ printk(KERN_NOTICE "ad1816: ISAPnP reports '%s' at i/o %#x, irq %d, dma %d, %d\n",
+ busname,
+ hw_config->io_base, hw_config->irq, hw_config->dma,
+ hw_config->dma2);
+ return 1;
+ } else
+ printk(KERN_INFO "ad1816: Failed to initialize %s\n", busname);
+ } else
+ printk(KERN_ERR "ad1816: Bad entry in ad1816.c PnP table\n");
+
+ return 0;
+}
+
+/*
+ * Actually this routine will detect and configure only the first card with successful
+ * initialization. isapnpjump could be used to jump to a specific entry.
+ * Please always add entries at the end of the array.
+ * Should this be fixed? - azummo
+ */
+
+int __init ad1816_probe_isapnp(struct address_info *hw_config)
+{
+ int i;
+
+ /* Count entries in isapnp_ad1816_list */
+ for (i = 0; isapnp_ad1816_list[i].vendor != 0; i++)
+ ;
+ /* Check and adjust isapnpjump */
+ if( isapnpjump < 0 || isapnpjump > ( i - 1 ) ) {
+ printk(KERN_ERR "ad1816: Valid range for isapnpjump is 0-%d. Adjusted to 0.\n", i-1);
+ isapnpjump = 0;
+ }
+
+ for (i = isapnpjump; isapnp_ad1816_list[i].vendor != 0; i++) {
+ struct pci_dev *card = NULL;
+
+ while ((card = isapnp_find_dev(NULL, isapnp_ad1816_list[i].vendor,
+ isapnp_ad1816_list[i].function, card)))
+ if(ad1816_init_isapnp(hw_config, card->bus, card, i))
+ return 0;
+ }
+
+ return -ENODEV;
+}
+#endif
+
static int __init init_ad1816(void)
{
- cfg.io_base = io;
- cfg.irq = irq;
- cfg.dma = dma;
- cfg.dma2 = dma2;
+
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+ if(isapnp && (ad1816_probe_isapnp(&cfg) < 0) ) {
+ printk(KERN_NOTICE "ad1816: No ISAPnP cards found, trying standard ones...\n");
+ isapnp = 0;
+ }
+#endif
+
+ if( isapnp == 0) {
+ cfg.io_base = io;
+ cfg.irq = irq;
+ cfg.dma = dma;
+ cfg.dma2 = dma2;
+ }
if (cfg.io_base == -1 || cfg.irq == -1 || cfg.dma == -1 || cfg.dma2 == -1) {
printk(KERN_INFO "ad1816: dma, dma2, irq and io must be set.\n");
@@ -1352,9 +1423,11 @@ static int __init init_ad1816(void)
if (probe_ad1816(&cfg) == 0) {
return -ENODEV;
}
-
+
attach_ad1816(&cfg);
SOUND_LOCK;
+
+ return 0;
}
static void __exit cleanup_ad1816 (void)
@@ -1370,6 +1443,11 @@ static void __exit cleanup_ad1816 (void)
nr_ad1816_devs=0;
SOUND_LOCK_END;
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+ if(activated)
+ if(ad1816_dev)
+ ad1816_dev->deactivate(ad1816_dev);
+#endif
}
module_init(init_ad1816);
diff --git a/drivers/sound/bin2hex.c b/drivers/sound/bin2hex.c
index 100f18f9c..3b546b93c 100644
--- a/drivers/sound/bin2hex.c
+++ b/drivers/sound/bin2hex.c
@@ -33,6 +33,6 @@ int main( int argc, const char * argv [] )
i++;
}
- printf( "};\n#define %sLen %d\n", varname, i );
+ printf( "};\nstatic int %sLen = %d;\n", varname, i );
return 0;
}
diff --git a/drivers/sound/dev_table.c b/drivers/sound/dev_table.c
index 99a4d8f44..0a1e2981c 100644
--- a/drivers/sound/dev_table.c
+++ b/drivers/sound/dev_table.c
@@ -210,6 +210,7 @@ void sound_unload_mixerdev(int dev)
{
mixer_devs[dev] = NULL;
unregister_sound_mixer(dev<<4);
+ num_mixers--;
}
}
diff --git a/drivers/sound/sb_common.c b/drivers/sound/sb_common.c
index 25318c1ac..5fb40f46d 100644
--- a/drivers/sound/sb_common.c
+++ b/drivers/sound/sb_common.c
@@ -908,10 +908,13 @@ void sb_dsp_unload(struct address_info *hw_config, int sbmpu)
}
if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI))
{
+ extern int sbmixnum;
+
if (devc->irq > 0)
free_irq(devc->irq, devc);
sound_unload_mixerdev(devc->my_mixerdev);
+ sbmixnum--;
/* We don't have to do this bit any more the UART401 is its own
master -- Krzysztof Halasa */
/* But we have to do it, if UART401 is not detected */
diff --git a/drivers/sound/trident.c b/drivers/sound/trident.c
index b3a9f7504..38afc31c9 100644
--- a/drivers/sound/trident.c
+++ b/drivers/sound/trident.c
@@ -29,6 +29,10 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* History
+ * v0.14 Mar 15 2000 Ollie Lho
+ * 5.1 channel output support with channel binding. What's the Matrix ?
+ * v0.13.1 Mar 10 2000 Ollie Lho
+ * few minor bugs on dual codec support, needs more testing
* v0.13 Mar 03 2000 Ollie Lho
* new pci_* for 2.4 kernel, back ported to 2.2
* v0.12 Feb 23 2000 Ollie Lho
@@ -62,14 +66,14 @@
*
* ToDo
* Clean up of low level channel register access code. (done)
- * Fix the bug on dma buffer management in update_ptr, read/write, drain_dac (done)
- * Dual AC97 codecs support (done partially, need channel binding to test)
+ * Fix the bug on dma buffer management in update_ptr, read/write, drain_dac (done)
+ * Dual AC97 codecs support (done)
* Recording support (done)
* Mmap support
- * "Channel Binding" ioctl extension
- * new pci device driver interface for 2.4 kernel
+ * "Channel Binding" ioctl extension (done)
+ * new pci device driver interface for 2.4 kernel (done)
*/
-
+
#include <linux/module.h>
#include <linux/version.h>
#include <linux/string.h>
@@ -92,7 +96,7 @@
#include "trident.h"
-#define DRIVER_VERSION "0.13"
+#define DRIVER_VERSION "0.14"
/* magic numbers to protect our data structures */
#define TRIDENT_CARD_MAGIC 0x5072696E /* "Prin" */
@@ -107,8 +111,14 @@
#define NR_AC97 2
/* minor number of /dev/dspW */
+#define SND_DEV_DSP8 3
+
+/* minor number of /dev/dspW */
#define SND_DEV_DSP16 5
+/* minor number of /dev/swmodem (temporary, experimental) */
+#define SND_DEV_SWMODEM 7
+
static const unsigned sample_size[] = { 1, 2, 2, 4 };
static const unsigned sample_shift[] = { 0, 1, 1, 2 };
@@ -126,7 +136,7 @@ static char * card_names[] = {
"SiS 7018 PCI Audio"
};
-static struct pci_device_id trident_pci_tbl [] __devinitdata = {
+static struct pci_device_id trident_pci_tbl [] __initdata = {
{PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_DX,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, TRIDENT_4D_DX},
{PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_NX,
@@ -256,6 +266,19 @@ struct trident_card {
u32 irq;
};
+/* table to map from CHANNELMASK to channel attribute for SiS 7018 */
+static u16 mask2attr [] =
+{
+ PCM_LR, PCM_LR, SURR_LR, CENTER_LFE,
+ HSET, MIC, MODEM_LINE1, MODEM_LINE2,
+ I2S_LR, SPDIF_LR
+};
+/* table to map from channel attribute to CHANNELMASK for SiS 7018 */
+static int attr2mask [] = {
+ DSP_BIND_MODEM1, DSP_BIND_MODEM2, DSP_BIND_FRONT, DSP_BIND_HANDSET,
+ DSP_BIND_I2S, DSP_BIND_CENTER_LFE, DSP_BIND_SURR, DSP_BIND_SPDIF
+};
+
static struct trident_card *devs = NULL;
static void trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val);
@@ -596,11 +619,7 @@ static void trident_play_setup(struct trident_state *state)
channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt];
channel->eso -= 1;
- if (state->card->pci_id == PCI_DEVICE_ID_SI_7018) {
- /* FIXME: channel attributes are configured by ioctls, but it is not
- implemented so just set to ZERO for the moment */
- channel->attribute = 0;
- } else {
+ if (state->card->pci_id != PCI_DEVICE_ID_SI_7018) {
channel->attribute = 0;
}
@@ -660,12 +679,7 @@ static void trident_rec_setup(struct trident_state *state)
channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt];
channel->eso -= 1;
- if (state->card->pci_id == PCI_DEVICE_ID_SI_7018) {
- /* FIXME: channel attributes are configured by ioctls, but it is not
- implemented so just set to 0x8a80 for the moment, record from PCM L/R
- input and mono = (left + right + 1)/2*/
- channel->attribute = 0x8A80;
- } else {
+ if (state->card->pci_id != PCI_DEVICE_ID_SI_7018) {
channel->attribute = 0;
}
@@ -1577,7 +1591,8 @@ static int trident_ioctl(struct inode *inode, struct file *file, unsigned int cm
return 0;
case SNDCTL_DSP_GETCAPS:
- return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP, (int *)arg);
+ return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP|DSP_CAP_BIND,
+ (int *)arg);
case SNDCTL_DSP_GETTRIGGER:
val = 0;
@@ -1656,6 +1671,28 @@ static int trident_ioctl(struct inode *inode, struct file *file, unsigned int cm
return put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ?
AFMT_S16_LE : AFMT_U8, (int *)arg);
+ case SNDCTL_DSP_GETCHANNELMASK:
+ return put_user(DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE,
+ (int *)arg);
+
+ case SNDCTL_DSP_BIND_CHANNEL:
+ if (state->card->pci_id != PCI_DEVICE_ID_SI_7018)
+ return -EINVAL;
+
+ get_user_ret(val, (int *)arg, -EFAULT);
+ if (val == DSP_BIND_QUERY) {
+ val = dmabuf->channel->attribute | 0x3c00;
+ val = attr2mask[val >> 8];
+ } else {
+ dmabuf->ready = 0;
+ if (file->f_mode & FMODE_READ)
+ dmabuf->channel->attribute = (CHANNEL_REC|SRC_ENABLE);
+ if (file->f_mode & FMODE_WRITE)
+ dmabuf->channel->attribute = (CHANNEL_SPC_PB|SRC_ENABLE);
+ dmabuf->channel->attribute |= mask2attr[ffs(val)];
+ }
+ return put_user(val, (int *)arg);
+
case SNDCTL_DSP_MAPINBUF:
case SNDCTL_DSP_MAPOUTBUF:
case SNDCTL_DSP_SETSYNCRO:
@@ -1673,6 +1710,7 @@ static int trident_open(struct inode *inode, struct file *file)
int minor = MINOR(inode->i_rdev);
struct trident_card *card = devs;
struct trident_state *state = NULL;
+ struct dmabuf *dmabuf = NULL;
/* find an avaiable virtual channel (instance of /dev/dsp) */
while (card != NULL) {
@@ -1683,6 +1721,7 @@ static int trident_open(struct inode *inode, struct file *file)
if (state == NULL)
return -ENOMEM;
memset(state, 0, sizeof(struct trident_state));
+ dmabuf = &state->dmabuf;
goto found_virt;
}
}
@@ -1694,7 +1733,7 @@ static int trident_open(struct inode *inode, struct file *file)
found_virt:
/* found a free virtual channel, allocate hardware channels */
- if ((state->dmabuf.channel = trident_alloc_pcm_channel(card)) == NULL) {
+ if ((dmabuf->channel = trident_alloc_pcm_channel(card)) == NULL) {
kfree (card->states[i]);
card->states[i] = NULL;;
return -ENODEV;
@@ -1704,7 +1743,7 @@ static int trident_open(struct inode *inode, struct file *file)
state->virt = i;
state->card = card;
state->magic = TRIDENT_STATE_MAGIC;
- init_waitqueue_head(&state->dmabuf.wait);
+ init_waitqueue_head(&dmabuf->wait);
init_MUTEX(&state->open_sem);
file->private_data = state;
@@ -1714,24 +1753,34 @@ static int trident_open(struct inode *inode, struct file *file)
should be default to unsigned 8-bits, mono, with sample rate 8kHz and
/dev/dspW will accept 16-bits sample */
if (file->f_mode & FMODE_WRITE) {
- state->dmabuf.fmt &= ~TRIDENT_FMT_MASK;
- if ((minor & 0xf) == SND_DEV_DSP16)
- state->dmabuf.fmt |= TRIDENT_FMT_16BIT;
- state->dmabuf.ossfragshift = 0;
- state->dmabuf.ossmaxfrags = 0;
- state->dmabuf.subdivision = 0;
+ dmabuf->fmt &= ~TRIDENT_FMT_MASK;
+ if ((minor & 0x0f) == SND_DEV_DSP16)
+ dmabuf->fmt |= TRIDENT_FMT_16BIT;
+ dmabuf->ossfragshift = 0;
+ dmabuf->ossmaxfrags = 0;
+ dmabuf->subdivision = 0;
+ if (card->pci_id == PCI_DEVICE_ID_SI_7018) {
+ /* set default channel attribute to normal playback */
+ dmabuf->channel->attribute = CHANNEL_PB;
+ }
trident_set_dac_rate(state, 8000);
}
if (file->f_mode & FMODE_READ) {
/* FIXME: Trident 4d can only record in singed 16-bits stereo, 48kHz sample,
to be dealed with in trident_set_adc_rate() ?? */
- state->dmabuf.fmt &= ~TRIDENT_FMT_MASK;
- if ((minor & 0xf) == SND_DEV_DSP16)
- state->dmabuf.fmt |= TRIDENT_FMT_16BIT;
- state->dmabuf.ossfragshift = 0;
- state->dmabuf.ossmaxfrags = 0;
- state->dmabuf.subdivision = 0;
+ dmabuf->fmt &= ~TRIDENT_FMT_MASK;
+ if ((minor & 0x0f) == SND_DEV_DSP16)
+ dmabuf->fmt |= TRIDENT_FMT_16BIT;
+ dmabuf->ossfragshift = 0;
+ dmabuf->ossmaxfrags = 0;
+ dmabuf->subdivision = 0;
+ if (card->pci_id == PCI_DEVICE_ID_SI_7018) {
+ /* set default channel attribute to 0x8a80, record from
+ PCM L/R FIFO and mono = (left + right + 1)/2*/
+ dmabuf->channel->attribute =
+ (CHANNEL_REC|PCM_LR|MONO_MIX);
+ }
trident_set_adc_rate(state, 8000);
}
@@ -1860,7 +1909,7 @@ static u16 trident_ac97_get(struct ac97_codec *codec, u8 reg)
address = SI_AC97_READ;
mask = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY;
if (codec->id)
- mask |= SI_AC97_SECONDARY;
+ mask |= SI_AC97_SECONDARY;
busy = SI_AC97_BUSY_READ;
break;
case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
@@ -1873,7 +1922,7 @@ static u16 trident_ac97_get(struct ac97_codec *codec, u8 reg)
else
address = NX_ACR2_AC97_R_PRIMARY;
mask = NX_AC97_BUSY_READ;
- busy = 0x0c00;
+ busy = NX_AC97_BUSY_READ | NX_AC97_BUSY_DATA;
break;
}
@@ -1953,9 +2002,13 @@ static int __init trident_ac97_init(struct trident_card *card)
case PCI_DEVICE_ID_SI_7018:
/* disable AC97 GPIO interrupt */
outl(0x00, TRID_REG(card, SI_AC97_GPIO));
- /* stop AC97 cold reset process */
- outl(PCMOUT|SECONDARY_ID, TRID_REG(card, SI_SERIAL_INTF_CTRL));
- ready_2nd = inl(TRID_REG(card, SI_SERIAL_INTF_CTRL));
+ /* when power up the AC link is in cold reset mode so stop it */
+ outl(PCMOUT|SURROUT|CENTEROUT|LFEOUT|SECONDARY_ID,
+ TRID_REG(card, SI_SERIAL_INTF_CTRL));
+ /* it take a long time to recover from a cold reset (especially when you have
+ more than one codec) */
+ udelay(2000);
+ ready_2nd = inl(TRID_REG(card, SI_SERIAL_INTF_CTRL));
ready_2nd &= SI_AC97_SECONDARY_READY;
break;
case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX:
@@ -1972,7 +2025,7 @@ static int __init trident_ac97_init(struct trident_card *card)
for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) {
if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL)
- return -1;
+ return -ENOMEM;
memset(codec, 0, sizeof(struct ac97_codec));
/* initialize some basic codec information, other fields will be filled
@@ -2004,7 +2057,7 @@ static int __init trident_ac97_init(struct trident_card *card)
/* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered
untill "ACCESS" time (in prog_dmabuf called by open/read/write/ioctl/mmap) */
-static int __devinit trident_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
+static int __init trident_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
{
unsigned long iobase;
struct trident_card *card;
@@ -2012,19 +2065,19 @@ static int __devinit trident_probe(struct pci_dev *pci_dev, const struct pci_dev
if (!pci_dma_supported(pci_dev, TRIDENT_DMA_MASK)) {
printk(KERN_ERR "trident: architecture does not support"
" 30bit PCI busmaster DMA\n");
- return -1;
+ return -ENODEV;
}
iobase = pci_dev->resource[0].start;
if (check_region(iobase, 256)) {
printk(KERN_ERR "trident: can't allocate I/O space at 0x%4.4lx\n",
iobase);
- return -1;
+ return -ENODEV;
}
if ((card = kmalloc(sizeof(struct trident_card), GFP_KERNEL)) == NULL) {
printk(KERN_ERR "trident: out of memory\n");
- return -1;
+ return -ENOMEM;
}
memset(card, 0, sizeof(*card));
@@ -2054,7 +2107,7 @@ static int __devinit trident_probe(struct pci_dev *pci_dev, const struct pci_dev
printk(KERN_ERR "trident: unable to allocate irq %d\n", card->irq);
release_region(card->iobase, 256);
kfree(card);
- return 0;
+ return -ENODEV;
}
/* register /dev/dsp */
if ((card->dev_audio = register_sound_dsp(&trident_audio_fops, -1)) < 0) {
@@ -2062,7 +2115,7 @@ static int __devinit trident_probe(struct pci_dev *pci_dev, const struct pci_dev
release_region(iobase, 256);
free_irq(card->irq, card);
kfree(card);
- return -1;
+ return -ENODEV;
}
/* initilize AC97 codec and register /dev/mixer */
if (trident_ac97_init(card) <= 0) {
@@ -2070,7 +2123,7 @@ static int __devinit trident_probe(struct pci_dev *pci_dev, const struct pci_dev
release_region(iobase, 256);
free_irq(card->irq, card);
kfree(card);
- return -1;
+ return -ENODEV;
}
outl(0x00, TRID_REG(card, T4D_MUSICVOL_WAVEVOL));
@@ -2083,7 +2136,7 @@ static int __devinit trident_probe(struct pci_dev *pci_dev, const struct pci_dev
return 0;
}
-static void __devexit trident_remove(struct pci_dev *pci_dev)
+static void __exit trident_remove(struct pci_dev *pci_dev)
{
int i;
struct trident_card *card = pci_dev->driver_data;
@@ -2120,15 +2173,22 @@ static struct pci_driver trident_pci_driver = {
static int __init trident_init_module (void)
{
+ if (!pci_present()) /* No PCI bus in this machine! */
+ return -ENODEV;
+
printk(KERN_INFO "Trident 4DWave/SiS 7018 PCI Audio, version "
DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n");
- return pci_module_init (&trident_pci_driver);
+ if (!pci_register_driver(&trident_pci_driver)) {
+ pci_unregister_driver(&trident_pci_driver);
+ return -ENODEV;
+ }
+ return 0;
}
static void __exit trident_cleanup_module (void)
{
- pci_unregister_driver (&trident_pci_driver);
+ pci_unregister_driver(&trident_pci_driver);
}
module_init(trident_init_module);
diff --git a/drivers/sound/trident.h b/drivers/sound/trident.h
index 6bbb25bd1..847f85d87 100644
--- a/drivers/sound/trident.h
+++ b/drivers/sound/trident.h
@@ -62,6 +62,7 @@
#define DAC_RUNNING 0x01
#define ADC_RUNNING 0x02
+
/* Register Addresses */
/* operational registers common to DX, NX, 7018 */
@@ -130,7 +131,7 @@ enum trident_dx_ac97_bits {
enum trident_nx_ac97_bits {
/* ACR1-3 */
NX_AC97_BUSY_WRITE = 0x0800, NX_AC97_BUSY_READ = 0x0800,
- NX_AC97_WRITE_SECONDARY = 0x0100,
+ NX_AC97_BUSY_DATA = 0x0400, NX_AC97_WRITE_SECONDARY = 0x0100,
/* ACR0 */
NX_AC97_SECONDARY_READY = 0x0040, NX_AC97_SECONDARY_RECORD = 0x0020,
NX_AC97_SURROUND_OUTPUT = 0x0010,
@@ -148,7 +149,7 @@ enum serial_intf_ctrl_bits {
MICIN = 0x00000400, LINE2IN = 0x00000800,
HEAD_SET_IN = 0x00001000, GPIOIN = 0x00002000,
/* 7018 spec says id = 01 but the demo board routed to 10
- SECONDARY_ID= 0x00008000, */
+ SECONDARY_ID= 0x00004000, */
SECONDARY_ID= 0x00004000,
PCMOUT = 0x00010000, SURROUT = 0x00020000,
CENTEROUT = 0x00040000, LFEOUT = 0x00080000,
@@ -173,10 +174,18 @@ enum channel_control_bits {
};
enum channel_attribute {
- MODEM_LINE1, MODEM_LINE2, PCM_LR, HSET,
- I2SLR, CENTER_LFE, SURR_LR, SPDIF_LR,
- CHANNEL_PB = 0x00000000, CHANNEL_SPC_PB = 0x40000000,
- CHANNEL_REC = 0x80000000, CHANNEL_REC_PB = 0xc0000000
+ /* playback/record select */
+ CHANNEL_PB = 0x0000, CHANNEL_SPC_PB = 0x4000,
+ CHANNEL_REC = 0x8000, CHANNEL_REC_PB = 0xc000,
+ /* playback destination/record source select */
+ MODEM_LINE1 = 0x0000, MODEM_LINE2 = 0x0400,
+ PCM_LR = 0x0800, HSET = 0x0c00,
+ I2S_LR = 0x1000, CENTER_LFE = 0x1400,
+ SURR_LR = 0x1800, SPDIF_LR = 0x1c00,
+ MIC = 0x1400,
+ /* mist stuff */
+ MONO_LEFT = 0x0000, MONO_RIGHT = 0x0100,
+ MONO_MIX = 0x0200, SRC_ENABLE = 0x0080,
};
enum miscint_bits {
@@ -190,12 +199,6 @@ enum miscint_bits {
ST_IRQ_EN = 0x00800000, ACGPIO_IRQ = 0x01000000
};
-#define AC97_SIGMATEL_DAC2INVERT 0x6E
-#define AC97_SIGMATEL_BIAS1 0x70
-#define AC97_SIGMATEL_BIAS2 0x72
-#define AC97_SIGMATEL_CIC1 0x76
-#define AC97_SIGMATEL_CIC2 0x78
-
#define TRID_REG( trident, x ) ( (trident) -> iobase + (x) )
#define VALIDATE_MAGIC(FOO,MAG) \
diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in
index fbb7562dc..012a27187 100644
--- a/drivers/usb/Config.in
+++ b/drivers/usb/Config.in
@@ -8,7 +8,9 @@ tristate 'Support for USB' CONFIG_USB
if [ ! "$CONFIG_USB" = "n" ]; then
comment 'USB Controllers'
- dep_tristate ' UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB
+ if [ "$CONFIG_USB_UHCI_ALT" != "y" ]; then
+ dep_tristate ' UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB
+ fi
if [ "$CONFIG_USB_UHCI" != "y" ]; then
dep_tristate ' UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB
if [ "$CONFIG_USB_UHCI_ALT" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
@@ -34,21 +36,26 @@ comment 'USB Devices'
bool ' USB FTDI Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_FTDI_SIO
bool ' USB Keyspan PDA Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_KEYSPAN_PDA
fi
+ bool ' USB Serial Converter verbose debug' CONFIG_USB_SERIAL_DEBUG
fi
dep_tristate ' USB CPiA Camera support' CONFIG_USB_CPIA $CONFIG_USB
dep_tristate ' USB IBM (Xirlink) C-it Camera support' CONFIG_USB_IBMCAM $CONFIG_USB
dep_tristate ' USB OV511 Camera support' CONFIG_USB_OV511 $CONFIG_USB
dep_tristate ' USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB
- dep_tristate ' USB Mass Storage support' CONFIG_USB_STORAGE $CONFIG_USB
- if [ "$CONFIG_USB_STORAGE" != "n" ]; then
- bool ' USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ dep_tristate ' USB Mass Storage support (EXPERIMENTAL)' CONFIG_USB_STORAGE $CONFIG_USB m
+ if [ "$CONFIG_USB_STORAGE" != "n" ]; then
+ bool ' USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG
+ fi
fi
dep_tristate ' USS720 parport driver' CONFIG_USB_USS720 $CONFIG_USB $CONFIG_PARPORT
dep_tristate ' DABUSB driver' CONFIG_USB_DABUSB $CONFIG_USB
- dep_tristate ' PLUSB Prolific USB-Network driver' CONFIG_USB_PLUSB $CONFIG_USB
- dep_tristate ' USB ADMtek Pegasus-based device support' CONFIG_USB_PEGASUS $CONFIG_USB
- dep_tristate ' USB Diamond Rio500 support' CONFIG_USB_RIO500 $CONFIG_USB
- dep_tristate ' D-Link USB FM radio support' CONFIG_USB_DSBR $CONFIG_USB
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ dep_tristate ' PLUSB Prolific USB-Network driver (EXPERIMENTAL)' CONFIG_USB_PLUSB $CONFIG_USB
+ dep_tristate ' USB ADMtek Pegasus-based device support (EXPERIMENTAL)' CONFIG_USB_PEGASUS $CONFIG_USB
+ dep_tristate ' USB Diamond Rio500 support (EXPERIMENTAL)' CONFIG_USB_RIO500 $CONFIG_USB
+ dep_tristate ' D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB
+ fi
comment 'USB HID'
dep_tristate ' USB Human Interface Device (HID) support' CONFIG_USB_HID $CONFIG_USB
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 35bef0e45..6e9e99714 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -45,7 +45,7 @@ ifeq ($(CONFIG_USB_SERIAL),y)
obj-y += serial/serial.o
else
ifeq ($(CONFIG_USB_SERIAL),m)
- MOD_SUB_DIRS += serial
+ MOD_IN_SUB_DIRS += serial
endif
endif
diff --git a/drivers/usb/dc2xx.c b/drivers/usb/dc2xx.c
index 3a2df9155..9dff6bda1 100644
--- a/drivers/usb/dc2xx.c
+++ b/drivers/usb/dc2xx.c
@@ -25,7 +25,8 @@
* and have fun!
*
* This should also work for a number of other digital (non-Kodak) cameras,
- * by adding the vendor and product IDs to the table below.
+ * by adding the vendor and product IDs to the table below. They'll need
+ * to be the sort using USB just as a fast bulk data channel.
*/
/*
@@ -100,7 +101,8 @@ static const struct camera {
// { 0x03f0, 0xffff }, // HP PhotoSmart C500
/* Other USB devices may well work here too, so long as they
- * just stick to half duplex bulk packet exchanges.
+ * just stick to half duplex bulk packet exchanges. That
+ * means, among other things, no iso or interrupt endpoints.
*/
};
@@ -162,7 +164,7 @@ static ssize_t camera_read (struct file *file,
usb_rcvbulkpipe (camera->dev, camera->inEP),
camera->buf, len, &count, HZ*10);
- dbg ("read (%d) - 0x%x %ld", len, result, count);
+ dbg ("read (%d) - 0x%x %d", len, result, count);
if (!result) {
if (copy_to_user (buf, camera->buf, count))
@@ -382,7 +384,7 @@ static void * camera_probe(struct usb_device *dev, unsigned int ifnum)
err ("no memory!");
return NULL;
}
- camera->dev = dev;
+ camera->info = camera_info;
camera->subminor = i;
camera->isActive = 0;
camera->buf = NULL;
@@ -413,19 +415,22 @@ static void * camera_probe(struct usb_device *dev, unsigned int ifnum)
|| endpoint [1].bmAttributes != USB_ENDPOINT_XFER_BULK
) {
dbg ("Bogus endpoints");
- camera->dev = NULL;
- return NULL;
+ goto error;
}
if (usb_set_configuration (dev, dev->config[0].bConfigurationValue)) {
err ("Failed usb_set_configuration");
- camera->dev = NULL;
- return NULL;
+ goto error;
}
- camera->info = camera_info;
+ camera->dev = dev;
return camera;
+
+error:
+ minor_data [camera->subminor] = NULL;
+ kfree (camera);
+ return NULL;
}
static void camera_disconnect(struct usb_device *dev, void *ptr)
diff --git a/drivers/usb/drivers.c b/drivers/usb/drivers.c
index 7682c7442..0dcc9f719 100644
--- a/drivers/usb/drivers.c
+++ b/drivers/usb/drivers.c
@@ -69,7 +69,11 @@ static ssize_t usb_driver_read(struct file *file, char *buf, size_t nbytes, loff
pos = *ppos;
for (; tmp != &usb_driver_list; tmp = tmp->next) {
struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list);
- start += sprintf (start, "%s\n", driver->name);
+ int minor = driver->fops ? driver->minor : -1;
+ if (minor == -1)
+ start += sprintf (start, " %s\n", driver->name);
+ else
+ start += sprintf (start, "%3d-%3d: %s\n", minor, minor + 15, driver->name);
if (start > end) {
start += sprintf(start, "(truncated)\n");
break;
diff --git a/drivers/usb/dsbr100.c b/drivers/usb/dsbr100.c
index 8b81e06c0..22c21bb33 100644
--- a/drivers/usb/dsbr100.c
+++ b/drivers/usb/dsbr100.c
@@ -48,12 +48,6 @@
#include <linux/kernel.h>
-
-#if CONFIG_MODVERSIONS==1
-#define MODVERSIONS
-#include <linux/modversions.h>
-#endif
-
#include <linux/module.h>
#include <linux/init.h>
#include <linux/malloc.h>
diff --git a/drivers/usb/hub.c b/drivers/usb/hub.c
index 9138ee4da..8bd919d67 100644
--- a/drivers/usb/hub.c
+++ b/drivers/usb/hub.c
@@ -4,8 +4,6 @@
* (C) Copyright 1999 Linus Torvalds
* (C) Copyright 1999 Johannes Erdfelt
* (C) Copyright 1999 Gregory P. Smith
- *
- * $Id: hub.c,v 1.21 2000/01/16 21:19:44 acher Exp $
*/
#include <linux/kernel.h>
@@ -75,31 +73,29 @@ static int usb_get_port_status(struct usb_device *dev, int port, void *data)
* the low-level driver that it wants to be re-activated,
* or zero to say "I'm done".
*/
-static int hub_irq(int status, void *__buffer, int len, void *dev_id)
+static void hub_irq(struct urb *urb)
{
- struct usb_hub *hub = dev_id;
+ struct usb_hub *hub = (struct usb_hub *)urb->context;
unsigned long flags;
- switch (status) {
- case -ENODEV:
- /* Just ignore it */
- break;
- case 0:
- /* Something happened, let khubd figure it out */
- if (waitqueue_active(&khubd_wait)) {
- /* Add the hub to the event queue */
- spin_lock_irqsave(&hub_event_lock, flags);
- if (hub->event_list.next == &hub->event_list) {
- list_add(&hub->event_list, &hub_event_list);
- /* Wake up khubd */
- wake_up(&khubd_wait);
- }
- spin_unlock_irqrestore(&hub_event_lock, flags);
- }
- break;
+ if (urb->status) {
+ if (urb->status != -ENOENT)
+ dbg("nonzero status in irq %d", urb->status);
+
+ return;
}
- return 1;
+ /* Something happened, let khubd figure it out */
+ if (waitqueue_active(&khubd_wait)) {
+ /* Add the hub to the event queue */
+ spin_lock_irqsave(&hub_event_lock, flags);
+ if (hub->event_list.next == &hub->event_list) {
+ list_add(&hub->event_list, &hub_event_list);
+ /* Wake up khubd */
+ wake_up(&khubd_wait);
+ }
+ spin_unlock_irqrestore(&hub_event_lock, flags);
+ }
}
static void usb_hub_power_on(struct usb_hub *hub)
@@ -196,13 +192,14 @@ static int usb_hub_configure(struct usb_hub *hub)
return 0;
}
-static void * hub_probe(struct usb_device *dev, unsigned int i)
+static void *hub_probe(struct usb_device *dev, unsigned int i)
{
struct usb_interface_descriptor *interface;
struct usb_endpoint_descriptor *endpoint;
struct usb_hub *hub;
unsigned long flags;
- int ret;
+ unsigned int pipe;
+ int maxp, ret;
interface = &dev->actconfig->interface[i].altsetting[0];
@@ -233,7 +230,8 @@ static void * hub_probe(struct usb_device *dev, unsigned int i)
/* We found a hub */
info("USB hub found");
- if ((hub = kmalloc(sizeof(*hub), GFP_KERNEL)) == NULL) {
+ hub = kmalloc(sizeof(*hub), GFP_KERNEL);
+ if (!hub) {
err("couldn't kmalloc hub struct");
return NULL;
}
@@ -250,26 +248,24 @@ static void * hub_probe(struct usb_device *dev, unsigned int i)
spin_unlock_irqrestore(&hub_event_lock, flags);
if (usb_hub_configure(hub) >= 0) {
- hub->irqpipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
- ret = usb_request_irq(dev, hub->irqpipe,
- hub_irq, endpoint->bInterval,
- hub, &hub->irq_handle);
- if (ret) {
- err("usb_request_irq failed (%d)", ret);
- /* free hub, but first clean up its list. */
- spin_lock_irqsave(&hub_event_lock, flags);
+ pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
+ maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
- /* Delete it and then reset it */
- list_del(&hub->event_list);
- INIT_LIST_HEAD(&hub->event_list);
- list_del(&hub->hub_list);
- INIT_LIST_HEAD(&hub->hub_list);
+ if (maxp > sizeof(hub->buffer))
+ maxp = sizeof(hub->buffer);
- spin_unlock_irqrestore(&hub_event_lock, flags);
-
- kfree(hub);
+ hub->urb = usb_alloc_urb(0);
+ if (!hub->urb) {
+ err("couldn't allocate interrupt urb");
+ goto fail;
+ }
- return NULL;
+ FILL_INT_URB(hub->urb, dev, pipe, hub->buffer, maxp, hub_irq,
+ hub, endpoint->bInterval);
+ ret = usb_submit_urb(hub->urb);
+ if (ret) {
+ err("usb_submit_urb failed (%d)", ret);
+ goto fail;
}
/* Wake up khubd */
@@ -277,11 +273,27 @@ static void * hub_probe(struct usb_device *dev, unsigned int i)
}
return hub;
+
+fail:
+ /* free hub, but first clean up its list. */
+ spin_lock_irqsave(&hub_event_lock, flags);
+
+ /* Delete it and then reset it */
+ list_del(&hub->event_list);
+ INIT_LIST_HEAD(&hub->event_list);
+ list_del(&hub->hub_list);
+ INIT_LIST_HEAD(&hub->hub_list);
+
+ spin_unlock_irqrestore(&hub_event_lock, flags);
+
+ kfree(hub);
+
+ return NULL;
}
static void hub_disconnect(struct usb_device *dev, void *ptr)
{
- struct usb_hub *hub = ptr;
+ struct usb_hub *hub = (struct usb_hub *)ptr;
unsigned long flags;
spin_lock_irqsave(&hub_event_lock, flags);
@@ -294,8 +306,10 @@ static void hub_disconnect(struct usb_device *dev, void *ptr)
spin_unlock_irqrestore(&hub_event_lock, flags);
- if (hub->irq_handle) {
- usb_release_irq(hub->dev, hub->irq_handle, hub->irqpipe);
+ if (hub->urb) {
+ usb_unlink_urb(hub->urb);
+ usb_free_urb(hub->urb);
+ hub->urb = NULL;
}
/* Free the memory */
diff --git a/drivers/usb/hub.h b/drivers/usb/hub.h
index 0da7eb87c..913c44a2d 100644
--- a/drivers/usb/hub.h
+++ b/drivers/usb/hub.h
@@ -78,16 +78,10 @@ struct usb_hub_descriptor {
__u8 bDescriptorType;
__u8 bNbrPorts;
__u16 wHubCharacteristics;
-#if 0
- __u8 wHubCharacteristics[2]; /* __u16 but not aligned! */
-#endif
__u8 bPwrOn2PwrGood;
__u8 bHubContrCurrent;
/* DeviceRemovable and PortPwrCtrlMask want to be variable-length
bitmaps that hold max 256 entries, but for now they're ignored */
-#if 0
- __u8 filler;
-#endif
} __attribute__ ((packed));
struct usb_device;
@@ -112,9 +106,10 @@ struct usb_hub {
/* Device structure */
struct usb_device *dev;
- /* Reference to the hub's polling IRQ and its associated pipe */
- void *irq_handle;
- unsigned int irqpipe;
+ /* Interrupt polling pipe */
+ struct urb *urb;
+
+ char buffer[USB_MAXCHILDREN / 8];
/* List of hubs */
struct list_head hub_list;
diff --git a/drivers/usb/joydev.c b/drivers/usb/joydev.c
index a5dcabdab..9b54300e2 100644
--- a/drivers/usb/joydev.c
+++ b/drivers/usb/joydev.c
@@ -224,8 +224,8 @@ static ssize_t joydev_read(struct file *file, char *buf, size_t count, loff_t *p
struct JS_DATA_TYPE data;
- data.buttons = (joydev->nkey > 0 && test_bit(joydev->keypam[0], input->key)) ? 1 : 0 |
- (joydev->nkey > 1 && test_bit(joydev->keypam[1], input->key)) ? 2 : 0;
+ data.buttons = ((joydev->nkey > 0 && test_bit(joydev->keypam[0], input->key)) ? 1 : 0) |
+ ((joydev->nkey > 1 && test_bit(joydev->keypam[1], input->key)) ? 2 : 0);
data.x = ((joydev_correct(input->abs[ABS_X], &joydev->corr[0]) / 256) + 128) >> joydev->glue.JS_CORR.x;
data.y = ((joydev_correct(input->abs[ABS_Y], &joydev->corr[1]) / 256) + 128) >> joydev->glue.JS_CORR.y;
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
index 20dc5dde5..02bd7aad6 100644
--- a/drivers/usb/serial/Makefile
+++ b/drivers/usb/serial/Makefile
@@ -14,7 +14,7 @@ ALL_SUB_DIRS := $(SUB_DIRS)
O_TARGET := serial.o
M_OBJS := usb-serial.o
O_OBJS := usb-serial.o
-#MOD_LIST_NAME := USB_MODULES
+MOD_LIST_NAME := USB_SERIAL_MODULES
# Objects that export symbols.
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index ad86c6a80..6effcc048 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -14,6 +14,10 @@
*
* See Documentation/usb/usb-serial.txt for more information on using this driver
*
+ * (03/17/2000) gkh
+ * Added config option for debugging messages.
+ * Added patch for keyspan pda from Brian Warner.
+ *
* (03/06/2000) gkh
* Added the keyspan pda code from Brian Warner <warner@lothar.com>
* Moved a bunch of the port specific stuff into its own structure. This
@@ -175,7 +179,12 @@
#include <linux/tty.h>
#include <linux/module.h>
#include <linux/spinlock.h>
-#define DEBUG
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+ #define DEBUG
+#else
+ #undef DEBUG
+#endif
#include <linux/usb.h>
#ifdef CONFIG_USB_SERIAL_WHITEHEAT
@@ -1428,6 +1437,7 @@ static void keyspan_pda_rx_interrupt (struct urb *urb)
case 2: /* tx unthrottle interrupt */
serial->tx_throttled = 0;
wake_up(&serial->write_wait); /* wake up writer */
+ wake_up(&tty->write_wait); /* them too */
break;
default:
break;
@@ -1846,25 +1856,12 @@ static int keyspan_pda_write_room (struct tty_struct *tty)
static int keyspan_pda_chars_in_buffer (struct tty_struct *tty)
{
struct usb_serial *serial = (struct usb_serial *)tty->driver_data;
- unsigned char count;
- int rc;
-
- /* used by tty stuff to wait for output to drain. Go ask the
- device how much is still queued in the tx ring */
- rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
- 6, /* write_room */
- USB_TYPE_VENDOR | USB_RECIP_INTERFACE
- | USB_DIR_IN,
- 1, /* value: 1 means chars_in_buffer */
- 0, /* index */
- &count,
- 1,
- 2*HZ);
- if (rc < 0)
- return rc; /* failed */
- if (rc == 0)
- return -EIO; /* device didn't return any data */
- return (count);
+
+ /* when throttled, return at least WAKEUP_CHARS to tell select() (via
+ n_tty.c:normal_poll() ) that we're not writeable. */
+ if (serial->tx_throttled)
+ return 256;
+ return 0;
}
diff --git a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h
index f02eeedf5..71fecb0ec 100644
--- a/drivers/usb/serial/usb-serial.h
+++ b/drivers/usb/serial/usb-serial.h
@@ -371,6 +371,7 @@ static struct usb_serial_device_type keyspan_pda_fake_device = {
num_interrupt_in: NUM_DONT_CARE,
num_bulk_in: NUM_DONT_CARE,
num_bulk_out: NUM_DONT_CARE,
+ num_ports: 1,
startup: keyspan_pda_fake_startup
};
static struct usb_serial_device_type keyspan_pda_device = {
diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c
index 480c6252d..f5e67acec 100644
--- a/drivers/usb/uhci.c
+++ b/drivers/usb/uhci.c
@@ -689,19 +689,18 @@ status_phase:
return 0;
td_error:
- /* Some debugging code */
- if (debug) {
+ if (status & TD_CTRL_STALLED)
+ /* endpoint has stalled - mark it halted */
+ usb_endpoint_halt(urb->dev, uhci_endpoint(td->info),
+ uhci_packetout(td->info));
+ else if (debug) {
+ /* Some debugging code */
dbg("uhci_result_control() failed with status %x", status);
/* Print the chain for debugging purposes */
uhci_show_queue(urbp->qh);
}
- if (status & TD_CTRL_STALLED)
- /* endpoint has stalled - mark it halted */
- usb_endpoint_halt(urb->dev, uhci_endpoint(td->info),
- uhci_packetout(td->info));
-
return uhci_map_status(status, uhci_packetout(td->info));
}
@@ -818,8 +817,12 @@ static int uhci_result_interrupt(urb_t *urb)
return 0;
td_error:
- /* Some debugging code */
- if (debug) {
+ if (status & TD_CTRL_STALLED)
+ /* endpoint has stalled - mark it halted */
+ usb_endpoint_halt(urb->dev, uhci_endpoint(td->info),
+ uhci_packetout(td->info));
+ else if (debug) {
+ /* Some debugging code */
dbg("uhci_result_interrupt/bulk() failed with status %x",
status);
@@ -830,11 +833,6 @@ td_error:
uhci_show_td(td);
}
- if (status & TD_CTRL_STALLED)
- /* endpoint has stalled - mark it halted */
- usb_endpoint_halt(urb->dev, uhci_endpoint(td->info),
- uhci_packetout(td->info));
-
return uhci_map_status(status, uhci_packetout(td->info));
}
@@ -1251,12 +1249,14 @@ static int uhci_unlink_urb(urb_t *urb)
uhci_unlink_generic(urb);
if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+ urb->status = -ECONNABORTED;
+
spin_lock_irqsave(&uhci->urb_remove_lock, flags);
list_add(&urb->urb_list, &uhci->urb_remove_list);
spin_unlock_irqrestore(&uhci->urb_remove_lock, flags);
-
- urb->status = -ECONNABORTED;
} else {
+ urb->status = -ENOENT;
+
if (in_interrupt()) { /* wait at least 1 frame */
static int errorcount = 10;
@@ -1268,8 +1268,6 @@ static int uhci_unlink_urb(urb_t *urb)
if (urb->complete)
urb->complete(urb);
-
- urb->status = -ENOENT;
}
}
diff --git a/drivers/usb/usb-ohci.c b/drivers/usb/usb-ohci.c
index cf457762a..78ae0a0ea 100644
--- a/drivers/usb/usb-ohci.c
+++ b/drivers/usb/usb-ohci.c
@@ -252,6 +252,9 @@ static int sohci_submit_urb (urb_t * urb)
if (urb->hcpriv) return -EINVAL; /* urb already in use */
+// if(usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)))
+// return -EPIPE;
+
usb_inc_dev_use (urb->dev);
ohci = (ohci_t *) urb->dev->bus->hcpriv;
@@ -838,28 +841,36 @@ static void td_submit_urb (urb_t * urb)
int data_len = urb->transfer_buffer_length;
int cnt = 0;
__u32 info = 0;
-
+ unsigned int toggle = 0;
+ /* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */
+ if(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) {
+ toggle = TD_T_TOGGLE;
+ } else {
+ toggle = TD_T_DATA0;
+ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 1);
+ }
+
urb_priv->td_cnt = 0;
switch (usb_pipetype (urb->pipe)) {
case PIPE_BULK:
info = usb_pipeout (urb->pipe)?
- TD_CC | TD_DP_OUT | TD_T_TOGGLE: TD_CC | TD_DP_IN | TD_T_TOGGLE;
+ TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ;
while(data_len > 4096) {
- td_fill (info, data, 4096, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt);
+ td_fill (info | (cnt? TD_T_TOGGLE:toggle), data, 4096, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt);
data += 4096; data_len -= 4096; cnt++;
}
info = usb_pipeout (urb->pipe)?
- TD_CC | TD_DP_OUT | TD_T_TOGGLE: TD_CC | TD_R | TD_DP_IN | TD_T_TOGGLE;
- td_fill (info, data, data_len, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt);
+ TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ;
+ td_fill (info | (cnt? TD_T_TOGGLE:toggle), data, data_len, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt);
cnt++;
writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
break;
case PIPE_INTERRUPT:
info = usb_pipeout (urb->pipe)?
- TD_CC | TD_DP_OUT | TD_T_TOGGLE: TD_CC | TD_R | TD_DP_IN | TD_T_TOGGLE;
+ TD_CC | TD_DP_OUT | toggle: TD_CC | TD_R | TD_DP_IN | toggle;
td_fill (info, data, data_len, urb, ST_ADDR | ADD_LEN, cnt++);
break;
@@ -1059,6 +1070,8 @@ static void dl_done_list (ohci_t * ohci, td_t * td_list)
}
/* error code of transfer */
cc = TD_CC_GET (tdINFO);
+ if( cc == TD_CC_STALL) usb_endpoint_halt(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+
if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN))
cc = TD_CC_NOERROR;
if (++(urb_priv->td_cnt) == urb_priv->length) {
diff --git a/drivers/usb/usb-storage.c b/drivers/usb/usb-storage.c
index 956e80a0a..0cac2ef13 100644
--- a/drivers/usb/usb-storage.c
+++ b/drivers/usb/usb-storage.c
@@ -52,6 +52,10 @@
/* direction table -- this indicates the direction of the data
* transfer for each command code -- a 1 indicates input
*/
+/* FIXME: we need to use the new direction indicators in the Scsi_Cmnd
+ * structure, not this table. First we need to evaluate if it's being set
+ * correctly for us, though
+ */
unsigned char us_direction[256/8] = {
0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77,
0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
@@ -81,13 +85,12 @@ struct us_data {
__u8 ep_int; /* interrupt . */
__u8 subclass; /* as in overview */
__u8 protocol; /* .............. */
- __u8 attention_done; /* force attn on first cmd */
trans_cmnd transport; /* protocol specific do cmd */
trans_reset transport_reset; /* .......... device reset */
proto_cmnd proto_handler; /* protocol handler */
GUID(guid); /* unique dev id */
struct Scsi_Host *host; /* our dummy host data */
- Scsi_Host_Template *htmplt; /* own host template */
+ Scsi_Host_Template htmplt; /* own host template */
int host_number; /* to find us */
int host_no; /* allocated by scsi */
Scsi_Cmnd *srb; /* current srb */
@@ -114,7 +117,9 @@ struct us_data {
#define US_ACT_BUS_RESET 4
#define US_ACT_HOST_RESET 5
+/* The list of structures and the protective lock for them */
static struct us_data *us_list;
+spinlock_t us_list_spinlock = SPIN_LOCK_UNLOCKED;
static void * storage_probe(struct usb_device *dev, unsigned int ifnum);
static void storage_disconnect(struct usb_device *dev, void *ptr);
@@ -225,7 +230,7 @@ static void us_transfer(Scsi_Cmnd *srb, int dir_in)
srb->result = result;
}
-/* calculate the length of the data transfer (not the command) for any
+/* Calculate the length of the data transfer (not the command) for any
* given SCSI command
*/
static unsigned int us_transfer_length(Scsi_Cmnd *srb)
@@ -243,14 +248,18 @@ static unsigned int us_transfer_length(Scsi_Cmnd *srb)
case TEST_UNIT_READY:
return 0;
+ /* FIXME: these should be removed and tested */
case REQUEST_SENSE:
case INQUIRY:
case MODE_SENSE:
return srb->cmnd[4];
+ /* FIXME: this needs to come out when the other
+ * fix is in place */
case READ_CAPACITY:
return 8;
+ /* FIXME: these should be removed and tested */
case LOG_SENSE:
case MODE_SENSE_10:
return (srb->cmnd[7] << 8) + srb->cmnd[8];
@@ -795,7 +804,7 @@ static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
return USB_STOR_TRANSPORT_ERROR;
}
- /* FIXME: we need to handle NAKs here */
+ /* FIXME: we need to handle NAKs here */
return USB_STOR_TRANSPORT_ERROR;
}
@@ -812,7 +821,9 @@ static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
/* STATUS STAGE */
/* go to sleep until we get this interrup */
- /* FIXME: this should be changed to use a timeout */
+ /* FIXME: this should be changed to use a timeout -- or let the
+ * device reset routine up() this for us to unjam us
+ */
down(&(us->ip_waitq));
/* FIXME: currently this code is unreachable, but the idea is
@@ -827,8 +838,8 @@ static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
US_DEBUGP("Got interrupt data 0x%x\n", us->ip_data);
/* UFI gives us ASC and ASCQ, like a request sense */
- /* FIXME: is this right? Do REQUEST_SENSE and INQUIRY need special
- * case handling?
+ /* REQUEST_SENSE and INQUIRY don't affect the sense data, so we
+ * ignore the information for those commands
*/
if (us->subclass == US_SC_UFI) {
if (srb->cmnd[0] == REQUEST_SENSE ||
@@ -887,7 +898,7 @@ static int CB_transport(Scsi_Cmnd *srb, struct us_data *us)
return USB_STOR_TRANSPORT_ERROR;
}
- /* FIXME: we need to handle NAKs here */
+ /* FIXME: we need to handle NAKs here */
return USB_STOR_TRANSPORT_ERROR;
}
@@ -1103,7 +1114,8 @@ static int us_detect(struct SHT *sht)
static int us_release(struct Scsi_Host *psh)
{
struct us_data *us = (struct us_data *)psh->hostdata[0];
- struct us_data *prev = (struct us_data *)&us_list;
+ struct us_data *prev;
+ unsigned long flags;
if (us->irq_handle) {
usb_release_irq(us->pusb_dev, us->irq_handle, us->irqpipe);
@@ -1111,18 +1123,20 @@ static int us_release(struct Scsi_Host *psh)
}
/* FIXME: release the interface claim here? */
- // if (us->pusb_dev)
- // usb_deregister(&storage_driver);
- /* FIXME - leaves hanging host template copy */
- /* (because scsi layer uses it after removal !!!) */
+ /* FIXME: we need to move this elsewhere --
+ * the remove function only gets called to remove the module
+ */
+ spin_lock_irqsave(&us_list_spinlock, flags);
if (us_list == us)
us_list = us->next;
else {
+ prev = us_list;
while (prev->next != us)
prev = prev->next;
prev->next = us->next;
}
+ spin_unlock_irqrestore(&us_list_spinlock, flags);
return 0;
}
@@ -1190,11 +1204,20 @@ static int us_host_reset( Scsi_Cmnd *srb )
int usb_stor_proc_info (char *buffer, char **start, off_t offset,
int length, int hostno, int inout)
{
- struct us_data *us = us_list;
+ struct us_data *us;
char *pos = buffer;
char *tmp_ptr;
+ unsigned long flags;
+
+ /* if someone is sending us data, just throw it away */
+ if (inout)
+ return length;
+
+ /* lock the data structures */
+ spin_lock_irqsave(&us_list_spinlock, flags);
/* find our data from hostno */
+ us = us_list;
while (us) {
if (us->host_no == hostno)
break;
@@ -1202,13 +1225,11 @@ int usb_stor_proc_info (char *buffer, char **start, off_t offset,
}
/* if we couldn't find it, we return an error */
- if (!us)
+ if (!us) {
+ spin_unlock_irqrestore(&us_list_spinlock, flags);
return -ESRCH;
-
- /* if someone is sending us data, just throw it away */
- if (inout)
- return length;
-
+ }
+
/* print the controler name */
SPRINTF ("Host scsi%d: usb-storage\n", hostno);
@@ -1254,6 +1275,9 @@ int usb_stor_proc_info (char *buffer, char **start, off_t offset,
/* show the GUID of the device */
SPRINTF(" GUID: " GUID_FORMAT "\n", GUID_ARGS(us->guid));
+ /* release our lock on the data structures */
+ spin_unlock_irqrestore(&us_list_spinlock, flags);
+
/*
* Calculate start of next buffer, and return value.
*/
@@ -1365,6 +1389,7 @@ static int usb_stor_control_thread(void * __us)
switch (action) {
case US_ACT_COMMAND:
/* bad device */
+ /* FIXME: we need to enable and test multiple LUNs */
if (us->srb->target || us->srb->lun) {
US_DEBUGP( "Bad device number (%d/%d) or dev 0x%x\n",
us->srb->target, us->srb->lun, (unsigned int)us->pusb_dev);
@@ -1378,9 +1403,12 @@ static int usb_stor_control_thread(void * __us)
/* our device has gone - pretend not ready */
/* FIXME: we also need to handle INQUIRY here,
* probably */
+ /* FIXME: fix return codes and sense buffer handling */
if (!us->pusb_dev) {
+ US_DEBUGP("Request is for removed device\n");
if (us->srb->cmnd[0] == REQUEST_SENSE) {
- memcpy(us->srb->request_buffer, sense_notready,
+ memcpy(us->srb->request_buffer,
+ sense_notready,
sizeof(sense_notready));
us->srb->result = DID_OK << 16;
} else {
@@ -1443,34 +1471,29 @@ static int usb_stor_control_thread(void * __us)
/* Probe to see if a new device is actually a SCSI device */
static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
{
- struct usb_interface_descriptor *interface;
int i;
char mf[32]; /* manufacturer */
char prod[32]; /* product */
char serial[32]; /* serial number */
struct us_data *ss = NULL;
- unsigned int flags = 0;
GUID(guid); /* Global Unique Identifier */
- struct us_data *prev;
- int protocol = 0;
- int subclass = 0;
+ int result;
+ unsigned long flags;
+
+ /* these are temporary copies -- we test on these, then put them
+ * in the us-data structure
+ */
+ __u8 ep_in = 0;
+ __u8 ep_out = 0;
+ __u8 ep_int = 0;
+ __u8 subclass = 0;
+ __u8 protocol = 0;
+
+ /* the altsettting 0 on the interface we're probing */
struct usb_interface_descriptor *altsetting =
&(dev->actconfig->interface[ifnum].altsetting[0]);
- /* clear the GUID and fetch the strings */
- GUID_CLEAR(guid);
- memset(mf, 0, sizeof(mf));
- memset(prod, 0, sizeof(prod));
- memset(serial, 0, sizeof(serial));
- if (dev->descriptor.iManufacturer)
- usb_string(dev, dev->descriptor.iManufacturer, mf, sizeof(mf));
- if (dev->descriptor.iProduct)
- usb_string(dev, dev->descriptor.iProduct, prod, sizeof(prod));
- if (dev->descriptor.iSerialNumber)
- usb_string(dev, dev->descriptor.iSerialNumber, serial, sizeof(serial));
-
- /* let's examine the device now */
-
+ /* FIXME: this isn't quite right... */
/* We make an exception for the shuttle E-USB */
if (dev->descriptor.idVendor == 0x04e6 &&
dev->descriptor.idProduct == 0x0001) {
@@ -1487,6 +1510,91 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
/* At this point, we know we've got a live one */
US_DEBUGP("USB Mass Storage device detected\n");
+ /*
+ * We are expecting a minimum of 2 endpoints - in and out (bulk).
+ * An optional interrupt is OK (necessary for CBI protocol).
+ * We will ignore any others.
+ */
+ for (i = 0; i < altsetting->bNumEndpoints; i++) {
+ /* is it an BULK endpoint? */
+ if ((altsetting->endpoint[i].bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
+ if (altsetting->endpoint[i].bEndpointAddress &
+ USB_DIR_IN)
+ ep_in = altsetting->endpoint[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ else
+ ep_out = altsetting->endpoint[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ }
+
+ /* is it an interrupt endpoint? */
+ if ((altsetting->endpoint[i].bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) {
+ ep_int = altsetting->endpoint[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ }
+ }
+ US_DEBUGP("Endpoints In %d Out %d Int %d\n",
+ ep_in, ep_out, ep_int);
+
+ /* set the interface -- STALL is an acceptable response here */
+ result = usb_set_interface(dev, altsetting->bInterfaceNumber, 0);
+ US_DEBUGP("Result from usb_set_interface is %d\n", result);
+ if (result == -EPIPE) {
+ usb_clear_halt(dev, usb_sndctrlpipe(dev, 0));
+ } else if (result != 0) {
+ /* it's not a stall, but another error -- time to bail */
+ return NULL;
+ }
+
+ /* shuttle E-USB */
+ /* FIXME: all we should need to do here is determine the protocol */
+ if (dev->descriptor.idVendor == 0x04e6 &&
+ dev->descriptor.idProduct == 0x0001) {
+ __u8 qstat[2];
+
+ result = usb_control_msg(ss->pusb_dev,
+ usb_rcvctrlpipe(dev,0),
+ 1, 0xC0,
+ 0, ss->ifnum,
+ qstat, 2, HZ*5);
+ US_DEBUGP("C0 status 0x%x 0x%x\n", qstat[0], qstat[1]);
+ init_MUTEX_LOCKED(&(ss->ip_waitq));
+ ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
+ result = usb_request_irq(ss->pusb_dev, ss->irqpipe,
+ CBI_irq, 255, (void *)ss,
+ &ss->irq_handle);
+ if (result < 0)
+ return NULL;
+
+ /* FIXME: what is this?? */
+ down(&(ss->ip_waitq));
+ }
+
+ /* Do some basic sanity checks, and bail if we find a problem */
+ if (!ep_in || !ep_out || (protocol == US_PR_CBI && !ep_int)) {
+ US_DEBUGP("Problems with device\n");
+ return NULL;
+ }
+
+ /* At this point, we're committed to using the device */
+
+ /* clear the GUID and fetch the strings */
+ GUID_CLEAR(guid);
+ memset(mf, 0, sizeof(mf));
+ memset(prod, 0, sizeof(prod));
+ memset(serial, 0, sizeof(serial));
+ if (dev->descriptor.iManufacturer)
+ usb_string(dev, dev->descriptor.iManufacturer, mf,
+ sizeof(mf));
+ if (dev->descriptor.iProduct)
+ usb_string(dev, dev->descriptor.iProduct, prod,
+ sizeof(prod));
+ if (dev->descriptor.iSerialNumber)
+ usb_string(dev, dev->descriptor.iSerialNumber, serial,
+ sizeof(serial));
+
/* Create a GUID for this device */
if (dev->descriptor.iSerialNumber && serial[0]) {
/* If we have a serial number, and it's a non-NULL string */
@@ -1498,23 +1606,50 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
dev->descriptor.idProduct, "0");
}
- /* Now check if we have seen this GUID before, and restore
- * the flags if we find it
+ /* lock access to the data structures */
+ spin_lock_irqsave(&us_list_spinlock, flags);
+
+ /*
+ * Now check if we have seen this GUID before
+ * We're looking for a device with a matching GUID that isn't
+ * allready on the system
*/
- for (ss = us_list; ss != NULL; ss = ss->next) {
- if (!ss->pusb_dev && GUID_EQUAL(guid, ss->guid)) {
- US_DEBUGP("Found existing GUID " GUID_FORMAT "\n",
- GUID_ARGS(guid));
- flags = ss->flags;
- break;
+ ss = us_list;
+ while ((ss != NULL) &&
+ ((ss->pusb_dev) || !GUID_EQUAL(guid, ss->guid)))
+ ss = ss->next;
+
+ if (ss != NULL) {
+ /* Existing device -- re-connect */
+ US_DEBUGP("Found existing GUID " GUID_FORMAT "\n",
+ GUID_ARGS(guid));
+
+ /* establish the connection to the new device upon reconnect */
+ ss->ifnum = ifnum;
+ ss->pusb_dev = dev;
+
+ /* hook up the IRQ handler again */
+ if (ss->protocol == US_PR_CBI) {
+ /* set up so we'll wait for notification */
+ init_MUTEX_LOCKED(&(ss->ip_waitq));
+
+ /* set up the IRQ pipe and handler */
+ /* FIXME: This needs to get period from the device */
+ US_DEBUGP("Allocating IRQ for CBI transport\n");
+ ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
+ result = usb_request_irq(ss->pusb_dev, ss->irqpipe,
+ CBI_irq, 255,
+ (void *)ss, &ss->irq_handle);
+ US_DEBUGP("usb_request_irq returned %d\n", result);
}
- }
-
- /* If ss == NULL, then this is a new device. Allocate memory for it */
- if (!ss) {
- if ((ss = (struct us_data *)kmalloc(sizeof(*ss),
+ } else {
+ /* New device -- Allocate memory and initialize */
+ US_DEBUGP("New GUID " GUID_FORMAT "\n", GUID_ARGS(guid));
+
+ if ((ss = (struct us_data *)kmalloc(sizeof(struct us_data),
GFP_KERNEL)) == NULL) {
printk(KERN_WARNING USB_STORAGE "Out of memory\n");
+ spin_unlock_irqrestore(&us_list_spinlock, flags);
return NULL;
}
memset(ss, 0, sizeof(struct us_data));
@@ -1522,104 +1657,66 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
/* Initialize the mutexes only when the struct is new */
init_MUTEX_LOCKED(&(ss->sleeper));
init_MUTEX(&(ss->queue_exclusion));
- }
-
- /* establish the connection to the new device */
- interface = altsetting;
- ss->flags = flags;
- ss->ifnum = ifnum;
- ss->attention_done = 0;
- ss->pusb_dev = dev;
-
- /* If the device has subclass and protocol, then use that. Otherwise,
- * take data from the specific interface.
- */
- if (subclass) {
- ss->subclass = subclass;
- ss->protocol = protocol;
- } else {
- ss->subclass = interface->bInterfaceSubClass;
- ss->protocol = interface->bInterfaceProtocol;
- }
- /* set the handler pointers based on the protocol */
- US_DEBUGP("Transport: ");
- switch (ss->protocol) {
- case US_PR_CB:
- US_DEBUGPX("Control/Bulk\n");
- ss->transport = CB_transport;
- ss->transport_reset = CB_reset;
- break;
-
- case US_PR_CBI:
- US_DEBUGPX("Control/Bulk/Interrupt\n");
- ss->transport = CBI_transport;
- ss->transport_reset = CB_reset;
- break;
-
- case US_PR_BULK:
- US_DEBUGPX("Bulk\n");
- ss->transport = Bulk_transport;
- ss->transport_reset = Bulk_reset;
- break;
-
- default:
- US_DEBUGPX("Unknown\n");
- kfree(ss);
- return NULL;
- break;
- }
-
- /*
- * We are expecting a minimum of 2 endpoints - in and out (bulk).
- * An optional interrupt is OK (necessary for CBI protocol).
- * We will ignore any others.
- */
- for (i = 0; i < interface->bNumEndpoints; i++) {
- /* is it an BULK endpoint? */
- if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_BULK) {
- if (interface->endpoint[i].bEndpointAddress & USB_DIR_IN)
- ss->ep_in = interface->endpoint[i].bEndpointAddress &
- USB_ENDPOINT_NUMBER_MASK;
- else
- ss->ep_out = interface->endpoint[i].bEndpointAddress &
- USB_ENDPOINT_NUMBER_MASK;
- }
-
- /* is it an interrupt endpoint? */
- if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_INT) {
- ss->ep_int = interface->endpoint[i].bEndpointAddress &
- USB_ENDPOINT_NUMBER_MASK;
+ /*
+ * If we've allready determined the subclass and protocol,
+ * use that. Otherwise, use the interface ones. This
+ * allows us to support devices which are compliant but
+ * don't announce it. Note that this information is
+ * maintained in the us_data struct so we only have to do
+ * this for new devices.
+ */
+ if (subclass) {
+ ss->subclass = subclass;
+ ss->protocol = protocol;
+ } else {
+ ss->subclass = altsetting->bInterfaceSubClass;
+ ss->protocol = altsetting->bInterfaceProtocol;
}
- }
- US_DEBUGP("Endpoints In %d Out %d Int %d\n",
- ss->ep_in, ss->ep_out, ss->ep_int);
- /* Do some basic sanity checks, and bail if we find a problem */
- if (usb_set_interface(dev, interface->bInterfaceNumber, 0) ||
- !ss->ep_in || !ss->ep_out ||
- (ss->protocol == US_PR_CBI && ss->ep_int == 0)) {
- US_DEBUGP("Problems with device\n");
- if (ss->host) {
- kfree(ss->htmplt->name);
- kfree(ss->htmplt);
- }
+ /* copy over the endpoint data */
+ ss->ep_in = ep_in;
+ ss->ep_out = ep_out;
+ ss->ep_int = ep_int;
- kfree(ss);
- return NULL;
- }
+ /* establish the connection to the new device */
+ ss->ifnum = ifnum;
+ ss->pusb_dev = dev;
- /* If this is a new device (i.e. we haven't seen it before), we need to
- * generate a scsi host definition, and register with scsi above us
- */
- if (!ss->host) {
/* copy the GUID we created before */
- US_DEBUGP("New GUID " GUID_FORMAT "\n", GUID_ARGS(guid));
memcpy(ss->guid, guid, sizeof(guid));
+
+ /*
+ * Set the handler pointers based on the protocol
+ * Again, this data is persistant across reattachments
+ */
+ US_DEBUGP("Transport: ");
+ switch (ss->protocol) {
+ case US_PR_CB:
+ US_DEBUGPX("Control/Bulk\n");
+ ss->transport = CB_transport;
+ ss->transport_reset = CB_reset;
+ break;
+
+ case US_PR_CBI:
+ US_DEBUGPX("Control/Bulk/Interrupt\n");
+ ss->transport = CBI_transport;
+ ss->transport_reset = CB_reset;
+ break;
+
+ case US_PR_BULK:
+ US_DEBUGPX("Bulk\n");
+ ss->transport = Bulk_transport;
+ ss->transport_reset = Bulk_reset;
+ break;
+
+ default:
+ US_DEBUGPX("Unknown\n");
+ kfree(ss);
+ return NULL;
+ break;
+ }
- /* set class specific stuff */
US_DEBUGP("Protocol: ");
switch (ss->subclass) {
case US_SC_RBC:
@@ -1656,97 +1753,75 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
break;
}
- /* Allocate memory for the SCSI Host Template */
- if ((ss->htmplt = (Scsi_Host_Template *)
- kmalloc(sizeof(Scsi_Host_Template),GFP_KERNEL))==NULL ) {
- printk(KERN_WARNING USB_STORAGE "Out of memory\n");
-
- kfree(ss);
- return NULL;
+ if (ss->protocol == US_PR_CBI) {
+ /* set up so we'll wait for notification */
+ init_MUTEX_LOCKED(&(ss->ip_waitq));
+
+ /* set up the IRQ pipe and handler */
+ /* FIXME: This needs to get period from the device */
+ US_DEBUGP("Allocating IRQ for CBI transport\n");
+ ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
+ result = usb_request_irq(ss->pusb_dev, ss->irqpipe,
+ CBI_irq, 255,
+ (void *)ss, &ss->irq_handle);
+ US_DEBUGP("usb_request_irq returned %d", result);
}
+
+ /*
+ * Since this is a new device, we need to generate a scsi
+ * host definition, and register with the higher SCSI layers
+ */
/* Initialize the host template based on the default one */
- memcpy(ss->htmplt, &my_host_template, sizeof(my_host_template));
+ memcpy(&(ss->htmplt), &my_host_template,
+ sizeof(my_host_template));
/* Grab the next host number */
ss->host_number = my_host_number++;
-
- /* MDD: FIXME: this is bad. We abuse this pointer so we
+
+ /* FIXME: this is bad. We abuse this pointer so we
* can pass the ss pointer to the host controler thread
* in us_detect
*/
- (struct us_data *)ss->htmplt->proc_dir = ss;
-
- /* shuttle E-USB */
- if (dev->descriptor.idVendor == 0x04e6 &&
- dev->descriptor.idProduct == 0x0001) {
- __u8 qstat[2];
- int result;
-
- result = usb_control_msg(ss->pusb_dev,
- usb_rcvctrlpipe(dev,0),
- 1, 0xC0,
- 0, ss->ifnum,
- qstat, 2, HZ*5);
- US_DEBUGP("C0 status 0x%x 0x%x\n", qstat[0], qstat[1]);
- init_MUTEX_LOCKED(&(ss->ip_waitq));
- ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
- result = usb_request_irq(ss->pusb_dev, ss->irqpipe,
- CBI_irq, 255, (void *)ss,
- &ss->irq_handle);
- if (result < 0)
- return NULL;
- /* FIXME: what is this?? */
- down(&(ss->ip_waitq));
- } else if (ss->protocol == US_PR_CBI) {
- int result;
-
- /* set up so we'll wait for notification */
- init_MUTEX_LOCKED(&(ss->ip_waitq));
-
- /* set up the IRQ pipe and handler */
- /* FIXME: This needs to get the period from the device */
- ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
- result = usb_request_irq(ss->pusb_dev, ss->irqpipe, CBI_irq,
- 255, (void *)ss, &ss->irq_handle);
- if (result) {
- US_DEBUGP("usb_request_irq failed (0x%x), No interrupt for CBI\n",
- result);
- }
- }
-
-
- /* start up our thread */
+ (struct us_data *)ss->htmplt.proc_dir = ss;
+
+ /* start up our thread */
{
DECLARE_MUTEX_LOCKED(sem);
-
+
ss->notify = &sem;
ss->pid = kernel_thread(usb_stor_control_thread, ss,
- CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+ CLONE_FS | CLONE_FILES |
+ CLONE_SIGHAND);
if (ss->pid < 0) {
- printk(KERN_WARNING USB_STORAGE "Unable to start control thread\n");
- kfree(ss->htmplt);
-
+ printk(KERN_WARNING USB_STORAGE
+ "Unable to start control thread\n");
kfree(ss);
return NULL;
}
-
- /* wait for it to start */
+
+ /* wait for it to start */
down(&sem);
}
-
+
/* now register - our detect function will be called */
- ss->htmplt->module = &__this_module;
- scsi_register_module(MODULE_SCSI_HA, ss->htmplt);
-
+ ss->htmplt.module = &__this_module;
+ scsi_register_module(MODULE_SCSI_HA, &(ss->htmplt));
+
/* put us in the list */
ss->next = us_list;
- us_list = ss;
+ us_list = ss;
}
- printk(KERN_DEBUG "WARNING: USB Mass Storage data integrity not assured\n");
- printk(KERN_DEBUG "USB Mass Storage device found at %d\n", dev->devnum);
+ /* release the data structure lock */
+ spin_unlock_irqrestore(&us_list_spinlock, flags);
+ printk(KERN_DEBUG
+ "WARNING: USB Mass Storage data integrity not assured\n");
+ printk(KERN_DEBUG
+ "USB Mass Storage device found at %d\n", dev->devnum);
+
+ /* return a pointer for the disconnect function */
return ss;
}
@@ -1758,6 +1833,14 @@ static void storage_disconnect(struct usb_device *dev, void *ptr)
if (!ss)
return;
+ /* FIXME: we need mututal exclusion and resource freeing here */
+
+ /* release the IRQ, if we have one */
+ if (ss->irq_handle) {
+ usb_release_irq(ss->pusb_dev, ss->irq_handle, ss->irqpipe);
+ ss->irq_handle = NULL;
+ }
+
ss->pusb_dev = NULL;
}
@@ -1788,12 +1871,15 @@ void __exit usb_stor_exit(void)
{
static struct us_data *ptr;
- // FIXME: this needs to be put back to free _all_ the hosts
- // for (ptr = us_list; ptr != NULL; ptr = ptr->next)
- // scsi_unregister_module(MODULE_SCSI_HA, ptr->htmplt);
- printk("MDD: us_list->htmplt is 0x%x\n", (unsigned int)(us_list->htmplt));
- scsi_unregister_module(MODULE_SCSI_HA, us_list->htmplt);
+ /* unregister all the virtual hosts */
+ for (ptr = us_list; ptr != NULL; ptr = ptr->next)
+ scsi_unregister_module(MODULE_SCSI_HA, &(ptr->htmplt));
+
+ /* free up the data structures */
+
+ /* kill the threads */
+ /* deregister the driver */
usb_deregister(&storage_driver) ;
}
diff --git a/drivers/usb/usb-uhci.c b/drivers/usb/usb-uhci.c
index 85a5cd476..5dbde2959 100644
--- a/drivers/usb/usb-uhci.c
+++ b/drivers/usb/usb-uhci.c
@@ -28,6 +28,7 @@
#include <linux/unistd.h>
#include <linux/interrupt.h> /* for in_interrupt() */
#include <linux/init.h>
+#include <linux/version.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44)
#include <linux/pm.h>
#endif
diff --git a/drivers/video/aty128fb.c b/drivers/video/aty128fb.c
index fcc0f8c5c..50c1f0bda 100644
--- a/drivers/video/aty128fb.c
+++ b/drivers/video/aty128fb.c
@@ -18,7 +18,6 @@
* - determine MCLK from previous setting -done for x86
* - calculate XCLK, rather than probe BIOS
* - hardware cursor support
- * - acceleration (do not use with Rage128 Pro!)
* - ioctl()'s
*/
@@ -109,13 +108,13 @@ struct aty128_chip_info {
/* supported Rage128 chipsets */
static const struct aty128_chip_info aty128_pci_probe_list[] __initdata =
{
- {"Rage128 RE (PCI)", PCI_DEVICE_ID_ATI_RAGE128_RE},
- {"Rage128 RF (AGP)", PCI_DEVICE_ID_ATI_RAGE128_RF},
- {"Rage128 RK (PCI)", PCI_DEVICE_ID_ATI_RAGE128_RK},
- {"Rage128 RL (AGP)", PCI_DEVICE_ID_ATI_RAGE128_RL},
- {"Rage128 Pro PF (AGP)", PCI_DEVICE_ID_ATI_RAGE128_PF},
- {"Rage128 Pro PR (PCI)", PCI_DEVICE_ID_ATI_RAGE128_PR},
- {NULL, 0}
+ { "Rage128 RE (PCI)", PCI_DEVICE_ID_ATI_RAGE128_RE },
+ { "Rage128 RF (AGP)", PCI_DEVICE_ID_ATI_RAGE128_RF },
+ { "Rage128 RK (PCI)", PCI_DEVICE_ID_ATI_RAGE128_RK },
+ { "Rage128 RL (AGP)", PCI_DEVICE_ID_ATI_RAGE128_RL },
+ { "Rage128 Pro PF (AGP)", PCI_DEVICE_ID_ATI_RAGE128_PF },
+ { "Rage128 Pro PR (PCI)", PCI_DEVICE_ID_ATI_RAGE128_PR },
+ { NULL, 0 }
};
/* packed BIOS settings */
@@ -162,20 +161,20 @@ struct aty128_meminfo {
};
/* various memory configurations */
-const struct aty128_meminfo sdr_128 =
+static const struct aty128_meminfo sdr_128 =
{ 4, 4, 3, 3, 1, 3, 1, 16, 30, 16, "128-bit SDR SGRAM (1:1)" };
-const struct aty128_meminfo sdr_64 =
+static const struct aty128_meminfo sdr_64 =
{ 4, 8, 3, 3, 1, 3, 1, 17, 46, 17, "64-bit SDR SGRAM (1:1)" };
-const struct aty128_meminfo sdr_sgram =
+static const struct aty128_meminfo sdr_sgram =
{ 4, 4, 1, 2, 1, 2, 1, 16, 24, 16, "64-bit SDR SGRAM (2:1)" };
-const struct aty128_meminfo ddr_sgram =
+static const struct aty128_meminfo ddr_sgram =
{ 4, 4, 3, 3, 2, 3, 1, 16, 31, 16, "64-bit DDR SGRAM" };
static int currcon = 0;
static char *aty128fb_name = "ATY Rage128";
static char fontname[40] __initdata = { 0 };
-static char noaccel __initdata = 1;
+static char noaccel __initdata = 0;
static unsigned int initdepth __initdata = 8;
#ifndef MODULE
@@ -273,6 +272,7 @@ struct fb_info_aty128 {
#ifdef CONFIG_MTRR
struct { int vram; int vram_valid; } mtrr;
#endif
+ int fifo_slots; /* free slots in FIFO (64 max) */
};
static struct fb_info_aty128 *board_list = NULL;
@@ -344,7 +344,8 @@ static void aty128_init_engine(const struct aty128fb_par *par,
struct fb_info_aty128 *info);
static void aty128_reset_engine(const struct fb_info_aty128 *info);
static void aty128_flush_pixel_cache(const struct fb_info_aty128 *info);
-static void wait_for_fifo(u16 entries, const struct fb_info_aty128 *info);
+static void do_wait_for_fifo(u16 entries, struct fb_info_aty128 *info);
+static void wait_for_fifo(u16 entries, struct fb_info_aty128 *info);
static void wait_for_idle(struct fb_info_aty128 *info);
static u32 bpp_to_depth(u32 bpp);
@@ -483,7 +484,8 @@ _aty_st_pll(unsigned int pll_index, u32 val,
aty_st_8(CLOCK_CNTL_INDEX, (pll_index & 0x1F) | PLL_WR_EN);
aty_st_le32(CLOCK_CNTL_DATA, val);
}
-
+
+
/* return true when the PLL has completed an atomic update */
static int
aty_pll_readupdate(const struct fb_info_aty128 *info)
@@ -547,52 +549,64 @@ register_test(const struct fb_info_aty128 *info)
* Accelerator engine functions
*/
static void
-wait_for_idle(struct fb_info_aty128 *info)
+do_wait_for_fifo(u16 entries, struct fb_info_aty128 *info)
{
- unsigned long timeout = jiffies + HZ/20;
- int reset = 1;
-
- wait_for_fifo(64, info);
-
- while (time_before(jiffies, timeout))
- if ((aty_ld_le32(GUI_STAT) & GUI_ACTIVE) != ENGINE_IDLE) {
- reset = 0;
- break;
- }
+ int i;
- if (reset)
+ for (;;) {
+ for (i = 0; i < 2000000; i++) {
+ info->fifo_slots = aty_ld_le32(GUI_STAT) & 0x0fff;
+ if (info->fifo_slots >= entries)
+ return;
+ }
aty128_reset_engine(info);
-
- info->blitter_may_be_busy = 0;
+ }
}
static void
-wait_for_fifo(u16 entries, const struct fb_info_aty128 *info)
+wait_for_idle(struct fb_info_aty128 *info)
{
- unsigned long timeout = jiffies + HZ/20;
- int reset = 1;
+ int i;
- while (time_before(jiffies, timeout))
- if ((aty_ld_le32(GUI_STAT) & 0x00000FFF) < entries) {
- reset = 0;
- break;
- }
+ do_wait_for_fifo(64, info);
- if (reset)
- aty128_reset_engine(info);
+ for (;;) {
+ for (i = 0; i < 2000000; i++) {
+ if (!(aty_ld_le32(GUI_STAT) & (1 << 31))) {
+ aty128_flush_pixel_cache(info);
+ info->blitter_may_be_busy = 0;
+ return;
+ }
+ }
+ aty128_reset_engine(info);
+ }
+}
+
+
+static void
+wait_for_fifo(u16 entries, struct fb_info_aty128 *info)
+{
+ if (info->fifo_slots < entries)
+ do_wait_for_fifo(64, info);
+ info->fifo_slots -= entries;
}
static void
aty128_flush_pixel_cache(const struct fb_info_aty128 *info)
{
- int i = 16384;
+ int i;
+ u32 tmp;
- aty_st_le32(PC_NGUI_CTLSTAT, aty_ld_le32(PC_NGUI_CTLSTAT) | 0x000000ff);
+ tmp = aty_ld_le32(PC_NGUI_CTLSTAT);
+ tmp &= ~(0x00ff);
+ tmp |= 0x00ff;
+ aty_st_le32(PC_NGUI_CTLSTAT, tmp);
- while (i && ((aty_ld_le32(PC_NGUI_CTLSTAT) & PC_BUSY) == PC_BUSY))
- i--;
+ for (i = 0; i < 2000000; i++)
+ if (!(aty_ld_le32(PC_NGUI_CTLSTAT) & PC_BUSY))
+ break;
}
@@ -798,7 +812,7 @@ aty128_var_to_crtc(const struct fb_var_screeninfo *var,
return -EINVAL;
}
- h_disp = (xres/8) - 1;
+ h_disp = (xres >> 3) - 1;
h_total = (((xres + right + hslen + left) / 8) - 1) & 0xFFFFL;
v_disp = yres - 1;
@@ -1485,7 +1499,7 @@ aty128_encode_fix(struct fb_fix_screeninfo *fix,
fix->type = FB_TYPE_PACKED_PIXELS;
fix->type_aux = 0;
- fix->line_length = par->crtc.vxres*par->crtc.bpp/8;
+ fix->line_length = par->crtc.vxres*par->crtc.bpp >> 3;
fix->visual = par->crtc.bpp <= 8 ? FB_VISUAL_PSEUDOCOLOR
: FB_VISUAL_DIRECTCOLOR;
fix->ywrapstep = 0;
@@ -1662,7 +1676,7 @@ aty128fb_setup(char *options)
else if(!strncmp(this_opt, "nomtrr", 6)) {
mtrr = 0;
}
-#endif /* CONFIG_MTRR */
+#endif
#ifdef CONFIG_PPC
/* vmode and cmode depreciated */
else if (!strncmp(this_opt, "vmode:", 6)) {
@@ -1791,7 +1805,7 @@ aty128_init(struct fb_info_aty128 *info, const char *name)
dac = aty_ld_le32(DAC_CNTL);
dac |= (DAC_8BIT_EN | DAC_RANGE_CNTL | DAC_BLANKING);
- dac |= DAC_MASK; /* set DAC mask */
+ dac |= DAC_MASK;
aty_st_le32(DAC_CNTL, dac);
/* turn off bus mastering, just in case */
@@ -2309,22 +2323,22 @@ aty128_rectcopy(int srcx, int srcy, int dstx, int dsty,
wait_for_fifo(2, info);
save_dp_datatype = aty_ld_le32(DP_DATATYPE);
- save_dp_cntl = aty_ld_le32(DP_CNTL);
+ save_dp_cntl = aty_ld_le32(DP_CNTL);
wait_for_fifo(6, info);
- aty_st_le32(DP_DATATYPE, (0 | BRUSH_SOLIDCOLOR << 16) | SRC_DSTCOLOR);
+ aty_st_le32(DP_DATATYPE, (BRUSH_SOLIDCOLOR << 16) | SRC_DSTCOLOR);
aty_st_le32(DP_MIX, ROP3_SRCCOPY | DP_SRC_RECT);
aty_st_le32(DP_CNTL, DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM);
aty_st_le32(SRC_Y_X, (srcy << 16) | srcx);
aty_st_le32(DST_Y_X, (dsty << 16) | dstx);
aty_st_le32(DST_HEIGHT_WIDTH, (height << 16) | width);
+ info->blitter_may_be_busy = 1;
+
wait_for_fifo(2, info);
aty_st_le32(DP_DATATYPE, save_dp_datatype);
aty_st_le32(DP_CNTL, save_dp_cntl);
- info->blitter_may_be_busy = 1;
-
wait_for_idle(info);
}
@@ -2333,16 +2347,15 @@ aty128_rectcopy(int srcx, int srcy, int dstx, int dsty,
* Text mode accelerated functions
*/
-
static void
fbcon_aty128_bmove(struct display *p, int sy, int sx, int dy, int dx,
int height, int width)
{
- sx *= fontwidth(p);
- sy *= fontheight(p);
- dx *= fontwidth(p);
- dy *= fontheight(p);
- width *= fontwidth(p);
+ sx *= fontwidth(p);
+ sy *= fontheight(p);
+ dx *= fontwidth(p);
+ dy *= fontheight(p);
+ width *= fontwidth(p);
height *= fontheight(p);
aty128_rectcopy(sx, sy, dx, dy, width, height,
diff --git a/drivers/video/cyber2000fb.c b/drivers/video/cyber2000fb.c
index b51014eea..3a8ccbc83 100644
--- a/drivers/video/cyber2000fb.c
+++ b/drivers/video/cyber2000fb.c
@@ -1175,6 +1175,7 @@ static struct fb_ops cyber2000fb_ops =
int cyber2000fb_attach(struct cyberpro_info *info)
{
if (current_par.initialised) {
+ info->dev = current_par.dev;
info->regs = CyberRegs;
info->fb = current_par.screen_base;
info->fb_size = current_par.screen_size;
@@ -1402,8 +1403,16 @@ int __init cyber2000fb_init(void)
smem_base = dev->resource[0].start;
mmio_base = dev->resource[0].start + 0x00800000;
+ current_par.dev = dev;
current_par.dev_id = dev->device;
+ err = pci_enable_device(dev);
+ if (err) {
+ printk("%s: unable to enable device: %d\n",
+ current_par.dev_name, err);
+ return err;
+ }
+
/*
* Map in the registers
*/
diff --git a/drivers/video/cyber2000fb.h b/drivers/video/cyber2000fb.h
index 3f26854d5..019747df7 100644
--- a/drivers/video/cyber2000fb.h
+++ b/drivers/video/cyber2000fb.h
@@ -50,8 +50,9 @@ struct cyber2000fb_par {
unsigned int palette_size;
signed int currcon;
char dev_name[32];
- unsigned int initialised;
+ struct pci_dev *dev;
unsigned int dev_id;
+ unsigned int initialised:1;
unsigned int bus_64bit:1;
/*
@@ -276,6 +277,7 @@ struct cyber2000fb_par {
#define CO_REG_DEST_WIDTH 0xbf218
struct cyberpro_info {
+ struct pci_dev *dev;
unsigned char *regs;
char *fb;
char dev_name[32];
diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c
index 1aa665d1c..2c2e7d32d 100644
--- a/fs/autofs/inode.c
+++ b/fs/autofs/inode.c
@@ -253,7 +253,6 @@ fail_iput:
fail_free:
kfree(sbi);
fail_unlock:
-fail_dec:
return NULL;
}
diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c
index 5aa2f1b35..9339775ce 100644
--- a/fs/binfmt_aout.c
+++ b/fs/binfmt_aout.c
@@ -30,7 +30,7 @@
#include <asm/pgalloc.h>
static int load_aout_binary(struct linux_binprm *, struct pt_regs * regs);
-static int load_aout_library(int fd);
+static int load_aout_library(struct file*);
static int aout_core_dump(long signr, struct pt_regs * regs, struct file *file);
extern void dump_thread(struct pt_regs *, struct user *);
@@ -414,9 +414,8 @@ beyond_if:
return 0;
}
-static int load_aout_library(int fd)
+static int load_aout_library(struct file *file)
{
- struct file * file;
struct inode * inode;
unsigned long bss, start_addr, len;
unsigned long error;
@@ -424,12 +423,6 @@ static int load_aout_library(int fd)
loff_t offset = 0;
struct exec ex;
- retval = -EACCES;
- file = fget(fd);
- if (!file)
- goto out;
- if (!file->f_op)
- goto out_putf;
inode = file->f_dentry->d_inode;
retval = -ENOEXEC;
@@ -438,17 +431,17 @@ static int load_aout_library(int fd)
error = file->f_op->read(file, (char *) &ex, sizeof(ex), &offset);
set_fs(USER_DS);
if (error != sizeof(ex))
- goto out_putf;
+ goto out;
/* We come in here for the regular a.out style of shared libraries */
if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || N_TRSIZE(ex) ||
N_DRSIZE(ex) || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) ||
inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
- goto out_putf;
+ goto out;
}
if (N_FLAGS(ex))
- goto out_putf;
+ goto out;
/* For QMAGIC, the starting address is 0x20 into the page. We mask
this off to get the starting address for the page */
@@ -474,16 +467,18 @@ static int load_aout_library(int fd)
(unsigned long) start_addr + ex.a_text + ex.a_data);
retval = 0;
- goto out_putf;
+ goto out;
}
/* Now use mmap to map the library into memory. */
+ down(&current->mm->mmap_sem);
error = do_mmap(file, start_addr, ex.a_text + ex.a_data,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
N_TXTOFF(ex));
+ up(&current->mm->mmap_sem);
retval = error;
if (error != start_addr)
- goto out_putf;
+ goto out;
len = PAGE_ALIGN(ex.a_text + ex.a_data);
bss = ex.a_text + ex.a_data + ex.a_bss;
@@ -491,12 +486,9 @@ static int load_aout_library(int fd)
error = do_brk(start_addr + len, bss - len);
retval = error;
if (error != start_addr + len)
- goto out_putf;
+ goto out;
}
retval = 0;
-
-out_putf:
- fput(file);
out:
return retval;
}
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 877d9e6d7..28f82594f 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -40,7 +40,7 @@
#include <linux/elf.h>
static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs);
-static int load_elf_library(int fd);
+static int load_elf_library(struct file*);
extern int dump_fpu (struct pt_regs *, elf_fpregset_t *);
extern void dump_thread(struct pt_regs *, struct user *);
@@ -503,16 +503,14 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
current->personality = PER_SVR4;
lock_kernel();
- interpreter_dentry = open_namei(elf_interpreter,
- 0, 0);
+ interpreter_dentry = open_namei(elf_interpreter);
unlock_kernel();
current->personality = old_pers;
} else
#endif
{
lock_kernel();
- interpreter_dentry = open_namei(elf_interpreter,
- 0, 0);
+ interpreter_dentry = open_namei(elf_interpreter);
unlock_kernel();
}
set_fs(old_fs);
@@ -797,9 +795,8 @@ out_free_ph:
/* This is really simpleminded and specialized - we are loading an
a.out library that is given an ELF header. */
-static int load_elf_library(int fd)
+static int load_elf_library(struct file *file)
{
- struct file * file;
struct dentry * dentry;
struct inode * inode;
struct elf_phdr *elf_phdata;
@@ -809,9 +806,6 @@ static int load_elf_library(int fd)
loff_t offset = 0;
error = -EACCES;
- file = fget(fd);
- if (!file || !file->f_op)
- goto out;
dentry = file->f_dentry;
inode = dentry->d_inode;
@@ -823,27 +817,27 @@ static int load_elf_library(int fd)
retval = file->f_op->read(file, (char *) &elf_ex, sizeof(elf_ex), &offset);
set_fs(USER_DS);
if (retval != sizeof(elf_ex))
- goto out_putf;
+ goto out;
if (memcmp(elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
- goto out_putf;
+ goto out;
/* First of all, some simple consistency checks */
if (elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 ||
!elf_check_arch(elf_ex.e_machine) ||
(!inode->i_fop || !inode->i_fop->mmap))
- goto out_putf;
+ goto out;
/* Now read in all of the header information */
j = sizeof(struct elf_phdr) * elf_ex.e_phnum;
if (j > ELF_EXEC_PAGESIZE)
- goto out_putf;
+ goto out;
error = -ENOMEM;
elf_phdata = (struct elf_phdr *) kmalloc(j, GFP_KERNEL);
if (!elf_phdata)
- goto out_putf;
+ goto out;
/* N.B. check for error return?? */
retval = read_exec(dentry, elf_ex.e_phoff, (char *) elf_phdata,
@@ -858,6 +852,7 @@ static int load_elf_library(int fd)
while (elf_phdata->p_type != PT_LOAD) elf_phdata++;
/* Now use mmap to map the library into memory. */
+ down(&current->mm->mmap_sem);
error = do_mmap(file,
ELF_PAGESTART(elf_phdata->p_vaddr),
(elf_phdata->p_filesz +
@@ -866,6 +861,7 @@ static int load_elf_library(int fd)
MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
(elf_phdata->p_offset -
ELF_PAGEOFFSET(elf_phdata->p_vaddr)));
+ up(&current->mm->mmap_sem);
if (error != ELF_PAGESTART(elf_phdata->p_vaddr))
goto out_free_ph;
@@ -883,8 +879,6 @@ static int load_elf_library(int fd)
out_free_ph:
kfree(elf_phdata);
-out_putf:
- fput(file);
out:
return error;
}
diff --git a/fs/binfmt_em86.c b/fs/binfmt_em86.c
index 189d130ca..1a1533a10 100644
--- a/fs/binfmt_em86.c
+++ b/fs/binfmt_em86.c
@@ -12,6 +12,8 @@
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/malloc.h>
+#include <linux/locks.h>
+#include <linux/smp_lock.h>
#include <linux/binfmts.h>
#include <linux/elf.h>
#include <linux/init.h>
@@ -81,7 +83,7 @@ static int load_em86(struct linux_binprm *bprm,struct pt_regs *regs)
* space, and we don't need to copy it.
*/
lock_kernel();
- dentry = open_namei(interp, 0, 0);
+ dentry = open_namei(interp);
unlock_kernel();
if (IS_ERR(dentry))
return PTR_ERR(dentry);
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index c530a6ff2..9d98d7d70 100644
--- a/fs/binfmt_misc.c
+++ b/fs/binfmt_misc.c
@@ -213,7 +213,7 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
bprm->argc++;
bprm->filename = iname; /* for binfmt_script */
- dentry = open_namei(iname, 0, 0);
+ dentry = open_namei(iname);
retval = PTR_ERR(dentry);
if (IS_ERR(dentry))
goto _ret;
diff --git a/fs/binfmt_script.c b/fs/binfmt_script.c
index 84dbf11b0..450f918a4 100644
--- a/fs/binfmt_script.c
+++ b/fs/binfmt_script.c
@@ -82,7 +82,7 @@ static int load_script(struct linux_binprm *bprm,struct pt_regs *regs)
* OK, now restart the process with the interpreter's dentry.
*/
lock_kernel();
- dentry = open_namei(interp, 0, 0);
+ dentry = open_namei(interp);
unlock_kernel();
if (IS_ERR(dentry))
return PTR_ERR(dentry);
diff --git a/fs/buffer.c b/fs/buffer.c
index dabf1d39c..617188db0 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -147,13 +147,13 @@ void __wait_on_buffer(struct buffer_head * bh)
atomic_inc(&bh->b_count);
add_wait_queue(&bh->b_wait, &wait);
-repeat:
- run_task_queue(&tq_disk);
- set_task_state(tsk, TASK_UNINTERRUPTIBLE);
- if (buffer_locked(bh)) {
+ do {
+ run_task_queue(&tq_disk);
+ set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+ if (!buffer_locked(bh))
+ break;
schedule();
- goto repeat;
- }
+ } while (buffer_locked(bh));
tsk->state = TASK_RUNNING;
remove_wait_queue(&bh->b_wait, &wait);
atomic_dec(&bh->b_count);
diff --git a/fs/dquot.c b/fs/dquot.c
index 61dcef366..c602697f5 100644
--- a/fs/dquot.c
+++ b/fs/dquot.c
@@ -66,6 +66,11 @@ static char *quotatypes[] = INITQFNAMES;
static kmem_cache_t *dquot_cachep;
+static inline struct quota_mount_options *sb_dqopt(struct super_block *sb)
+{
+ return &sb->s_dquot;
+}
+
/*
* Dquot List Management:
* The quota code uses three lists for dquot management: the inuse_list,
@@ -98,29 +103,20 @@ static DECLARE_WAIT_QUEUE_HEAD(update_wait);
static void dqput(struct dquot *);
static struct dquot *dqduplicate(struct dquot *);
-static inline char is_enabled(struct vfsmount *vfsmnt, short type)
+static inline char is_enabled(struct quota_mount_options *dqopt, short type)
{
switch (type) {
case USRQUOTA:
- return((vfsmnt->mnt_dquot.flags & DQUOT_USR_ENABLED) != 0);
+ return((dqopt->flags & DQUOT_USR_ENABLED) != 0);
case GRPQUOTA:
- return((vfsmnt->mnt_dquot.flags & DQUOT_GRP_ENABLED) != 0);
+ return((dqopt->flags & DQUOT_GRP_ENABLED) != 0);
}
return(0);
}
static inline char sb_has_quota_enabled(struct super_block *sb, short type)
{
- struct vfsmount *vfsmnt;
-
- return((vfsmnt = lookup_vfsmnt(sb->s_dev)) != (struct vfsmount *)NULL && is_enabled(vfsmnt, type));
-}
-
-static inline char dev_has_quota_enabled(kdev_t dev, short type)
-{
- struct vfsmount *vfsmnt;
-
- return((vfsmnt = lookup_vfsmnt(dev)) != (struct vfsmount *)NULL && is_enabled(vfsmnt, type));
+ return is_enabled(sb_dqopt(sb), type);
}
static inline int const hashfn(kdev_t dev, unsigned int id, short type)
@@ -253,15 +249,15 @@ static void write_dquot(struct dquot *dquot)
mm_segment_t fs;
loff_t offset;
ssize_t ret;
- struct semaphore *sem = &dquot->dq_mnt->mnt_dquot.dqio_sem;
+ struct semaphore *sem = &dquot->dq_sb->s_dquot.dqio_sem;
lock_dquot(dquot);
- if (!dquot->dq_mnt) { /* Invalidated quota? */
+ if (!dquot->dq_sb) { /* Invalidated quota? */
unlock_dquot(dquot);
return;
}
down(sem);
- filp = dquot->dq_mnt->mnt_dquot.files[type];
+ filp = dquot->dq_sb->s_dquot.files[type];
offset = dqoff(dquot->dq_id);
fs = get_fs();
set_fs(KERNEL_DS);
@@ -293,20 +289,20 @@ static void read_dquot(struct dquot *dquot)
mm_segment_t fs;
loff_t offset;
- filp = dquot->dq_mnt->mnt_dquot.files[type];
+ filp = dquot->dq_sb->s_dquot.files[type];
if (filp == (struct file *)NULL)
return;
lock_dquot(dquot);
- if (!dquot->dq_mnt) /* Invalidated quota? */
+ if (!dquot->dq_sb) /* Invalidated quota? */
goto out_lock;
/* Now we are sure filp is valid - the dquot isn't invalidated */
- down(&dquot->dq_mnt->mnt_dquot.dqio_sem);
+ down(&dquot->dq_sb->s_dquot.dqio_sem);
offset = dqoff(dquot->dq_id);
fs = get_fs();
set_fs(KERNEL_DS);
filp->f_op->read(filp, (char *)&dquot->dq_dqb, sizeof(struct dqblk), &offset);
- up(&dquot->dq_mnt->mnt_dquot.dqio_sem);
+ up(&dquot->dq_sb->s_dquot.dqio_sem);
set_fs(fs);
if (dquot->dq_bhardlimit == 0 && dquot->dq_bsoftlimit == 0 &&
@@ -326,7 +322,7 @@ void clear_dquot(struct dquot *dquot)
{
/* unhash it first */
unhash_dquot(dquot);
- dquot->dq_mnt = NULL;
+ dquot->dq_sb = NULL;
dquot->dq_flags = 0;
dquot->dq_referenced = 0;
memset(&dquot->dq_dqb, 0, sizeof(struct dqblk));
@@ -346,7 +342,7 @@ restart:
continue;
if (dquot->dq_type != type)
continue;
- if (!dquot->dq_mnt) /* Already invalidated entry? */
+ if (!dquot->dq_sb) /* Already invalidated entry? */
continue;
if (dquot->dq_flags & DQ_LOCKED) {
__wait_on_dquot(dquot);
@@ -360,7 +356,7 @@ restart:
continue;
if (dquot->dq_type != type)
continue;
- if (!dquot->dq_mnt)
+ if (!dquot->dq_sb)
continue;
}
/*
@@ -368,7 +364,7 @@ restart:
* the quota needn't to be written to disk. So we write it
* ourselves before discarding the data just for sure...
*/
- if (dquot->dq_flags & DQ_MOD && dquot->dq_mnt)
+ if (dquot->dq_flags & DQ_MOD && dquot->dq_sb)
{
write_dquot(dquot);
need_restart = 1; /* We slept on IO */
@@ -397,7 +393,7 @@ restart:
continue;
if (type != -1 && dquot->dq_type != type)
continue;
- if (!dquot->dq_mnt) /* Invalidated? */
+ if (!dquot->dq_sb) /* Invalidated? */
continue;
if (!(dquot->dq_flags & (DQ_LOCKED | DQ_MOD)))
continue;
@@ -435,11 +431,11 @@ static void dqput(struct dquot *dquot)
}
/*
- * If the dq_mnt pointer isn't initialized this entry needs no
+ * If the dq_sb pointer isn't initialized this entry needs no
* checking and doesn't need to be written. It's just an empty
* dquot that is put back on to the freelist.
*/
- if (dquot->dq_mnt)
+ if (dquot->dq_sb)
dqstats.drops++;
we_slept:
if (dquot->dq_count > 1) {
@@ -451,7 +447,7 @@ we_slept:
printk(KERN_ERR "VFS: Locked quota to be put on the free list.\n");
dquot->dq_flags &= ~DQ_LOCKED;
}
- if (dquot->dq_mnt && dquot->dq_flags & DQ_MOD) {
+ if (dquot->dq_sb && dquot->dq_flags & DQ_MOD) {
write_dquot(dquot);
goto we_slept;
}
@@ -571,17 +567,17 @@ pressure:
goto repeat;
}
-struct dquot *dqget(kdev_t dev, unsigned int id, short type)
+static struct dquot *dqget(struct super_block *sb, unsigned int id, short type)
{
- unsigned int hashent = hashfn(dev, id, type);
+ unsigned int hashent = hashfn(sb->s_dev, id, type);
struct dquot *dquot, *empty = NULL;
- struct vfsmount *vfsmnt;
+ struct quota_mount_options *dqopt = sb_dqopt(sb);
- if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL || !is_enabled(vfsmnt, type))
+ if (!is_enabled(dqopt, type))
return(NODQUOT);
we_slept:
- if ((dquot = find_dquot(hashent, dev, id, type)) == NULL) {
+ if ((dquot = find_dquot(hashent, sb->s_dev, id, type)) == NULL) {
if (empty == NULL) {
dquot_updating[hashent]++;
empty = get_empty_dquot();
@@ -592,8 +588,8 @@ we_slept:
dquot = empty;
dquot->dq_id = id;
dquot->dq_type = type;
- dquot->dq_dev = dev;
- dquot->dq_mnt = vfsmnt;
+ dquot->dq_dev = sb->s_dev;
+ dquot->dq_sb = sb;
/* hash it first so it can be found */
hash_dquot(dquot);
read_dquot(dquot);
@@ -610,7 +606,7 @@ we_slept:
while (dquot_updating[hashent])
sleep_on(&update_wait);
- if (!dquot->dq_mnt) { /* Has somebody invalidated entry under us? */
+ if (!dquot->dq_sb) { /* Has somebody invalidated entry under us? */
/*
* Do it as if the quota was invalidated before we started
*/
@@ -625,11 +621,11 @@ we_slept:
static struct dquot *dqduplicate(struct dquot *dquot)
{
- if (dquot == NODQUOT || !dquot->dq_mnt)
+ if (dquot == NODQUOT || !dquot->dq_sb)
return NODQUOT;
dquot->dq_count++;
wait_on_dquot(dquot);
- if (!dquot->dq_mnt) {
+ if (!dquot->dq_sb) {
dquot->dq_count--;
return NODQUOT;
}
@@ -642,13 +638,12 @@ static struct dquot *dqduplicate(struct dquot *dquot)
static inline int is_quotafile(struct inode *inode)
{
int cnt;
- struct vfsmount *vfsmnt;
+ struct quota_mount_options *dqopt = sb_dqopt(inode->i_sb);
struct file **files;
- vfsmnt = lookup_vfsmnt(inode->i_dev);
- if (!vfsmnt)
+ if (!dqopt)
return 0;
- files = vfsmnt->mnt_dquot.files;
+ files = dqopt->files;
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
if (files[cnt] && files[cnt]->f_dentry->d_inode == inode)
return 1;
@@ -671,13 +666,12 @@ static int dqinit_needed(struct inode *inode, short type)
return 0;
}
-static void add_dquot_ref(kdev_t dev, short type)
+static void add_dquot_ref(struct super_block *sb, short type)
{
- struct super_block *sb = get_super(dev);
struct list_head *p;
struct inode *inode;
- if (!sb || !sb->dq_op)
+ if (!sb->dq_op)
return; /* nothing to do */
restart:
@@ -804,7 +798,7 @@ static void print_warning(struct dquot *dquot, int flag, const char *fmtstr)
if (!need_print_warning(dquot, flag))
return;
- root = dquot->dq_mnt->mnt_sb->s_root;
+ root = dquot->dq_sb->s_root;
dget(root);
buffer = (char *) __get_free_page(GFP_KERNEL);
path = buffer ? d_path(root, buffer, PAGE_SIZE) : "?";
@@ -817,7 +811,7 @@ static void print_warning(struct dquot *dquot, int flag, const char *fmtstr)
static inline char ignore_hardlimit(struct dquot *dquot)
{
- return capable(CAP_SYS_RESOURCE) && !dquot->dq_mnt->mnt_dquot.rsquash[dquot->dq_type];
+ return capable(CAP_SYS_RESOURCE) && !dquot->dq_sb->s_dquot.rsquash[dquot->dq_type];
}
static int check_idq(struct dquot *dquot, u_long inodes)
@@ -844,7 +838,7 @@ static int check_idq(struct dquot *dquot, u_long inodes)
(dquot->dq_curinodes + inodes) > dquot->dq_isoftlimit &&
dquot->dq_itime == 0) {
print_warning(dquot, 0, "%s: warning, %s file quota exceeded\n");
- dquot->dq_itime = CURRENT_TIME + dquot->dq_mnt->mnt_dquot.inode_expire[dquot->dq_type];
+ dquot->dq_itime = CURRENT_TIME + dquot->dq_sb->s_dquot.inode_expire[dquot->dq_type];
}
return QUOTA_OK;
@@ -877,7 +871,7 @@ static int check_bdq(struct dquot *dquot, u_long blocks, char prealloc)
dquot->dq_btime == 0) {
if (!prealloc) {
print_warning(dquot, 0, "%s: warning, %s disk quota exceeded\n");
- dquot->dq_btime = CURRENT_TIME + dquot->dq_mnt->mnt_dquot.block_expire[dquot->dq_type];
+ dquot->dq_btime = CURRENT_TIME + dquot->dq_sb->s_dquot.block_expire[dquot->dq_type];
}
else
/*
@@ -894,7 +888,7 @@ static int check_bdq(struct dquot *dquot, u_long blocks, char prealloc)
* Initialize a dquot-struct with new quota info. This is used by the
* system call interface functions.
*/
-static int set_dqblk(kdev_t dev, int id, short type, int flags, struct dqblk *dqblk)
+static int set_dqblk(struct super_block *sb, int id, short type, int flags, struct dqblk *dqblk)
{
struct dquot *dquot;
int error = -EFAULT;
@@ -909,7 +903,7 @@ static int set_dqblk(kdev_t dev, int id, short type, int flags, struct dqblk *dq
} else
memcpy((caddr_t)&dq_dqblk, (caddr_t)dqblk, sizeof(struct dqblk));
- if ((dquot = dqget(dev, id, type)) != NODQUOT) {
+ if (sb && (dquot = dqget(sb, id, type)) != NODQUOT) {
lock_dquot(dquot);
if (id > 0 && ((flags & SET_QUOTA) || (flags & SET_QLIMIT))) {
@@ -923,22 +917,22 @@ static int set_dqblk(kdev_t dev, int id, short type, int flags, struct dqblk *dq
if (dquot->dq_isoftlimit &&
dquot->dq_curinodes < dquot->dq_isoftlimit &&
dq_dqblk.dqb_curinodes >= dquot->dq_isoftlimit)
- dquot->dq_itime = CURRENT_TIME + dquot->dq_mnt->mnt_dquot.inode_expire[type];
+ dquot->dq_itime = CURRENT_TIME + dquot->dq_sb->s_dquot.inode_expire[type];
dquot->dq_curinodes = dq_dqblk.dqb_curinodes;
if (dquot->dq_curinodes < dquot->dq_isoftlimit)
dquot->dq_flags &= ~DQ_INODES;
if (dquot->dq_bsoftlimit &&
dquot->dq_curblocks < dquot->dq_bsoftlimit &&
dq_dqblk.dqb_curblocks >= dquot->dq_bsoftlimit)
- dquot->dq_btime = CURRENT_TIME + dquot->dq_mnt->mnt_dquot.block_expire[type];
+ dquot->dq_btime = CURRENT_TIME + dquot->dq_sb->s_dquot.block_expire[type];
dquot->dq_curblocks = dq_dqblk.dqb_curblocks;
if (dquot->dq_curblocks < dquot->dq_bsoftlimit)
dquot->dq_flags &= ~DQ_BLKS;
}
if (id == 0) {
- dquot->dq_mnt->mnt_dquot.block_expire[type] = dquot->dq_btime = dq_dqblk.dqb_btime;
- dquot->dq_mnt->mnt_dquot.inode_expire[type] = dquot->dq_itime = dq_dqblk.dqb_itime;
+ dquot->dq_sb->s_dquot.block_expire[type] = dquot->dq_btime = dq_dqblk.dqb_btime;
+ dquot->dq_sb->s_dquot.inode_expire[type] = dquot->dq_itime = dq_dqblk.dqb_itime;
}
if (dq_dqblk.dqb_bhardlimit == 0 && dq_dqblk.dqb_bsoftlimit == 0 &&
@@ -954,14 +948,14 @@ static int set_dqblk(kdev_t dev, int id, short type, int flags, struct dqblk *dq
return(0);
}
-static int get_quota(kdev_t dev, int id, short type, struct dqblk *dqblk)
+static int get_quota(struct super_block *sb, int id, short type, struct dqblk *dqblk)
{
struct dquot *dquot;
int error = -ESRCH;
- if (!dev_has_quota_enabled(dev, type))
+ if (!sb || !sb_has_quota_enabled(sb, type))
goto out;
- dquot = dqget(dev, id, type);
+ dquot = dqget(sb, id, type);
if (dquot == NODQUOT)
goto out;
@@ -990,17 +984,16 @@ static int get_stats(caddr_t addr)
return error;
}
-static int quota_root_squash(kdev_t dev, short type, int *addr)
+static int quota_root_squash(struct super_block *sb, short type, int *addr)
{
- struct vfsmount *vfsmnt;
int new_value, error;
- if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL)
+ if (!sb)
return(-ENODEV);
error = -EFAULT;
if (!copy_from_user(&new_value, addr, sizeof(int))) {
- vfsmnt->mnt_dquot.rsquash[type] = new_value;
+ sb_dqopt(sb)->rsquash[type] = new_value;
error = 0;
}
return error;
@@ -1063,7 +1056,7 @@ void dquot_initialize(struct inode *inode, short type)
id = inode->i_gid;
break;
}
- dquot = dqget(inode->i_dev, id, cnt);
+ dquot = dqget(inode->i_sb, id, cnt);
if (dquot == NODQUOT)
continue;
if (inode->i_dquot[cnt] != NODQUOT) {
@@ -1219,10 +1212,13 @@ int dquot_transfer(struct dentry *dentry, struct iattr *iattr)
if (!inode)
return -ENOENT;
+ /* Arguably we could consider that as error, but... no fs - no quota */
+ if (!inode->i_sb)
+ return 0;
/*
* Find out if this filesystem uses i_blocks.
*/
- if (!inode->i_sb || !inode->i_sb->s_blocksize)
+ if (!inode->i_sb->s_blocksize)
blocks = isize_to_blocks(inode->i_size, BLOCK_SIZE_BITS);
else
blocks = (inode->i_blocks >> 1);
@@ -1243,14 +1239,14 @@ int dquot_transfer(struct dentry *dentry, struct iattr *iattr)
if (inode->i_uid == iattr->ia_uid)
continue;
/* We can get transfer_from from inode, can't we? */
- transfer_from[cnt] = dqget(inode->i_dev, inode->i_uid, cnt);
- transfer_to[cnt] = dqget(inode->i_dev, iattr->ia_uid, cnt);
+ transfer_from[cnt] = dqget(inode->i_sb, inode->i_uid, cnt);
+ transfer_to[cnt] = dqget(inode->i_sb, iattr->ia_uid, cnt);
break;
case GRPQUOTA:
if (inode->i_gid == iattr->ia_gid)
continue;
- transfer_from[cnt] = dqget(inode->i_dev, inode->i_gid, cnt);
- transfer_to[cnt] = dqget(inode->i_dev, iattr->ia_gid, cnt);
+ transfer_from[cnt] = dqget(inode->i_sb, inode->i_gid, cnt);
+ transfer_to[cnt] = dqget(inode->i_sb, iattr->ia_gid, cnt);
break;
}
@@ -1286,7 +1282,7 @@ int dquot_transfer(struct dentry *dentry, struct iattr *iattr)
* dqget() could block and so the first structure might got
* invalidated or locked...
*/
- if (!transfer_to[cnt]->dq_mnt || !transfer_from[cnt]->dq_mnt ||
+ if (!transfer_to[cnt]->dq_sb || !transfer_from[cnt]->dq_sb ||
check_idq(transfer_to[cnt], 1) == NO_QUOTA ||
check_bdq(transfer_to[cnt], blocks, 0) == NO_QUOTA) {
cnt++;
@@ -1371,26 +1367,26 @@ struct dquot_operations dquot_operations = {
dquot_transfer
};
-static inline void set_enable_flags(struct vfsmount *vfsmnt, short type)
+static inline void set_enable_flags(struct quota_mount_options *dqopt, short type)
{
switch (type) {
case USRQUOTA:
- vfsmnt->mnt_dquot.flags |= DQUOT_USR_ENABLED;
+ dqopt->flags |= DQUOT_USR_ENABLED;
break;
case GRPQUOTA:
- vfsmnt->mnt_dquot.flags |= DQUOT_GRP_ENABLED;
+ dqopt->flags |= DQUOT_GRP_ENABLED;
break;
}
}
-static inline void reset_enable_flags(struct vfsmount *vfsmnt, short type)
+static inline void reset_enable_flags(struct quota_mount_options *dqopt, short type)
{
switch (type) {
case USRQUOTA:
- vfsmnt->mnt_dquot.flags &= ~DQUOT_USR_ENABLED;
+ dqopt->flags &= ~DQUOT_USR_ENABLED;
break;
case GRPQUOTA:
- vfsmnt->mnt_dquot.flags &= ~DQUOT_GRP_ENABLED;
+ dqopt->flags &= ~DQUOT_GRP_ENABLED;
break;
}
}
@@ -1401,38 +1397,36 @@ extern void remove_dquot_ref(kdev_t, short);
/*
* Turn quota off on a device. type == -1 ==> quotaoff for all types (umount)
*/
-int quota_off(kdev_t dev, short type)
+int quota_off(struct super_block *sb, short type)
{
- struct vfsmount *vfsmnt;
struct file *filp;
short cnt;
int enabled = 0;
+ struct quota_mount_options *dqopt = sb_dqopt(sb);
- /* We don't need to search for vfsmnt each time - umount has to wait for us */
- vfsmnt = lookup_vfsmnt(dev);
- if (!vfsmnt || !vfsmnt->mnt_sb)
+ if (!sb)
goto out;
/* We need to serialize quota_off() for device */
- down(&vfsmnt->mnt_dquot.dqoff_sem);
+ down(&dqopt->dqoff_sem);
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
if (type != -1 && cnt != type)
continue;
- if (!is_enabled(vfsmnt, cnt))
+ if (!is_enabled(dqopt, cnt))
continue;
- reset_enable_flags(vfsmnt, cnt);
+ reset_enable_flags(dqopt, cnt);
/* Note: these are blocking operations */
- remove_dquot_ref(dev, cnt);
- invalidate_dquots(dev, cnt);
+ remove_dquot_ref(sb->s_dev, cnt);
+ invalidate_dquots(sb->s_dev, cnt);
/* Wait for any pending IO - remove me as soon as invalidate is more polite */
- down(&vfsmnt->mnt_dquot.dqio_sem);
- filp = vfsmnt->mnt_dquot.files[cnt];
- vfsmnt->mnt_dquot.files[cnt] = (struct file *)NULL;
- vfsmnt->mnt_dquot.inode_expire[cnt] = 0;
- vfsmnt->mnt_dquot.block_expire[cnt] = 0;
- up(&vfsmnt->mnt_dquot.dqio_sem);
+ down(&dqopt->dqio_sem);
+ filp = dqopt->files[cnt];
+ dqopt->files[cnt] = (struct file *)NULL;
+ dqopt->inode_expire[cnt] = 0;
+ dqopt->block_expire[cnt] = 0;
+ up(&dqopt->dqio_sem);
fput(filp);
}
@@ -1441,10 +1435,10 @@ int quota_off(kdev_t dev, short type)
* and if not clear the dq_op pointer.
*/
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
- enabled |= is_enabled(vfsmnt, cnt);
+ enabled |= is_enabled(dqopt, cnt);
if (!enabled)
- vfsmnt->mnt_sb->dq_op = NULL;
- up(&vfsmnt->mnt_dquot.dqoff_sem);
+ sb->dq_op = NULL;
+ up(&dqopt->dqoff_sem);
out:
return(0);
}
@@ -1457,31 +1451,25 @@ static inline int check_quotafile_size(loff_t size)
return !(((blocks % sizeof(struct dqblk)) * BLOCK_SIZE + off % sizeof(struct dqblk)) % sizeof(struct dqblk));
}
-int quota_on(kdev_t dev, short type, char *path)
+static int quota_on(struct super_block *sb, short type, char *path)
{
struct file *f;
- struct vfsmount *vfsmnt;
struct inode *inode;
struct dquot *dquot;
- struct quota_mount_options *mnt_dquot;
+ struct quota_mount_options *dqopt = sb_dqopt(sb);
char *tmp;
int error;
- vfsmnt = lookup_vfsmnt(dev);
- if (vfsmnt == (struct vfsmount *)NULL)
- return -ENODEV;
-
- if (is_enabled(vfsmnt, type))
+ if (is_enabled(dqopt, type))
return -EBUSY;
- mnt_dquot = &vfsmnt->mnt_dquot;
- down(&mnt_dquot->dqoff_sem);
+ down(&dqopt->dqoff_sem);
tmp = getname(path);
error = PTR_ERR(tmp);
if (IS_ERR(tmp))
goto out_lock;
- f = filp_open(tmp, O_RDWR, 0600);
+ f = filp_open(tmp, O_RDWR, 0600, NULL);
putname(tmp);
error = PTR_ERR(f);
@@ -1499,24 +1487,24 @@ int quota_on(kdev_t dev, short type, char *path)
goto out_f;
dquot_drop(inode); /* We don't want quota on quota files */
- set_enable_flags(vfsmnt, type);
- mnt_dquot->files[type] = f;
+ set_enable_flags(dqopt, type);
+ dqopt->files[type] = f;
- dquot = dqget(dev, 0, type);
- mnt_dquot->inode_expire[type] = (dquot != NODQUOT) ? dquot->dq_itime : MAX_IQ_TIME;
- mnt_dquot->block_expire[type] = (dquot != NODQUOT) ? dquot->dq_btime : MAX_DQ_TIME;
+ dquot = dqget(sb, 0, type);
+ dqopt->inode_expire[type] = (dquot != NODQUOT) ? dquot->dq_itime : MAX_IQ_TIME;
+ dqopt->block_expire[type] = (dquot != NODQUOT) ? dquot->dq_btime : MAX_DQ_TIME;
dqput(dquot);
- vfsmnt->mnt_sb->dq_op = &dquot_operations;
- add_dquot_ref(dev, type);
+ sb->dq_op = &dquot_operations;
+ add_dquot_ref(sb, type);
- up(&mnt_dquot->dqoff_sem);
+ up(&dqopt->dqoff_sem);
return 0;
out_f:
filp_close(f, NULL);
out_lock:
- up(&mnt_dquot->dqoff_sem);
+ up(&dqopt->dqoff_sem);
return error;
}
@@ -1531,6 +1519,7 @@ asmlinkage long sys_quotactl(int cmd, const char *special, int id, caddr_t addr)
{
int cmds = 0, type = 0, flags = 0;
kdev_t dev;
+ struct super_block *sb = NULL;
int ret = -EINVAL;
lock_kernel();
@@ -1575,18 +1564,19 @@ asmlinkage long sys_quotactl(int cmd, const char *special, int id, caddr_t addr)
ret = -ENOTBLK;
if (!S_ISBLK(mode))
goto out;
+ sb = get_super(dev);
}
ret = -EINVAL;
switch (cmds) {
case Q_QUOTAON:
- ret = quota_on(dev, type, (char *) addr);
+ ret = sb ? quota_on(sb, type, (char *) addr) : -ENODEV;
goto out;
case Q_QUOTAOFF:
- ret = quota_off(dev, type);
+ ret = quota_off(sb, type);
goto out;
case Q_GETQUOTA:
- ret = get_quota(dev, id, type, (struct dqblk *) addr);
+ ret = get_quota(sb, id, type, (struct dqblk *) addr);
goto out;
case Q_SETQUOTA:
flags |= SET_QUOTA;
@@ -1604,7 +1594,7 @@ asmlinkage long sys_quotactl(int cmd, const char *special, int id, caddr_t addr)
ret = get_stats(addr);
goto out;
case Q_RSQUASH:
- ret = quota_root_squash(dev, type, (int *) addr);
+ ret = quota_root_squash(sb, type, (int *) addr);
goto out;
default:
goto out;
@@ -1613,8 +1603,8 @@ asmlinkage long sys_quotactl(int cmd, const char *special, int id, caddr_t addr)
flags |= QUOTA_SYSCALL;
ret = -ESRCH;
- if (dev_has_quota_enabled(dev, type))
- ret = set_dqblk(dev, id, type, flags, (struct dqblk *) addr);
+ if (sb && sb_has_quota_enabled(sb, type))
+ ret = set_dqblk(sb, id, type, flags, (struct dqblk *) addr);
out:
unlock_kernel();
return ret;
diff --git a/fs/exec.c b/fs/exec.c
index e3ff2d005..d7d5240be 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -165,14 +165,12 @@ asmlinkage long sys_uselib(const char * library)
if (file && file->f_dentry && file->f_op && file->f_op->read) {
spin_lock(&binfmt_lock);
for (fmt = formats ; fmt ; fmt = fmt->next) {
- int (*fn)(int) = fmt->load_shlib;
- if (!fn)
+ if (!fmt->load_shlib)
continue;
if (!try_inc_mod_count(fmt->module))
continue;
spin_unlock(&binfmt_lock);
- /* N.B. Should use file instead of fd */
- retval = fn(fd);
+ retval = fmt->load_shlib(file);
spin_lock(&binfmt_lock);
put_binfmt(fmt);
if (retval != -ENOEXEC)
@@ -718,6 +716,8 @@ void compute_creds(struct linux_binprm *bprm)
if (current->euid != current->uid || current->egid != current->gid ||
!cap_issubset(new_permitted, current->cap_permitted))
current->dumpable = 0;
+
+ current->keep_capabilities = 0;
}
@@ -775,7 +775,7 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
bprm_loader.page[i] = NULL;
lock_kernel();
- dentry = open_namei(dynloader[0], 0, 0);
+ dentry = open_namei(dynloader[0]);
unlock_kernel();
retval = PTR_ERR(dentry);
if (IS_ERR(dentry))
@@ -855,7 +855,7 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs
memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page[0]));
lock_kernel();
- dentry = open_namei(filename, 0, 0);
+ dentry = open_namei(filename);
unlock_kernel();
retval = PTR_ERR(dentry);
@@ -944,7 +944,7 @@ int do_coredump(long signr, struct pt_regs * regs)
#else
corename[4] = '\0';
#endif
- file = filp_open(corename, O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600);
+ file = filp_open(corename, O_CREAT | 2 | O_TRUNC | O_NOFOLLOW, 0600, NULL);
if (IS_ERR(file))
goto fail;
dentry = file->f_dentry;
diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c
index 67dedf6d6..3a18b375c 100644
--- a/fs/ext2/dir.c
+++ b/fs/ext2/dir.c
@@ -137,8 +137,8 @@ revalidate:
bh, offset)) {
/* On error, skip the f_pos to the
next block. */
- filp->f_pos = (filp->f_pos & (sb->s_blocksize - 1))
- + sb->s_blocksize;
+ filp->f_pos = (filp->f_pos | (sb->s_blocksize - 1))
+ + 1;
brelse (bh);
return stored;
}
diff --git a/fs/namei.c b/fs/namei.c
index 97c239929..8675e28c5 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -451,6 +451,76 @@ no_inode:
}
/*
+ * Restricted form of lookup. Doesn't follow links, single-component only,
+ * needs parent already locked. Doesn't follow mounts.
+ */
+struct dentry * lookup_one(const char * name, struct dentry * base)
+{
+ struct dentry * dentry;
+ struct inode *inode;
+ int err;
+ unsigned long hash;
+ struct qstr this;
+ unsigned int c;
+
+ inode = base->d_inode;
+ err = permission(inode, MAY_EXEC);
+ dentry = ERR_PTR(err);
+ if (err)
+ goto out;
+
+ this.name = name;
+ c = *(const unsigned char *)name;
+ if (!c)
+ goto access;
+
+ hash = init_name_hash();
+ do {
+ name++;
+ if (c == '/')
+ goto access;
+ hash = partial_name_hash(c, hash);
+ c = *(const unsigned char *)name;
+ } while (c);
+ this.len = name - (const char *) this.name;
+ this.hash = end_name_hash(hash);
+
+ /*
+ * See if the low-level filesystem might want
+ * to use its own hash..
+ */
+ if (base->d_op && base->d_op->d_hash) {
+ err = base->d_op->d_hash(base, &this);
+ dentry = ERR_PTR(err);
+ if (err < 0)
+ goto out;
+ }
+
+ dentry = cached_lookup(base, &this, 0);
+ if (!dentry) {
+ struct dentry *new = d_alloc(base, &this);
+ dentry = ERR_PTR(-ENOMEM);
+ if (!new)
+ goto out;
+ dentry = inode->i_op->lookup(inode, new);
+ if (!dentry)
+ dentry = new;
+ else {
+ dput(new);
+ if (IS_ERR(dentry))
+ goto out;
+ }
+ }
+
+out:
+ dput(base);
+ return dentry;
+access:
+ dentry = ERR_PTR(-EACCES);
+ goto out;
+}
+
+/*
* namei()
*
* is used by most simple commands to get the inode of a specified name.
@@ -609,13 +679,13 @@ exit_lock:
* which is a lot more logical, and also allows the "no perm" needed
* for symlinks (where the permissions are checked later).
*/
-struct dentry * open_namei(const char * pathname, int flag, int mode)
+struct dentry * __open_namei(const char * pathname, int flag, int mode, struct dentry * dir)
{
int acc_mode, error;
struct inode *inode;
struct dentry *dentry;
- dentry = lookup_dentry(pathname, NULL, lookup_flags(flag));
+ dentry = lookup_dentry(pathname, dir, lookup_flags(flag));
if (IS_ERR(dentry))
return dentry;
@@ -1012,13 +1082,13 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry)
return error;
}
-int do_unlink(const char * name)
+int do_unlink(const char * name, struct dentry * base)
{
int error;
struct dentry *dir;
struct dentry *dentry;
- dentry = lookup_dentry(name, NULL, 0);
+ dentry = lookup_dentry(name, base, 0);
error = PTR_ERR(dentry);
if (IS_ERR(dentry))
goto exit;
@@ -1043,7 +1113,7 @@ asmlinkage long sys_unlink(const char * pathname)
if(IS_ERR(tmp))
return PTR_ERR(tmp);
lock_kernel();
- error = do_unlink(tmp);
+ error = do_unlink(tmp, NULL);
unlock_kernel();
putname(tmp);
@@ -1427,16 +1497,17 @@ asmlinkage long sys_rename(const char * oldname, const char * newname)
int vfs_readlink(struct dentry *dentry, char *buffer, int buflen, const char *link)
{
- u32 len;
+ int len;
len = PTR_ERR(link);
if (IS_ERR(link))
goto out;
len = strlen(link);
- if (len > buflen)
+ if (len > (unsigned) buflen)
len = buflen;
- copy_to_user(buffer, link, len);
+ if (copy_to_user(buffer, link, len))
+ len = -EFAULT;
out:
return len;
}
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c
index 7dc5739eb..6e102db9c 100644
--- a/fs/nfsd/nfs3xdr.c
+++ b/fs/nfsd/nfs3xdr.c
@@ -727,25 +727,40 @@ encode_entry(struct readdir_cd *cd, const char *name,
/* throw in readdirplus baggage */
if (plus) {
struct svc_fh fh;
+ struct svc_export *exp;
+ struct dentry *dparent, *dchild;
+
+ dparent = cd->dirfh->fh_dentry;
+ exp = cd->dirfh->fh_export;
fh_init(&fh, NFS3_FHSIZE);
- /* Disabled for now because of lock-up */
- if (0 && nfsd_lookup(cd->rqstp, cd->dirfh, name, namlen, &fh) == 0) {
- p = encode_post_op_attr(cd->rqstp, p, fh.fh_dentry);
- p = encode_fh(p, &fh);
- fh_put(&fh);
- } else {
- /* Didn't find this entry... weird.
- * Proceed without the attrs anf fh anyway.
- */
- *p++ = 0;
- *p++ = 0;
- }
+ if (fh_verify(cd->rqstp, cd->dirfh, S_IFDIR, MAY_EXEC) != 0)
+ goto noexec;
+ if (isdotent(name, namlen)) {
+ dchild = dparent;
+ if (namlen == 2)
+ dchild = dchild->d_parent;
+ dchild = dget(dchild);
+ } else
+ dchild = lookup_one(name, dget(dparent));
+ if (IS_ERR(dchild))
+ goto noexec;
+ if (fh_compose(&fh, exp, dchild) != 0 || !dchild->d_inode)
+ goto noexec;
+ p = encode_post_op_attr(cd->rqstp, p, fh.fh_dentry);
+ p = encode_fh(p, &fh);
+ fh_put(&fh);
}
+out:
cd->buflen = buflen;
cd->buffer = p;
return 0;
+
+noexec:
+ *p++ = 0;
+ *p++ = 0;
+ goto out;
}
int
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 815208e09..f755adc8c 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -42,8 +42,10 @@ static int nfsctl_export(struct nfsctl_export *data);
static int nfsctl_unexport(struct nfsctl_export *data);
static int nfsctl_getfh(struct nfsctl_fhparm *, __u8 *);
static int nfsctl_getfd(struct nfsctl_fdparm *, __u8 *);
+#ifdef notyet
static int nfsctl_getfs(struct nfsctl_fsparm *, struct knfsd_fh *);
-/* static int nfsctl_ugidupdate(struct nfsctl_ugidmap *data); */
+static int nfsctl_ugidupdate(struct nfsctl_ugidmap *data);
+#endif
static int initialized = 0;
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index 7baab32d9..a5fcdcf7d 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -147,7 +147,7 @@ static struct dentry *nfsd_iget(struct super_block *sb, unsigned long ino, __u32
generation);
iput(inode);
- return NULL;
+ return ERR_PTR(-ESTALE);
}
/* now to find a dentry.
* If possible, get a well-connected one
@@ -353,10 +353,6 @@ find_fh_dentry(struct super_block *sb, ino_t ino, int generation, ino_t dirino,
if (IS_ERR(result))
goto err_out;
err = -ESTALE;
- if (!result) {
- dprintk("find_fh_dentry: No inode found.\n");
- goto err_out;
- }
if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED))
return result;
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index 7d570299f..6f69225cc 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -195,6 +195,7 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
svc_fh *newfhp = &resp->fh;
struct iattr *attr = &argp->attrs;
struct inode *inode;
+ struct dentry *dchild;
int nfserr, type, mode, rdonly = 0;
dev_t rdev = NODEV;
@@ -214,14 +215,24 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
} else if (nfserr)
goto done;
- /*
- * Do a lookup to verify the new file handle.
- */
+ nfserr = nfserr_acces;
+ if (!argp->len)
+ goto done;
+ nfserr = nfserr_exist;
+ if (isdotent(argp->name, argp->len))
+ goto done;
+ fh_lock(dirfhp);
+ dchild = lookup_one(argp->name, dget(dirfhp->fh_dentry));
+ nfserr = nfserrno(PTR_ERR(dchild));
+ if (IS_ERR(dchild))
+ goto out_unlock;
fh_init(newfhp, NFS_FHSIZE);
- nfserr = nfsd_lookup(rqstp, dirfhp, argp->name, argp->len, newfhp);
+ nfserr = fh_compose(newfhp, dirfhp->fh_export, dchild);
+ if (!nfserr && !dchild->d_inode)
+ nfserr = nfserr_noent;
if (nfserr) {
if (nfserr != nfserr_noent)
- goto done;
+ goto out_unlock;
/*
* If the new file handle wasn't verified, we can't tell
* whether the file exists or not. Time to bail ...
@@ -230,22 +241,11 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
if (!newfhp->fh_dverified) {
printk(KERN_WARNING
"nfsd_proc_create: file handle not verified\n");
- goto done;
+ goto out_unlock;
}
}
- /*
- * Lock the parent directory and check for existence.
- */
- nfserr = fh_lock_parent(dirfhp, newfhp->fh_dentry);
- if (nfserr)
- goto done;
inode = newfhp->fh_dentry->d_inode;
- if (inode && newfhp->fh_handle.fh_fileid_type == 0)
- /* inode might have been instantiated while we slept */
- nfserr = fh_update(newfhp);
- if (nfserr)
- goto done;
/* Unfudge the mode bits */
if (attr->ia_valid & ATTR_MODE) {
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 06a795841..e3be271a2 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -52,9 +52,6 @@
*/
#define IS_ISMNDLK(i) (S_ISREG((i)->i_mode) && MANDATORY_LOCK(i))
-/* Check for dir entries '.' and '..' */
-#define isdotent(n, l) (l < 3 && n[0] == '.' && (l == 1 || n[1] == '.'))
-
/*
* This is a cache of readahead params that help us choose the proper
* readahead strategy. Initially, we set all readahead parameters to 0
@@ -77,47 +74,6 @@ struct raparms {
static struct raparms * raparml = NULL;
static struct raparms * raparm_cache = NULL;
-
-/*
- * We need to do a check-parent every time
- * after we have locked the parent - to verify
- * that the parent is still our parent and
- * that we are still hashed onto it..
- *
- * This is required in case two processes race
- * on removing (or moving) the same entry: the
- * parent lock will serialize them, but the
- * other process will be too late..
- *
- * Note that this nfsd_check_parent is identical
- * the check_parent in linux/fs/namei.c.
- */
-#define nfsd_check_parent(dir, dentry) \
- ((dir) == (dentry)->d_parent && !d_unhashed(dentry))
-
-/*
- * Lock a parent directory following the VFS locking protocol.
- */
-int
-fh_lock_parent(struct svc_fh *parent_fh, struct dentry *dchild)
-{
- fh_lock(parent_fh);
- /*
- * Make sure the parent->child relationship still holds,
- * and that the child is still hashed.
- */
- if (nfsd_check_parent(parent_fh->fh_dentry, dchild))
- return 0;
-
- printk(KERN_WARNING
- "fh_lock_parent: %s/%s parent changed or child unhashed\n",
- dchild->d_parent->d_name.name, dchild->d_name.name);
-
- fh_unlock(parent_fh);
- return nfserr_noent;
-}
-
-
/*
* Look up one component of a pathname.
* N.B. After this call _both_ fhp and resfh need an fh_put
@@ -156,35 +112,57 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
err = nfserr_acces;
/* Lookup the name, but don't follow links */
- if (strcmp(name,"..")==0 && dparent->d_covers != dparent)
- dchild = dget(dparent);
- else
+ if (strcmp(name, "..")==0) {
+ /* checking mountpoint crossing is very different when stepping up */
+ if (dparent == exp->ex_dentry) {
+ if (!EX_CROSSMNT(exp))
+ dchild = dget(dparent); /* .. == . just like at / */
+ else
+ {
+ struct svc_export *exp2 = NULL;
+ struct dentry *dp;
+ dchild = dparent->d_covers->d_parent;
+ for (dp=dchild;
+ exp2 == NULL && dp->d_covers->d_parent != dp;
+ dp=dp->d_covers->d_parent)
+ exp2 = exp_get(exp->ex_client, dp->d_inode->i_dev, dp->d_inode->i_ino);
+ if (exp2==NULL || dchild->d_sb != exp2->ex_dentry->d_sb) {
+ dchild = dget(dparent);
+ } else {
+ dget(dchild);
+ exp = exp2;
+ }
+ }
+ } else
+ dchild = dget(dparent->d_parent);
+ } else {
dchild = lookup_dentry(name, dget(dparent), 0);
- if (IS_ERR(dchild))
- goto out_nfserr;
- /*
- * check if we have crossed a mount point ...
- */
- if (dchild->d_sb != dparent->d_sb) {
- struct svc_export *exp2 = NULL;
- exp2 = exp_get(rqstp->rq_client,
- dchild->d_inode->i_dev,
- dchild->d_inode->i_ino);
- if (exp2 && EX_CROSSMNT(exp2))
- /* successfully crossed mount point */
- exp = exp2;
- else if (dchild->d_covers->d_sb == dparent->d_sb) {
- /* stay in the original filesystem */
- struct dentry *tdentry = dget(dchild->d_covers);
- dput(dchild);
- dchild = tdentry;
- } else {
- /* This cannot possibly happen */
- printk("nfsd_lookup: %s/%s impossible mount point!\n", dparent->d_name.name, dchild->d_name.name);
- dput(dchild);
- err = nfserr_acces;
- goto out;
+ if (IS_ERR(dchild))
+ goto out_nfserr;
+ /*
+ * check if we have crossed a mount point ...
+ */
+ if (dchild->d_sb != dparent->d_sb) {
+ struct svc_export *exp2 = NULL;
+ exp2 = exp_get(rqstp->rq_client,
+ dchild->d_inode->i_dev,
+ dchild->d_inode->i_ino);
+ if (exp2 && EX_CROSSMNT(exp2))
+ /* successfully crossed mount point */
+ exp = exp2;
+ else if (dchild->d_covers->d_sb == dparent->d_sb) {
+ /* stay in the original filesystem */
+ struct dentry *tdentry = dget(dchild->d_covers);
+ dput(dchild);
+ dchild = tdentry;
+ } else {
+ /* This cannot possibly happen */
+ printk("nfsd_lookup: %s/%s impossible mount point!\n", dparent->d_name.name, dchild->d_name.name);
+ dput(dchild);
+ err = nfserr_acces;
+ goto out;
+ }
}
}
/*
@@ -216,6 +194,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
int imode;
int err;
kernel_cap_t saved_cap = 0;
+ int size_change = 0;
if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE))
accmode |= MAY_WRITE;
@@ -305,14 +284,31 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
saved_cap = current->cap_effective;
cap_clear(current->cap_effective);
}
+#ifdef CONFIG_QUOTA
+ /* DQUOT_TRANSFER needs both ia_uid and ia_gid defined */
+ if (iap->ia_valid & (ATTR_UID|ATTR_GID)) {
+ if (! (iap->ia_valid & ATTR_UID))
+ iap->ia_uid = inode->i_uid;
+ if (! (iap->ia_valid & ATTR_GID))
+ iap->ia_gid = inode->i_gid;
+ iap->ia_valid |= ATTR_UID|ATTR_GID;
+ }
+#endif /* CONFIG_QUOTA */
+
if (iap->ia_valid & ATTR_SIZE) {
fh_lock(fhp);
+ size_change = 1;
+ }
+#ifdef CONFIG_QUOTA
+ if (iap->ia_valid & (ATTR_UID|ATTR_GID))
+ err = DQUOT_TRANSFER(dentry, iap);
+ else
+#endif
err = notify_change(dentry, iap);
+ if (size_change) {
fh_unlock(fhp);
put_write_access(inode);
}
- else
- err = notify_change(dentry, iap);
if (current->fsuid != 0)
current->cap_effective = saved_cap;
if (err)
@@ -647,11 +643,11 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
uid_t saved_euid;
#endif
- if (!cnt)
- goto out;
err = nfsd_open(rqstp, fhp, S_IFREG, MAY_WRITE, &file);
if (err)
goto out;
+ if (!cnt)
+ goto out_close;
err = nfserr_perm;
if (!file.f_op->write)
goto out_close;
@@ -812,6 +808,9 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
err = nfserr_perm;
if (!flen)
goto out;
+ err = nfserr_exist;
+ if (isdotent(fname, flen))
+ goto out;
err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE);
if (err)
@@ -829,14 +828,11 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
*/
if (!resfhp->fh_dverified) {
/* called from nfsd_proc_mkdir, or possibly nfsd3_proc_create */
- dchild = lookup_dentry(fname, dget(dentry), 0);
+ fh_lock(fhp);
+ dchild = lookup_one(fname, dget(dentry));
err = PTR_ERR(dchild);
if (IS_ERR(dchild))
goto out_nfserr;
- /* Lock the parent and check for errors ... */
- err = fh_lock_parent(fhp, dchild);
- if (err)
- goto out;
err = fh_compose(resfhp, fhp->fh_export, dchild);
if (err)
goto out;
@@ -934,6 +930,9 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp,
err = nfserr_perm;
if (!flen)
goto out;
+ err = nfserr_exist;
+ if (isdotent(fname, flen))
+ goto out;
if (!(iap->ia_valid & ATTR_MODE))
iap->ia_mode = 0;
err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE);
@@ -948,21 +947,16 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp,
err = nfserr_notdir;
if(!dirp->i_op || !dirp->i_op->lookup)
goto out;
+ fh_lock(fhp);
/*
* Compose the response file handle.
*/
- dchild = lookup_dentry(fname, dget(dentry), 0);
+ dchild = lookup_one(fname, dget(dentry));
err = PTR_ERR(dchild);
if(IS_ERR(dchild))
goto out_nfserr;
- /*
- * We must lock the directory before we check for the inode.
- */
- err = fh_lock_parent(fhp, dchild);
- if (err)
- goto out;
err = fh_compose(resfhp, fhp->fh_export, dchild);
if (err)
goto out;
@@ -1096,24 +1090,20 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
err = nfserr_noent;
if (!flen || !plen)
goto out;
+ err = nfserr_exist;
+ if (isdotent(fname, flen))
+ goto out;
err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE);
if (err)
goto out;
+ fh_lock(fhp);
dentry = fhp->fh_dentry;
-
- dnew = lookup_dentry(fname, dget(dentry), 0);
+ dnew = lookup_one(fname, dget(dentry));
err = PTR_ERR(dnew);
if (IS_ERR(dnew))
goto out_nfserr;
- /*
- * Lock the parent before checking for existence
- */
- err = fh_lock_parent(fhp, dnew);
- if (err)
- goto out_compose;
-
err = vfs_symlink(dentry->d_inode, dnew, path);
if (!err) {
if (EX_ISSYNC(fhp->fh_export))
@@ -1134,7 +1124,6 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
fh_unlock(fhp);
/* Compose the fh so the dentry will be freed ... */
-out_compose:
cerr = fh_compose(resfhp, fhp->fh_export, dnew);
if (err==0) err = cerr;
out:
@@ -1167,20 +1156,18 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
err = nfserr_perm;
if (!len)
goto out;
+ err = nfserr_exist;
+ if (isdotent(fname, len))
+ goto out;
+ fh_lock(ffhp);
ddir = ffhp->fh_dentry;
dirp = ddir->d_inode;
- dnew = lookup_dentry(fname, dget(ddir), 0);
+ dnew = lookup_one(fname, dget(ddir));
err = PTR_ERR(dnew);
if (IS_ERR(dnew))
goto out_nfserr;
- /*
- * Lock the parent before checking for existence
- */
- err = fh_lock_parent(ffhp, dnew);
- if (err)
- goto out_dput;
dold = tfhp->fh_dentry;
dest = dold->d_inode;
@@ -1199,7 +1186,6 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
}
fh_unlock(ffhp);
-out_dput:
dput(dnew);
out:
return err;
@@ -1210,29 +1196,6 @@ out_nfserr:
}
/*
- * This follows the model of double_lock() in the VFS.
- */
-static inline void nfsd_double_down(struct semaphore *s1, struct semaphore *s2)
-{
- if (s1 != s2) {
- if ((unsigned long) s1 < (unsigned long) s2) {
- struct semaphore *tmp = s1;
- s1 = s2;
- s2 = tmp;
- }
- down(s1);
- }
- down(s2);
-}
-
-static inline void nfsd_double_up(struct semaphore *s1, struct semaphore *s2)
-{
- up(s1);
- if (s1 != s2)
- up(s2);
-}
-
-/*
* Rename a file
* N.B. After this call _both_ ffhp and tfhp need an fh_put
*/
@@ -1261,15 +1224,12 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
if (fdir->i_dev != tdir->i_dev)
goto out;
- /* N.B. We shouldn't need this ... dentry layer handles it */
err = nfserr_perm;
- if (!flen || (fname[0] == '.' &&
- (flen == 1 || (flen == 2 && fname[1] == '.'))) ||
- !tlen || (tname[0] == '.' &&
- (tlen == 1 || (tlen == 2 && tname[1] == '.'))))
+ if (!flen || isdotent(fname, flen) || !tlen || isdotent(tname, tlen))
goto out;
- odentry = lookup_dentry(fname, dget(fdentry), 0);
+ double_down(&tdir->i_sem, &fdir->i_sem);
+ odentry = lookup_one(fname, dget(fdentry));
err = PTR_ERR(odentry);
if (IS_ERR(odentry))
goto out_nfserr;
@@ -1278,16 +1238,11 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
if (!odentry->d_inode)
goto out_dput_old;
- ndentry = lookup_dentry(tname, dget(tdentry), 0);
+ ndentry = lookup_one(tname, dget(tdentry));
err = PTR_ERR(ndentry);
if (IS_ERR(ndentry))
goto out_dput_old;
- /*
- * Lock the parent directories.
- */
- nfsd_double_down(&tdir->i_sem, &fdir->i_sem);
-
#ifdef CONFIG_NFSD_V3
/* Fill in the pre-op attr for the wcc data for both
* tdir and fdir
@@ -1296,19 +1251,11 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
fill_pre_wcc(tfhp);
#endif /* CONFIG_NFSD_V3 */
- err = -ENOENT;
- /* GAM3 check for parent changes after locking. */
- if (nfsd_check_parent(fdentry, odentry) &&
- nfsd_check_parent(tdentry, ndentry)) {
-
- err = vfs_rename(fdir, odentry, tdir, ndentry);
- if (!err && EX_ISSYNC(tfhp->fh_export)) {
- nfsd_sync_dir(tdentry);
- nfsd_sync_dir(fdentry);
- }
- } else
- dprintk("nfsd: Caught race in nfsd_rename");
-
+ err = vfs_rename(fdir, odentry, tdir, ndentry);
+ if (!err && EX_ISSYNC(tfhp->fh_export)) {
+ nfsd_sync_dir(tdentry);
+ nfsd_sync_dir(fdentry);
+ }
#ifdef CONFIG_NFSD_V3
/* Fill in the post-op attr for the wcc data for both
* tdir and fdir
@@ -1316,7 +1263,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
fill_post_wcc(ffhp);
fill_post_wcc(tfhp);
#endif /* CONFIG_NFSD_V3 */
- nfsd_double_up(&tdir->i_sem, &fdir->i_sem);
+ double_up(&tdir->i_sem, &fdir->i_sem);
dput(ndentry);
out_dput_old:
@@ -1343,7 +1290,6 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
struct inode *dirp;
int err;
- /* N.B. We shouldn't need this test ... handled by dentry layer */
err = nfserr_acces;
if (!flen || isdotent(fname, flen))
goto out;
@@ -1351,10 +1297,11 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
if (err)
goto out;
+ fh_lock(fhp);
dentry = fhp->fh_dentry;
dirp = dentry->d_inode;
- rdentry = lookup_dentry(fname, dget(dentry), 0);
+ rdentry = lookup_one(fname, dget(dentry));
err = PTR_ERR(rdentry);
if (IS_ERR(rdentry))
goto out_nfserr;
@@ -1365,12 +1312,6 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
goto out;
}
- err = fh_lock_parent(fhp, rdentry);
- if (err) {
- dput(rdentry);
- goto out;
- }
-
if (type != S_IFDIR) { /* It's UNLINK */
err = vfs_unlink(dirp, rdentry);
} else { /* It's RMDIR */
@@ -1436,6 +1377,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
* may choose to do less.
*/
inode = file.f_dentry->d_inode;
+ down(&inode->i_sem);
while (1) {
oldlen = cd.buflen;
@@ -1444,9 +1386,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
file.f_inode->i_dev, file.f_inode->i_ino,
(int) file.f_pos, (int) oldlen, (int) cd.buflen);
*/
- down(&inode->i_sem);
err = file.f_op->readdir(&file, &cd, (filldir_t) func);
- up(&inode->i_sem);
if (err < 0)
goto out_nfserr;
if (oldlen == cd.buflen)
@@ -1454,6 +1394,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
if (cd.eob)
break;
}
+ up(&inode->i_sem);
/* If we didn't fill the buffer completely, we're at EOF */
eof = !cd.eob;
@@ -1482,6 +1423,7 @@ out:
return err;
out_nfserr:
+ up(&inode->i_sem);
err = nfserrno(err);
goto out_close;
}
diff --git a/fs/open.c b/fs/open.c
index 6b11f4ab0..9f4d50a79 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -644,7 +644,7 @@ out:
* for the internal routines (ie open_namei()/follow_link() etc). 00 is
* used by symlinks.
*/
-struct file *filp_open(const char * filename, int flags, int mode)
+struct file *filp_open(const char * filename, int flags, int mode, struct dentry * base)
{
struct inode * inode;
struct dentry * dentry;
@@ -661,7 +661,7 @@ struct file *filp_open(const char * filename, int flags, int mode)
flag++;
if (flag & O_TRUNC)
flag |= 2;
- dentry = open_namei(filename,flag,mode);
+ dentry = __open_namei(filename, flag, mode, base);
error = PTR_ERR(dentry);
if (IS_ERR(dentry))
goto cleanup_file;
@@ -787,7 +787,7 @@ asmlinkage long sys_open(const char * filename, int flags, int mode)
if (fd >= 0) {
struct file * f;
lock_kernel();
- f = filp_open(tmp, flags, mode);
+ f = filp_open(tmp, flags, mode, NULL);
unlock_kernel();
error = PTR_ERR(f);
if (IS_ERR(f))
diff --git a/fs/partitions/acorn.c b/fs/partitions/acorn.c
index 856cc30aa..561c87065 100644
--- a/fs/partitions/acorn.c
+++ b/fs/partitions/acorn.c
@@ -98,7 +98,7 @@ static int riscix_partition(struct gendisk *hd, kdev_t dev, unsigned long first_
}
}
- printk(" >");
+ printk(" >\n");
if (hd->part[riscix_minor].nr_sects > 2)
hd->part[riscix_minor].nr_sects = 2;
@@ -139,7 +139,7 @@ static int linux_partition(struct gendisk *hd, kdev_t dev, unsigned long first_s
le32_to_cpu(linuxp->nr_sects));
linuxp ++;
}
- printk(" >");
+ printk(" >\n");
/*
* Prevent someone doing a mkswap or mkfs on this partition
*/
diff --git a/fs/partitions/msdos.c b/fs/partitions/msdos.c
index 2f315ccd9..fc9555b77 100644
--- a/fs/partitions/msdos.c
+++ b/fs/partitions/msdos.c
@@ -347,19 +347,19 @@ int msdos_partition(struct gendisk *hd, kdev_t dev,
unsigned char *data;
int mask = (1 << hd->minor_shift) - 1;
int sector_size = get_hardsect_size(dev) / 512;
-#ifdef CONFIG_BLK_DEV_IDE
+#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
int tested_for_xlate = 0;
read_mbr:
-#endif /* CONFIG_BLK_DEV_IDE */
+#endif /* (CONFIG_BLK_DEV_IDE) || (CONFIG_BLK_DEV_IDE_MODULE) */
if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) {
if (warn_no_part) printk(" unable to read partition table\n");
return -1;
}
data = bh->b_data;
-#ifdef CONFIG_BLK_DEV_IDE
+#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
check_table:
-#endif /* CONFIG_BLK_DEV_IDE */
+#endif /* (CONFIG_BLK_DEV_IDE) || (CONFIG_BLK_DEV_IDE_MODULE) */
/* Use bforget(), because we may have changed the disk geometry */
if (*(unsigned short *) (0x1fe + data) != cpu_to_le16(MSDOS_LABEL_MAGIC)) {
bforget(bh);
@@ -367,7 +367,7 @@ check_table:
}
p = (struct partition *) (0x1be + data);
-#ifdef CONFIG_BLK_DEV_IDE
+#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
if (!tested_for_xlate++) { /* Do this only once per disk */
/*
* Look for various forms of IDE disk geometry translation
@@ -423,7 +423,7 @@ check_table:
(void) ide_xlate_1024(dev, 2, heads, " [PTBL]");
}
}
-#endif /* CONFIG_BLK_DEV_IDE */
+#endif /* (CONFIG_BLK_DEV_IDE) || (CONFIG_BLK_DEV_IDE_MODULE) */
/* Look for partitions in two passes:
First find the primary partitions, and the DOS-type extended partitions.
diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c
index cef1e93cf..9ed649a3b 100644
--- a/fs/smbfs/inode.c
+++ b/fs/smbfs/inode.c
@@ -546,7 +546,7 @@ static int __init init_smb_fs(void)
smb_current_vmalloced = 0;
#endif
- return init_smb_fs();
+ return register_filesystem(&smb_fs_type);
}
static void __exit exit_smb_fs(void)
diff --git a/fs/super.c b/fs/super.c
index 8aed9ce90..dd34ddc70 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -247,32 +247,9 @@ static struct file_system_type *get_fs_type(const char *name)
return fs;
}
-
struct vfsmount *vfsmntlist = NULL;
static struct vfsmount *vfsmnttail = NULL, *mru_vfsmnt = NULL;
-/*
- * This part handles the management of the list of mounted filesystems.
- */
-struct vfsmount *lookup_vfsmnt(kdev_t dev)
-{
- struct vfsmount *lptr;
-
- if (vfsmntlist == NULL)
- return NULL;
-
- if (mru_vfsmnt != NULL && mru_vfsmnt->mnt_dev == dev)
- return (mru_vfsmnt);
-
- for (lptr = vfsmntlist; lptr != NULL; lptr = lptr->mnt_next)
- if (lptr->mnt_dev == dev) {
- mru_vfsmnt = lptr;
- return (lptr);
- }
-
- return NULL;
-}
-
static struct vfsmount *add_vfsmnt(struct super_block *sb,
const char *dev_name, const char *dir_name)
{
@@ -286,11 +263,6 @@ static struct vfsmount *add_vfsmnt(struct super_block *sb,
lptr->mnt_sb = sb;
lptr->mnt_dev = sb->s_dev;
- lptr->mnt_flags = sb->s_flags;
-
- sema_init(&lptr->mnt_dquot.dqio_sem, 1);
- sema_init(&lptr->mnt_dquot.dqoff_sem, 1);
- lptr->mnt_dquot.flags = 0;
/* N.B. Is it really OK to have a vfsmount without names? */
if (dev_name && !IS_ERR(tmp = getname(dev_name))) {
@@ -399,9 +371,9 @@ int get_filesystem_info( char *buf )
len += sprintf( buf + len, "%s %s %s %s",
tmp->mnt_devname, path,
tmp->mnt_sb->s_type->name,
- tmp->mnt_flags & MS_RDONLY ? "ro" : "rw" );
+ tmp->mnt_sb->s_flags & MS_RDONLY ? "ro" : "rw" );
for (fs_infop = fs_info; fs_infop->flag; fs_infop++) {
- if (tmp->mnt_flags & fs_infop->flag) {
+ if (tmp->mnt_sb->s_flags & fs_infop->flag) {
strcpy(buf + len, fs_infop->str);
len += strlen(fs_infop->str);
}
@@ -592,6 +564,9 @@ static struct super_block * read_super(kdev_t dev, struct block_device *bdev,
sema_init(&s->s_vfs_rename_sem,1);
sema_init(&s->s_nfsd_free_path_sem,1);
s->s_type = type;
+ sema_init(&s->s_dquot.dqio_sem, 1);
+ sema_init(&s->s_dquot.dqoff_sem, 1);
+ s->s_dquot.flags = 0;
lock_super(s);
if (!type->read_super(s, data, silent))
goto out_fail;
@@ -606,7 +581,6 @@ out_fail:
s->s_dev = 0;
s->s_bdev = 0;
s->s_type = NULL;
- put_filesystem(type);
unlock_super(s);
return NULL;
}
@@ -688,7 +662,7 @@ static struct block_device *do_umount(kdev_t dev, int unmount_root, int flags)
* on the device. If the umount fails, too bad -- there
* are no quotas running any more. Just turn them on again.
*/
- DQUOT_OFF(dev);
+ DQUOT_OFF(sb);
acct_auto_close(dev);
/*
@@ -990,7 +964,6 @@ out:
static int do_remount_sb(struct super_block *sb, int flags, char *data)
{
int retval;
- struct vfsmount *vfsmnt;
if (!(flags & MS_RDONLY) && sb->s_dev && is_read_only(sb->s_dev))
return -EACCES;
@@ -1007,9 +980,6 @@ static int do_remount_sb(struct super_block *sb, int flags, char *data)
return retval;
}
sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) | (flags & MS_RMT_MASK);
- vfsmnt = lookup_vfsmnt(sb->s_dev);
- if (vfsmnt)
- vfsmnt->mnt_flags = sb->s_flags;
/*
* Invalidate the inodes, as some mount options may be changed.
@@ -1093,8 +1063,8 @@ static int copy_mount_options (const void * data, unsigned long *where)
* aren't used, as the syscall assumes we are talking to an older
* version that didn't understand them.
*/
-asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type,
- unsigned long new_flags, void * data)
+long do_sys_mount(char * dev_name, char * dir_name, unsigned long type_page,
+ unsigned long new_flags, unsigned long data_page)
{
struct file_system_type * fstype;
struct dentry * dentry = NULL;
@@ -1102,28 +1072,19 @@ asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type,
struct block_device *bdev = NULL;
int retval;
unsigned long flags = 0;
- unsigned long page = 0;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- lock_kernel();
+
if ((new_flags &
(MS_MGC_MSK | MS_REMOUNT)) == (MS_MGC_VAL | MS_REMOUNT)) {
- retval = copy_mount_options (data, &page);
- if (retval < 0)
- goto out;
retval = do_remount(dir_name,
new_flags & ~MS_MGC_MSK & ~MS_REMOUNT,
- (char *) page);
- free_page(page);
+ (char *) data_page);
goto out;
}
- retval = copy_mount_options (type, &page);
- if (retval < 0)
- goto out;
- fstype = get_fs_type((char *) page);
- free_page(page);
+ fstype = get_fs_type((char *) type_page);
retval = -ENODEV;
if (!fstype)
goto out;
@@ -1150,22 +1111,50 @@ asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type,
if (bdops) bdev->bd_op = bdops;
}
- page = 0;
- if ((new_flags & MS_MGC_MSK) == MS_MGC_VAL) {
+ if ((new_flags & MS_MGC_MSK) == MS_MGC_VAL)
flags = new_flags & ~MS_MGC_MSK;
- retval = copy_mount_options(data, &page);
- if (retval < 0)
- goto dput_and_out;
- }
+
retval = do_mount(bdev, dev_name, dir_name, fstype->name, flags,
- (void *) page);
- free_page(page);
+ (void *) data_page);
dput_and_out:
dput(dentry);
fs_out:
put_filesystem(fstype);
out:
+ return retval;
+}
+
+asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type,
+ unsigned long new_flags, void * data)
+{
+ int retval;
+ unsigned long data_page = 0;
+ unsigned long type_page = 0;
+
+ lock_kernel();
+ retval = copy_mount_options (type, &type_page);
+ if (retval < 0)
+ goto out;
+
+ /* copy_mount_options allows a NULL user pointer,
+ * and just returns zero in that case. But if we
+ * allow the type to be NULL we will crash.
+ * Previously we did not check this case.
+ */
+ if (type_page == 0) {
+ retval = -EINVAL;
+ goto out;
+ }
+
+ retval = copy_mount_options (data, &data_page);
+ if (retval >= 0) {
+ retval = do_sys_mount(dev_name, dir_name, type_page,
+ new_flags, data_page);
+ free_page(data_page);
+ }
+ free_page(type_page);
+out:
unlock_kernel();
return retval;
}
@@ -1255,7 +1244,7 @@ void __init mount_root(void)
* devfs crap and checking it right now. Later.
*/
if (!ROOT_DEV)
- panic("I have no root and I want to sream");
+ panic("I have no root and I want to scream");
bdev = bdget(kdev_t_to_nr(ROOT_DEV));
if (!bdev)
diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c
index c1fb13c31..08fa1f4bf 100644
--- a/fs/ufs/dir.c
+++ b/fs/ufs/dir.c
@@ -106,9 +106,9 @@ revalidate:
bh, offset)) {
/* On error, skip the f_pos to the
next block. */
- filp->f_pos = (filp->f_pos &
+ filp->f_pos = (filp->f_pos |
(sb->s_blocksize - 1)) +
- sb->s_blocksize;
+ 1;
brelse (bh);
return stored;
}
diff --git a/include/asm-alpha/core_apecs.h b/include/asm-alpha/core_apecs.h
index 927aa2ea7..fee9f3c13 100644
--- a/include/asm-alpha/core_apecs.h
+++ b/include/asm-alpha/core_apecs.h
@@ -511,32 +511,27 @@ __EXTERN_INLINE int apecs_is_ioaddr(unsigned long addr)
#ifdef __WANT_IO_DEF
-#define __inb apecs_inb
-#define __inw apecs_inw
-#define __inl apecs_inl
-#define __outb apecs_outb
-#define __outw apecs_outw
-#define __outl apecs_outl
-#define __readb apecs_readb
-#define __readw apecs_readw
-#define __readl apecs_readl
-#define __readq apecs_readq
-#define __writeb apecs_writeb
-#define __writew apecs_writew
-#define __writel apecs_writel
-#define __writeq apecs_writeq
-#define __ioremap apecs_ioremap
-#define __is_ioaddr apecs_is_ioaddr
-
-#define inb(port) \
- (__builtin_constant_p((port))?__inb(port):_inb(port))
-#define outb(x, port) \
- (__builtin_constant_p((port))?__outb((x),(port)):_outb((x),(port)))
-
-#define __raw_readl(a) __readl((unsigned long)(a))
-#define __raw_readq(a) __readq((unsigned long)(a))
-#define __raw_writel(v,a) __writel((v),(unsigned long)(a))
-#define __raw_writeq(v,a) __writeq((v),(unsigned long)(a))
+#define __inb(p) apecs_inb((unsigned long)(p))
+#define __inw(p) apecs_inw((unsigned long)(p))
+#define __inl(p) apecs_inl((unsigned long)(p))
+#define __outb(x,p) apecs_outb((x),(unsigned long)(p))
+#define __outw(x,p) apecs_outw((x),(unsigned long)(p))
+#define __outl(x,p) apecs_outl((x),(unsigned long)(p))
+#define __readb(a) apecs_readb((unsigned long)(a))
+#define __readw(a) apecs_readw((unsigned long)(a))
+#define __readl(a) apecs_readl((unsigned long)(a))
+#define __readq(a) apecs_readq((unsigned long)(a))
+#define __writeb(x,a) apecs_writeb((x),(unsigned long)(a))
+#define __writew(x,a) apecs_writew((x),(unsigned long)(a))
+#define __writel(x,a) apecs_writel((x),(unsigned long)(a))
+#define __writeq(x,a) apecs_writeq((x),(unsigned long)(a))
+#define __ioremap(a) apecs_ioremap((unsigned long)(a))
+#define __is_ioaddr(a) apecs_is_ioaddr((unsigned long)(a))
+
+#define __raw_readl(a) __readl(a)
+#define __raw_readq(a) __readq(a)
+#define __raw_writel(v,a) __writel((v),(a))
+#define __raw_writeq(v,a) __writeq((v),(a))
#endif /* __WANT_IO_DEF */
diff --git a/include/asm-alpha/core_cia.h b/include/asm-alpha/core_cia.h
index 418aa3492..32d2319b6 100644
--- a/include/asm-alpha/core_cia.h
+++ b/include/asm-alpha/core_cia.h
@@ -4,12 +4,18 @@
/* Define to experiment with fitting everything into one 512MB HAE window. */
#define CIA_ONE_HAE_WINDOW 1
+#include <linux/config.h>
#include <linux/types.h>
#include <asm/compiler.h>
/*
- * CIA is the internal name for the 2117x chipset which provides
+ * CIA is the internal name for the 21171 chipset which provides
* memory controller and PCI access for the 21164 chip based systems.
+ * Also supported here is the 21172 (CIA-2) and 21174 (PYXIS).
+ *
+ * The lineage is a bit confused, since the 21174 was reportedly started
+ * from the 21171 Pass 1 mask, and so is missing bug fixes that appear
+ * in 21171 Pass 2 and 21172, but it also contains additional features.
*
* This file is based on:
*
@@ -22,24 +28,8 @@
*
*/
-/*------------------------------------------------------------------------**
-** **
-** EB164 I/O procedures **
-** **
-** inport[b|w|t|l], outport[b|w|t|l] 8:16:24:32 IO xfers **
-** inportbxt: 8 bits only **
-** inport: alias of inportw **
-** outport: alias of outportw **
-** **
-** inmem[b|w|t|l], outmem[b|w|t|l] 8:16:24:32 ISA memory xfers **
-** inmembxt: 8 bits only **
-** inmem: alias of inmemw **
-** outmem: alias of outmemw **
-** **
-**------------------------------------------------------------------------*/
-
-
-/* CIA ADDRESS BIT DEFINITIONS
+/*
+ * CIA ADDRESS BIT DEFINITIONS
*
* 3333 3333 3322 2222 2222 1111 1111 11
* 9876 5432 1098 7654 3210 9876 5432 1098 7654 3210
@@ -78,91 +68,168 @@
#define CIA_MEM_R3_MASK 0x03ffffff /* SPARSE Mem region 3 mask is 26 bits */
/*
- * 21171-CA Control and Status Registers (p4-1)
+ * 21171-CA Control and Status Registers
+ */
+#define CIA_IOC_CIA_REV (IDENT_ADDR + 0x8740000080UL)
+# define CIA_REV_MASK 0xff
+#define CIA_IOC_PCI_LAT (IDENT_ADDR + 0x87400000C0UL)
+#define CIA_IOC_CIA_CTRL (IDENT_ADDR + 0x8740000100UL)
+# define CIA_CTRL_PCI_EN (1 << 0)
+# define CIA_CTRL_PCI_LOCK_EN (1 << 1)
+# define CIA_CTRL_PCI_LOOP_EN (1 << 2)
+# define CIA_CTRL_FST_BB_EN (1 << 3)
+# define CIA_CTRL_PCI_MST_EN (1 << 4)
+# define CIA_CTRL_PCI_MEM_EN (1 << 5)
+# define CIA_CTRL_PCI_REQ64_EN (1 << 6)
+# define CIA_CTRL_PCI_ACK64_EN (1 << 7)
+# define CIA_CTRL_ADDR_PE_EN (1 << 8)
+# define CIA_CTRL_PERR_EN (1 << 9)
+# define CIA_CTRL_FILL_ERR_EN (1 << 10)
+# define CIA_CTRL_MCHK_ERR_EN (1 << 11)
+# define CIA_CTRL_ECC_CHK_EN (1 << 12)
+# define CIA_CTRL_ASSERT_IDLE_BC (1 << 13)
+# define CIA_CTRL_COM_IDLE_BC (1 << 14)
+# define CIA_CTRL_CSR_IOA_BYPASS (1 << 15)
+# define CIA_CTRL_IO_FLUSHREQ_EN (1 << 16)
+# define CIA_CTRL_CPU_FLUSHREQ_EN (1 << 17)
+# define CIA_CTRL_ARB_CPU_EN (1 << 18)
+# define CIA_CTRL_EN_ARB_LINK (1 << 19)
+# define CIA_CTRL_RD_TYPE_SHIFT 20
+# define CIA_CTRL_RL_TYPE_SHIFT 24
+# define CIA_CTRL_RM_TYPE_SHIFT 28
+# define CIA_CTRL_EN_DMA_RD_PERF (1 << 31)
+#define CIA_IOC_CIA_CNFG (IDENT_ADDR + 0x8740000140UL)
+# define CIA_CNFG_IOA_BWEN (1 << 0)
+# define CIA_CNFG_PCI_MWEN (1 << 4)
+# define CIA_CNFG_PCI_DWEN (1 << 5)
+# define CIA_CNFG_PCI_WLEN (1 << 8)
+#define CIA_IOC_FLASH_CTRL (IDENT_ADDR + 0x8740000200UL)
+#define CIA_IOC_HAE_MEM (IDENT_ADDR + 0x8740000400UL)
+#define CIA_IOC_HAE_IO (IDENT_ADDR + 0x8740000440UL)
+#define CIA_IOC_CFG (IDENT_ADDR + 0x8740000480UL)
+#define CIA_IOC_CACK_EN (IDENT_ADDR + 0x8740000600UL)
+# define CIA_CACK_EN_LOCK_EN (1 << 0)
+# define CIA_CACK_EN_MB_EN (1 << 1)
+# define CIA_CACK_EN_SET_DIRTY_EN (1 << 2)
+# define CIA_CACK_EN_BC_VICTIM_EN (1 << 3)
+
+
+/*
+ * 21171-CA Diagnostic Registers
*/
-#define CIA_IOC_CIA_REV (IDENT_ADDR + 0x8740000080UL)
-#define CIA_IOC_PCI_LAT (IDENT_ADDR + 0x87400000C0UL)
-#define CIA_IOC_CIA_CTRL (IDENT_ADDR + 0x8740000100UL)
-#define CIA_IOC_CIA_CNFG (IDENT_ADDR + 0x8740000140UL)
-#define CIA_IOC_HAE_MEM (IDENT_ADDR + 0x8740000400UL)
-#define CIA_IOC_HAE_IO (IDENT_ADDR + 0x8740000440UL)
-#define CIA_IOC_CFG (IDENT_ADDR + 0x8740000480UL)
-#define CIA_IOC_CACK_EN (IDENT_ADDR + 0x8740000600UL)
+#define CIA_IOC_CIA_DIAG (IDENT_ADDR + 0x8740002000UL)
+#define CIA_IOC_DIAG_CHECK (IDENT_ADDR + 0x8740003000UL)
/*
- * 21171-CA Diagnostic Registers (p4-2)
+ * 21171-CA Performance Monitor registers
*/
-#define CIA_IOC_CIA_DIAG (IDENT_ADDR + 0x8740002000UL)
-#define CIA_IOC_DIAG_CHECK (IDENT_ADDR + 0x8740003000UL)
+#define CIA_IOC_PERF_MONITOR (IDENT_ADDR + 0x8740004000UL)
+#define CIA_IOC_PERF_CONTROL (IDENT_ADDR + 0x8740004040UL)
/*
- * 21171-CA Performance Monitor registers (p4-3)
+ * 21171-CA Error registers
*/
-#define CIA_IOC_PERF_MONITOR (IDENT_ADDR + 0x8740004000UL)
-#define CIA_IOC_PERF_CONTROL (IDENT_ADDR + 0x8740004040UL)
+#define CIA_IOC_CPU_ERR0 (IDENT_ADDR + 0x8740008000UL)
+#define CIA_IOC_CPU_ERR1 (IDENT_ADDR + 0x8740008040UL)
+#define CIA_IOC_CIA_ERR (IDENT_ADDR + 0x8740008200UL)
+# define CIA_ERR_COR_ERR (1 << 0)
+# define CIA_ERR_UN_COR_ERR (1 << 1)
+# define CIA_ERR_CPU_PE (1 << 2)
+# define CIA_ERR_MEM_NEM (1 << 3)
+# define CIA_ERR_PCI_SERR (1 << 4)
+# define CIA_ERR_PERR (1 << 5)
+# define CIA_ERR_PCI_ADDR_PE (1 << 6)
+# define CIA_ERR_RCVD_MAS_ABT (1 << 7)
+# define CIA_ERR_RCVD_TAR_ABT (1 << 8)
+# define CIA_ERR_PA_PTE_INV (1 << 9)
+# define CIA_ERR_FROM_WRT_ERR (1 << 10)
+# define CIA_ERR_IOA_TIMEOUT (1 << 11)
+# define CIA_ERR_LOST_CORR_ERR (1 << 16)
+# define CIA_ERR_LOST_UN_CORR_ERR (1 << 17)
+# define CIA_ERR_LOST_CPU_PE (1 << 18)
+# define CIA_ERR_LOST_MEM_NEM (1 << 19)
+# define CIA_ERR_LOST_PERR (1 << 21)
+# define CIA_ERR_LOST_PCI_ADDR_PE (1 << 22)
+# define CIA_ERR_LOST_RCVD_MAS_ABT (1 << 23)
+# define CIA_ERR_LOST_RCVD_TAR_ABT (1 << 24)
+# define CIA_ERR_LOST_PA_PTE_INV (1 << 25)
+# define CIA_ERR_LOST_FROM_WRT_ERR (1 << 26)
+# define CIA_ERR_LOST_IOA_TIMEOUT (1 << 27)
+# define CIA_ERR_VALID (1 << 31)
+#define CIA_IOC_CIA_STAT (IDENT_ADDR + 0x8740008240UL)
+#define CIA_IOC_ERR_MASK (IDENT_ADDR + 0x8740008280UL)
+#define CIA_IOC_CIA_SYN (IDENT_ADDR + 0x8740008300UL)
+#define CIA_IOC_MEM_ERR0 (IDENT_ADDR + 0x8740008400UL)
+#define CIA_IOC_MEM_ERR1 (IDENT_ADDR + 0x8740008440UL)
+#define CIA_IOC_PCI_ERR0 (IDENT_ADDR + 0x8740008800UL)
+#define CIA_IOC_PCI_ERR1 (IDENT_ADDR + 0x8740008840UL)
+#define CIA_IOC_PCI_ERR3 (IDENT_ADDR + 0x8740008880UL)
/*
- * 21171-CA Error registers (p4-3)
+ * 21171-CA System configuration registers
*/
-#define CIA_IOC_CPU_ERR0 (IDENT_ADDR + 0x8740008000UL)
-#define CIA_IOC_CPU_ERR1 (IDENT_ADDR + 0x8740008040UL)
-#define CIA_IOC_CIA_ERR (IDENT_ADDR + 0x8740008200UL)
-#define CIA_IOC_CIA_STAT (IDENT_ADDR + 0x8740008240UL)
-#define CIA_IOC_ERR_MASK (IDENT_ADDR + 0x8740008280UL)
-#define CIA_IOC_CIA_SYN (IDENT_ADDR + 0x8740008300UL)
-#define CIA_IOC_MEM_ERR0 (IDENT_ADDR + 0x8740008400UL)
-#define CIA_IOC_MEM_ERR1 (IDENT_ADDR + 0x8740008440UL)
-#define CIA_IOC_PCI_ERR0 (IDENT_ADDR + 0x8740008800UL)
-#define CIA_IOC_PCI_ERR1 (IDENT_ADDR + 0x8740008840UL)
-#define CIA_IOC_PCI_ERR3 (IDENT_ADDR + 0x8740008880UL)
+#define CIA_IOC_MCR (IDENT_ADDR + 0x8750000000UL)
+#define CIA_IOC_MBA0 (IDENT_ADDR + 0x8750000600UL)
+#define CIA_IOC_MBA2 (IDENT_ADDR + 0x8750000680UL)
+#define CIA_IOC_MBA4 (IDENT_ADDR + 0x8750000700UL)
+#define CIA_IOC_MBA6 (IDENT_ADDR + 0x8750000780UL)
+#define CIA_IOC_MBA8 (IDENT_ADDR + 0x8750000800UL)
+#define CIA_IOC_MBAA (IDENT_ADDR + 0x8750000880UL)
+#define CIA_IOC_MBAC (IDENT_ADDR + 0x8750000900UL)
+#define CIA_IOC_MBAE (IDENT_ADDR + 0x8750000980UL)
+#define CIA_IOC_TMG0 (IDENT_ADDR + 0x8750000B00UL)
+#define CIA_IOC_TMG1 (IDENT_ADDR + 0x8750000B40UL)
+#define CIA_IOC_TMG2 (IDENT_ADDR + 0x8750000B80UL)
/*
- * 2117A-CA PCI Address Translation Registers.
+ * 2117A-CA PCI Address and Scatter-Gather Registers.
*/
-#define CIA_IOC_PCI_TBIA (IDENT_ADDR + 0x8760000100UL)
+#define CIA_IOC_PCI_TBIA (IDENT_ADDR + 0x8760000100UL)
+
+#define CIA_IOC_PCI_W0_BASE (IDENT_ADDR + 0x8760000400UL)
+#define CIA_IOC_PCI_W0_MASK (IDENT_ADDR + 0x8760000440UL)
+#define CIA_IOC_PCI_T0_BASE (IDENT_ADDR + 0x8760000480UL)
-#define CIA_IOC_PCI_W0_BASE (IDENT_ADDR + 0x8760000400UL)
-#define CIA_IOC_PCI_W0_MASK (IDENT_ADDR + 0x8760000440UL)
-#define CIA_IOC_PCI_T0_BASE (IDENT_ADDR + 0x8760000480UL)
+#define CIA_IOC_PCI_W1_BASE (IDENT_ADDR + 0x8760000500UL)
+#define CIA_IOC_PCI_W1_MASK (IDENT_ADDR + 0x8760000540UL)
+#define CIA_IOC_PCI_T1_BASE (IDENT_ADDR + 0x8760000580UL)
-#define CIA_IOC_PCI_W1_BASE (IDENT_ADDR + 0x8760000500UL)
-#define CIA_IOC_PCI_W1_MASK (IDENT_ADDR + 0x8760000540UL)
-#define CIA_IOC_PCI_T1_BASE (IDENT_ADDR + 0x8760000580UL)
+#define CIA_IOC_PCI_W2_BASE (IDENT_ADDR + 0x8760000600UL)
+#define CIA_IOC_PCI_W2_MASK (IDENT_ADDR + 0x8760000640UL)
+#define CIA_IOC_PCI_T2_BASE (IDENT_ADDR + 0x8760000680UL)
-#define CIA_IOC_PCI_W2_BASE (IDENT_ADDR + 0x8760000600UL)
-#define CIA_IOC_PCI_W2_MASK (IDENT_ADDR + 0x8760000640UL)
-#define CIA_IOC_PCI_T2_BASE (IDENT_ADDR + 0x8760000680UL)
+#define CIA_IOC_PCI_W3_BASE (IDENT_ADDR + 0x8760000700UL)
+#define CIA_IOC_PCI_W3_MASK (IDENT_ADDR + 0x8760000740UL)
+#define CIA_IOC_PCI_T3_BASE (IDENT_ADDR + 0x8760000780UL)
-#define CIA_IOC_PCI_W3_BASE (IDENT_ADDR + 0x8760000700UL)
-#define CIA_IOC_PCI_W3_MASK (IDENT_ADDR + 0x8760000740UL)
-#define CIA_IOC_PCI_T3_BASE (IDENT_ADDR + 0x8760000780UL)
+#define CIA_IOC_PCI_W_DAC (IDENT_ADDR + 0x87600007C0UL)
/*
- * 21171-CA System configuration registers (p4-3)
+ * 2117A-CA Address Translation Registers.
*/
-#define CIA_IOC_MCR (IDENT_ADDR + 0x8750000000UL)
-#define CIA_IOC_MBA0 (IDENT_ADDR + 0x8750000600UL)
-#define CIA_IOC_MBA2 (IDENT_ADDR + 0x8750000680UL)
-#define CIA_IOC_MBA4 (IDENT_ADDR + 0x8750000700UL)
-#define CIA_IOC_MBA6 (IDENT_ADDR + 0x8750000780UL)
-#define CIA_IOC_MBA8 (IDENT_ADDR + 0x8750000800UL)
-#define CIA_IOC_MBAA (IDENT_ADDR + 0x8750000880UL)
-#define CIA_IOC_MBAC (IDENT_ADDR + 0x8750000900UL)
-#define CIA_IOC_MBAE (IDENT_ADDR + 0x8750000980UL)
-#define CIA_IOC_TMG0 (IDENT_ADDR + 0x8750000B00UL)
-#define CIA_IOC_TMG1 (IDENT_ADDR + 0x8750000B40UL)
-#define CIA_IOC_TMG2 (IDENT_ADDR + 0x8750000B80UL)
+
+/* 8 tag registers, the first 4 of which are lockable. */
+#define CIA_IOC_TB_TAGn(n) \
+ (IDENT_ADDR + 0x8760000800UL + (n)*0x40)
+
+/* 4 page registers per tag register. */
+#define CIA_IOC_TBn_PAGEm(n,m) \
+ (IDENT_ADDR + 0x8760001000UL + (n)*0x100 + (m)*0x40)
/*
* Memory spaces:
*/
-#define CIA_IACK_SC (IDENT_ADDR + 0x8720000000UL)
-#define CIA_CONF (IDENT_ADDR + 0x8700000000UL)
+#define CIA_IACK_SC (IDENT_ADDR + 0x8720000000UL)
+#define CIA_CONF (IDENT_ADDR + 0x8700000000UL)
#define CIA_IO (IDENT_ADDR + 0x8580000000UL)
#define CIA_SPARSE_MEM (IDENT_ADDR + 0x8000000000UL)
#define CIA_SPARSE_MEM_R2 (IDENT_ADDR + 0x8400000000UL)
#define CIA_SPARSE_MEM_R3 (IDENT_ADDR + 0x8500000000UL)
#define CIA_DENSE_MEM (IDENT_ADDR + 0x8600000000UL)
+#define CIA_BW_MEM (IDENT_ADDR + 0x8800000000UL)
+#define CIA_BW_IO (IDENT_ADDR + 0x8900000000UL)
+#define CIA_BW_CFG_0 (IDENT_ADDR + 0x8a00000000UL)
+#define CIA_BW_CFG_1 (IDENT_ADDR + 0x8b00000000UL)
/*
* ALCOR's GRU ASIC registers
@@ -182,23 +249,19 @@
#define XLT_GRU_INT_REQ_BITS 0x80003fffUL
#define GRU_INT_REQ_BITS (alpha_mv.sys.cia.gru_int_req_bits+0)
-
/*
- * Bit definitions for I/O Controller status register 0:
+ * PYXIS interrupt control registers
*/
-#define CIA_IOC_STAT0_CMD 0xf
-#define CIA_IOC_STAT0_ERR (1<<4)
-#define CIA_IOC_STAT0_LOST (1<<5)
-#define CIA_IOC_STAT0_THIT (1<<6)
-#define CIA_IOC_STAT0_TREF (1<<7)
-#define CIA_IOC_STAT0_CODE_SHIFT 8
-#define CIA_IOC_STAT0_CODE_MASK 0x7
-#define CIA_IOC_STAT0_P_NBR_SHIFT 13
-#define CIA_IOC_STAT0_P_NBR_MASK 0x7ffff
-
-#if !CIA_ONE_HAE_WINDOW
-#define CIA_HAE_ADDRESS CIA_IOC_HAE_MEM
-#endif
+#define PYXIS_INT_REQ (IDENT_ADDR + 0x87A0000000UL)
+#define PYXIS_INT_MASK (IDENT_ADDR + 0x87A0000040UL)
+#define PYXIS_INT_HILO (IDENT_ADDR + 0x87A00000C0UL)
+#define PYXIS_INT_ROUTE (IDENT_ADDR + 0x87A0000140UL)
+#define PYXIS_GPO (IDENT_ADDR + 0x87A0000180UL)
+#define PYXIS_INT_CNFG (IDENT_ADDR + 0x87A00001C0UL)
+#define PYXIS_RT_COUNT (IDENT_ADDR + 0x87A0000200UL)
+#define PYXIS_INT_TIME (IDENT_ADDR + 0x87A0000240UL)
+#define PYXIS_IIC_CTRL (IDENT_ADDR + 0x87A00002C0UL)
+#define PYXIS_RESET (IDENT_ADDR + 0x8780000900UL)
/*
* Data structure for handling CIA machine checks.
@@ -206,52 +269,6 @@
/* System-specific info. */
struct el_CIA_sysdata_mcheck {
-#if 0
- /* ??? Where did this come from. It appears to bear no
- relation to the cia logout written in the milo sources.
- Who knows what happens in the srm console... */
- unsigned long coma_gcr;
- unsigned long coma_edsr;
- unsigned long coma_ter;
- unsigned long coma_elar;
- unsigned long coma_ehar;
- unsigned long coma_ldlr;
- unsigned long coma_ldhr;
- unsigned long coma_base0;
- unsigned long coma_base1;
- unsigned long coma_base2;
- unsigned long coma_cnfg0;
- unsigned long coma_cnfg1;
- unsigned long coma_cnfg2;
- unsigned long epic_dcsr;
- unsigned long epic_pear;
- unsigned long epic_sear;
- unsigned long epic_tbr1;
- unsigned long epic_tbr2;
- unsigned long epic_pbr1;
- unsigned long epic_pbr2;
- unsigned long epic_pmr1;
- unsigned long epic_pmr2;
- unsigned long epic_harx1;
- unsigned long epic_harx2;
- unsigned long epic_pmlt;
- unsigned long epic_tag0;
- unsigned long epic_tag1;
- unsigned long epic_tag2;
- unsigned long epic_tag3;
- unsigned long epic_tag4;
- unsigned long epic_tag5;
- unsigned long epic_tag6;
- unsigned long epic_tag7;
- unsigned long epic_data0;
- unsigned long epic_data1;
- unsigned long epic_data2;
- unsigned long epic_data3;
- unsigned long epic_data4;
- unsigned long epic_data5;
- unsigned long epic_data6;
- unsigned long epic_data7;
-#else
unsigned long cpu_err0;
unsigned long cpu_err1;
unsigned long cia_err;
@@ -263,7 +280,6 @@ struct el_CIA_sysdata_mcheck {
unsigned long pci_err0;
unsigned long pci_err1;
unsigned long pci_err2;
-#endif
};
@@ -282,6 +298,8 @@ struct el_CIA_sysdata_mcheck {
* get at PCI memory and I/O.
*/
+#define vucp volatile unsigned char *
+#define vusp volatile unsigned short *
#define vip volatile int *
#define vuip volatile unsigned int *
#define vulp volatile unsigned long *
@@ -325,6 +343,44 @@ __EXTERN_INLINE void cia_outl(unsigned int b, unsigned long addr)
mb();
}
+__EXTERN_INLINE unsigned int cia_bwx_inb(unsigned long addr)
+{
+ /* ??? I wish I could get rid of this. But there's no ioremap
+ equivalent for I/O space. PCI I/O can be forced into the
+ CIA BWX I/O region, but that doesn't take care of legacy
+ ISA crap. */
+
+ return __kernel_ldbu(*(vucp)(addr+CIA_BW_IO));
+}
+
+__EXTERN_INLINE void cia_bwx_outb(unsigned char b, unsigned long addr)
+{
+ __kernel_stb(b, *(vucp)(addr+CIA_BW_IO));
+ mb();
+}
+
+__EXTERN_INLINE unsigned int cia_bwx_inw(unsigned long addr)
+{
+ return __kernel_ldwu(*(vusp)(addr+CIA_BW_IO));
+}
+
+__EXTERN_INLINE void cia_bwx_outw(unsigned short b, unsigned long addr)
+{
+ __kernel_stw(b, *(vusp)(addr+CIA_BW_IO));
+ mb();
+}
+
+__EXTERN_INLINE unsigned int cia_bwx_inl(unsigned long addr)
+{
+ return *(vuip)(addr+CIA_BW_IO);
+}
+
+__EXTERN_INLINE void cia_bwx_outl(unsigned int b, unsigned long addr)
+{
+ *(vuip)(addr+CIA_BW_IO) = b;
+ mb();
+}
+
/*
* Memory functions. 64-bit and 32-bit accesses are done through
@@ -362,15 +418,7 @@ __EXTERN_INLINE unsigned long cia_readb(unsigned long addr)
{
unsigned long result;
-#if !CIA_ONE_HAE_WINDOW
- unsigned long msb;
- /* Note that CIA_DENSE_MEM has no bits not masked in these
- operations, so we don't have to subtract it back out. */
- msb = addr & 0xE0000000;
- set_hae(msb);
-#endif
addr &= CIA_MEM_R1_MASK;
-
result = *(vip) ((addr << 5) + CIA_SPARSE_MEM + 0x00);
return __kernel_extbl(result, addr & 3);
}
@@ -379,15 +427,7 @@ __EXTERN_INLINE unsigned long cia_readw(unsigned long addr)
{
unsigned long result;
-#if !CIA_ONE_HAE_WINDOW
- unsigned long msb;
- /* Note that CIA_DENSE_MEM has no bits not masked in these
- operations, so we don't have to subtract it back out. */
- msb = addr & 0xE0000000;
- set_hae(msb);
-#endif
addr &= CIA_MEM_R1_MASK;
-
result = *(vip) ((addr << 5) + CIA_SPARSE_MEM + 0x08);
return __kernel_extwl(result, addr & 3);
}
@@ -396,15 +436,7 @@ __EXTERN_INLINE void cia_writeb(unsigned char b, unsigned long addr)
{
unsigned long w;
-#if !CIA_ONE_HAE_WINDOW
- unsigned long msb;
- /* Note that CIA_DENSE_MEM has no bits not masked in these
- operations, so we don't have to subtract it back out. */
- msb = addr & 0xE0000000;
- set_hae(msb);
-#endif
addr &= CIA_MEM_R1_MASK;
-
w = __kernel_insbl(b, addr & 3);
*(vuip) ((addr << 5) + CIA_SPARSE_MEM + 0x00) = w;
}
@@ -413,15 +445,7 @@ __EXTERN_INLINE void cia_writew(unsigned short b, unsigned long addr)
{
unsigned long w;
-#if !CIA_ONE_HAE_WINDOW
- unsigned long msb;
- /* Note that CIA_DENSE_MEM has no bits not masked in these
- operations, so we don't have to subtract it back out. */
- msb = addr & 0xE0000000;
- set_hae(msb);
-#endif
addr &= CIA_MEM_R1_MASK;
-
w = __kernel_inswl(b, addr & 3);
*(vuip) ((addr << 5) + CIA_SPARSE_MEM + 0x08) = w;
}
@@ -451,44 +475,117 @@ __EXTERN_INLINE unsigned long cia_ioremap(unsigned long addr)
return addr + CIA_DENSE_MEM;
}
+__EXTERN_INLINE unsigned long cia_bwx_readb(unsigned long addr)
+{
+ return __kernel_ldbu(*(vucp)addr);
+}
+
+__EXTERN_INLINE unsigned long cia_bwx_readw(unsigned long addr)
+{
+ return __kernel_ldwu(*(vusp)addr);
+}
+
+__EXTERN_INLINE unsigned long cia_bwx_readl(unsigned long addr)
+{
+ return *(vuip)addr;
+}
+
+__EXTERN_INLINE unsigned long cia_bwx_readq(unsigned long addr)
+{
+ return *(vulp)addr;
+}
+
+__EXTERN_INLINE void cia_bwx_writeb(unsigned char b, unsigned long addr)
+{
+ __kernel_stb(b, *(vucp)addr);
+}
+
+__EXTERN_INLINE void cia_bwx_writew(unsigned short b, unsigned long addr)
+{
+ __kernel_stw(b, *(vusp)addr);
+}
+
+__EXTERN_INLINE void cia_bwx_writel(unsigned int b, unsigned long addr)
+{
+ *(vuip)addr = b;
+}
+
+__EXTERN_INLINE void cia_bwx_writeq(unsigned long b, unsigned long addr)
+{
+ *(vulp)addr = b;
+}
+
+__EXTERN_INLINE unsigned long cia_bwx_ioremap(unsigned long addr)
+{
+ return addr + CIA_BW_MEM;
+}
+
__EXTERN_INLINE int cia_is_ioaddr(unsigned long addr)
{
return addr >= IDENT_ADDR + 0x8000000000UL;
}
+#undef vucp
+#undef vusp
#undef vip
#undef vuip
#undef vulp
#ifdef __WANT_IO_DEF
-#define __inb cia_inb
-#define __inw cia_inw
-#define __inl cia_inl
-#define __outb cia_outb
-#define __outw cia_outw
-#define __outl cia_outl
-
-#define __readb cia_readb
-#define __readw cia_readw
-#define __writeb cia_writeb
-#define __writew cia_writew
-#define __readl cia_readl
-#define __readq cia_readq
-#define __writel cia_writel
-#define __writeq cia_writeq
-#define __ioremap cia_ioremap
-#define __is_ioaddr cia_is_ioaddr
-
-#define inb(port) \
- (__builtin_constant_p((port))?__inb(port):_inb(port))
-#define outb(x, port) \
- (__builtin_constant_p((port))?__outb((x),(port)):_outb((x),(port)))
-
-#define __raw_readl(a) __readl((unsigned long)(a))
-#define __raw_readq(a) __readq((unsigned long)(a))
-#define __raw_writel(v,a) __writel((v),(unsigned long)(a))
-#define __raw_writeq(v,a) __writeq((v),(unsigned long)(a))
+#ifdef CONFIG_ALPHA_PYXIS
+# define __inb(p) cia_bwx_inb((unsigned long)(p))
+# define __inw(p) cia_bwx_inw((unsigned long)(p))
+# define __inl(p) cia_bwx_inl((unsigned long)(p))
+# define __outb(x,p) cia_bwx_outb((x),(unsigned long)(p))
+# define __outw(x,p) cia_bwx_outw((x),(unsigned long)(p))
+# define __outl(x,p) cia_bwx_outl((x),(unsigned long)(p))
+# define __readb(a) cia_bwx_readb((unsigned long)(a))
+# define __readw(a) cia_bwx_readw((unsigned long)(a))
+# define __readl(a) cia_bwx_readl((unsigned long)(a))
+# define __readq(a) cia_bwx_readq((unsigned long)(a))
+# define __writeb(x,a) cia_bwx_writeb((x),(unsigned long)(a))
+# define __writew(x,a) cia_bwx_writew((x),(unsigned long)(a))
+# define __writel(x,a) cia_bwx_writel((x),(unsigned long)(a))
+# define __writeq(x,a) cia_bwx_writeq((x),(unsigned long)(a))
+# define __ioremap(a) cia_bwx_ioremap((unsigned long)(a))
+# define inb(p) __inb(p)
+# define inw(p) __inw(p)
+# define inl(p) __inl(p)
+# define outb(x,p) __outb((x),(p))
+# define outw(x,p) __outw((x),(p))
+# define outl(x,p) __outl((x),(p))
+# define __raw_readb(a) __readb(a)
+# define __raw_readw(a) __readw(a)
+# define __raw_readl(a) __readl(a)
+# define __raw_readq(a) __readq(a)
+# define __raw_writeb(x,a) __writeb((x),(a))
+# define __raw_writew(x,a) __writew((x),(a))
+# define __raw_writel(x,a) __writel((x),(a))
+# define __raw_writeq(x,a) __writeq((x),(a))
+#else
+# define __inb(p) cia_inb((unsigned long)(p))
+# define __inw(p) cia_inw((unsigned long)(p))
+# define __inl(p) cia_inl((unsigned long)(p))
+# define __outb(x,p) cia_outb((x),(unsigned long)(p))
+# define __outw(x,p) cia_outw((x),(unsigned long)(p))
+# define __outl(x,p) cia_outl((x),(unsigned long)(p))
+# define __readb(a) cia_readb((unsigned long)(a))
+# define __readw(a) cia_readw((unsigned long)(a))
+# define __readl(a) cia_readl((unsigned long)(a))
+# define __readq(a) cia_readq((unsigned long)(a))
+# define __writeb(x,a) cia_writeb((x),(unsigned long)(a))
+# define __writew(x,a) cia_writew((x),(unsigned long)(a))
+# define __writel(x,a) cia_writel((x),(unsigned long)(a))
+# define __writeq(x,a) cia_writeq((x),(unsigned long)(a))
+# define __ioremap(a) cia_ioremap((unsigned long)(a))
+# define __raw_readl(a) __readl(a)
+# define __raw_readq(a) __readq(a)
+# define __raw_writel(v,a) __writel((v),(a))
+# define __raw_writeq(v,a) __writeq((v),(a))
+#endif /* PYXIS */
+
+#define __is_ioaddr(a) cia_is_ioaddr((unsigned long)(a))
#endif /* __WANT_IO_DEF */
diff --git a/include/asm-alpha/core_irongate.h b/include/asm-alpha/core_irongate.h
index 5c30feb37..c97283d89 100644
--- a/include/asm-alpha/core_irongate.h
+++ b/include/asm-alpha/core_irongate.h
@@ -493,38 +493,37 @@ __EXTERN_INLINE int irongate_is_ioaddr(unsigned long addr)
#ifdef __WANT_IO_DEF
-#define __inb irongate_inb
-#define __inw irongate_inw
-#define __inl irongate_inl
-#define __outb irongate_outb
-#define __outw irongate_outw
-#define __outl irongate_outl
-#define __readb irongate_readb
-#define __readw irongate_readw
-#define __writeb irongate_writeb
-#define __writew irongate_writew
-#define __readl irongate_readl
-#define __readq irongate_readq
-#define __writel irongate_writel
-#define __writeq irongate_writeq
-#define __ioremap irongate_ioremap
-#define __is_ioaddr irongate_is_ioaddr
-
-#define inb(port) __inb((port))
-#define inw(port) __inw((port))
-#define inl(port) __inl((port))
-#define outb(v, port) __outb((v),(port))
-#define outw(v, port) __outw((v),(port))
-#define outl(v, port) __outl((v),(port))
-
-#define __raw_readb(a) __readb((unsigned long)(a))
-#define __raw_readw(a) __readw((unsigned long)(a))
-#define __raw_readl(a) __readl((unsigned long)(a))
-#define __raw_readq(a) __readq((unsigned long)(a))
-#define __raw_writeb(v,a) __writeb((v),(unsigned long)(a))
-#define __raw_writew(v,a) __writew((v),(unsigned long)(a))
-#define __raw_writel(v,a) __writel((v),(unsigned long)(a))
-#define __raw_writeq(v,a) __writeq((v),(unsigned long)(a))
+#define __inb(p) irongate_inb((unsigned long)(p))
+#define __inw(p) irongate_inw((unsigned long)(p))
+#define __inl(p) irongate_inl((unsigned long)(p))
+#define __outb(x,p) irongate_outb((x),(unsigned long)(p))
+#define __outw(x,p) irongate_outw((x),(unsigned long)(p))
+#define __outl(x,p) irongate_outl((x),(unsigned long)(p))
+#define __readb(a) irongate_readb((unsigned long)(a))
+#define __readw(a) irongate_readw((unsigned long)(a))
+#define __readl(a) irongate_readl((unsigned long)(a))
+#define __readq(a) irongate_readq((unsigned long)(a))
+#define __writeb(x,a) irongate_writeb((x),(unsigned long)(a))
+#define __writew(x,a) irongate_writew((x),(unsigned long)(a))
+#define __writel(x,a) irongate_writel((x),(unsigned long)(a))
+#define __writeq(x,a) irongate_writeq((x),(unsigned long)(a))
+#define __ioremap(a) irongate_ioremap((unsigned long)(a))
+#define __is_ioaddr(a) irongate_is_ioaddr((unsigned long)(a))
+
+#define inb(p) __inb(p)
+#define inw(p) __inw(p)
+#define inl(p) __inl(p)
+#define outb(x,p) __outb((x),(p))
+#define outw(x,p) __outw((x),(p))
+#define outl(x,p) __outl((x),(p))
+#define __raw_readb(a) __readb(a)
+#define __raw_readw(a) __readw(a)
+#define __raw_readl(a) __readl(a)
+#define __raw_readq(a) __readq(a)
+#define __raw_writeb(v,a) __writeb((v),(a))
+#define __raw_writew(v,a) __writew((v),(a))
+#define __raw_writel(v,a) __writel((v),(a))
+#define __raw_writeq(v,a) __writeq((v),(a))
#endif /* __WANT_IO_DEF */
diff --git a/include/asm-alpha/core_lca.h b/include/asm-alpha/core_lca.h
index 6ba2bbd83..28b899308 100644
--- a/include/asm-alpha/core_lca.h
+++ b/include/asm-alpha/core_lca.h
@@ -360,32 +360,27 @@ __EXTERN_INLINE int lca_is_ioaddr(unsigned long addr)
#ifdef __WANT_IO_DEF
-#define __inb lca_inb
-#define __inw lca_inw
-#define __inl lca_inl
-#define __outb lca_outb
-#define __outw lca_outw
-#define __outl lca_outl
-#define __readb lca_readb
-#define __readw lca_readw
-#define __writeb lca_writeb
-#define __writew lca_writew
-#define __readl lca_readl
-#define __readq lca_readq
-#define __writel lca_writel
-#define __writeq lca_writeq
-#define __ioremap lca_ioremap
-#define __is_ioaddr lca_is_ioaddr
-
-#define inb(port) \
- (__builtin_constant_p((port))?__inb(port):_inb(port))
-#define outb(x, port) \
- (__builtin_constant_p((port))?__outb((x),(port)):_outb((x),(port)))
-
-#define __raw_readl(a) __readl((unsigned long)(a))
-#define __raw_readq(a) __readq((unsigned long)(a))
-#define __raw_writel(v,a) __writel((v),(unsigned long)(a))
-#define __raw_writeq(v,a) __writeq((v),(unsigned long)(a))
+#define __inb(p) lca_inb((unsigned long)(p))
+#define __inw(p) lca_inw((unsigned long)(p))
+#define __inl(p) lca_inl((unsigned long)(p))
+#define __outb(x,p) lca_outb((x),(unsigned long)(p))
+#define __outw(x,p) lca_outw((x),(unsigned long)(p))
+#define __outl(x,p) lca_outl((x),(unsigned long)(p))
+#define __readb(a) lca_readb((unsigned long)(a))
+#define __readw(a) lca_readw((unsigned long)(a))
+#define __readl(a) lca_readl((unsigned long)(a))
+#define __readq(a) lca_readq((unsigned long)(a))
+#define __writeb(x,a) lca_writeb((x),(unsigned long)(a))
+#define __writew(x,a) lca_writew((x),(unsigned long)(a))
+#define __writel(x,a) lca_writel((x),(unsigned long)(a))
+#define __writeq(x,a) lca_writeq((x),(unsigned long)(a))
+#define __ioremap(a) lca_ioremap((unsigned long)(a))
+#define __is_ioaddr(a) lca_is_ioaddr((unsigned long)(a))
+
+#define __raw_readl(a) __readl(a)
+#define __raw_readq(a) __readq(a)
+#define __raw_writel(v,a) __writel((v),(a))
+#define __raw_writeq(v,a) __writeq((v),(a))
#endif /* __WANT_IO_DEF */
diff --git a/include/asm-alpha/core_mcpcia.h b/include/asm-alpha/core_mcpcia.h
index bc3e44267..d35ad9190 100644
--- a/include/asm-alpha/core_mcpcia.h
+++ b/include/asm-alpha/core_mcpcia.h
@@ -438,32 +438,27 @@ __EXTERN_INLINE void mcpcia_writeq(unsigned long b, unsigned long addr)
#ifdef __WANT_IO_DEF
-#define __inb mcpcia_inb
-#define __inw mcpcia_inw
-#define __inl mcpcia_inl
-#define __outb mcpcia_outb
-#define __outw mcpcia_outw
-#define __outl mcpcia_outl
-#define __readb mcpcia_readb
-#define __readw mcpcia_readw
-#define __writeb mcpcia_writeb
-#define __writew mcpcia_writew
-#define __readl mcpcia_readl
-#define __readq mcpcia_readq
-#define __writel mcpcia_writel
-#define __writeq mcpcia_writeq
-#define __ioremap mcpcia_ioremap
-#define __is_ioaddr mcpcia_is_ioaddr
-
-# define inb(port) \
- (__builtin_constant_p((port))?__inb(port):_inb(port))
-# define outb(x, port) \
- (__builtin_constant_p((port))?__outb((x),(port)):_outb((x),(port)))
-
-#define __raw_readl(a) __readl((unsigned long)(a))
-#define __raw_readq(a) __readq((unsigned long)(a))
-#define __raw_writel(v,a) __writel((v),(unsigned long)(a))
-#define __raw_writeq(v,a) __writeq((v),(unsigned long)(a))
+#define __inb(p) mcpcia_inb((unsigned long)(p))
+#define __inw(p) mcpcia_inw((unsigned long)(p))
+#define __inl(p) mcpcia_inl((unsigned long)(p))
+#define __outb(x,p) mcpcia_outb((x),(unsigned long)(p))
+#define __outw(x,p) mcpcia_outw((x),(unsigned long)(p))
+#define __outl(x,p) mcpcia_outl((x),(unsigned long)(p))
+#define __readb(a) mcpcia_readb((unsigned long)(a))
+#define __readw(a) mcpcia_readw((unsigned long)(a))
+#define __readl(a) mcpcia_readl((unsigned long)(a))
+#define __readq(a) mcpcia_readq((unsigned long)(a))
+#define __writeb(x,a) mcpcia_writeb((x),(unsigned long)(a))
+#define __writew(x,a) mcpcia_writew((x),(unsigned long)(a))
+#define __writel(x,a) mcpcia_writel((x),(unsigned long)(a))
+#define __writeq(x,a) mcpcia_writeq((x),(unsigned long)(a))
+#define __ioremap(a) mcpcia_ioremap((unsigned long)(a))
+#define __is_ioaddr(a) mcpcia_is_ioaddr((unsigned long)(a))
+
+#define __raw_readl(a) __readl(a)
+#define __raw_readq(a) __readq(a)
+#define __raw_writel(v,a) __writel((v),(a))
+#define __raw_writeq(v,a) __writeq((v),(a))
#endif /* __WANT_IO_DEF */
diff --git a/include/asm-alpha/core_polaris.h b/include/asm-alpha/core_polaris.h
index 626b24a22..88a0573d2 100644
--- a/include/asm-alpha/core_polaris.h
+++ b/include/asm-alpha/core_polaris.h
@@ -170,38 +170,37 @@ __EXTERN_INLINE int polaris_is_ioaddr(unsigned long addr)
#ifdef __WANT_IO_DEF
-#define __inb polaris_inb
-#define __inw polaris_inw
-#define __inl polaris_inl
-#define __outb polaris_outb
-#define __outw polaris_outw
-#define __outl polaris_outl
-#define __readb polaris_readb
-#define __readw polaris_readw
-#define __writeb polaris_writeb
-#define __writew polaris_writew
-#define __readl polaris_readl
-#define __readq polaris_readq
-#define __writel polaris_writel
-#define __writeq polaris_writeq
-#define __ioremap polaris_ioremap
-#define __is_ioaddr polaris_is_ioaddr
-
-#define inb(port) __inb((port))
-#define inw(port) __inw((port))
-#define inl(port) __inl((port))
-#define outb(v, port) __outb((v),(port))
-#define outw(v, port) __outw((v),(port))
-#define outl(v, port) __outl((v),(port))
-
-#define __raw_readb(a) __readb((unsigned long)(a))
-#define __raw_readw(a) __readw((unsigned long)(a))
-#define __raw_readl(a) __readl((unsigned long)(a))
-#define __raw_readq(a) __readq((unsigned long)(a))
-#define __raw_writeb(v,a) __writeb((v),(unsigned long)(a))
-#define __raw_writew(v,a) __writew((v),(unsigned long)(a))
-#define __raw_writel(v,a) __writel((v),(unsigned long)(a))
-#define __raw_writeq(v,a) __writeq((v),(unsigned long)(a))
+#define __inb(p) polaris_inb((unsigned long)(p))
+#define __inw(p) polaris_inw((unsigned long)(p))
+#define __inl(p) polaris_inl((unsigned long)(p))
+#define __outb(x,p) polaris_outb((x),(unsigned long)(p))
+#define __outw(x,p) polaris_outw((x),(unsigned long)(p))
+#define __outl(x,p) polaris_outl((x),(unsigned long)(p))
+#define __readb(a) polaris_readb((unsigned long)(a))
+#define __readw(a) polaris_readw((unsigned long)(a))
+#define __readl(a) polaris_readl((unsigned long)(a))
+#define __readq(a) polaris_readq((unsigned long)(a))
+#define __writeb(x,a) polaris_writeb((x),(unsigned long)(a))
+#define __writew(x,a) polaris_writew((x),(unsigned long)(a))
+#define __writel(x,a) polaris_writel((x),(unsigned long)(a))
+#define __writeq(x,a) polaris_writeq((x),(unsigned long)(a))
+#define __ioremap(a) polaris_ioremap((unsigned long)(a))
+#define __is_ioaddr(a) polaris_is_ioaddr((unsigned long)(a))
+
+#define inb(p) __inb(p)
+#define inw(p) __inw(p)
+#define inl(p) __inl(p)
+#define outb(x,p) __outb((x),(p))
+#define outw(x,p) __outw((x),(p))
+#define outl(x,p) __outl((x),(p))
+#define __raw_readb(a) __readb(a)
+#define __raw_readw(a) __readw(a)
+#define __raw_readl(a) __readl(a)
+#define __raw_readq(a) __readq(a)
+#define __raw_writeb(v,a) __writeb((v),(a))
+#define __raw_writew(v,a) __writew((v),(a))
+#define __raw_writel(v,a) __writel((v),(a))
+#define __raw_writeq(v,a) __writeq((v),(a))
#endif /* __WANT_IO_DEF */
diff --git a/include/asm-alpha/core_pyxis.h b/include/asm-alpha/core_pyxis.h
deleted file mode 100644
index 18b523ef4..000000000
--- a/include/asm-alpha/core_pyxis.h
+++ /dev/null
@@ -1,444 +0,0 @@
-#ifndef __ALPHA_PYXIS__H__
-#define __ALPHA_PYXIS__H__
-
-#include <linux/types.h>
-#include <asm/compiler.h>
-
-/*
- * PYXIS is the internal name for a core logic chipset which provides
- * memory controller and PCI access for the 21164A chip based systems.
- *
- * This file is based on:
- *
- * Pyxis Chipset Spec
- * 14-Jun-96
- * Rev. X2.0
- *
- */
-
-/*------------------------------------------------------------------------**
-** **
-** I/O procedures **
-** **
-** inport[b|w|t|l], outport[b|w|t|l] 8:16:24:32 IO xfers **
-** inportbxt: 8 bits only **
-** inport: alias of inportw **
-** outport: alias of outportw **
-** **
-** inmem[b|w|t|l], outmem[b|w|t|l] 8:16:24:32 ISA memory xfers **
-** inmembxt: 8 bits only **
-** inmem: alias of inmemw **
-** outmem: alias of outmemw **
-** **
-**------------------------------------------------------------------------*/
-
-
-/* PYXIS ADDRESS BIT DEFINITIONS
- *
- * 3333 3333 3322 2222 2222 1111 1111 11
- * 9876 5432 1098 7654 3210 9876 5432 1098 7654 3210
- * ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
- * 1 000
- * ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
- * | |\|
- * | Byte Enable --+ |
- * | Transfer Length --+
- * +-- IO space, not cached
- *
- * Byte Transfer
- * Enable Length Transfer Byte Address
- * adr<6:5> adr<4:3> Length Enable Adder
- * ---------------------------------------------
- * 00 00 Byte 1110 0x000
- * 01 00 Byte 1101 0x020
- * 10 00 Byte 1011 0x040
- * 11 00 Byte 0111 0x060
- *
- * 00 01 Word 1100 0x008
- * 01 01 Word 1001 0x028 <= Not supported in this code.
- * 10 01 Word 0011 0x048
- *
- * 00 10 Tribyte 1000 0x010
- * 01 10 Tribyte 0001 0x030
- *
- * 10 11 Longword 0000 0x058
- *
- * Note that byte enables are asserted low.
- *
- */
-
-#define PYXIS_MEM_R1_MASK 0x1fffffff /* SPARSE Mem region 1 mask is 29 bits */
-#define PYXIS_MEM_R2_MASK 0x07ffffff /* SPARSE Mem region 2 mask is 27 bits */
-#define PYXIS_MEM_R3_MASK 0x03ffffff /* SPARSE Mem region 3 mask is 26 bits */
-
-/*
- * General Registers
- */
-#define PYXIS_REV (IDENT_ADDR + 0x8740000080UL)
-#define PYXIS_PCI_LAT (IDENT_ADDR + 0x87400000C0UL)
-#define PYXIS_CTRL (IDENT_ADDR + 0x8740000100UL)
-#define PYXIS_CTRL1 (IDENT_ADDR + 0x8740000140UL)
-#define PYXIS_FLASH_CTRL (IDENT_ADDR + 0x8740000200UL)
-
-#define PYXIS_HAE_MEM (IDENT_ADDR + 0x8740000400UL)
-#define PYXIS_HAE_IO (IDENT_ADDR + 0x8740000440UL)
-#define PYXIS_CFG (IDENT_ADDR + 0x8740000480UL)
-
-/*
- * Diagnostic Registers
- */
-#define PYXIS_DIAG (IDENT_ADDR + 0x8740002000UL)
-#define PYXIS_DIAG_CHECK (IDENT_ADDR + 0x8740003000UL)
-
-/*
- * Performance Monitor registers
- */
-#define PYXIS_PERF_MONITOR (IDENT_ADDR + 0x8740004000UL)
-#define PYXIS_PERF_CONTROL (IDENT_ADDR + 0x8740004040UL)
-
-/*
- * Error registers
- */
-#define PYXIS_ERR (IDENT_ADDR + 0x8740008200UL)
-#define PYXIS_STAT (IDENT_ADDR + 0x8740008240UL)
-#define PYXIS_ERR_MASK (IDENT_ADDR + 0x8740008280UL)
-#define PYXIS_SYN (IDENT_ADDR + 0x8740008300UL)
-#define PYXIS_ERR_DATA (IDENT_ADDR + 0x8740008308UL)
-
-#define PYXIS_MEAR (IDENT_ADDR + 0x8740008400UL)
-#define PYXIS_MESR (IDENT_ADDR + 0x8740008440UL)
-#define PYXIS_PCI_ERR0 (IDENT_ADDR + 0x8740008800UL)
-#define PYXIS_PCI_ERR1 (IDENT_ADDR + 0x8740008840UL)
-#define PYXIS_PCI_ERR2 (IDENT_ADDR + 0x8740008880UL)
-
-/*
- * PCI Address Translation Registers.
- */
-#define PYXIS_TBIA (IDENT_ADDR + 0x8760000100UL)
-
-#define PYXIS_W0_BASE (IDENT_ADDR + 0x8760000400UL)
-#define PYXIS_W0_MASK (IDENT_ADDR + 0x8760000440UL)
-#define PYXIS_T0_BASE (IDENT_ADDR + 0x8760000480UL)
-
-#define PYXIS_W1_BASE (IDENT_ADDR + 0x8760000500UL)
-#define PYXIS_W1_MASK (IDENT_ADDR + 0x8760000540UL)
-#define PYXIS_T1_BASE (IDENT_ADDR + 0x8760000580UL)
-
-#define PYXIS_W2_BASE (IDENT_ADDR + 0x8760000600UL)
-#define PYXIS_W2_MASK (IDENT_ADDR + 0x8760000640UL)
-#define PYXIS_T2_BASE (IDENT_ADDR + 0x8760000680UL)
-
-#define PYXIS_W3_BASE (IDENT_ADDR + 0x8760000700UL)
-#define PYXIS_W3_MASK (IDENT_ADDR + 0x8760000740UL)
-#define PYXIS_T3_BASE (IDENT_ADDR + 0x8760000780UL)
-
-/*
- * Memory Control registers
- */
-#define PYXIS_MCR (IDENT_ADDR + 0x8750000000UL)
-
-/*
- * Memory spaces:
- */
-#define PYXIS_IACK_SC (IDENT_ADDR + 0x8720000000UL)
-#define PYXIS_CONF (IDENT_ADDR + 0x8700000000UL)
-#define PYXIS_IO (IDENT_ADDR + 0x8580000000UL)
-#define PYXIS_SPARSE_MEM (IDENT_ADDR + 0x8000000000UL)
-#define PYXIS_SPARSE_MEM_R2 (IDENT_ADDR + 0x8400000000UL)
-#define PYXIS_SPARSE_MEM_R3 (IDENT_ADDR + 0x8500000000UL)
-#define PYXIS_DENSE_MEM (IDENT_ADDR + 0x8600000000UL)
-
-/*
- * Byte/Word PCI Memory Spaces:
- */
-#define PYXIS_BW_MEM (IDENT_ADDR + 0x8800000000UL)
-#define PYXIS_BW_IO (IDENT_ADDR + 0x8900000000UL)
-#define PYXIS_BW_CFG_0 (IDENT_ADDR + 0x8a00000000UL)
-#define PYXIS_BW_CFG_1 (IDENT_ADDR + 0x8b00000000UL)
-
-/*
- * Interrupt Control registers
- */
-#define PYXIS_INT_REQ (IDENT_ADDR + 0x87A0000000UL)
-#define PYXIS_INT_MASK (IDENT_ADDR + 0x87A0000040UL)
-#define PYXIS_INT_HILO (IDENT_ADDR + 0x87A00000C0UL)
-#define PYXIS_INT_ROUTE (IDENT_ADDR + 0x87A0000140UL)
-#define PYXIS_GPO (IDENT_ADDR + 0x87A0000180UL)
-#define PYXIS_INT_CNFG (IDENT_ADDR + 0x87A00001C0UL)
-#define PYXIS_RT_COUNT (IDENT_ADDR + 0x87A0000200UL)
-#define PYXIS_INT_TIME (IDENT_ADDR + 0x87A0000240UL)
-#define PYXIS_IIC_CTRL (IDENT_ADDR + 0x87A00002C0UL)
-#define PYXIS_RESET (IDENT_ADDR + 0x8780000900UL)
-
-/*
- * Bit definitions for I/O Controller status register 0:
- */
-#define PYXIS_STAT0_CMD 0xf
-#define PYXIS_STAT0_ERR (1<<4)
-#define PYXIS_STAT0_LOST (1<<5)
-#define PYXIS_STAT0_THIT (1<<6)
-#define PYXIS_STAT0_TREF (1<<7)
-#define PYXIS_STAT0_CODE_SHIFT 8
-#define PYXIS_STAT0_CODE_MASK 0x7
-#define PYXIS_STAT0_P_NBR_SHIFT 13
-#define PYXIS_STAT0_P_NBR_MASK 0x7ffff
-
-#define PYXIS_HAE_ADDRESS PYXIS_HAE_MEM
-
-/*
- * Data structure for handling PYXIS machine checks:
- */
-struct el_PYXIS_sysdata_mcheck {
-#if 0
- /* ??? Where did this come from. It appears to bear no
- relation to the pyxis logout written in the milo sources.
- Who knows what happens in the srm console... */
- u_long coma_gcr;
- u_long coma_edsr;
- u_long coma_ter;
- u_long coma_elar;
- u_long coma_ehar;
- u_long coma_ldlr;
- u_long coma_ldhr;
- u_long coma_base0;
- u_long coma_base1;
- u_long coma_base2;
- u_long coma_cnfg0;
- u_long coma_cnfg1;
- u_long coma_cnfg2;
- u_long epic_dcsr;
- u_long epic_pear;
- u_long epic_sear;
- u_long epic_tbr1;
- u_long epic_tbr2;
- u_long epic_pbr1;
- u_long epic_pbr2;
- u_long epic_pmr1;
- u_long epic_pmr2;
- u_long epic_harx1;
- u_long epic_harx2;
- u_long epic_pmlt;
- u_long epic_tag0;
- u_long epic_tag1;
- u_long epic_tag2;
- u_long epic_tag3;
- u_long epic_tag4;
- u_long epic_tag5;
- u_long epic_tag6;
- u_long epic_tag7;
- u_long epic_data0;
- u_long epic_data1;
- u_long epic_data2;
- u_long epic_data3;
- u_long epic_data4;
- u_long epic_data5;
- u_long epic_data6;
- u_long epic_data7;
-#else
- unsigned long cpu_err0;
- unsigned long cpu_err1;
- unsigned long cia_err;
- unsigned long cia_stat;
- unsigned long err_mask;
- unsigned long cia_syn;
- unsigned long mem_err0;
- unsigned long mem_err1;
- unsigned long pci_err0;
- unsigned long pci_err1;
- unsigned long pci_err2;
-#endif
-};
-
-
-#ifdef __KERNEL__
-
-#ifndef __EXTERN_INLINE
-#define __EXTERN_INLINE extern inline
-#define __IO_EXTERN_INLINE
-#endif
-
-/*
- * I/O functions:
- *
- * PYXIS, the 21174 PCI/memory support chipset for the EV56 (21164A)
- * and PCA56 (21164PC) processors, can use either a sparse address
- * mapping scheme, or the so-called byte-word PCI address space, to
- * get at PCI memory and I/O.
- */
-
-#define vucp volatile unsigned char *
-#define vusp volatile unsigned short *
-#define vip volatile int *
-#define vuip volatile unsigned int *
-#define vulp volatile unsigned long *
-
-__EXTERN_INLINE unsigned int pyxis_inb(unsigned long addr)
-{
- /* ??? I wish I could get rid of this. But there's no ioremap
- equivalent for I/O space. PCI I/O can be forced into the
- PYXIS I/O region, but that doesn't take care of legacy ISA crap. */
-
- return __kernel_ldbu(*(vucp)(addr+PYXIS_BW_IO));
-}
-
-__EXTERN_INLINE void pyxis_outb(unsigned char b, unsigned long addr)
-{
- __kernel_stb(b, *(vucp)(addr+PYXIS_BW_IO));
- mb();
-}
-
-__EXTERN_INLINE unsigned int pyxis_inw(unsigned long addr)
-{
- return __kernel_ldwu(*(vusp)(addr+PYXIS_BW_IO));
-}
-
-__EXTERN_INLINE void pyxis_outw(unsigned short b, unsigned long addr)
-{
- __kernel_stw(b, *(vusp)(addr+PYXIS_BW_IO));
- mb();
-}
-
-__EXTERN_INLINE unsigned int pyxis_inl(unsigned long addr)
-{
- return *(vuip)(addr+PYXIS_BW_IO);
-}
-
-__EXTERN_INLINE void pyxis_outl(unsigned int b, unsigned long addr)
-{
- *(vuip)(addr+PYXIS_BW_IO) = b;
- mb();
-}
-
-
-/*
- * Memory functions. 64-bit and 32-bit accesses are done through
- * dense memory space, everything else through sparse space.
- *
- * For reading and writing 8 and 16 bit quantities we need to
- * go through one of the three sparse address mapping regions
- * and use the HAE_MEM CSR to provide some bits of the address.
- * The following few routines use only sparse address region 1
- * which gives 1Gbyte of accessible space which relates exactly
- * to the amount of PCI memory mapping *into* system address space.
- * See p 6-17 of the specification but it looks something like this:
- *
- * 21164 Address:
- *
- * 3 2 1
- * 9876543210987654321098765432109876543210
- * 1ZZZZ0.PCI.QW.Address............BBLL
- *
- * ZZ = SBZ
- * BB = Byte offset
- * LL = Transfer length
- *
- * PCI Address:
- *
- * 3 2 1
- * 10987654321098765432109876543210
- * HHH....PCI.QW.Address........ 00
- *
- * HHH = 31:29 HAE_MEM CSR
- *
- */
-
-__EXTERN_INLINE unsigned long pyxis_readb(unsigned long addr)
-{
- return __kernel_ldbu(*(vucp)addr);
-}
-
-__EXTERN_INLINE unsigned long pyxis_readw(unsigned long addr)
-{
- return __kernel_ldwu(*(vusp)addr);
-}
-
-__EXTERN_INLINE unsigned long pyxis_readl(unsigned long addr)
-{
- return *(vuip)addr;
-}
-
-__EXTERN_INLINE unsigned long pyxis_readq(unsigned long addr)
-{
- return *(vulp)addr;
-}
-
-__EXTERN_INLINE void pyxis_writeb(unsigned char b, unsigned long addr)
-{
- __kernel_stb(b, *(vucp)addr);
-}
-
-__EXTERN_INLINE void pyxis_writew(unsigned short b, unsigned long addr)
-{
- __kernel_stw(b, *(vusp)addr);
-}
-
-__EXTERN_INLINE void pyxis_writel(unsigned int b, unsigned long addr)
-{
- *(vuip)addr = b;
-}
-
-__EXTERN_INLINE void pyxis_writeq(unsigned long b, unsigned long addr)
-{
- *(vulp)addr = b;
-}
-
-__EXTERN_INLINE unsigned long pyxis_ioremap(unsigned long addr)
-{
- return addr + PYXIS_BW_MEM;
-}
-
-__EXTERN_INLINE int pyxis_is_ioaddr(unsigned long addr)
-{
- return addr >= IDENT_ADDR + 0x8740000000UL;
-}
-
-#undef vucp
-#undef vusp
-#undef vip
-#undef vuip
-#undef vulp
-
-#ifdef __WANT_IO_DEF
-
-#define __inb pyxis_inb
-#define __inw pyxis_inw
-#define __inl pyxis_inl
-#define __outb pyxis_outb
-#define __outw pyxis_outw
-#define __outl pyxis_outl
-#define __readb pyxis_readb
-#define __readw pyxis_readw
-#define __writeb pyxis_writeb
-#define __writew pyxis_writew
-#define __readl pyxis_readl
-#define __readq pyxis_readq
-#define __writel pyxis_writel
-#define __writeq pyxis_writeq
-#define __ioremap pyxis_ioremap
-#define __is_ioaddr pyxis_is_ioaddr
-
-#define inb(port) __inb((port))
-#define inw(port) __inw((port))
-#define inl(port) __inl((port))
-#define outb(x, port) __outb((x),(port))
-#define outw(x, port) __outw((x),(port))
-#define outl(x, port) __outl((x),(port))
-#define __raw_readb(addr) __readb((addr))
-#define __raw_readw(addr) __readw((addr))
-#define __raw_writeb(b, addr) __writeb((b),(addr))
-#define __raw_writew(b, addr) __writew((b),(addr))
-#define __raw_readl(a) __readl((unsigned long)(a))
-#define __raw_readq(a) __readq((unsigned long)(a))
-#define __raw_writel(v,a) __writel((v),(unsigned long)(a))
-#define __raw_writeq(v,a) __writeq((v),(unsigned long)(a))
-
-#endif /* __WANT_IO_DEF */
-
-#ifdef __IO_EXTERN_INLINE
-#undef __EXTERN_INLINE
-#undef __IO_EXTERN_INLINE
-#endif
-
-#endif /* __KERNEL__ */
-
-#endif /* __ALPHA_PYXIS__H__ */
diff --git a/include/asm-alpha/core_t2.h b/include/asm-alpha/core_t2.h
index d11506cd5..4eab7fa64 100644
--- a/include/asm-alpha/core_t2.h
+++ b/include/asm-alpha/core_t2.h
@@ -516,27 +516,22 @@ __EXTERN_INLINE int t2_is_ioaddr(unsigned long addr)
#ifdef __WANT_IO_DEF
-#define __inb t2_inb
-#define __inw t2_inw
-#define __inl t2_inl
-#define __outb t2_outb
-#define __outw t2_outw
-#define __outl t2_outl
-#define __readb t2_readb
-#define __readw t2_readw
-#define __readl t2_readl
-#define __readq t2_readq
-#define __writeb t2_writeb
-#define __writew t2_writew
-#define __writel t2_writel
-#define __writeq t2_writeq
-#define __ioremap t2_ioremap
-#define __is_ioaddr t2_is_ioaddr
-
-#define inb(port) \
- (__builtin_constant_p((port))?__inb(port):_inb(port))
-#define outb(x, port) \
- (__builtin_constant_p((port))?__outb((x),(port)):_outb((x),(port)))
+#define __inb(p) t2_inb((unsigned long)(p))
+#define __inw(p) t2_inw((unsigned long)(p))
+#define __inl(p) t2_inl((unsigned long)(p))
+#define __outb(x,p) t2_outb((x),(unsigned long)(p))
+#define __outw(x,p) t2_outw((x),(unsigned long)(p))
+#define __outl(x,p) t2_outl((x),(unsigned long)(p))
+#define __readb(a) t2_readb((unsigned long)(a))
+#define __readw(a) t2_readw((unsigned long)(a))
+#define __readl(a) t2_readl((unsigned long)(a))
+#define __readq(a) t2_readq((unsigned long)(a))
+#define __writeb(x,a) t2_writeb((x),(unsigned long)(a))
+#define __writew(x,a) t2_writew((x),(unsigned long)(a))
+#define __writel(x,a) t2_writel((x),(unsigned long)(a))
+#define __writeq(x,a) t2_writeq((x),(unsigned long)(a))
+#define __ioremap(a) t2_ioremap((unsigned long)(a))
+#define __is_ioaddr(a) t2_is_ioaddr((unsigned long)(a))
#endif /* __WANT_IO_DEF */
diff --git a/include/asm-alpha/core_tsunami.h b/include/asm-alpha/core_tsunami.h
index ffbcc5c34..5d0d7cf7a 100644
--- a/include/asm-alpha/core_tsunami.h
+++ b/include/asm-alpha/core_tsunami.h
@@ -405,38 +405,37 @@ __EXTERN_INLINE void tsunami_writeq(unsigned long b, unsigned long addr)
#ifdef __WANT_IO_DEF
-#define __inb tsunami_inb
-#define __inw tsunami_inw
-#define __inl tsunami_inl
-#define __outb tsunami_outb
-#define __outw tsunami_outw
-#define __outl tsunami_outl
-#define __readb tsunami_readb
-#define __readw tsunami_readw
-#define __writeb tsunami_writeb
-#define __writew tsunami_writew
-#define __readl tsunami_readl
-#define __readq tsunami_readq
-#define __writel tsunami_writel
-#define __writeq tsunami_writeq
-#define __ioremap tsunami_ioremap
-#define __is_ioaddr tsunami_is_ioaddr
-
-#define inb(port) __inb((port))
-#define inw(port) __inw((port))
-#define inl(port) __inl((port))
-#define outb(v, port) __outb((v),(port))
-#define outw(v, port) __outw((v),(port))
-#define outl(v, port) __outl((v),(port))
-
-#define __raw_readb(a) __readb((unsigned long)(a))
-#define __raw_readw(a) __readw((unsigned long)(a))
-#define __raw_readl(a) __readl((unsigned long)(a))
-#define __raw_readq(a) __readq((unsigned long)(a))
-#define __raw_writeb(v,a) __writeb((v),(unsigned long)(a))
-#define __raw_writew(v,a) __writew((v),(unsigned long)(a))
-#define __raw_writel(v,a) __writel((v),(unsigned long)(a))
-#define __raw_writeq(v,a) __writeq((v),(unsigned long)(a))
+#define __inb(p) tsunami_inb((unsigned long)(p))
+#define __inw(p) tsunami_inw((unsigned long)(p))
+#define __inl(p) tsunami_inl((unsigned long)(p))
+#define __outb(x,p) tsunami_outb((x),(unsigned long)(p))
+#define __outw(x,p) tsunami_outw((x),(unsigned long)(p))
+#define __outl(x,p) tsunami_outl((x),(unsigned long)(p))
+#define __readb(a) tsunami_readb((unsigned long)(a))
+#define __readw(a) tsunami_readw((unsigned long)(a))
+#define __readl(a) tsunami_readl((unsigned long)(a))
+#define __readq(a) tsunami_readq((unsigned long)(a))
+#define __writeb(x,a) tsunami_writeb((x),(unsigned long)(a))
+#define __writew(x,a) tsunami_writew((x),(unsigned long)(a))
+#define __writel(x,a) tsunami_writel((x),(unsigned long)(a))
+#define __writeq(x,a) tsunami_writeq((x),(unsigned long)(a))
+#define __ioremap(a) tsunami_ioremap((unsigned long)(a))
+#define __is_ioaddr(a) tsunami_is_ioaddr((unsigned long)(a))
+
+#define inb(p) __inb(p)
+#define inw(p) __inw(p)
+#define inl(p) __inl(p)
+#define outb(x,p) __outb((x),(p))
+#define outw(x,p) __outw((x),(p))
+#define outl(x,p) __outl((x),(p))
+#define __raw_readb(a) __readb(a)
+#define __raw_readw(a) __readw(a)
+#define __raw_readl(a) __readl(a)
+#define __raw_readq(a) __readq(a)
+#define __raw_writeb(v,a) __writeb((v),(a))
+#define __raw_writew(v,a) __writew((v),(a))
+#define __raw_writel(v,a) __writel((v),(a))
+#define __raw_writeq(v,a) __writeq((v),(a))
#endif /* __WANT_IO_DEF */
diff --git a/include/asm-alpha/delay.h b/include/asm-alpha/delay.h
index 8db687bd4..84b6d1eb5 100644
--- a/include/asm-alpha/delay.h
+++ b/include/asm-alpha/delay.h
@@ -4,74 +4,43 @@
#include <asm/smp.h>
/*
- * Copyright (C) 1993 Linus Torvalds
+ * Copyright (C) 1993, 2000 Linus Torvalds
*
* Delay routines, using a pre-computed "loops_per_second" value.
*/
-/* We can make the delay loop inline, but we have to be very careful wrt
- scheduling for ev6 machines, so that we keep a consistent number of
- iterations for all invocations. */
-
-extern __inline__ void
-__delay(unsigned long loops)
-{
- __asm__ __volatile__(
- ".align 4\n"
- "1: subq %0,1,%0\n"
- " bge %0,1b\n"
- " nop"
- : "=r" (loops) : "0"(loops));
-}
-
/*
- * division by multiplication: you don't have to worry about
- * loss of precision.
- *
- * Use only for very small delays ( < 1 msec). Should probably use a
- * lookup table, really, as the multiplications take much too long with
- * short delays. This is a "reasonable" implementation, though (and the
- * first constant multiplications gets optimized away if the delay is
- * a constant).
+ * Use only for very small delays (< 1 msec).
*
- * Optimize small constants further by exposing the second multiplication
- * to the compiler. In addition, mulq is 2 cycles faster than umulh.
+ * The active part of our cycle counter is only 32-bits wide, and
+ * we're treating the difference between two marks as signed. On
+ * a 1GHz box, that's about 2 seconds.
*/
extern __inline__ void
-__udelay(unsigned long usecs, unsigned long lps)
+__delay(int loops)
{
- /* compute (usecs * 2**64 / 10**6) * loops_per_sec / 2**64 */
-
- usecs *= 0x000010c6f7a0b5edUL; /* 2**64 / 1000000 */
- __asm__("umulh %1,%2,%0" :"=r" (usecs) :"r" (usecs),"r" (lps));
- __delay(usecs);
+ int tmp;
+ __asm__ __volatile__(
+ " rpcc %0\n"
+ " addl %1,%0,%1\n"
+ "1: rpcc %0\n"
+ " subl %1,%0,%0\n"
+ " bgt %0,1b"
+ : "=&r" (tmp), "=r" (loops) : "1"(loops));
}
extern __inline__ void
-__small_const_udelay(unsigned long usecs, unsigned long lps)
+__udelay(unsigned long usecs, unsigned long lps)
{
- /* compute (usecs * 2**32 / 10**6) * loops_per_sec / 2**32 */
-
- usecs *= 0x10c6; /* 2^32 / 10^6 */
- usecs *= lps;
- usecs >>= 32;
- __delay(usecs);
+ usecs *= ((1UL << 32) / 1000000) * lps;
+ __delay((long)usecs >> 32);
}
#ifdef __SMP__
-#define udelay(usecs) \
- (__builtin_constant_p(usecs) && usecs < 0x100000000UL \
- ? __small_const_udelay(usecs, \
- cpu_data[smp_processor_id()].loops_per_sec) \
- : __udelay(usecs, \
- cpu_data[smp_processor_id()].loops_per_sec))
+#define udelay(u) __udelay((u), cpu_data[smp_processor_id()].loops_per_sec)
#else
-#define udelay(usecs) \
- (__builtin_constant_p(usecs) && usecs < 0x100000000UL \
- ? __small_const_udelay(usecs, loops_per_sec) \
- : __udelay(usecs, loops_per_sec))
+#define udelay(u) __udelay((u), loops_per_sec)
#endif
-
#endif /* defined(__ALPHA_DELAY_H) */
diff --git a/include/asm-alpha/io.h b/include/asm-alpha/io.h
index 59eeb9545..0e532f6e5 100644
--- a/include/asm-alpha/io.h
+++ b/include/asm-alpha/io.h
@@ -112,12 +112,12 @@ extern void _sethae (unsigned long addr); /* cached version */
/* In a generic kernel, we always go through the machine vector. */
-# define __inb alpha_mv.mv_inb
-# define __inw alpha_mv.mv_inw
-# define __inl alpha_mv.mv_inl
-# define __outb alpha_mv.mv_outb
-# define __outw alpha_mv.mv_outw
-# define __outl alpha_mv.mv_outl
+# define __inb(p) alpha_mv.mv_inb((unsigned long)(p))
+# define __inw(p) alpha_mv.mv_inw((unsigned long)(p))
+# define __inl(p) alpha_mv.mv_inl((unsigned long)(p))
+# define __outb(x,p) alpha_mv.mv_outb((x),(unsigned long)(p))
+# define __outw(x,p) alpha_mv.mv_outw((x),(unsigned long)(p))
+# define __outl(x,p) alpha_mv.mv_outl((x),(unsigned long)(p))
# define __readb(a) alpha_mv.mv_readb((unsigned long)(a))
# define __readw(a) alpha_mv.mv_readw((unsigned long)(a))
@@ -128,8 +128,8 @@ extern void _sethae (unsigned long addr); /* cached version */
# define __writel(v,a) alpha_mv.mv_writel((v),(unsigned long)(a))
# define __writeq(v,a) alpha_mv.mv_writeq((v),(unsigned long)(a))
-# define __ioremap(a) alpha_mv.mv_ioremap(a)
-# define __is_ioaddr(a) alpha_mv.mv_is_ioaddr(a)
+# define __ioremap(a) alpha_mv.mv_ioremap((unsigned long)(a))
+# define __is_ioaddr(a) alpha_mv.mv_is_ioaddr((unsigned long)(a))
# define inb __inb
# define inw __inw
@@ -166,8 +166,6 @@ extern void _sethae (unsigned long addr); /* cached version */
# include <asm/core_mcpcia.h>
#elif defined(CONFIG_ALPHA_POLARIS)
# include <asm/core_polaris.h>
-#elif defined(CONFIG_ALPHA_PYXIS)
-# include <asm/core_pyxis.h>
#elif defined(CONFIG_ALPHA_T2)
# include <asm/core_t2.h>
#elif defined(CONFIG_ALPHA_TSUNAMI)
@@ -212,13 +210,13 @@ extern void _writeq(unsigned long b, unsigned long addr);
* redefined by userlevel programs.
*/
#ifndef inb
-# define inb(p) _inb((p))
+# define inb(p) _inb(p)
#endif
#ifndef inw
-# define inw(p) _inw((p))
+# define inw(p) _inw(p)
#endif
#ifndef inl
-# define inl(p) _inl((p))
+# define inl(p) _inl(p)
#endif
#ifndef outb
# define outb(b,p) _outb((b),(p))
@@ -256,12 +254,12 @@ extern void _writeq(unsigned long b, unsigned long addr);
/* Userspace declarations. */
-extern unsigned int inb (unsigned long port);
-extern unsigned int inw (unsigned long port);
-extern unsigned int inl (unsigned long port);
-extern void outb (unsigned char b,unsigned long port);
-extern void outw (unsigned short w,unsigned long port);
-extern void outl (unsigned int l,unsigned long port);
+extern unsigned int inb(unsigned long port);
+extern unsigned int inw(unsigned long port);
+extern unsigned int inl(unsigned long port);
+extern void outb(unsigned char b,unsigned long port);
+extern void outw(unsigned short w,unsigned long port);
+extern void outl(unsigned int l,unsigned long port);
extern unsigned long readb(unsigned long addr);
extern unsigned long readw(unsigned long addr);
extern unsigned long readl(unsigned long addr);
diff --git a/include/asm-alpha/mman.h b/include/asm-alpha/mman.h
index ddfe6bda4..3e9b58d98 100644
--- a/include/asm-alpha/mman.h
+++ b/include/asm-alpha/mman.h
@@ -31,6 +31,13 @@
#define MCL_CURRENT 8192 /* lock all currently mapped pages */
#define MCL_FUTURE 16384 /* lock all additions to address space */
+#define MADV_NORMAL 0 /* no further special treatment */
+#define MADV_RANDOM 1 /* expect random page references */
+#define MADV_SEQUENTIAL 2 /* expect sequential page references */
+#define MADV_WILLNEED 3 /* will need these pages */
+#define MADV_SPACEAVAIL 5 /* ensure resources are available */
+#define MADV_DONTNEED 6 /* dont need these pages */
+
/* compatibility flags */
#define MAP_ANON MAP_ANONYMOUS
#define MAP_FILE 0
diff --git a/include/asm-alpha/mmu_context.h b/include/asm-alpha/mmu_context.h
index 6b268f4e6..c42c1ae54 100644
--- a/include/asm-alpha/mmu_context.h
+++ b/include/asm-alpha/mmu_context.h
@@ -22,11 +22,6 @@
#include <asm/io.h>
#endif
-static inline void
-enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk, unsigned cpu)
-{
-}
-
extern inline unsigned long
__reload_thread(struct thread_struct *pcb)
{
@@ -223,6 +218,12 @@ destroy_context(struct mm_struct *mm)
/* Nothing to do. */
}
+static inline void
+enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk, unsigned cpu)
+{
+ tsk->thread.ptbr = ((unsigned long)mm->pgd - IDENT_ADDR) >> PAGE_SHIFT;
+}
+
#ifdef __MMU_EXTERN_INLINE
#undef __EXTERN_INLINE
#undef __MMU_EXTERN_INLINE
diff --git a/include/asm-alpha/page.h b/include/asm-alpha/page.h
index d1dde606e..62a72144f 100644
--- a/include/asm-alpha/page.h
+++ b/include/asm-alpha/page.h
@@ -38,6 +38,8 @@ static inline void clear_page(void * page)
} while (count);
}
+#define clear_user_page(page, vaddr) clear_page(page)
+
static inline void copy_page(void * _to, void * _from)
{
unsigned long count = PAGE_SIZE/64;
@@ -68,6 +70,8 @@ static inline void copy_page(void * _to, void * _from)
} while (count);
}
+#define copy_user_page(to, from, vaddr) copy_page(to, from)
+
#ifdef STRICT_MM_TYPECHECKS
/*
* These are used to make use of C type-checking..
diff --git a/include/asm-alpha/pci.h b/include/asm-alpha/pci.h
index 1a68d43a3..8f89479b5 100644
--- a/include/asm-alpha/pci.h
+++ b/include/asm-alpha/pci.h
@@ -39,6 +39,11 @@ struct pci_controler {
#define PCIBIOS_MIN_IO alpha_mv.min_io_address
#define PCIBIOS_MIN_MEM alpha_mv.min_mem_address
+extern inline void pcibios_set_master(struct pci_dev *dev)
+{
+ /* No special bus mastering setup handling */
+}
+
/* IOMMU controls. */
/* Allocate and map kernel buffer using consistant mode DMA for PCI
diff --git a/include/asm-alpha/smp.h b/include/asm-alpha/smp.h
index 12ebc5087..b05076263 100644
--- a/include/asm-alpha/smp.h
+++ b/include/asm-alpha/smp.h
@@ -30,7 +30,6 @@ struct cpuinfo_alpha {
unsigned long pgtable_cache_sz;
unsigned long ipi_count;
unsigned long irq_attempt[NR_IRQS];
- unsigned long smp_local_irq_count;
unsigned long prof_multiplier;
unsigned long prof_counter;
int irq_count, bh_count;
diff --git a/include/asm-alpha/unistd.h b/include/asm-alpha/unistd.h
index e80f0ae3b..93ed9ed85 100644
--- a/include/asm-alpha/unistd.h
+++ b/include/asm-alpha/unistd.h
@@ -311,6 +311,8 @@
#define __NR_setresgid 371
#define __NR_getresgid 372
#define __NR_dipc 373
+#define __NR_pivot_root 374
+#define __NR_mincore 375
#if defined(__LIBRARY__) && defined(__GNUC__)
diff --git a/include/asm-alpha/vga.h b/include/asm-alpha/vga.h
index 44b6abcc0..4fc557ca1 100644
--- a/include/asm-alpha/vga.h
+++ b/include/asm-alpha/vga.h
@@ -14,7 +14,7 @@
#define VT_BUF_HAVE_MEMCPYW
#define VT_BUF_HAVE_MEMCPYF
-extern inline void scr_writew(u16 val, u16 *addr)
+extern inline void scr_writew(u16 val, volatile u16 *addr)
{
if (__is_ioaddr((unsigned long) addr))
__raw_writew(val, (unsigned long) addr);
@@ -22,7 +22,7 @@ extern inline void scr_writew(u16 val, u16 *addr)
*addr = val;
}
-extern inline u16 scr_readw(const u16 *addr)
+extern inline u16 scr_readw(volatile const u16 *addr)
{
if (__is_ioaddr((unsigned long) addr))
return __raw_readw((unsigned long) addr);
diff --git a/include/asm-arm/arch-cl7500/system.h b/include/asm-arm/arch-cl7500/system.h
index 6daffc785..a8a0ee518 100644
--- a/include/asm-arm/arch-cl7500/system.h
+++ b/include/asm-arm/arch-cl7500/system.h
@@ -8,18 +8,14 @@
#include <asm/iomd.h>
-#define arch_do_idle() \
+#define arch_do_idle() \
outb(0, IOMD_SUSMODE)
-#define arch_reset(mode) { \
- outb (0, IOMD_ROMCR0); \
- cli(); \
- __asm__ __volatile__("msr spsr, r1;" \
- "mcr p15, 0, %0, c1, c0, 0;" \
- "movs pc, #0" \
- : \
- : "r" (cpu_reset())); \
- }
+#define arch_reset(mode) \
+ do { \
+ outb (0, IOMD_ROMCR0); \
+ cpu_reset(0); \
+ } while (0);
#define arch_power_off() do { } while (0)
diff --git a/include/asm-arm/arch-ebsa110/system.h b/include/asm-arm/arch-ebsa110/system.h
index 39f4c84a4..2ec712d06 100644
--- a/include/asm-arm/arch-ebsa110/system.h
+++ b/include/asm-arm/arch-ebsa110/system.h
@@ -8,15 +8,6 @@
#define arch_do_idle() cpu_do_idle()
#define arch_power_off() do { } while (0)
-
-extern __inline__ void arch_reset(char mode)
-{
- if (mode == 's') {
- __asm__ volatile(
- "mcr p15, 0, %0, c1, c0, 0 @ MMU off
- mov pc, #0x80000000 @ jump to flash"
- : : "r" (cpu_reset()) : "cc");
- }
-}
+#define arch_reset(mode) cpu_reset(0x80000000)
#endif
diff --git a/include/asm-arm/arch-ebsa285/system.h b/include/asm-arm/arch-ebsa285/system.h
index ace6afbd6..46e60a084 100644
--- a/include/asm-arm/arch-ebsa285/system.h
+++ b/include/asm-arm/arch-ebsa285/system.h
@@ -14,10 +14,10 @@
extern __inline__ void arch_reset(char mode)
{
if (mode == 's') {
- __asm__ volatile (
- "mcr p15, 0, %0, c1, c0, 0 @ MMU off
- mov pc, #0x41000000 @ jump to ROM" : :
- "r" (cpu_reset()) : "cc");
+ /*
+ * Jump into the ROM
+ */
+ cpu_reset(0x41000000);
} else {
if (machine_is_netwinder()) {
/* open up the SuperIO chip
diff --git a/include/asm-arm/arch-rpc/system.h b/include/asm-arm/arch-rpc/system.h
index c5100fd1a..1282c9505 100644
--- a/include/asm-arm/arch-rpc/system.h
+++ b/include/asm-arm/arch-rpc/system.h
@@ -18,8 +18,8 @@ extern __inline__ void arch_reset(char mode)
outb(0, IOMD_ROMCR0);
- __asm__ __volatile__(
- "mcr p15, 0, %0, c1, c0, 0\n\t"
- "mov pc, #0"
- : : "r" (cpu_reset()));
+ /*
+ * Jump into the ROM
+ */
+ cpu_reset(0);
}
diff --git a/include/asm-arm/arch-sa1100/system.h b/include/asm-arm/arch-sa1100/system.h
index 3ec5c9074..4e883ee6a 100644
--- a/include/asm-arm/arch-sa1100/system.h
+++ b/include/asm-arm/arch-sa1100/system.h
@@ -16,11 +16,7 @@
#else
-#define arch_reset(x) { \
- __asm__ volatile ( \
-" mcr p15, 0, %0, c1, c0 @ MMU off\n" \
-" mov pc, #0\n" : : "r" (cpu_reset()) : "cc"); \
- }
+#define arch_reset(x) cpu_reset(0)
#endif
diff --git a/include/asm-arm/cpu-multi32.h b/include/asm-arm/cpu-multi32.h
index 500e473ca..b959f4994 100644
--- a/include/asm-arm/cpu-multi32.h
+++ b/include/asm-arm/cpu-multi32.h
@@ -75,7 +75,7 @@ extern struct processor {
/*
* Special stuff for a reset
*/
- unsigned long (*reset)(void);
+ volatile void (*reset)(unsigned long addr);
/*
* flush an icached page
*/
@@ -123,7 +123,7 @@ extern const struct processor sa110_processor_functions;
#define cpu_set_pgd(pgd) processor._set_pgd(pgd)
#define cpu_set_pmd(pmdp, pmd) processor._set_pmd(pmdp, pmd)
#define cpu_set_pte(ptep, pte) processor._set_pte(ptep, pte)
-#define cpu_reset() processor.reset()
+#define cpu_reset(addr) processor.reset(addr)
#define cpu_flush_icache_area(start,end) processor._flush_icache_area(start,end)
#define cpu_cache_wback_area(start,end) processor._cache_wback_area(start,end)
#define cpu_cache_purge_area(start,end) processor._cache_purge_area(start,end)
diff --git a/include/asm-arm/cpu-single.h b/include/asm-arm/cpu-single.h
index 0e992a564..74cffcdb5 100644
--- a/include/asm-arm/cpu-single.h
+++ b/include/asm-arm/cpu-single.h
@@ -62,7 +62,7 @@ extern void cpu_flush_tlb_page(unsigned long address, int flags);
extern void cpu_set_pgd(unsigned long pgd_phys);
extern void cpu_set_pmd(pmd_t *pmdp, pmd_t pmd);
extern void cpu_set_pte(pte_t *ptep, pte_t pte);
-extern unsigned long cpu_reset(void);
+extern volatile void cpu_reset(unsigned long addr);
extern void cpu_flush_icache_area(unsigned long start, unsigned long size);
extern void cpu_cache_wback_area(unsigned long start, unsigned long end);
extern void cpu_cache_purge_area(unsigned long start, unsigned long end);
diff --git a/include/asm-arm/mman.h b/include/asm-arm/mman.h
index 9fa76d215..5b6540779 100644
--- a/include/asm-arm/mman.h
+++ b/include/asm-arm/mman.h
@@ -25,6 +25,12 @@
#define MCL_CURRENT 1 /* lock all current mappings */
#define MCL_FUTURE 2 /* lock all future mappings */
+#define MADV_NORMAL 0x0 /* default page-in behavior */
+#define MADV_RANDOM 0x1 /* page-in minimum required */
+#define MADV_SEQUENTIAL 0x2 /* read-ahead aggressively */
+#define MADV_WILLNEED 0x3 /* pre-fault pages */
+#define MADV_DONTNEED 0x4 /* discard these pages */
+
/* compatibility flags */
#define MAP_ANON MAP_ANONYMOUS
#define MAP_FILE 0
diff --git a/include/asm-arm/page.h b/include/asm-arm/page.h
index e33597ff6..e1de021c4 100644
--- a/include/asm-arm/page.h
+++ b/include/asm-arm/page.h
@@ -14,6 +14,9 @@
#define clear_page(page) memzero((void *)(page), PAGE_SIZE)
extern void copy_page(void *to, void *from);
+#define clear_user_page(page, vaddr) clear_page(page)
+#define copy_user_page(to, from, vaddr) copy_page(to, from)
+
#ifdef STRICT_MM_TYPECHECKS
/*
* These are used to make use of C type-checking..
diff --git a/include/asm-arm/pci.h b/include/asm-arm/pci.h
index 542487cf6..c835f4c17 100644
--- a/include/asm-arm/pci.h
+++ b/include/asm-arm/pci.h
@@ -1,12 +1,17 @@
#ifndef ASMARM_PCI_H
#define ASMARM_PCI_H
+#ifdef __KERNEL__
+
#define pcibios_assign_all_busses() 0
#define PCIBIOS_MIN_IO 0x8000
#define PCIBIOS_MIN_MEM 0x40000000
-#ifdef __KERNEL__
+extern inline void pcibios_set_master(struct pci_dev *dev)
+{
+ /* No special bus mastering setup handling */
+}
#include <asm/scatterlist.h>
#include <asm/io.h>
diff --git a/include/asm-arm/system.h b/include/asm-arm/system.h
index a43117330..a33b9e08b 100644
--- a/include/asm-arm/system.h
+++ b/include/asm-arm/system.h
@@ -96,7 +96,7 @@ extern unsigned int __machine_arch_type;
# define machine_is_netwinder() (0)
#endif
-#ifdef CONFIG_CATS
+#ifdef CONFIG_ARCH_CATS
# ifdef machine_arch_type
# undef machine_arch_type
# define machine_arch_type __machine_arch_type
@@ -180,7 +180,7 @@ extern unsigned int __machine_arch_type;
# define machine_is_sa1100() (0)
#endif
-#ifdef CONFIG_PERSONAL_SERVER
+#ifdef CONFIG_ARCH_PERSONAL_SERVER
# ifdef machine_arch_type
# undef machine_arch_type
# define machine_arch_type __machine_arch_type
diff --git a/include/asm-i386/hw_irq.h b/include/asm-i386/hw_irq.h
index eb5d9124e..b371bd79f 100644
--- a/include/asm-i386/hw_irq.h
+++ b/include/asm-i386/hw_irq.h
@@ -189,6 +189,9 @@ extern unsigned long prof_shift;
*/
static inline void x86_do_profile (unsigned long eip)
{
+ if (!prof_buffer)
+ return;
+
/*
* Only measure the CPUs specified by /proc/irq/prof_cpu_mask.
* (default is all CPUs.)
@@ -196,18 +199,16 @@ static inline void x86_do_profile (unsigned long eip)
if (!((1<<smp_processor_id()) & prof_cpu_mask))
return;
- if (prof_buffer) {
- eip -= (unsigned long) &_stext;
- eip >>= prof_shift;
- /*
- * Don't ignore out-of-bounds EIP values silently,
- * put them into the last histogram slot, so if
- * present, they will show up as a sharp peak.
- */
- if (eip > prof_len-1)
- eip = prof_len-1;
- atomic_inc((atomic_t *)&prof_buffer[eip]);
- }
+ eip -= (unsigned long) &_stext;
+ eip >>= prof_shift;
+ /*
+ * Don't ignore out-of-bounds EIP values silently,
+ * put them into the last histogram slot, so if
+ * present, they will show up as a sharp peak.
+ */
+ if (eip > prof_len-1)
+ eip = prof_len-1;
+ atomic_inc((atomic_t *)&prof_buffer[eip]);
}
#ifdef __SMP__ /*more of this file should probably be ifdefed SMP */
diff --git a/include/asm-i386/page.h b/include/asm-i386/page.h
index 9e159523a..78d07c207 100644
--- a/include/asm-i386/page.h
+++ b/include/asm-i386/page.h
@@ -30,6 +30,9 @@
#endif
+#define clear_user_page(page, vaddr) clear_page(page)
+#define copy_user_page(to, from, vaddr) copy_page(to, from)
+
/*
* These are used to make use of C type-checking..
*/
diff --git a/include/asm-i386/pci.h b/include/asm-i386/pci.h
index 1e55ec60f..d6d0311b7 100644
--- a/include/asm-i386/pci.h
+++ b/include/asm-i386/pci.h
@@ -1,6 +1,8 @@
#ifndef __i386_PCI_H
#define __i386_PCI_H
+#ifdef __KERNEL__
+
/* Can be used to override the logic in pci_scan_bus for skipping
already-configured bus numbers - to be used for buggy BIOSes
or architectures with incomplete PCI setup by the loader */
@@ -10,7 +12,7 @@
#define PCIBIOS_MIN_IO 0x1000
#define PCIBIOS_MIN_MEM 0x10000000
-#ifdef __KERNEL__
+void pcibios_set_master(struct pci_dev *dev);
/* Dynamic DMA mapping stuff.
* i386 has everything mapped statically.
@@ -164,4 +166,3 @@ extern inline int pci_dma_supported(struct pci_dev *hwdev, dma_addr_t mask)
#endif /* __KERNEL__ */
#endif /* __i386_PCI_H */
-
diff --git a/include/asm-i386/processor.h b/include/asm-i386/processor.h
index 1a4c151cd..9f594ab5d 100644
--- a/include/asm-i386/processor.h
+++ b/include/asm-i386/processor.h
@@ -116,6 +116,10 @@ extern struct cpuinfo_x86 cpu_data[];
(boot_cpu_data.x86_capability & X86_FEATURE_PAE)
#define cpu_has_tsc \
(boot_cpu_data.x86_capability & X86_FEATURE_TSC)
+#define cpu_has_de \
+ (boot_cpu_data.x86_capability & X86_FEATURE_DE)
+#define cpu_has_vme \
+ (boot_cpu_data.x86_capability & X86_FEATURE_VME)
extern char ignore_irq13;
diff --git a/include/asm-ia64/page.h b/include/asm-ia64/page.h
index 2361bc06d..648ff8a12 100644
--- a/include/asm-ia64/page.h
+++ b/include/asm-ia64/page.h
@@ -40,6 +40,9 @@
extern void clear_page (void *page);
extern void copy_page (void *to, void *from);
+#define clear_user_page(page, vaddr) clear_page(page)
+#define copy_user_page(to, from, vaddr) copy_page(to, from)
+
# ifdef STRICT_MM_TYPECHECKS
/*
* These are used to make use of C type-checking..
diff --git a/include/asm-ia64/pci.h b/include/asm-ia64/pci.h
index de50f98f0..bbabe63e8 100644
--- a/include/asm-ia64/pci.h
+++ b/include/asm-ia64/pci.h
@@ -11,6 +11,11 @@
#define PCIBIOS_MIN_IO 0x1000
#define PCIBIOS_MIN_MEM 0x10000000
+extern inline void pcibios_set_master(struct pci_dev *dev)
+{
+ /* No special bus mastering setup handling */
+}
+
/*
* Dynamic DMA mapping API.
* IA-64 has everything mapped statically.
diff --git a/include/asm-m68k/page.h b/include/asm-m68k/page.h
index 117abf25d..7cf002fb7 100644
--- a/include/asm-m68k/page.h
+++ b/include/asm-m68k/page.h
@@ -76,6 +76,9 @@ static inline void clear_page(void *page)
#define copy_page(to,from) memcpy((to), (from), PAGE_SIZE)
#endif
+#define clear_user_page(page, vaddr) clear_page(page)
+#define copy_user_page(to, from, vaddr) copy_page(to, from)
+
/*
* These are used to make use of C type-checking..
*/
diff --git a/include/asm-m68k/pci.h b/include/asm-m68k/pci.h
index 76915264d..efbaf41b5 100644
--- a/include/asm-m68k/pci.h
+++ b/include/asm-m68k/pci.h
@@ -35,4 +35,9 @@ struct pci_bus_info
#define pcibios_assign_all_busses() 0
+extern inline void pcibios_set_master(struct pci_dev *dev)
+{
+ /* No special bus mastering setup handling */
+}
+
#endif /* _ASM_M68K_PCI_H */
diff --git a/include/asm-mips/page.h b/include/asm-mips/page.h
index c458e3db1..fd391fb85 100644
--- a/include/asm-mips/page.h
+++ b/include/asm-mips/page.h
@@ -1,4 +1,4 @@
-/* $Id: page.h,v 1.8 2000/01/27 01:05:37 ralf Exp $
+/* $Id: page.h,v 1.9 2000/02/24 00:13:19 ralf Exp $
*
* Definitions for page handling
*
@@ -28,6 +28,8 @@ extern void (*_copy_page)(void * to, void * from);
#define clear_page(page) _clear_page(page)
#define copy_page(to, from) _copy_page(to, from)
+#define clear_user_page(page, vaddr) clear_page(page)
+#define copy_user_page(to, from, vaddr) copy_page(to, from)
/*
* These are used to make use of C type-checking..
diff --git a/include/asm-mips/pci.h b/include/asm-mips/pci.h
index 3f890fe68..aaa5c8fb7 100644
--- a/include/asm-mips/pci.h
+++ b/include/asm-mips/pci.h
@@ -1,4 +1,4 @@
-/* $Id: pci.h,v 1.8 2000/02/18 00:24:48 ralf Exp $
+/* $Id: pci.h,v 1.9 2000/02/24 00:13:19 ralf Exp $
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
@@ -7,6 +7,8 @@
#ifndef _ASM_PCI_H
#define _ASM_PCI_H
+#ifdef __KERNEL__
+
/* Can be used to override the logic in pci_scan_bus for skipping
already-configured bus numbers - to be used for buggy BIOSes
or architectures with incomplete PCI setup by the loader */
@@ -16,7 +18,10 @@
#define PCIBIOS_MIN_IO 0x1000
#define PCIBIOS_MIN_MEM 0x10000000
-#ifdef __KERNEL__
+extern inline void pcibios_set_master(struct pci_dev *dev)
+{
+ /* No special bus mastering setup handling */
+}
/*
* Dynamic DMA mapping stuff.
diff --git a/include/asm-mips64/page.h b/include/asm-mips64/page.h
index 287c2995a..27bcf8346 100644
--- a/include/asm-mips64/page.h
+++ b/include/asm-mips64/page.h
@@ -1,4 +1,4 @@
-/* $Id: page.h,v 1.5 2000/02/06 00:20:27 kanoj Exp $
+/* $Id: page.h,v 1.6 2000/02/24 00:13:20 ralf Exp $
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
@@ -29,6 +29,8 @@ extern void (*_copy_page)(void * to, void * from);
#define clear_page(page) _clear_page(page)
#define copy_page(to, from) _copy_page(to, from)
+#define clear_user_page(page, vaddr) clear_page(page)
+#define copy_user_page(to, from, vaddr) copy_page(to, from)
/*
* These are used to make use of C type-checking..
diff --git a/include/asm-mips64/pci.h b/include/asm-mips64/pci.h
index 217eea7d3..7769fef70 100644
--- a/include/asm-mips64/pci.h
+++ b/include/asm-mips64/pci.h
@@ -1,4 +1,4 @@
-/* $Id: pci.h,v 1.3 2000/02/18 00:24:48 ralf Exp $
+/* $Id: pci.h,v 1.4 2000/02/24 00:13:20 ralf Exp $
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
@@ -7,6 +7,8 @@
#ifndef _ASM_PCI_H
#define _ASM_PCI_H
+#ifdef __KERNEL__
+
/* Can be used to override the logic in pci_scan_bus for skipping
already-configured bus numbers - to be used for buggy BIOSes
or architectures with incomplete PCI setup by the loader */
@@ -16,7 +18,10 @@
#define PCIBIOS_MIN_IO 0x1000
#define PCIBIOS_MIN_MEM 0x10000000
-#ifdef __KERNEL__
+extern inline void pcibios_set_master(struct pci_dev *dev)
+{
+ /* No special bus mastering setup handling */
+}
/*
* Dynamic DMA mapping stuff.
diff --git a/include/asm-ppc/page.h b/include/asm-ppc/page.h
index 1be8ddfa5..e069f8b15 100644
--- a/include/asm-ppc/page.h
+++ b/include/asm-ppc/page.h
@@ -78,6 +78,8 @@ typedef unsigned long pgprot_t;
extern void clear_page(void *page);
extern void copy_page(void *to, void *from);
+#define clear_user_page(page, vaddr) clear_page(page)
+#define copy_user_page(to, from, vaddr) copy_page(to, from)
/* map phys->virtual and virtual->phys for RAM pages */
static inline unsigned long ___pa(unsigned long v)
diff --git a/include/asm-ppc/pci.h b/include/asm-ppc/pci.h
index d543c90cc..479f8ba2c 100644
--- a/include/asm-ppc/pci.h
+++ b/include/asm-ppc/pci.h
@@ -10,6 +10,11 @@
#define PCIBIOS_MIN_IO 0x1000
#define PCIBIOS_MIN_MEM 0x10000000
+extern inline void pcibios_set_master(struct pci_dev *dev)
+{
+ /* No special bus mastering setup handling */
+}
+
/* Dynamic DMA Mapping stuff
* ++ajoshi
*/
diff --git a/include/asm-sh/page.h b/include/asm-sh/page.h
index 23309458c..1348466a9 100644
--- a/include/asm-sh/page.h
+++ b/include/asm-sh/page.h
@@ -26,6 +26,8 @@
#define clear_page(page) memset((void *)(page), 0, PAGE_SIZE)
#define copy_page(to,from) memcpy((void *)(to), (void *)(from), PAGE_SIZE)
+#define clear_user_page(page, vaddr) clear_page(page)
+#define copy_user_page(to, from, vaddr) copy_page(to, from)
/*
* These are used to make use of C type-checking..
diff --git a/include/asm-sh/pci.h b/include/asm-sh/pci.h
index e8f6e1d66..bf8fe93f5 100644
--- a/include/asm-sh/pci.h
+++ b/include/asm-sh/pci.h
@@ -1,13 +1,18 @@
#ifndef __ASM_SH_PCI_H
#define __ASM_SH_PCI_H
+#ifdef __KERNEL__
+
/* Can be used to override the logic in pci_scan_bus for skipping
already-configured bus numbers - to be used for buggy BIOSes
or architectures with incomplete PCI setup by the loader */
#define pcibios_assign_all_busses() 0
-#ifdef __KERNEL__
+extern inline void pcibios_set_master(struct pci_dev *dev)
+{
+ /* No special bus mastering setup handling */
+}
/* Dynamic DMA mapping stuff.
* SuperH has everything mapped statically like x86.
diff --git a/include/asm-sparc/mman.h b/include/asm-sparc/mman.h
index 68bc526a1..e6011ebbd 100644
--- a/include/asm-sparc/mman.h
+++ b/include/asm-sparc/mman.h
@@ -1,4 +1,4 @@
-/* $Id: mman.h,v 1.8 1996/10/27 08:55:28 davem Exp $ */
+/* $Id: mman.h,v 1.9 2000/03/15 02:44:23 davem Exp $ */
#ifndef __SPARC_MMAN_H__
#define __SPARC_MMAN_H__
@@ -42,6 +42,13 @@
#define MC_LOCKAS 5 /* Lock an entire address space of the calling process */
#define MC_UNLOCKAS 6 /* Unlock entire address space of calling process */
+#define MADV_NORMAL 0x0 /* default page-in behavior */
+#define MADV_RANDOM 0x1 /* page-in minimum required */
+#define MADV_SEQUENTIAL 0x2 /* read-ahead aggressively */
+#define MADV_WILLNEED 0x3 /* pre-fault pages */
+#define MADV_DONTNEED 0x4 /* discard these pages */
+#define MADV_FREE 0x5 /* (Solaris) contents can be freed */
+
/* compatibility flags */
#define MAP_ANON MAP_ANONYMOUS
#define MAP_FILE 0
diff --git a/include/asm-sparc/page.h b/include/asm-sparc/page.h
index 3c61eb636..0fc2aa247 100644
--- a/include/asm-sparc/page.h
+++ b/include/asm-sparc/page.h
@@ -1,4 +1,4 @@
-/* $Id: page.h,v 1.48 2000/02/16 07:34:51 davem Exp $
+/* $Id: page.h,v 1.51 2000/03/15 07:19:25 davem Exp $
* page.h: Various defines and such for MMU operations on the Sparc for
* the Linux kernel.
*
@@ -14,7 +14,12 @@
#else
#define PAGE_SHIFT 12
#endif
+#ifndef __ASSEMBLY__
+/* I have my suspicions... -DaveM */
+#define PAGE_SIZE (1UL << PAGE_SHIFT)
+#else
#define PAGE_SIZE (1 << PAGE_SHIFT)
+#endif
#define PAGE_MASK (~(PAGE_SIZE-1))
#ifdef __KERNEL__
@@ -33,8 +38,10 @@
BUG(); \
} while (0)
-#define clear_page(page) memset((void *)(page), 0, PAGE_SIZE)
-#define copy_page(to,from) memcpy((void *)(to), (void *)(from), PAGE_SIZE)
+#define clear_page(page) memset((void *)(page), 0, PAGE_SIZE)
+#define copy_page(to,from) memcpy((void *)(to), (void *)(from), PAGE_SIZE)
+#define clear_user_page(page, vaddr) clear_page(page)
+#define copy_user_page(to, from, vaddr) copy_page(to, from)
/* The following structure is used to hold the physical
* memory configuration of the machine. This is filled in
diff --git a/include/asm-sparc/pci.h b/include/asm-sparc/pci.h
index 80ea9d11d..f96225e2a 100644
--- a/include/asm-sparc/pci.h
+++ b/include/asm-sparc/pci.h
@@ -1,6 +1,8 @@
#ifndef __SPARC_PCI_H
#define __SPARC_PCI_H
+#ifdef __KERNEL__
+
/* Can be used to override the logic in pci_scan_bus for skipping
* already-configured bus numbers - to be used for buggy BIOSes
* or architectures with incomplete PCI setup by the loader.
@@ -10,7 +12,10 @@
#define PCIBIOS_MIN_IO 0UL
#define PCIBIOS_MIN_MEM 0UL
-#ifdef __KERNEL__
+extern inline void pcibios_set_master(struct pci_dev *dev)
+{
+ /* No special bus mastering setup handling */
+}
/* Dynamic DMA mapping stuff.
*/
diff --git a/include/asm-sparc64/atomic.h b/include/asm-sparc64/atomic.h
index 96ef1a585..db3cd5831 100644
--- a/include/asm-sparc64/atomic.h
+++ b/include/asm-sparc64/atomic.h
@@ -1,61 +1,32 @@
-/* $Id: atomic.h,v 1.19 1999/07/03 22:11:17 davem Exp $
+/* $Id: atomic.h,v 1.20 2000/03/16 16:44:44 davem Exp $
* atomic.h: Thankfully the V9 is at least reasonable for this
* stuff.
*
- * Copyright (C) 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996, 1997, 2000 David S. Miller (davem@redhat.com)
*/
#ifndef __ARCH_SPARC64_ATOMIC__
#define __ARCH_SPARC64_ATOMIC__
-/* 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)
-
typedef struct { int counter; } atomic_t;
#define ATOMIC_INIT(i) { (i) }
#define atomic_read(v) ((v)->counter)
#define atomic_set(v, i) (((v)->counter) = i)
-#define atomic_add_return(__i, __v) \
-({ register atomic_t *__V asm("g1"); \
- register int __I asm("g2"); \
- __V = (__v); __I = (__i); \
- __asm__ __volatile__("sethi %%hi(__atomic_add), %%g3\n\t" \
- "jmpl %%g3 + %%lo(__atomic_add), %%g3\n\t" \
- " nop\n1:" \
- : "=&r" (__I) \
- : "0" (__I), "r" (__V) \
- : "g3", "g5", "g7", "cc", "memory"); \
- __I; \
-})
-
-#define atomic_sub_return(__i, __v) \
-({ register atomic_t *__V asm("g1"); \
- register int __I asm("g2"); \
- __V = (__v); __I = (__i); \
- __asm__ __volatile__("sethi %%hi(__atomic_sub), %%g3\n\t" \
- "jmpl %%g3 + %%lo(__atomic_sub), %%g3\n\t" \
- " nop\n1:" \
- : "=&r" (__I) \
- : "0" (__I), "r" (__V) \
- : "g3", "g5", "g7", "cc", "memory"); \
- __I; \
-})
-
-#define atomic_add(i, v) atomic_add_return(i, v)
-#define atomic_sub(i, v) atomic_sub_return(i, v)
-
-#define atomic_dec_return(v) atomic_sub_return(1,(v))
-#define atomic_inc_return(v) atomic_add_return(1,(v))
-
-#define atomic_sub_and_test(i,v) (atomic_sub_return((i), (v)) == 0)
-#define atomic_dec_and_test(v) (atomic_sub_return(1, (v)) == 0)
-
-#define atomic_inc(v) atomic_add(1,(v))
-#define atomic_dec(v) atomic_sub(1,(v))
+extern int __atomic_add(int, atomic_t *);
+extern int __atomic_sub(int, atomic_t *);
+
+#define atomic_add(i, v) ((void)__atomic_add(i, v))
+#define atomic_sub(i, v) ((void)__atomic_sub(i, v))
+
+#define atomic_dec_return(v) __atomic_sub(1, v)
+#define atomic_inc_return(v) __atomic_add(1, v)
+
+#define atomic_sub_and_test(i, v) (__atomic_sub(i, v) == 0)
+#define atomic_dec_and_test(v) (__atomic_sub(1, v) == 0)
+
+#define atomic_inc(v) ((void)__atomic_add(1, v))
+#define atomic_dec(v) ((void)__atomic_sub(1, v))
#endif /* !(__ARCH_SPARC64_ATOMIC__) */
diff --git a/include/asm-sparc64/mman.h b/include/asm-sparc64/mman.h
index bfb510af7..553e42906 100644
--- a/include/asm-sparc64/mman.h
+++ b/include/asm-sparc64/mman.h
@@ -1,4 +1,4 @@
-/* $Id: mman.h,v 1.1 1996/12/02 00:07:29 davem Exp $ */
+/* $Id: mman.h,v 1.2 2000/03/15 02:44:26 davem Exp $ */
#ifndef __SPARC64_MMAN_H__
#define __SPARC64_MMAN_H__
@@ -42,6 +42,13 @@
#define MC_LOCKAS 5 /* Lock an entire address space of the calling process */
#define MC_UNLOCKAS 6 /* Unlock entire address space of calling process */
+#define MADV_NORMAL 0x0 /* default page-in behavior */
+#define MADV_RANDOM 0x1 /* page-in minimum required */
+#define MADV_SEQUENTIAL 0x2 /* read-ahead aggressively */
+#define MADV_WILLNEED 0x3 /* pre-fault pages */
+#define MADV_DONTNEED 0x4 /* discard these pages */
+#define MADV_FREE 0x5 /* (Solaris) contents can be freed */
+
/* compatibility flags */
#define MAP_ANON MAP_ANONYMOUS
#define MAP_FILE 0
diff --git a/include/asm-sparc64/page.h b/include/asm-sparc64/page.h
index 7653f4af1..757f43c4d 100644
--- a/include/asm-sparc64/page.h
+++ b/include/asm-sparc64/page.h
@@ -1,4 +1,4 @@
-/* $Id: page.h,v 1.30 2000/02/16 07:34:54 davem Exp $ */
+/* $Id: page.h,v 1.32 2000/03/15 07:19:28 davem Exp $ */
#ifndef _SPARC64_PAGE_H
#define _SPARC64_PAGE_H
@@ -23,6 +23,8 @@
extern void clear_page(void *page);
extern void copy_page(void *to, void *from);
+extern void clear_user_page(void *page, unsigned long vaddr);
+extern void copy_user_page(void *to, void *from, unsigned long vaddr);
/* GROSS, defining this makes gcc pass these types as aggregates,
* and thus on the stack, turn this crap off... -DaveM
diff --git a/include/asm-sparc64/parport.h b/include/asm-sparc64/parport.h
index dac004a6b..6840476dd 100644
--- a/include/asm-sparc64/parport.h
+++ b/include/asm-sparc64/parport.h
@@ -1,4 +1,4 @@
-/* $Id: parport.h,v 1.7 2000/01/28 13:43:14 jj Exp $
+/* $Id: parport.h,v 1.9 2000/03/16 07:47:27 davem Exp $
* parport.h: sparc64 specific parport initialization and dma.
*
* Copyright (C) 1999 Eddie C. Dost (ecd@skynet.be)
@@ -7,7 +7,6 @@
#ifndef _ASM_SPARC64_PARPORT_H
#define _ASM_SPARC64_PARPORT_H 1
-#include <linux/config.h>
#include <asm/ebus.h>
#include <asm/ns87303.h>
diff --git a/include/asm-sparc64/pci.h b/include/asm-sparc64/pci.h
index 267cbf797..1fd24a47f 100644
--- a/include/asm-sparc64/pci.h
+++ b/include/asm-sparc64/pci.h
@@ -1,6 +1,8 @@
#ifndef __SPARC64_PCI_H
#define __SPARC64_PCI_H
+#ifdef __KERNEL__
+
/* Can be used to override the logic in pci_scan_bus for skipping
* already-configured bus numbers - to be used for buggy BIOSes
* or architectures with incomplete PCI setup by the loader.
@@ -10,7 +12,10 @@
#define PCIBIOS_MIN_IO 0UL
#define PCIBIOS_MIN_MEM 0UL
-#ifdef __KERNEL__
+extern inline void pcibios_set_master(struct pci_dev *dev)
+{
+ /* No special bus mastering setup handling */
+}
/* Dynamic DMA mapping stuff.
*/
diff --git a/include/asm-sparc64/semaphore.h b/include/asm-sparc64/semaphore.h
index 1d7b0e31c..85ae21c9d 100644
--- a/include/asm-sparc64/semaphore.h
+++ b/include/asm-sparc64/semaphore.h
@@ -90,7 +90,7 @@ extern __inline__ void down(struct semaphore * sem)
ba,pt %%xcc, 2b
restore %%l3, %%g0, %%g3
.previous\n"
- : : "r" (__atomic_fool_gcc(sem)), "i" (__down)
+ : : "r" (sem), "i" (__down)
: "g5", "g7", "memory", "cc");
}
@@ -126,7 +126,7 @@ extern __inline__ int down_interruptible(struct semaphore *sem)
restore %%o0, %%g0, %0
.previous\n"
: "=r" (ret)
- : "0" (ret), "r" (__atomic_fool_gcc(sem)), "i" (__down_interruptible)
+ : "0" (ret), "r" (sem), "i" (__down_interruptible)
: "g5", "g7", "memory", "cc");
return ret;
}
@@ -162,7 +162,7 @@ extern inline int down_trylock(struct semaphore *sem)
restore %%o0, %%g0, %0
.previous\n"
: "=r" (ret)
- : "0" (ret), "r" (__atomic_fool_gcc(sem)), "i" (__down_trylock)
+ : "0" (ret), "r" (sem), "i" (__down_trylock)
: "g5", "g7", "memory", "cc");
return ret;
}
@@ -196,7 +196,7 @@ extern __inline__ void up(struct semaphore * sem)
ba,pt %%xcc, 2b
restore %%l3, %%g0, %%g3
.previous\n"
- : : "r" (__atomic_fool_gcc(sem)), "i" (__up)
+ : : "r" (sem), "i" (__up)
: "g5", "g7", "memory", "cc");
}
@@ -309,7 +309,7 @@ extern inline void down_read(struct rw_semaphore *sem)
ba,pt %%xcc, 2b
restore %%l3, %%g0, %%g3
.previous\n"
- : : "r" (__atomic_fool_gcc(sem)), "i" (__down_read_failed)
+ : : "r" (sem), "i" (__down_read_failed)
: "g5", "g7", "memory", "cc");
#if WAITQUEUE_DEBUG
if (test_le_bit(1, &sem->granted))
@@ -348,7 +348,7 @@ extern inline void down_write(struct rw_semaphore *sem)
ba,pt %%xcc, 2b
restore %%l3, %%g0, %%g3
.previous\n"
- : : "r" (__atomic_fool_gcc(sem)), "i" (__down_write_failed)
+ : : "r" (sem), "i" (__down_write_failed)
: "g5", "g7", "memory", "cc");
#if WAITQUEUE_DEBUG
if (atomic_read(&sem->writers))
@@ -394,7 +394,7 @@ extern inline void __up_read(struct rw_semaphore *sem)
ba,pt %%xcc, 2b
restore %%l3, %%g0, %%g3
.previous\n"
- : : "r" (__atomic_fool_gcc(sem)), "i" (__rwsem_wake)
+ : : "r" (sem), "i" (__rwsem_wake)
: "g5", "g7", "memory", "cc");
}
@@ -430,7 +430,7 @@ extern inline void __up_write(struct rw_semaphore *sem)
ba,pt %%xcc, 2b
restore %%l3, %%g0, %%g3
.previous\n"
- : : "r" (__atomic_fool_gcc(sem)), "i" (__rwsem_wake)
+ : : "r" (sem), "i" (__rwsem_wake)
: "g5", "g7", "memory", "cc");
}
diff --git a/include/asm-sparc64/spinlock.h b/include/asm-sparc64/spinlock.h
index c4e571564..ea34fbefe 100644
--- a/include/asm-sparc64/spinlock.h
+++ b/include/asm-sparc64/spinlock.h
@@ -110,41 +110,15 @@ extern int _spin_trylock (spinlock_t *lock);
typedef unsigned int rwlock_t;
#define RW_LOCK_UNLOCKED 0
-#define read_lock(__rw_lck) \
-do { register rwlock_t *__X asm("g1"); \
- __asm__ __volatile__("sethi %%hi(__read_lock), %%g3\n\t" \
- "jmpl %%g3 + %%lo(__read_lock), %%g3\n\t" \
- " nop\n1:" \
- : : "r" (__X = (__rw_lck)) \
- : "g3", "g5", "g7", "cc", "memory"); \
-} while(0)
-
-#define read_unlock(__rw_lck) \
-do { register rwlock_t *__X asm("g1"); \
- __asm__ __volatile__("sethi %%hi(__read_unlock), %%g3\n\t" \
- "jmpl %%g3 + %%lo(__read_unlock), %%g3\n\t" \
- " nop\n1:" \
- : : "r" (__X = (__rw_lck)) \
- : "g3", "g5", "g7", "cc", "memory"); \
-} while(0)
-
-#define write_lock(__rw_lck) \
-do { register rwlock_t *__X asm("g1"); \
- __asm__ __volatile__("sethi %%hi(__write_lock), %%g3\n\t" \
- "jmpl %%g3 + %%lo(__write_lock), %%g3\n\t" \
- " nop\n1:" \
- : : "r" (__X = (__rw_lck)) \
- : "g2", "g3", "g5", "g7", "cc", "memory"); \
-} while(0)
-
-#define write_unlock(__rw_lck) \
-do { register rwlock_t *__X asm("g1"); \
- __asm__ __volatile__("sethi %%hi(__write_unlock), %%g3\n\t" \
- "jmpl %%g3 + %%lo(__write_unlock), %%g3\n\t" \
- " nop\n1:" \
- : : "r" (__X = (__rw_lck)) \
- : "g2", "g3", "g5", "g7", "cc", "memory"); \
-} while(0)
+extern void __read_lock(rwlock_t *);
+extern void __read_unlock(rwlock_t *);
+extern void __write_lock(rwlock_t *);
+extern void __write_unlock(rwlock_t *);
+
+#define read_lock(p) __read_lock(p)
+#define read_unlock(p) __read_unlock(p)
+#define write_lock(p) __write_lock(p)
+#define write_unlock(p) __write_unlock(p)
#else /* !(SPIN_LOCK_DEBUG) */
diff --git a/include/linux/ac97_codec.h b/include/linux/ac97_codec.h
index 50c75c3a8..da8730aef 100644
--- a/include/linux/ac97_codec.h
+++ b/include/linux/ac97_codec.h
@@ -39,6 +39,21 @@
#define AC97_RESERVED_3A 0x003A /* Reserved */
/* range 0x3c-0x58 - MODEM */
+#define AC97_EXTENDED_MODEM_ID 0x003C
+#define AC97_EXTEND_MODEM_STAT 0x003E
+#define AC97_LINE1_RATE 0x0040
+#define AC97_LINE2_RATE 0x0042
+#define AC97_HANDSET_RATE 0x0044
+#define AC97_LINE1_LEVEL 0x0046
+#define AC97_LINE2_LEVEL 0x0048
+#define AC97_HANDSET_LEVEL 0x004A
+#define AC97_GPIO_CONFIG 0x004C
+#define AC97_GPIO_POLARITY 0x004E
+#define AC97_GPIO_STICKY 0x0050
+#define AC97_GPIO_WAKE_UP 0x0052
+#define AC97_GPIO_STATUS 0x0054
+#define AC97_MISC_MODEM_STAT 0x0056
+#define AC97_RESERVED_58 0x0058
/* registers 0x005a - 0x007a are vendor reserved */
@@ -150,6 +165,9 @@ struct ac97_codec {
/* saved OSS mixer states */
unsigned int mixer_state[SOUND_MIXER_NRDEVICES];
+
+ /* Software Modem interface */
+ int (*modem_ioctl)(struct ac97_codec *codec, unsigned int cmd, unsigned long arg);
};
extern int ac97_read_proc (char *page_out, char **start, off_t off,
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
index 31721c101..14e163b0e 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -37,7 +37,7 @@ struct linux_binfmt {
struct linux_binfmt * next;
struct module *module;
int (*load_binary)(struct linux_binprm *, struct pt_regs * regs);
- int (*load_shlib)(int fd);
+ int (*load_shlib)(struct file *);
int (*core_dump)(long signr, struct pt_regs * regs, struct file * file);
unsigned long min_coredump; /* minimal dump size */
};
diff --git a/include/linux/fs.h b/include/linux/fs.h
index bd37850b7..1001d5cba 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -589,6 +589,7 @@ struct super_block {
struct list_head s_files;
struct block_device *s_bdev;
+ struct quota_mount_options s_dquot; /* Diskquota specific options */
union {
struct minix_sb_info minix_sb;
@@ -730,7 +731,6 @@ struct file_system_type {
struct file_system_type * next;
};
-#ifdef MODULE
#define DECLARE_FSTYPE(var,type,read,flags) \
struct file_system_type var = { \
name: type, \
@@ -738,14 +738,6 @@ struct file_system_type var = { \
fs_flags: flags, \
owner: THIS_MODULE, \
}
-#else
-#define DECLARE_FSTYPE(var,type,read,flags) \
-struct file_system_type var = { \
- name: type, \
- read_super: read, \
- fs_flags: flags, \
-}
-#endif
#define DECLARE_FSTYPE_DEV(var,type,read) \
DECLARE_FSTYPE(var,type,read,FS_REQUIRES_DEV)
@@ -821,7 +813,7 @@ extern int do_truncate(struct dentry *, loff_t start);
extern int get_unused_fd(void);
extern void put_unused_fd(unsigned int);
-extern struct file *filp_open(const char *, int, int);
+extern struct file *filp_open(const char *, int, int, struct dentry *);
extern int filp_close(struct file *, fl_owner_t id);
extern char * getname(const char *);
@@ -936,10 +928,15 @@ extern int notify_change(struct dentry *, struct iattr *);
extern int permission(struct inode *, int);
extern int get_write_access(struct inode *);
extern void put_write_access(struct inode *);
-extern struct dentry * open_namei(const char *, int, int);
extern struct dentry * do_mknod(const char *, int, dev_t);
extern int do_pipe(int *);
-extern int do_unlink(const char * name);
+extern int do_unlink(const char * name, struct dentry *);
+extern struct dentry * __open_namei(const char *, int, int, struct dentry *);
+
+static inline struct dentry * open_namei(const char *pathname)
+{
+ return __open_namei(pathname, 0, 0, NULL);
+}
/* fs/dcache.c -- generic fs support functions */
extern int is_subdir(struct dentry *, struct dentry *);
@@ -989,6 +986,7 @@ typedef int (*read_actor_t)(read_descriptor_t *, struct page *, unsigned long, u
extern struct dentry * lookup_dentry(const char *, struct dentry *, unsigned int);
+extern struct dentry * lookup_one(const char *, struct dentry *);
extern struct dentry * __namei(const char *, unsigned int);
#define namei(pathname) __namei(pathname, 1)
diff --git a/include/linux/hdreg.h b/include/linux/hdreg.h
index 3509c5cb9..94030b18d 100644
--- a/include/linux/hdreg.h
+++ b/include/linux/hdreg.h
@@ -193,6 +193,8 @@ struct hd_geometry {
#define HDIO_SET_NICE 0x0329 /* set nice flags */
#define HDIO_UNREGISTER_HWIF 0x032a /* unregister interface */
+#define __NEW_HD_DRIVE_ID
+
/* structure returned by HDIO_GET_IDENTITY, as per ANSI ATA2 rev.2f spec */
struct hd_driveid {
unsigned short config; /* lots of obsolete bit flags */
@@ -256,7 +258,7 @@ struct hd_driveid {
unsigned short CurAPMvalues; /* current APM values */
unsigned short word92; /* reserved (word 92) */
unsigned short hw_config; /* hardware config */
- unsigned short words94_125[33];/* reserved words 94-125 */
+ unsigned short words94_125[31];/* reserved words 94-125 */
unsigned short last_lun; /* reserved (word 126) */
unsigned short word127; /* reserved (word 127) */
unsigned short dlf; /* device lock function
diff --git a/include/linux/highmem.h b/include/linux/highmem.h
index 2e31d77f2..3049ad259 100644
--- a/include/linux/highmem.h
+++ b/include/linux/highmem.h
@@ -29,6 +29,15 @@ extern inline unsigned int nr_free_highpages(void) { return 0; }
#endif /* CONFIG_HIGHMEM */
/* when CONFIG_HIGHMEM is not set these will be plain clear/copy_page */
+extern inline void clear_user_highpage(struct page *page, unsigned long vaddr)
+{
+ unsigned long kaddr;
+
+ kaddr = kmap(page);
+ clear_user_page((void *)kaddr, vaddr);
+ kunmap(page);
+}
+
extern inline void clear_highpage(struct page *page)
{
unsigned long kaddr;
@@ -64,6 +73,17 @@ extern inline void memclear_highpage_flush(struct page *page, unsigned int offse
kunmap(page);
}
+extern inline void copy_user_highpage(struct page *to, struct page *from, unsigned long vaddr)
+{
+ unsigned long vfrom, vto;
+
+ vfrom = kmap(from);
+ vto = kmap(to);
+ copy_user_page((void *)vto, (void *)vfrom, vaddr);
+ kunmap(from);
+ kunmap(to);
+}
+
extern inline void copy_highpage(struct page *to, struct page *from)
{
unsigned long vfrom, vto;
diff --git a/include/linux/i2c-algo-pcf.h b/include/linux/i2c-algo-pcf.h
index edb6ce644..892d737ca 100644
--- a/include/linux/i2c-algo-pcf.h
+++ b/include/linux/i2c-algo-pcf.h
@@ -22,10 +22,10 @@
/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
Frodo Looijaard <frodol@dds.nl> */
-/* $Id: i2c-algo-pcf.h,v 1.6 1999/12/21 23:45:58 frodo Exp $ */
+/* $Id: i2c-algo-pcf.h,v 1.7 2000/02/27 23:02:45 frodo Exp $ */
#ifndef I2C_ALGO_PCF_H
-#define I2C_AGLO_PCF_H 1
+#define I2C_ALGO_PCF_H 1
/* --- Defines for pcf-adapters --------------------------------------- */
#include <linux/i2c.h>
diff --git a/include/linux/i2c-dev.h b/include/linux/i2c-dev.h
index 7588f626b..39ed2adfa 100644
--- a/include/linux/i2c-dev.h
+++ b/include/linux/i2c-dev.h
@@ -19,7 +19,7 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-/* $Id: i2c-dev.h,v 1.6 2000/01/24 21:56:58 frodo Exp $ */
+/* $Id: i2c-dev.h,v 1.7 2000/02/15 17:57:27 frodo Exp $ */
#ifndef I2C_DEV_H
#define I2C_DEV_H
@@ -39,6 +39,12 @@ struct i2c_smbus_ioctl_data {
union i2c_smbus_data *data;
};
+/* This is the structure as used in the I2C_RDWR ioctl call */
+struct i2c_rdwr_ioctl_data {
+ struct i2c_msg *msgs; /* pointers to i2c_msgs */
+ int nmsgs; /* number of i2c_msgs */
+};
+
#ifndef __KERNEL__
#include <sys/ioctl.h>
diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h
index ed98106ed..ae788ce52 100644
--- a/include/linux/i2c-id.h
+++ b/include/linux/i2c-id.h
@@ -20,7 +20,7 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* ------------------------------------------------------------------------- */
-/* $Id: i2c-id.h,v 1.10 2000/02/04 02:47:41 mds Exp $ */
+/* $Id: i2c-id.h,v 1.15 2000/03/16 14:01:57 frodo Exp $ */
#ifndef I2C_ID_H
#define I2C_ID_H
@@ -92,9 +92,7 @@
#define I2C_ALGO_ATI 0x030000 /* ATI video card */
#define I2C_ALGO_SMBUS 0x040000
#define I2C_ALGO_ISA 0x050000 /* lm_sensors ISA pseudo-adapter */
-#define I2C_ALGO_SAA714 0x060000 /* SAA 7146 video decoder bus */
-#define I2C_ALGO_SAA7146A 0x060001 /* SAA 7146A - enhanced version */
-
+#define I2C_ALGO_SAA7146 0x060000 /* SAA 7146 video decoder bus */
#define I2C_ALGO_EXP 0x800000 /* experimental */
@@ -120,6 +118,7 @@
#define I2C_HW_B_HYDRA 0x08 /* Apple Hydra Mac I/O */
#define I2C_HW_B_G400 0x09 /* Matrox G400 */
#define I2C_HW_B_I810 0x0a /* Intel I810 */
+#define I2C_HW_B_VOO 0x0b /* 3dfx Voodoo 3 / Banshee */
#define I2C_HW_B_RIVA 0x10 /* Riva based graphics cards */
#define I2C_HW_B_IOC 0x11 /* IOC bit-wiggling */
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index 3aa308bc2..f1f82a1c8 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -23,7 +23,7 @@
/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and
Frodo Looijaard <frodol@dds.nl> */
-/* $Id: i2c.h,v 1.36 2000/01/18 23:54:07 frodo Exp $ */
+/* $Id: i2c.h,v 1.37 2000/02/15 17:57:27 frodo Exp $ */
#ifndef I2C_H
#define I2C_H
@@ -441,6 +441,7 @@ union i2c_smbus_data {
#define I2C_TENBIT 0x0704 /* 0 for 7 bit addrs, != 0 for 10 bit */
#define I2C_FUNCS 0x0705 /* Get the adapter functionality */
+#define I2C_RDWR 0x0707 /* Combined R/W transfer (one stop only)*/
#if 0
#define I2C_ACK_TEST 0x0710 /* See if a slave is at a specific adress */
#endif
@@ -452,7 +453,6 @@ union i2c_smbus_data {
/* written byte (except address) */
#define I2C_MDELAY 0x0706 /* millisec delay between written bytes */
-
/* ----- I2C-DEV: char device interface stuff ------------------------- */
#define I2C_MAJOR 89 /* Device major number */
diff --git a/include/linux/icmp.h b/include/linux/icmp.h
index fb4ed8b9a..292888923 100644
--- a/include/linux/icmp.h
+++ b/include/linux/icmp.h
@@ -82,10 +82,31 @@ struct icmphdr {
#ifdef __KERNEL__
+#include <linux/ip.h>
+
struct icmp_err {
int errno;
unsigned fatal:1;
};
+
+/*
+ * Build xmit assembly blocks
+ */
+
+struct icmp_bxm
+{
+ void *data_ptr;
+ int data_len;
+ struct icmphdr icmph;
+ unsigned long csum;
+ struct ip_options replyopts;
+ unsigned char optbuf[40];
+};
+
+struct sk_buff;
+
+extern void icmp_reply(struct icmp_bxm *, struct sk_buff *);
+
#endif
/*
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index d5b204c2c..02341868d 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -15,8 +15,10 @@
#define barrier() __asm__ __volatile__("": : :"memory")
#define INT_MAX ((int)(~0U>>1))
+#define INT_MIN (-INT_MAX - 1)
#define UINT_MAX (~0U)
#define LONG_MAX ((long)(~0UL>>1))
+#define LONG_MIN (-LONG_MAX - 1)
#define ULONG_MAX (~0UL)
#define STACK_MAGIC 0xdeadbeef
diff --git a/include/linux/module.h b/include/linux/module.h
index 915342edd..7507d6add 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -207,15 +207,13 @@ extern struct module __this_module;
#define MOD_DEC_USE_COUNT __MOD_DEC_USE_COUNT(THIS_MODULE)
#define MOD_IN_USE __MOD_IN_USE(THIS_MODULE)
-#ifndef __NO_VERSION__
#include <linux/version.h>
-const char __module_kernel_version[] __attribute__((section(".modinfo"))) =
+static const char __module_kernel_version[] __attribute__((section(".modinfo"))) =
"kernel_version=" UTS_RELEASE;
#ifdef MODVERSIONS
-const char __module_using_checksums[] __attribute__((section(".modinfo"))) =
+static const char __module_using_checksums[] __attribute__((section(".modinfo"))) =
"using_checksums=1";
#endif
-#endif
#else /* MODULE */
diff --git a/include/linux/mount.h b/include/linux/mount.h
index 86dc89c47..c2dcfe93f 100644
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -30,14 +30,10 @@ struct vfsmount
kdev_t mnt_dev; /* Device this applies to */
char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */
char *mnt_dirname; /* Name of directory mounted on */
- unsigned int mnt_flags; /* Flags of this device */
struct super_block *mnt_sb; /* pointer to superblock */
- struct quota_mount_options mnt_dquot; /* Diskquota specific mount options */
struct vfsmount *mnt_next; /* pointer to next in linkedlist */
};
-struct vfsmount *lookup_vfsmnt(kdev_t dev);
-
/*
* Umount options
*/
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
index 9b72c14de..c18810e52 100644
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -16,7 +16,8 @@
#define NF_ACCEPT 1
#define NF_STOLEN 2
#define NF_QUEUE 3
-#define NF_MAX_VERDICT NF_QUEUE
+#define NF_REPEAT 4
+#define NF_MAX_VERDICT NF_REPEAT
/* Generic cache responses from hook functions. */
#define NFC_ALTERED 0x8000
@@ -66,6 +67,10 @@ struct nf_sockopt_ops
int get_optmin;
int get_optmax;
int (*get)(struct sock *sk, int optval, void *user, int *len);
+
+ /* Number of users inside set() or get(). */
+ unsigned int use;
+ struct task_struct *cleanup_task;
};
/* Each queued (to userspace) skbuff has one of these. */
@@ -173,12 +178,4 @@ extern void nf_invalidate_cache(int pf);
#define SUMIN(a,b) ((size_t)(a)<(size_t)(b) ? (ssize_t)(a) : (ssize_t)(b))
#endif /*__KERNEL__*/
-enum nf_reason {
- /* Do not, NOT, reorder these. Add at end. */
- NF_REASON_NONE,
- NF_REASON_SET_BY_IPCHAINS,
- NF_REASON_FOR_ROUTING,
- NF_REASON_FOR_CLS_FW,
-};
-
#endif /*__LINUX_NETFILTER_H*/
diff --git a/include/linux/netfilter_ipv4.h b/include/linux/netfilter_ipv4.h
index 6111c47d8..3a41356db 100644
--- a/include/linux/netfilter_ipv4.h
+++ b/include/linux/netfilter_ipv4.h
@@ -49,6 +49,16 @@
#define NF_IP_POST_ROUTING 4
#define NF_IP_NUMHOOKS 5
+enum nf_ip_hook_priorities {
+ NF_IP_PRI_FIRST = INT_MIN,
+ NF_IP_PRI_CONNTRACK = -200,
+ NF_IP_PRI_MANGLE = -150,
+ NF_IP_PRI_NAT_DST = -100,
+ NF_IP_PRI_FILTER = 0,
+ NF_IP_PRI_NAT_SRC = 100,
+ NF_IP_PRI_LAST = INT_MAX,
+};
+
#ifdef CONFIG_NETFILTER_DEBUG
#ifdef __KERNEL__
void nf_debug_ip_local_deliver(struct sk_buff *skb);
diff --git a/include/linux/netfilter_ipv4/compat_firewall.h b/include/linux/netfilter_ipv4/compat_firewall.h
new file mode 100644
index 000000000..ed07ff91c
--- /dev/null
+++ b/include/linux/netfilter_ipv4/compat_firewall.h
@@ -0,0 +1,45 @@
+/* Minor modifications to fit on compatibility framework:
+ Rusty.Russell@rustcorp.com.au
+*/
+
+#ifndef __LINUX_FIREWALL_H
+#define __LINUX_FIREWALL_H
+
+/*
+ * Definitions for loadable firewall modules
+ */
+
+#define FW_QUEUE 0
+#define FW_BLOCK 1
+#define FW_ACCEPT 2
+#define FW_REJECT (-1)
+#define FW_REDIRECT 3
+#define FW_MASQUERADE 4
+#define FW_SKIP 5
+
+struct firewall_ops
+{
+ struct firewall_ops *next;
+ int (*fw_forward)(struct firewall_ops *this, int pf,
+ struct net_device *dev, void *phdr, void *arg,
+ struct sk_buff **pskb);
+ int (*fw_input)(struct firewall_ops *this, int pf,
+ struct net_device *dev, void *phdr, void *arg,
+ struct sk_buff **pskb);
+ int (*fw_output)(struct firewall_ops *this, int pf,
+ struct net_device *dev, void *phdr, void *arg,
+ struct sk_buff **pskb);
+ /* These may be NULL. */
+ int (*fw_acct_in)(struct firewall_ops *this, int pf,
+ struct net_device *dev, void *phdr, void *arg,
+ struct sk_buff **pskb);
+ int (*fw_acct_out)(struct firewall_ops *this, int pf,
+ struct net_device *dev, void *phdr, void *arg,
+ struct sk_buff **pskb);
+};
+
+extern int register_firewall(int pf, struct firewall_ops *fw);
+extern int unregister_firewall(int pf, struct firewall_ops *fw);
+
+extern int ip_fw_masq_timeouts(void *user, int len);
+#endif /* __LINUX_FIREWALL_H */
diff --git a/include/linux/netfilter_ipv4/ip_conntrack.h b/include/linux/netfilter_ipv4/ip_conntrack.h
new file mode 100644
index 000000000..12cf75136
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ip_conntrack.h
@@ -0,0 +1,177 @@
+#ifndef _IP_CONNTRACK_H
+#define _IP_CONNTRACK_H
+/* Connection state tracking for netfilter. This is separated from,
+ but required by, the NAT layer; it can also be used by an iptables
+ extension. */
+
+#include <linux/config.h>
+#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
+
+enum ip_conntrack_info
+{
+ /* Part of an established connection (either direction). */
+ IP_CT_ESTABLISHED,
+
+ /* Like NEW, but related to an existing connection, or ICMP error
+ (in either direction). */
+ IP_CT_RELATED,
+
+ /* Started a new connection to track (only
+ IP_CT_DIR_ORIGINAL); may be a retransmission. */
+ IP_CT_NEW,
+
+ /* >= this indicates reply direction */
+ IP_CT_IS_REPLY,
+
+ /* Number of distinct IP_CT types (no NEW in reply dirn). */
+ IP_CT_NUMBER = IP_CT_IS_REPLY * 2 - 1
+};
+
+#ifdef __KERNEL__
+
+#include <linux/types.h>
+#include <linux/skbuff.h>
+
+#ifdef CONFIG_NF_DEBUG
+#define IP_NF_ASSERT(x) \
+do { \
+ if (!(x)) \
+ /* Wooah! I'm tripping my conntrack in a frenzy of \
+ netplay... */ \
+ printk("NF_IP_ASSERT: %s:%i(%s)\n", \
+ __FILE__, __LINE__, __FUNCTION__); \
+} while(0)
+#else
+#define IP_NF_ASSERT(x)
+#endif
+
+/* Bitset representing status of connection. */
+enum ip_conntrack_status {
+ /* It's an expected connection: bit 0 set. This bit never changed */
+ IPS_EXPECTED = 0x01,
+
+ /* We've seen packets both ways: bit 1 set. Can be set, not unset. */
+ IPS_SEEN_REPLY = 0x02
+};
+
+struct ip_conntrack_expect
+{
+ /* Internal linked list */
+ struct list_head list;
+
+ /* We expect this tuple, but DON'T CARE ABOUT THE SOURCE
+ per-protocol part. */
+ struct ip_conntrack_tuple tuple;
+
+ /* The conntrack we are part of (set iff we're live) */
+ struct ip_conntrack *expectant;
+};
+
+#if defined(CONFIG_IP_NF_NAT) || defined(CONFIG_IP_NF_NAT_MODULE)
+#include <linux/netfilter_ipv4/ip_nat.h>
+#endif
+
+#if defined(CONFIG_IP_NF_FTP) || defined(CONFIG_IP_NF_FTP_MODULE)
+#include <linux/netfilter_ipv4/ip_conntrack_ftp.h>
+#if defined(CONFIG_IP_NF_NAT) || defined(CONFIG_IP_NF_NAT_MODULE)
+#include <linux/netfilter_ipv4/ip_nat_ftp.h>
+#endif
+#endif
+
+struct ip_conntrack
+{
+ /* Usage count in here is 1 for destruct timer, 1 per skb,
+ plus 1 for any connection(s) we are `master' for */
+ struct nf_conntrack ct_general;
+
+ /* These are my tuples; original and reply */
+ struct ip_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX];
+
+ /* Have we seen traffic both ways yet? (bitset) */
+ unsigned int status;
+
+ /* Timer function; drops refcnt when it goes off. */
+ struct timer_list timeout;
+
+ /* If we're expecting another related connection, this will be
+ in expected linked list */
+ struct ip_conntrack_expect expected;
+
+ /* If we were expected by another connection, this will be it */
+ struct nf_ct_info master;
+
+ /* Helper, if any. */
+ struct ip_conntrack_helper *helper;
+
+ /* Our various nf_ct_info structs specify *what* relation this
+ packet has to the conntrack */
+ struct nf_ct_info infos[IP_CT_NUMBER];
+
+ /* Storage reserved for other modules: */
+
+ union {
+ int /*enum tcp_conntrack*/ tcp_state;
+ } proto;
+
+ union {
+#if defined(CONFIG_IP_NF_FTP) || defined(CONFIG_IP_NF_FTP_MODULE)
+ struct ip_ct_ftp ct_ftp_info;
+#endif
+ } help;
+
+#if defined(CONFIG_IP_NF_NAT) || defined(CONFIG_IP_NF_NAT_MODULE)
+ struct {
+ struct ip_nat_info info;
+ union {
+#if defined(CONFIG_IP_NF_FTP) || defined(CONFIG_IP_NF_FTP_MODULE)
+ struct ip_nat_ftp_info ftp_info[IP_CT_DIR_MAX];
+#endif
+ } help;
+#if defined(CONFIG_IP_NF_TARGET_MASQUERADE) || \
+ defined(CONFIG_IP_NF_TARGET_MASQUERADE_MODULE)
+ int masq_index;
+#endif
+ } nat;
+#endif /* CONFIG_IP_NF_NAT || CONFIG_IP_NF_NAT_MODULE */
+
+};
+
+/* Alter reply tuple (maybe alter helper). If it's already taken,
+ return 0 and don't do alteration. */
+extern int
+ip_conntrack_alter_reply(struct ip_conntrack *conntrack,
+ const struct ip_conntrack_tuple *newreply);
+
+/* Is this tuple taken? (ignoring any belonging to the given
+ conntrack). */
+extern int
+ip_conntrack_tuple_taken(const struct ip_conntrack_tuple *tuple,
+ const struct ip_conntrack *ignored_conntrack);
+
+/* Return conntrack_info and tuple hash for given skb. */
+extern struct ip_conntrack *
+ip_conntrack_get(struct sk_buff *skb, enum ip_conntrack_info *ctinfo);
+
+extern struct module *ip_conntrack_module;
+
+extern int invert_tuplepr(struct ip_conntrack_tuple *inverse,
+ const struct ip_conntrack_tuple *orig);
+
+/* Refresh conntrack for this many jiffies */
+extern void ip_ct_refresh(struct ip_conntrack *ct,
+ unsigned long extra_jiffies);
+
+/* These are for NAT. Icky. */
+/* Call me when a conntrack is destroyed. */
+extern void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack);
+
+/* Returns new sk_buff, or NULL */
+struct sk_buff *
+ip_ct_gather_frags(struct sk_buff *skb);
+
+/* Delete all conntracks which match. */
+extern void
+ip_ct_selective_cleanup(int (*kill)(const struct ip_conntrack *i, void *data),
+ void *data);
+#endif /* __KERNEL__ */
+#endif /* _IP_CONNTRACK_H */
diff --git a/include/linux/netfilter_ipv4/ip_conntrack_core.h b/include/linux/netfilter_ipv4/ip_conntrack_core.h
new file mode 100644
index 000000000..9cb49afaf
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ip_conntrack_core.h
@@ -0,0 +1,39 @@
+#ifndef _IP_CONNTRACK_CORE_H
+#define _IP_CONNTRACK_CORE_H
+#include <linux/netfilter_ipv4/lockhelp.h>
+
+/* This header is used to share core functionality between the
+ standalone connection tracking module, and the compatibility layer's use
+ of connection tracking. */
+extern unsigned int ip_conntrack_in(unsigned int hooknum,
+ struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *));
+
+extern int ip_conntrack_init(void);
+extern void ip_conntrack_cleanup(void);
+
+struct ip_conntrack_protocol;
+extern struct ip_conntrack_protocol *find_proto(u_int8_t protocol);
+/* Like above, but you already have conntrack read lock. */
+extern struct ip_conntrack_protocol *__find_proto(u_int8_t protocol);
+extern struct list_head protocol_list;
+
+/* Returns TRUE if it dealt with ICMP, and filled in skb->nfct */
+int icmp_error_track(struct sk_buff *skb);
+extern int get_tuple(const struct iphdr *iph, size_t len,
+ struct ip_conntrack_tuple *tuple,
+ struct ip_conntrack_protocol *protocol);
+
+/* Find a connection corresponding to a tuple. */
+struct ip_conntrack_tuple_hash *
+ip_conntrack_find_get(const struct ip_conntrack_tuple *tuple,
+ const struct ip_conntrack *ignored_conntrack);
+
+extern unsigned int ip_conntrack_htable_size;
+extern struct list_head *ip_conntrack_hash;
+extern struct list_head expect_list;
+DECLARE_RWLOCK_EXTERN(ip_conntrack_lock);
+#endif /* _IP_CONNTRACK_CORE_H */
+
diff --git a/include/linux/netfilter_ipv4/ip_conntrack_ftp.h b/include/linux/netfilter_ipv4/ip_conntrack_ftp.h
new file mode 100644
index 000000000..a164aed4f
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ip_conntrack_ftp.h
@@ -0,0 +1,41 @@
+#ifndef _IP_CONNTRACK_FTP_H
+#define _IP_CONNTRACK_FTP_H
+/* FTP tracking. */
+
+#ifndef __KERNEL__
+#error Only in kernel.
+#endif
+
+#include <linux/netfilter_ipv4/lockhelp.h>
+
+/* Protects ftp part of conntracks */
+DECLARE_LOCK_EXTERN(ip_ftp_lock);
+
+enum ip_ct_ftp_type
+{
+ /* PORT command from client */
+ IP_CT_FTP_PORT = IP_CT_DIR_ORIGINAL,
+ /* PASV response from server */
+ IP_CT_FTP_PASV = IP_CT_DIR_REPLY
+};
+
+/* Protected by ip_conntrack_lock */
+/* We record seq number and length of ftp ip/port text here: all in
+ host order. */
+struct ip_ct_ftp
+{
+ /* This tells NAT that this is an ftp connection */
+ int is_ftp;
+ u_int32_t seq;
+ /* 0 means not found yet */
+ u_int32_t len;
+ enum ip_ct_ftp_type ftptype;
+ /* Port that was to be used */
+ u_int16_t port;
+ /* Next valid seq position for cmd matching after newline */
+ u_int32_t seq_aft_nl[IP_CT_DIR_MAX];
+ /* 0 means seq_match_aft_nl not set */
+ int seq_aft_nl_set[IP_CT_DIR_MAX];
+};
+
+#endif /* _IP_CONNTRACK_FTP_H */
diff --git a/include/linux/netfilter_ipv4/ip_conntrack_helper.h b/include/linux/netfilter_ipv4/ip_conntrack_helper.h
new file mode 100644
index 000000000..006cedef5
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ip_conntrack_helper.h
@@ -0,0 +1,30 @@
+/* IP connection tracking helpers. */
+#ifndef _IP_CONNTRACK_HELPER_H
+#define _IP_CONNTRACK_HELPER_H
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+
+struct module;
+
+struct ip_conntrack_helper
+{
+ /* Internal use. */
+ struct list_head list;
+
+ /* Returns TRUE if it wants to help this connection (tuple is
+ the tuple of REPLY packets from server). */
+ int (*will_help)(const struct ip_conntrack_tuple *rtuple);
+
+ /* Function to call when data passes; return verdict, or -1 to
+ invalidate. */
+ int (*help)(const struct iphdr *, size_t len,
+ struct ip_conntrack *ct,
+ enum ip_conntrack_info conntrackinfo);
+};
+
+extern int ip_conntrack_helper_register(struct ip_conntrack_helper *);
+extern void ip_conntrack_helper_unregister(struct ip_conntrack_helper *);
+
+/* Add an expected connection. */
+extern int ip_conntrack_expect_related(struct ip_conntrack *related_to,
+ const struct ip_conntrack_tuple *tuple);
+#endif /*_IP_CONNTRACK_HELPER_H*/
diff --git a/include/linux/netfilter_ipv4/ip_conntrack_protocol.h b/include/linux/netfilter_ipv4/ip_conntrack_protocol.h
new file mode 100644
index 000000000..3c1b4a4c6
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ip_conntrack_protocol.h
@@ -0,0 +1,58 @@
+/* Header for use in defining a given protocol for connection tracking. */
+#ifndef _IP_CONNTRACK_PROTOCOL_H
+#define _IP_CONNTRACK_PROTOCOL_H
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+
+struct ip_conntrack_protocol
+{
+ /* Next pointer. */
+ struct list_head list;
+
+ /* Protocol number. */
+ u_int8_t proto;
+
+ /* Protocol name */
+ const char *name;
+
+ /* Try to fill in the third arg; return true if possible. */
+ int (*pkt_to_tuple)(const void *datah, size_t datalen,
+ struct ip_conntrack_tuple *tuple);
+
+ /* Invert the per-proto part of the tuple: ie. turn xmit into reply.
+ * Some packets can't be inverted: return 0 in that case.
+ */
+ int (*invert_tuple)(struct ip_conntrack_tuple *inverse,
+ const struct ip_conntrack_tuple *orig);
+
+ /* Print out the per-protocol part of the tuple. */
+ unsigned int (*print_tuple)(char *buffer,
+ const struct ip_conntrack_tuple *);
+
+ /* Print out the private part of the conntrack. */
+ unsigned int (*print_conntrack)(char *buffer,
+ const struct ip_conntrack *);
+
+ /* Returns verdict for packet, or -1 for invalid. */
+ int (*packet)(struct ip_conntrack *conntrack,
+ struct iphdr *iph, size_t len,
+ enum ip_conntrack_info ctinfo);
+
+ /* Called when a new connection for this protocol found; returns
+ * TRUE if it's OK. If so, packet() called next. */
+ int (*new)(struct ip_conntrack *conntrack,
+ struct iphdr *iph, size_t len);
+
+ /* Module (if any) which this is connected to. */
+ struct module *me;
+};
+
+/* Protocol registration. */
+extern int ip_conntrack_protocol_register(struct ip_conntrack_protocol *proto);
+extern void ip_conntrack_protocol_unregister(struct ip_conntrack_protocol *proto);
+
+/* Existing built-in protocols */
+extern struct ip_conntrack_protocol ip_conntrack_protocol_tcp;
+extern struct ip_conntrack_protocol ip_conntrack_protocol_udp;
+extern struct ip_conntrack_protocol ip_conntrack_protocol_icmp;
+extern int ip_conntrack_protocol_tcp_init(void);
+#endif /*_IP_CONNTRACK_PROTOCOL_H*/
diff --git a/include/linux/netfilter_ipv4/ip_conntrack_tuple.h b/include/linux/netfilter_ipv4/ip_conntrack_tuple.h
new file mode 100644
index 000000000..c0a845caf
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ip_conntrack_tuple.h
@@ -0,0 +1,105 @@
+#ifndef _IP_CONNTRACK_TUPLE_H
+#define _IP_CONNTRACK_TUPLE_H
+
+/* A `tuple' is a structure containing the information to uniquely
+ identify a connection. ie. if two packets have the same tuple, they
+ are in the same connection; if not, they are not.
+
+ We divide the structure along "manipulatable" and
+ "non-manipulatable" lines, for the benefit of the NAT code.
+*/
+
+/* The protocol-specific manipulable parts of the tuple. */
+union ip_conntrack_manip_proto
+{
+ /* Add other protocols here. */
+ u_int16_t all;
+
+ struct {
+ u_int16_t port;
+ } tcp;
+ struct {
+ u_int16_t port;
+ } udp;
+ struct {
+ u_int16_t id;
+ } icmp;
+};
+
+/* The manipulable part of the tuple. */
+struct ip_conntrack_manip
+{
+ u_int32_t ip;
+ union ip_conntrack_manip_proto u;
+ u_int16_t pad; /* Must be set to 0 for memcmp. */
+};
+
+/* This contains the information to distinguish a connection. */
+struct ip_conntrack_tuple
+{
+ struct ip_conntrack_manip src;
+
+ /* These are the parts of the tuple which are fixed. */
+ struct {
+ u_int32_t ip;
+ union {
+ /* Add other protocols here. */
+ u_int16_t all;
+
+ struct {
+ u_int16_t port;
+ } tcp;
+ struct {
+ u_int16_t port;
+ } udp;
+ struct {
+ u_int8_t type, code;
+ } icmp;
+ } u;
+
+ /* The protocol. */
+ u_int16_t protonum;
+ } dst;
+};
+
+#define IP_PARTS_NATIVE(n) \
+(unsigned int)((n)>>24)&0xFF, \
+(unsigned int)((n)>>16)&0xFF, \
+(unsigned int)((n)>>8)&0xFF, \
+(unsigned int)((n)&0xFF)
+
+#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
+
+#ifdef __KERNEL__
+
+#define DUMP_TUPLE(tp) \
+DEBUGP("tuple %p: %u %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u\n", \
+ (tp), (tp)->dst.protonum, \
+ IP_PARTS((tp)->src.ip), ntohs((tp)->src.u.all), \
+ IP_PARTS((tp)->dst.ip), ntohs((tp)->dst.u.all))
+
+#define CTINFO2DIR(ctinfo) ((ctinfo) == IP_CT_IS_REPLY ? IP_CT_DIR_REPLY : IP_CT_DIR_ORIGINAL)
+
+/* If we're the first tuple, it's the original dir. */
+#define DIRECTION(h) ((enum ip_conntrack_dir)(&(h)->ctrack->tuplehash[1] == (h)))
+
+enum ip_conntrack_dir
+{
+ IP_CT_DIR_ORIGINAL,
+ IP_CT_DIR_REPLY,
+ IP_CT_DIR_MAX
+};
+
+/* Connections have two entries in the hash table: one for each way */
+struct ip_conntrack_tuple_hash
+{
+ struct list_head list;
+
+ struct ip_conntrack_tuple tuple;
+
+ /* this == &ctrack->tuplehash[DIRECTION(this)]. */
+ struct ip_conntrack *ctrack;
+};
+
+#endif /* __KERNEL__ */
+#endif /* _IP_CONNTRACK_TUPLE_H */
diff --git a/include/linux/netfilter_ipv4/ip_nat.h b/include/linux/netfilter_ipv4/ip_nat.h
new file mode 100644
index 000000000..8eb37d6d5
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ip_nat.h
@@ -0,0 +1,117 @@
+#ifndef _IP_NAT_H
+#define _IP_NAT_H
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
+
+#define IP_NAT_MAPPING_TYPE_MAX_NAMELEN 16
+
+enum ip_nat_manip_type
+{
+ IP_NAT_MANIP_SRC,
+ IP_NAT_MANIP_DST
+};
+
+/* SRC manip occurs only on POST_ROUTING */
+#define HOOK2MANIP(hooknum) ((hooknum) != NF_IP_POST_ROUTING)
+
+/* 2.3.19 (I hope) will define this in linux/netfilter_ipv4.h. */
+#ifndef SO_ORIGINAL_DST
+#define SO_ORIGINAL_DST 80
+#endif
+
+#define IP_NAT_RANGE_MAP_IPS 1
+#define IP_NAT_RANGE_PROTO_SPECIFIED 2
+/* Used internally by get_unique_tuple(). */
+#define IP_NAT_RANGE_FULL 4
+
+/* Single range specification. */
+struct ip_nat_range
+{
+ /* Set to OR of flags above. */
+ unsigned int flags;
+
+ /* Inclusive: network order. */
+ u_int32_t min_ip, max_ip;
+
+ /* Inclusive: network order */
+ union ip_conntrack_manip_proto min, max;
+};
+
+/* A range consists of an array of 1 or more ip_nat_range */
+struct ip_nat_multi_range
+{
+ unsigned int rangesize;
+
+ /* hangs off end. */
+ struct ip_nat_range range[1];
+};
+
+#ifdef __KERNEL__
+#include <linux/list.h>
+#include <linux/netfilter_ipv4/lockhelp.h>
+
+/* Protects NAT hash tables, and NAT-private part of conntracks. */
+DECLARE_RWLOCK_EXTERN(ip_nat_lock);
+
+/* Hashes for by-source and IP/protocol. */
+struct ip_nat_hash
+{
+ struct list_head list;
+
+ /* conntrack we're embedded in: NULL if not in hash. */
+ struct ip_conntrack *conntrack;
+};
+
+/* Worst case: local-out manip + 1 post-routing, and reverse dirn. */
+#define IP_NAT_MAX_MANIPS (2*3)
+
+struct ip_nat_info_manip
+{
+ /* The direction. */
+ u_int8_t direction;
+
+ /* Which hook the manipulation happens on. */
+ u_int8_t hooknum;
+
+ /* The manipulation type. */
+ u_int8_t maniptype;
+
+ /* Manipulations to occur at each conntrack in this dirn. */
+ struct ip_conntrack_manip manip;
+};
+
+/* The structure embedded in the conntrack structure. */
+struct ip_nat_info
+{
+ /* Set to zero when conntrack created: bitmask of maniptypes */
+ int initialized;
+
+ unsigned int num_manips;
+
+ /* Manipulations to be done on this conntrack. */
+ struct ip_nat_info_manip manips[IP_NAT_MAX_MANIPS];
+
+ /* The mapping type which created us (NULL for null mapping). */
+ const struct ip_nat_mapping_type *mtype;
+
+ struct ip_nat_hash bysource, byipsproto;
+
+ /* Helper (NULL if none). */
+ struct ip_nat_helper *helper;
+};
+
+/* Set up the info structure to map into this range. */
+extern unsigned int ip_nat_setup_info(struct ip_conntrack *conntrack,
+ const struct ip_nat_multi_range *mr,
+ unsigned int hooknum);
+
+/* Is this tuple already taken? (not by us)*/
+extern int ip_nat_used_tuple(const struct ip_conntrack_tuple *tuple,
+ const struct ip_conntrack *ignored_conntrack);
+
+/* Calculate relative checksum. */
+extern u_int16_t ip_nat_cheat_check(u_int32_t oldvalinv,
+ u_int32_t newval,
+ u_int16_t oldcheck);
+#endif /*__KERNEL__*/
+#endif
diff --git a/include/linux/netfilter_ipv4/ip_nat_core.h b/include/linux/netfilter_ipv4/ip_nat_core.h
new file mode 100644
index 000000000..28735e0c1
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ip_nat_core.h
@@ -0,0 +1,33 @@
+#ifndef _IP_NAT_CORE_H
+#define _IP_NAT_CORE_H
+#include <linux/list.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+
+/* This header used to share core functionality between the standalone
+ NAT module, and the compatibility layer's use of NAT for masquerading. */
+extern int ip_nat_init(void);
+extern void ip_nat_cleanup(void);
+
+extern unsigned int do_bindings(struct ip_conntrack *ct,
+ enum ip_conntrack_info conntrackinfo,
+ struct ip_nat_info *info,
+ unsigned int hooknum,
+ struct sk_buff **pskb);
+
+extern struct list_head protos;
+
+extern void icmp_reply_translation(struct sk_buff *skb,
+ struct ip_conntrack *conntrack,
+ unsigned int hooknum,
+ int dir);
+
+extern void replace_in_hashes(struct ip_conntrack *conntrack,
+ struct ip_nat_info *info);
+extern void place_in_hashes(struct ip_conntrack *conntrack,
+ struct ip_nat_info *info);
+
+/* Built-in protocols. */
+extern struct ip_nat_protocol ip_nat_protocol_tcp;
+extern struct ip_nat_protocol ip_nat_protocol_udp;
+extern struct ip_nat_protocol ip_nat_protocol_icmp;
+#endif /* _IP_NAT_CORE_H */
diff --git a/include/linux/netfilter_ipv4/ip_nat_ftp.h b/include/linux/netfilter_ipv4/ip_nat_ftp.h
new file mode 100644
index 000000000..d84015529
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ip_nat_ftp.h
@@ -0,0 +1,21 @@
+#ifndef _IP_NAT_FTP_H
+#define _IP_NAT_FTP_H
+/* FTP extension for TCP NAT alteration. */
+
+#ifndef __KERNEL__
+#error Only in kernel.
+#endif
+
+/* Protects ftp part of conntracks */
+DECLARE_LOCK_EXTERN(ip_ftp_lock);
+
+/* We keep track of where the last SYN correction was, and the SYN
+ offsets before and after that correction. Two of these (indexed by
+ direction). */
+struct ip_nat_ftp_info
+{
+ u_int32_t syn_correction_pos;
+ int32_t syn_offset_before, syn_offset_after;
+};
+
+#endif /* _IP_NAT_FTP_H */
diff --git a/include/linux/netfilter_ipv4/ip_nat_helper.h b/include/linux/netfilter_ipv4/ip_nat_helper.h
new file mode 100644
index 000000000..1578d6efc
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ip_nat_helper.h
@@ -0,0 +1,30 @@
+#ifndef _IP_NAT_HELPER_H
+#define _IP_NAT_HELPER_H
+/* NAT protocol helper routines. */
+
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+
+struct sk_buff;
+
+struct ip_nat_helper
+{
+ /* Internal use */
+ struct list_head list;
+
+ /* Here's the protocol and dst we care about. */
+ u_int16_t protocol;
+ u_int16_t protocol_dst;
+
+ /* Helper function: returns verdict */
+ unsigned int (*help)(struct ip_conntrack *ct,
+ struct ip_nat_info *info,
+ enum ip_conntrack_info ctinfo,
+ unsigned int hooknum,
+ struct sk_buff **pskb);
+
+ const char *name;
+};
+
+extern int ip_nat_helper_register(struct ip_nat_helper *me);
+extern void ip_nat_helper_unregister(struct ip_nat_helper *me);
+#endif
diff --git a/include/linux/netfilter_ipv4/ip_nat_protocol.h b/include/linux/netfilter_ipv4/ip_nat_protocol.h
new file mode 100644
index 000000000..42e2ebf33
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ip_nat_protocol.h
@@ -0,0 +1,57 @@
+/* Header for use in defining a given protocol. */
+#ifndef _IP_NAT_PROTOCOL_H
+#define _IP_NAT_PROTOCOL_H
+#include <linux/init.h>
+#include <linux/list.h>
+
+struct iphdr;
+struct ip_nat_range;
+
+struct ip_nat_protocol
+{
+ struct list_head list;
+
+ /* Protocol name */
+ const char *name;
+
+ /* Protocol number. */
+ unsigned int protonum;
+
+ /* Do a packet translation according to the ip_nat_proto_manip
+ * and manip type. */
+ void (*manip_pkt)(struct iphdr *iph, size_t len,
+ const struct ip_conntrack_manip *manip,
+ enum ip_nat_manip_type maniptype);
+
+ /* Is the manipable part of the tuple between min and max incl? */
+ int (*in_range)(const struct ip_conntrack_tuple *tuple,
+ enum ip_nat_manip_type maniptype,
+ const union ip_conntrack_manip_proto *min,
+ const union ip_conntrack_manip_proto *max);
+
+ /* Alter the per-proto part of the tuple (depending on
+ maniptype), to give a unique tuple in the given range if
+ possible; return false if not. Per-protocol part of tuple
+ is initialized to the incoming packet. */
+ int (*unique_tuple)(struct ip_conntrack_tuple *tuple,
+ const struct ip_nat_range *range,
+ enum ip_nat_manip_type maniptype,
+ const struct ip_conntrack *conntrack);
+
+ unsigned int (*print)(char *buffer,
+ const struct ip_conntrack_tuple *match,
+ const struct ip_conntrack_tuple *mask);
+
+ unsigned int (*print_range)(char *buffer,
+ const struct ip_nat_range *range);
+};
+
+/* Protocol registration. */
+extern int ip_nat_protocol_register(struct ip_nat_protocol *proto);
+extern void ip_nat_protocol_unregister(struct ip_nat_protocol *proto);
+
+extern int init_protocols(void) __init;
+extern void cleanup_protocols(void);
+extern struct ip_nat_protocol *find_nat_proto(u_int16_t protonum);
+
+#endif /*_IP_NAT_PROTO_H*/
diff --git a/include/linux/netfilter_ipv4/ip_nat_rule.h b/include/linux/netfilter_ipv4/ip_nat_rule.h
new file mode 100644
index 000000000..6c92b285d
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ip_nat_rule.h
@@ -0,0 +1,35 @@
+#ifndef _IP_NAT_RULE_H
+#define _IP_NAT_RULE_H
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ip_nat.h>
+
+#ifdef __KERNEL__
+/* Want to be told when we first NAT an expected packet for a conntrack? */
+struct ip_nat_expect
+{
+ struct list_head list;
+
+ /* Returns 1 (and sets verdict) if it has setup NAT for this
+ connection */
+ int (*expect)(struct sk_buff **pskb,
+ unsigned int hooknum,
+ struct ip_conntrack *ct,
+ struct ip_nat_info *info,
+ struct ip_conntrack *master,
+ struct ip_nat_info *masterinfo,
+ unsigned int *verdict);
+};
+
+extern int ip_nat_expect_register(struct ip_nat_expect *expect);
+extern void ip_nat_expect_unregister(struct ip_nat_expect *expect);
+extern int ip_nat_rule_init(void) __init;
+extern void ip_nat_rule_cleanup(void);
+extern int ip_nat_rule_find(struct sk_buff **pskb,
+ unsigned int hooknum,
+ const struct net_device *in,
+ const struct net_device *out,
+ struct ip_conntrack *ct,
+ struct ip_nat_info *info);
+#endif
+#endif /* _IP_NAT_RULE_H */
diff --git a/include/linux/netfilter_ipv4/ip_queue.h b/include/linux/netfilter_ipv4/ip_queue.h
new file mode 100644
index 000000000..8bbd6230f
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ip_queue.h
@@ -0,0 +1,86 @@
+/*
+ * This is a module which is used for queueing IPv4 packets and
+ * communicating with userspace via netlink.
+ *
+ * (C) 2000 James Morris
+ */
+#ifndef _IP_QUEUE_H
+#define _IP_QUEUE_H
+
+#ifdef __KERNEL__
+#ifdef DEBUG_IPQ
+#define QDEBUG(x...) printk(KERN_DEBUG ## x)
+#else
+#define QDEBUG(x...)
+#endif /* DEBUG_IPQ */
+#else
+#include <net/if.h>
+#endif /* ! __KERNEL__ */
+
+/* Messages sent from kernel */
+typedef struct ipq_packet_msg {
+ unsigned long packet_id; /* ID of queued packet */
+ unsigned long mark; /* Netfilter mark value */
+ long timestamp_sec; /* Packet arrival time (seconds) */
+ long timestamp_usec; /* Packet arrvial time (+useconds) */
+ unsigned int hook; /* Netfilter hook we rode in on */
+ char indev_name[IFNAMSIZ]; /* Name of incoming interface */
+ char outdev_name[IFNAMSIZ]; /* Name of outgoing interface */
+ size_t data_len; /* Length of packet data */
+ /* Optional packet data follows */
+} ipq_packet_msg_t;
+
+/* Messages sent from userspace */
+typedef struct ipq_mode_msg {
+ unsigned char value; /* Requested mode */
+ size_t range; /* Optional range of packet requested */
+} ipq_mode_msg_t;
+
+typedef struct ipq_verdict_msg {
+ unsigned int value; /* Verdict to hand to netfilter */
+ unsigned long id; /* Packet ID for this verdict */
+ size_t data_len; /* Length of replacement data */
+ /* Optional replacement data follows */
+} ipq_verdict_msg_t;
+
+typedef struct ipq_peer_msg {
+ union {
+ ipq_verdict_msg_t verdict;
+ ipq_mode_msg_t mode;
+ } msg;
+} ipq_peer_msg_t;
+
+/* Each queued packet has one of these states */
+enum {
+ IPQ_PS_NEW, /* Newly arrived packet */
+ IPQ_PS_WAITING, /* User has been notified of packet,
+ we're waiting for a verdict */
+ IPQ_PS_VERDICT /* Packet has been assigned verdict,
+ waiting to be reinjected */
+};
+#define IPQ_PS_MAX IPQ_PS_VERDICT
+
+/* The queue operates in one of these states */
+enum {
+ IPQ_QS_HOLD, /* Hold all packets in queue */
+ IPQ_QS_COPY, /* Copy metadata and/or packets to user */
+ IPQ_QS_FLUSH /* Flush and drop all queue entries */
+};
+#define IPQ_QS_MAX IPQ_QS_FLUSH
+
+/* Modes requested by peer */
+enum {
+ IPQ_COPY_NONE, /* Copy nothing */
+ IPQ_COPY_META, /* Copy metadata */
+ IPQ_COPY_PACKET /* Copy metadata + packet (range) */
+};
+#define IPQ_COPY_MAX IPQ_COPY_PACKET
+
+/* Types of messages */
+#define IPQM_BASE 0x10 /* standard netlink messages below this */
+#define IPQM_MODE (IPQM_BASE + 1) /* Mode request from peer */
+#define IPQM_VERDICT (IPQM_BASE + 2) /* Verdict from peer */
+#define IPQM_PACKET (IPQM_BASE + 3) /* Packet from kernel */
+#define IPQM_MAX (IPQM_BASE + 4)
+
+#endif /*_IP_QUEUE_H*/
diff --git a/include/linux/netfilter_ipv4/ip_tables.h b/include/linux/netfilter_ipv4/ip_tables.h
new file mode 100644
index 000000000..20361b064
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ip_tables.h
@@ -0,0 +1,420 @@
+/*
+ * 25-Jul-1998 Major changes to allow for ip chain table
+ *
+ * 3-Jan-2000 Named tables to allow packet selection for different uses.
+ */
+
+/*
+ * Format of an IP firewall descriptor
+ *
+ * src, dst, src_mask, dst_mask are always stored in network byte order.
+ * flags are stored in host byte order (of course).
+ * Port numbers are stored in HOST byte order.
+ */
+
+#ifndef _IPTABLES_H
+#define _IPTABLES_H
+
+#ifdef __KERNEL__
+#include <linux/if.h>
+#include <linux/types.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/skbuff.h>
+#endif
+#include <linux/netfilter_ipv4.h>
+
+#define IPT_FUNCTION_MAXNAMELEN 32
+#define IPT_TABLE_MAXNAMELEN 32
+
+/* Yes, Virginia, you have to zero the padding. */
+struct ipt_ip {
+ /* Source and destination IP addr */
+ struct in_addr src, dst;
+ /* Mask for src and dest IP addr */
+ struct in_addr smsk, dmsk;
+ char iniface[IFNAMSIZ], outiface[IFNAMSIZ];
+ unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ];
+
+ /* Protocol, 0 = ANY */
+ u_int16_t proto;
+
+ /* Flags word */
+ u_int8_t flags;
+ /* Inverse flags */
+ u_int8_t invflags;
+};
+
+struct ipt_entry_match
+{
+ /* Total length */
+ u_int16_t match_size;
+ union {
+ /* Used by userspace */
+ char name[IPT_FUNCTION_MAXNAMELEN];
+ /* Used inside the kernel */
+ struct ipt_match *match;
+ } u;
+
+ unsigned char data[0];
+};
+
+struct ipt_entry_target
+{
+ /* Total length */
+ u_int16_t target_size;
+ union {
+ /* Used by userspace */
+ char name[IPT_FUNCTION_MAXNAMELEN];
+ /* Used inside the kernel */
+ struct ipt_target *target;
+ } u;
+
+ unsigned char data[0];
+};
+
+struct ipt_standard_target
+{
+ struct ipt_entry_target target;
+ int verdict;
+};
+
+struct ipt_counters
+{
+ u_int64_t pcnt, bcnt; /* Packet and byte counters */
+};
+
+/* Values for "flag" field in struct ipt_ip (general ip structure). */
+#define IPT_F_FRAG 0x01 /* Set if rule is a fragment rule */
+#define IPT_F_MASK 0x01 /* All possible flag bits mask. */
+
+/* Values for "inv" field in struct ipt_ip. */
+#define IPT_INV_VIA_IN 0x01 /* Invert the sense of IN IFACE. */
+#define IPT_INV_VIA_OUT 0x02 /* Invert the sense of OUT IFACE */
+#define IPT_INV_TOS 0x04 /* Invert the sense of TOS. */
+#define IPT_INV_SRCIP 0x08 /* Invert the sense of SRC IP. */
+#define IPT_INV_DSTIP 0x10 /* Invert the sense of DST OP. */
+#define IPT_INV_FRAG 0x20 /* Invert the sense of FRAG. */
+#define IPT_INV_PROTO 0x40 /* Invert the sense of PROTO. */
+#define IPT_INV_MASK 0x7F /* All possible flag bits mask. */
+
+/* This structure defines each of the firewall rules. Consists of 3
+ parts which are 1) general IP header stuff 2) match specific
+ stuff 3) the target to perform if the rule matches */
+struct ipt_entry
+{
+ struct ipt_ip ip;
+
+ /* Mark with fields that we care about. */
+ unsigned int nfcache;
+
+ /* Size of ipt_entry + matches */
+ u_int16_t target_offset;
+ /* Size of ipt_entry + matches + target */
+ u_int16_t next_offset;
+
+ /* Back pointer */
+ unsigned int comefrom;
+
+ /* Packet and byte counters. */
+ struct ipt_counters counters;
+
+ /* The matches (if any), then the target. */
+ unsigned char elems[0];
+};
+
+/*
+ * New IP firewall options for [gs]etsockopt at the RAW IP level.
+ * Unlike BSD Linux inherits IP options so you don't have to use a raw
+ * socket for this. Instead we check rights in the calls. */
+#define IPT_BASE_CTL 64 /* base for firewall socket options */
+
+#define IPT_SO_SET_REPLACE (IPT_BASE_CTL)
+#define IPT_SO_SET_ADD_COUNTERS (IPT_BASE_CTL + 1)
+#define IPT_SO_SET_MAX IPT_SO_SET_ADD_COUNTERS
+
+#define IPT_SO_GET_INFO (IPT_BASE_CTL)
+#define IPT_SO_GET_ENTRIES (IPT_BASE_CTL + 1)
+#define IPT_SO_GET_MAX IPT_SO_GET_ENTRIES
+
+/* CONTINUE verdict for targets */
+#define IPT_CONTINUE 0xFFFFFFFF
+
+/* For standard target */
+#define IPT_RETURN (-NF_MAX_VERDICT - 1)
+
+/* TCP matching stuff */
+struct ipt_tcp
+{
+ u_int16_t spts[2]; /* Source port range. */
+ u_int16_t dpts[2]; /* Destination port range. */
+ u_int8_t option; /* TCP Option iff non-zero*/
+ u_int8_t flg_mask; /* TCP flags mask byte */
+ u_int8_t flg_cmp; /* TCP flags compare byte */
+ u_int8_t invflags; /* Inverse flags */
+};
+
+/* Values for "inv" field in struct ipt_tcp. */
+#define IPT_TCP_INV_SRCPT 0x01 /* Invert the sense of source ports. */
+#define IPT_TCP_INV_DSTPT 0x02 /* Invert the sense of dest ports. */
+#define IPT_TCP_INV_FLAGS 0x04 /* Invert the sense of TCP flags. */
+#define IPT_TCP_INV_OPTION 0x08 /* Invert the sense of option test. */
+#define IPT_TCP_INV_MASK 0x0F /* All possible flags. */
+
+/* UDP matching stuff */
+struct ipt_udp
+{
+ u_int16_t spts[2]; /* Source port range. */
+ u_int16_t dpts[2]; /* Destination port range. */
+ u_int8_t invflags; /* Inverse flags */
+};
+
+/* Values for "invflags" field in struct ipt_udp. */
+#define IPT_UDP_INV_SRCPT 0x01 /* Invert the sense of source ports. */
+#define IPT_UDP_INV_DSTPT 0x02 /* Invert the sense of dest ports. */
+#define IPT_UDP_INV_MASK 0x03 /* All possible flags. */
+
+/* ICMP matching stuff */
+struct ipt_icmp
+{
+ u_int8_t type; /* type to match */
+ u_int8_t code[2]; /* range of code */
+ u_int8_t invflags; /* Inverse flags */
+};
+
+/* Values for "inv" field for struct ipt_icmp. */
+#define IPT_ICMP_INV 0x01 /* Invert the sense of type/code test */
+
+/* The argument to IPT_SO_GET_INFO */
+struct ipt_getinfo
+{
+ /* Which table: caller fills this in. */
+ char name[IPT_TABLE_MAXNAMELEN];
+
+ /* Kernel fills these in. */
+ /* Which hook entry points are valid: bitmask */
+ unsigned int valid_hooks;
+
+ /* Hook entry points: one per netfilter hook. */
+ unsigned int hook_entry[NF_IP_NUMHOOKS];
+
+ /* Underflow points. */
+ unsigned int underflow[NF_IP_NUMHOOKS];
+
+ /* Number of entries */
+ unsigned int num_entries;
+
+ /* Size of entries. */
+ unsigned int size;
+};
+
+/* The argument to IPT_SO_SET_REPLACE. */
+struct ipt_replace
+{
+ /* Which table. */
+ char name[IPT_TABLE_MAXNAMELEN];
+
+ /* Which hook entry points are valid: bitmask. You can't
+ change this. */
+ unsigned int valid_hooks;
+
+ /* Number of entries */
+ unsigned int num_entries;
+
+ /* Total size of new entries */
+ unsigned int size;
+
+ /* Hook entry points. */
+ unsigned int hook_entry[NF_IP_NUMHOOKS];
+
+ /* Underflow points. */
+ unsigned int underflow[NF_IP_NUMHOOKS];
+
+ /* Information about old entries: */
+ /* Number of counters (must be equal to current number of entries). */
+ unsigned int num_counters;
+ /* The old entries' counters. */
+ struct ipt_counters *counters;
+
+ /* The entries (hang off end: not really an array). */
+ struct ipt_entry entries[0];
+};
+
+/* The argument to IPT_SO_ADD_COUNTERS. */
+struct ipt_counters_info
+{
+ /* Which table. */
+ char name[IPT_TABLE_MAXNAMELEN];
+
+ unsigned int num_counters;
+
+ /* The counters (actually `number' of these). */
+ struct ipt_counters counters[0];
+};
+
+/* The argument to IPT_SO_GET_ENTRIES. */
+struct ipt_get_entries
+{
+ /* Which table: user fills this in. */
+ char name[IPT_TABLE_MAXNAMELEN];
+
+ /* User fills this in: total entry size. */
+ unsigned int size;
+
+ /* The entries. */
+ unsigned char entries[0];
+};
+
+/* Standard return verdict, or do jump. */
+#define IPT_STANDARD_TARGET ""
+/* Error verdict. */
+#define IPT_ERROR_TARGET "ERROR"
+
+/* Helper functions */
+extern __inline__ struct ipt_entry_target *
+ipt_get_target(struct ipt_entry *e)
+{
+ return (void *)e + e->target_offset;
+}
+
+/* fn returns 0 to continue iteration */
+#define IPT_MATCH_ITERATE(e, fn, args...) \
+({ \
+ unsigned int __i; \
+ int __ret = 0; \
+ struct ipt_entry_match *__m; \
+ \
+ for (__i = sizeof(struct ipt_entry); \
+ __i < (e)->target_offset; \
+ __i += __m->match_size) { \
+ __m = (void *)(e) + __i; \
+ \
+ __ret = fn(__m , ## args); \
+ if (__ret != 0) \
+ break; \
+ } \
+ __ret; \
+})
+
+/* fn returns 0 to continue iteration */
+#define IPT_ENTRY_ITERATE(entries, size, fn, args...) \
+({ \
+ unsigned int __i; \
+ int __ret = 0; \
+ struct ipt_entry *__e; \
+ \
+ for (__i = 0; __i < (size); __i += __e->next_offset) { \
+ __e = (void *)(entries) + __i; \
+ \
+ __ret = fn(__e , ## args); \
+ if (__ret != 0) \
+ break; \
+ } \
+ __ret; \
+})
+
+/*
+ * Main firewall chains definitions and global var's definitions.
+ */
+#ifdef __KERNEL__
+
+#include <linux/init.h>
+extern void ipt_init(void) __init;
+
+struct ipt_match
+{
+ struct list_head list;
+
+ const char name[IPT_FUNCTION_MAXNAMELEN];
+
+ /* Return true or false: return FALSE and set *hotdrop = 1 to
+ force immediate packet drop. */
+ int (*match)(const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *matchinfo,
+ int offset,
+ const void *hdr,
+ u_int16_t datalen,
+ int *hotdrop);
+
+ /* Called when user tries to insert an entry of this type. */
+ /* Should return true or false. */
+ int (*checkentry)(const char *tablename,
+ const struct ipt_ip *ip,
+ void *matchinfo,
+ unsigned int matchinfosize,
+ unsigned int hook_mask);
+
+ /* Set this to THIS_MODULE if you are a module, otherwise NULL */
+ struct module *me;
+};
+
+/* Registration hooks for targets. */
+struct ipt_target
+{
+ struct list_head list;
+
+ const char name[IPT_FUNCTION_MAXNAMELEN];
+
+ /* Returns verdict. */
+ unsigned int (*target)(struct sk_buff **pskb,
+ unsigned int hooknum,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *targinfo,
+ void *userdata);
+
+ /* Called when user tries to insert an entry of this type:
+ hook_mask is a bitmask of hooks from which it can be
+ called. */
+ /* Should return true or false. */
+ int (*checkentry)(const char *tablename,
+ const struct ipt_entry *e,
+ void *targinfo,
+ unsigned int targinfosize,
+ unsigned int hook_mask);
+
+ /* Set this to THIS_MODULE if you are a module, otherwise NULL */
+ struct module *me;
+};
+
+extern int ipt_register_target(struct ipt_target *target);
+extern void ipt_unregister_target(struct ipt_target *target);
+
+extern int ipt_register_match(struct ipt_match *match);
+extern void ipt_unregister_match(struct ipt_match *match);
+
+/* Furniture shopping... */
+struct ipt_table
+{
+ struct list_head list;
+
+ /* A unique name... */
+ char name[IPT_TABLE_MAXNAMELEN];
+
+ /* Seed table: copied in register_table */
+ struct ipt_replace *table;
+
+ /* What hooks you will enter on */
+ unsigned int valid_hooks;
+
+ /* Lock for the curtain */
+ rwlock_t lock;
+
+ /* Man behind the curtain... */
+ struct ipt_table_info *private;
+};
+
+extern int ipt_register_table(struct ipt_table *table);
+extern void ipt_unregister_table(struct ipt_table *table);
+extern unsigned int ipt_do_table(struct sk_buff **pskb,
+ unsigned int hook,
+ const struct net_device *in,
+ const struct net_device *out,
+ struct ipt_table *table,
+ void *userdata);
+
+#define IPT_ALIGN(s) (((s) + (__alignof__(struct ipt_match)-1)) & ~(__alignof__(struct ipt_match)-1))
+#endif /*__KERNEL__*/
+#endif /* _IPTABLES_H */
diff --git a/include/linux/netfilter_ipv4/ipchains_core.h b/include/linux/netfilter_ipv4/ipchains_core.h
new file mode 100644
index 000000000..13546ff8d
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipchains_core.h
@@ -0,0 +1,193 @@
+/*
+ * This code is heavily based on the code in ip_fw.h; see that file for
+ * copyrights and attributions. This code is basically GPL.
+ *
+ * 15-Feb-1997: Major changes to allow graphs for firewall rules.
+ * Paul Russell <Paul.Russell@rustcorp.com.au> and
+ * Michael Neuling <Michael.Neuling@rustcorp.com.au>
+ * 2-Nov-1997: Changed types to __u16, etc.
+ * Removed IP_FW_F_TCPACK & IP_FW_F_BIDIR.
+ * Added inverse flags field.
+ * Removed multiple port specs.
+ */
+
+/*
+ * Format of an IP firewall descriptor
+ *
+ * src, dst, src_mask, dst_mask are always stored in network byte order.
+ * flags are stored in host byte order (of course).
+ * Port numbers are stored in HOST byte order.
+ */
+
+#ifndef _IP_FWCHAINS_H
+#define _IP_FWCHAINS_H
+
+#ifdef __KERNEL__
+#include <linux/icmp.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#endif /* __KERNEL__ */
+#define IP_FW_MAX_LABEL_LENGTH 8
+typedef char ip_chainlabel[IP_FW_MAX_LABEL_LENGTH+1];
+
+struct ip_fw
+{
+ struct in_addr fw_src, fw_dst; /* Source and destination IP addr */
+ struct in_addr fw_smsk, fw_dmsk; /* Mask for src and dest IP addr */
+ __u32 fw_mark; /* ID to stamp on packet */
+ __u16 fw_proto; /* Protocol, 0 = ANY */
+ __u16 fw_flg; /* Flags word */
+ __u16 fw_invflg; /* Inverse flags */
+ __u16 fw_spts[2]; /* Source port range. */
+ __u16 fw_dpts[2]; /* Destination port range. */
+ __u16 fw_redirpt; /* Port to redirect to. */
+ __u16 fw_outputsize; /* Max amount to output to
+ NETLINK */
+ char fw_vianame[IFNAMSIZ]; /* name of interface "via" */
+ __u8 fw_tosand, fw_tosxor; /* Revised packet priority */
+};
+
+struct ip_fwuser
+{
+ struct ip_fw ipfw;
+ ip_chainlabel label;
+};
+
+/* Values for "fw_flg" field . */
+#define IP_FW_F_PRN 0x0001 /* Print packet if it matches */
+#define IP_FW_F_TCPSYN 0x0002 /* For tcp packets-check SYN only */
+#define IP_FW_F_FRAG 0x0004 /* Set if rule is a fragment rule */
+#define IP_FW_F_MARKABS 0x0008 /* Set the mark to fw_mark, not add. */
+#define IP_FW_F_WILDIF 0x0010 /* Need only match start of interface name. */
+#define IP_FW_F_NETLINK 0x0020 /* Redirect to netlink: 2.1.x only */
+#define IP_FW_F_MASK 0x003F /* All possible flag bits mask */
+
+/* Values for "fw_invflg" field. */
+#define IP_FW_INV_SRCIP 0x0001 /* Invert the sense of fw_src. */
+#define IP_FW_INV_DSTIP 0x0002 /* Invert the sense of fw_dst. */
+#define IP_FW_INV_PROTO 0x0004 /* Invert the sense of fw_proto. */
+#define IP_FW_INV_SRCPT 0x0008 /* Invert the sense of source ports. */
+#define IP_FW_INV_DSTPT 0x0010 /* Invert the sense of destination ports. */
+#define IP_FW_INV_VIA 0x0020 /* Invert the sense of fw_vianame. */
+#define IP_FW_INV_SYN 0x0040 /* Invert the sense of IP_FW_F_TCPSYN. */
+#define IP_FW_INV_FRAG 0x0080 /* Invert the sense of IP_FW_F_FRAG. */
+
+/*
+ * New IP firewall options for [gs]etsockopt at the RAW IP level.
+ * Unlike BSD Linux inherits IP options so you don't have to use
+ * a raw socket for this. Instead we check rights in the calls. */
+
+#define IP_FW_BASE_CTL 64 /* base for firewall socket options */
+
+#define IP_FW_APPEND (IP_FW_BASE_CTL) /* Takes ip_fwchange */
+#define IP_FW_REPLACE (IP_FW_BASE_CTL+1) /* Takes ip_fwnew */
+#define IP_FW_DELETE_NUM (IP_FW_BASE_CTL+2) /* Takes ip_fwdelnum */
+#define IP_FW_DELETE (IP_FW_BASE_CTL+3) /* Takes ip_fwchange */
+#define IP_FW_INSERT (IP_FW_BASE_CTL+4) /* Takes ip_fwnew */
+#define IP_FW_FLUSH (IP_FW_BASE_CTL+5) /* Takes ip_chainlabel */
+#define IP_FW_ZERO (IP_FW_BASE_CTL+6) /* Takes ip_chainlabel */
+#define IP_FW_CHECK (IP_FW_BASE_CTL+7) /* Takes ip_fwtest */
+#define IP_FW_MASQ_TIMEOUTS (IP_FW_BASE_CTL+8) /* Takes 3 ints */
+#define IP_FW_CREATECHAIN (IP_FW_BASE_CTL+9) /* Takes ip_chainlabel */
+#define IP_FW_DELETECHAIN (IP_FW_BASE_CTL+10) /* Takes ip_chainlabel */
+#define IP_FW_POLICY (IP_FW_BASE_CTL+11) /* Takes ip_fwpolicy */
+/* Masquerade control, only 1 optname */
+
+#define IP_FW_MASQ_CTL (IP_FW_BASE_CTL+12) /* General ip_masq ctl */
+
+/* Builtin chain labels */
+#define IP_FW_LABEL_FORWARD "forward"
+#define IP_FW_LABEL_INPUT "input"
+#define IP_FW_LABEL_OUTPUT "output"
+
+/* Special targets */
+#define IP_FW_LABEL_MASQUERADE "MASQ"
+#define IP_FW_LABEL_REDIRECT "REDIRECT"
+#define IP_FW_LABEL_ACCEPT "ACCEPT"
+#define IP_FW_LABEL_BLOCK "DENY"
+#define IP_FW_LABEL_REJECT "REJECT"
+#define IP_FW_LABEL_RETURN "RETURN"
+#define IP_FW_LABEL_QUEUE "QUEUE"
+
+/* Files in /proc/net */
+#define IP_FW_PROC_CHAINS "ip_fwchains"
+#define IP_FW_PROC_CHAIN_NAMES "ip_fwnames"
+
+
+struct ip_fwpkt
+{
+ struct iphdr fwp_iph; /* IP header */
+ union {
+ struct tcphdr fwp_tcph; /* TCP header or */
+ struct udphdr fwp_udph; /* UDP header */
+ struct icmphdr fwp_icmph; /* ICMP header */
+ } fwp_protoh;
+ struct in_addr fwp_via; /* interface address */
+ char fwp_vianame[IFNAMSIZ]; /* interface name */
+};
+
+/* The argument to IP_FW_DELETE and IP_FW_APPEND */
+struct ip_fwchange
+{
+ struct ip_fwuser fwc_rule;
+ ip_chainlabel fwc_label;
+};
+
+/* The argument to IP_FW_CHECK. */
+struct ip_fwtest
+{
+ struct ip_fwpkt fwt_packet; /* Packet to be tested */
+ ip_chainlabel fwt_label; /* Block to start test in */
+};
+
+/* The argument to IP_FW_DELETE_NUM */
+struct ip_fwdelnum
+{
+ __u32 fwd_rulenum;
+ ip_chainlabel fwd_label;
+};
+
+/* The argument to IP_FW_REPLACE and IP_FW_INSERT */
+struct ip_fwnew
+{
+ __u32 fwn_rulenum;
+ struct ip_fwuser fwn_rule;
+ ip_chainlabel fwn_label;
+};
+
+/* The argument to IP_FW_POLICY */
+struct ip_fwpolicy
+{
+ ip_chainlabel fwp_policy;
+ ip_chainlabel fwp_label;
+};
+/*
+ * timeouts for ip masquerading
+ */
+
+extern int ip_fw_masq_timeouts(void *, int);
+
+
+/*
+ * Main firewall chains definitions and global var's definitions.
+ */
+
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+#include <linux/init.h>
+extern void ip_fw_init(void) __init;
+#else /* 2.0.x */
+extern void ip_fw_init(void);
+#endif /* 2.1.x */
+extern int ip_fw_ctl(int, void *, int);
+#ifdef CONFIG_IP_MASQUERADE
+extern int ip_masq_uctl(int, char *, int);
+#endif
+#endif /* KERNEL */
+
+#endif /* _IP_FWCHAINS_H */
diff --git a/include/linux/netfilter_ipv4/ipfwadm_core.h b/include/linux/netfilter_ipv4/ipfwadm_core.h
new file mode 100644
index 000000000..8294efd7e
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipfwadm_core.h
@@ -0,0 +1,256 @@
+#ifndef _IPFWADM_CORE_H
+#define _IPFWADM_CORE_H
+/* Minor modifications to fit on compatibility framework:
+ Rusty.Russell@rustcorp.com.au
+*/
+
+/*
+ * IP firewalling code. This is taken from 4.4BSD. Please note the
+ * copyright message below. As per the GPL it must be maintained
+ * and the licenses thus do not conflict. While this port is subject
+ * to the GPL I also place my modifications under the original
+ * license in recognition of the original copyright.
+ *
+ * Ported from BSD to Linux,
+ * Alan Cox 22/Nov/1994.
+ * Merged and included the FreeBSD-Current changes at Ugen's request
+ * (but hey it's a lot cleaner now). Ugen would prefer in some ways
+ * we waited for his final product but since Linux 1.2.0 is about to
+ * appear it's not practical - Read: It works, it's not clean but please
+ * don't consider it to be his standard of finished work.
+ * Alan.
+ *
+ * Fixes:
+ * Pauline Middelink : Added masquerading.
+ * Jos Vos : Separate input and output firewall
+ * chains, new "insert" and "append"
+ * commands to replace "add" commands,
+ * add ICMP header to struct ip_fwpkt.
+ * Jos Vos : Add support for matching device names.
+ * Willy Konynenberg : Add transparent proxying support.
+ * Jos Vos : Add options for input/output accounting.
+ *
+ * All the real work was done by .....
+ */
+
+/*
+ * Copyright (c) 1993 Daniel Boulet
+ * Copyright (c) 1994 Ugen J.S.Antsilevich
+ *
+ * Redistribution and use in source forms, with and without modification,
+ * are permitted provided that this entire comment appears intact.
+ *
+ * Redistribution in binary form may occur without any restrictions.
+ * Obviously, it would be nice if you gave credit where credit is due
+ * but requiring it would be too onerous.
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind.
+ */
+
+/*
+ * Format of an IP firewall descriptor
+ *
+ * src, dst, src_mask, dst_mask are always stored in network byte order.
+ * flags and num_*_ports are stored in host byte order (of course).
+ * Port numbers are stored in HOST byte order.
+ */
+
+#ifdef __KERNEL__
+#include <linux/icmp.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#endif
+
+struct ip_fw
+{
+ struct ip_fw *fw_next; /* Next firewall on chain */
+ struct in_addr fw_src, fw_dst; /* Source and destination IP addr */
+ struct in_addr fw_smsk, fw_dmsk; /* Mask for src and dest IP addr */
+ struct in_addr fw_via; /* IP address of interface "via" */
+ struct net_device *fw_viadev; /* device of interface "via" */
+ __u16 fw_flg; /* Flags word */
+ __u16 fw_nsp, fw_ndp; /* N'of src ports and # of dst ports */
+ /* in ports array (dst ports follow */
+ /* src ports; max of 10 ports in all; */
+ /* count of 0 means match all ports) */
+#define IP_FW_MAX_PORTS 10 /* A reasonable maximum */
+ __u16 fw_pts[IP_FW_MAX_PORTS]; /* Array of port numbers to match */
+ unsigned long fw_pcnt,fw_bcnt; /* Packet and byte counters */
+ __u8 fw_tosand, fw_tosxor; /* Revised packet priority */
+ char fw_vianame[IFNAMSIZ]; /* name of interface "via" */
+};
+
+/*
+ * Values for "flags" field .
+ */
+
+#define IP_FW_F_ALL 0x0000 /* This is a universal packet firewall*/
+#define IP_FW_F_TCP 0x0001 /* This is a TCP packet firewall */
+#define IP_FW_F_UDP 0x0002 /* This is a UDP packet firewall */
+#define IP_FW_F_ICMP 0x0003 /* This is a ICMP packet firewall */
+#define IP_FW_F_KIND 0x0003 /* Mask to isolate firewall kind */
+#define IP_FW_F_ACCEPT 0x0004 /* This is an accept firewall (as *
+ * opposed to a deny firewall)*
+ * */
+#define IP_FW_F_SRNG 0x0008 /* The first two src ports are a min *
+ * and max range (stored in host byte *
+ * order). *
+ * */
+#define IP_FW_F_DRNG 0x0010 /* The first two dst ports are a min *
+ * and max range (stored in host byte *
+ * order). *
+ * (ports[0] <= port <= ports[1]) *
+ * */
+#define IP_FW_F_PRN 0x0020 /* In verbose mode print this firewall*/
+#define IP_FW_F_BIDIR 0x0040 /* For bidirectional firewalls */
+#define IP_FW_F_TCPSYN 0x0080 /* For tcp packets-check SYN only */
+#define IP_FW_F_ICMPRPL 0x0100 /* Send back icmp unreachable packet */
+#define IP_FW_F_MASQ 0x0200 /* Masquerading */
+#define IP_FW_F_TCPACK 0x0400 /* For tcp-packets match if ACK is set*/
+#define IP_FW_F_REDIR 0x0800 /* Redirect to local port fw_pts[n] */
+#define IP_FW_F_ACCTIN 0x1000 /* Account incoming packets only. */
+#define IP_FW_F_ACCTOUT 0x2000 /* Account outgoing packets only. */
+
+#define IP_FW_F_MASK 0x3FFF /* All possible flag bits mask */
+
+/*
+ * New IP firewall options for [gs]etsockopt at the RAW IP level.
+ * Unlike BSD Linux inherits IP options so you don't have to use
+ * a raw socket for this. Instead we check rights in the calls.
+ */
+
+#define IP_FW_BASE_CTL 64 /* base for firewall socket options */
+
+#define IP_FW_COMMAND 0x00FF /* mask for command without chain */
+#define IP_FW_TYPE 0x0300 /* mask for type (chain) */
+#define IP_FW_SHIFT 8 /* shift count for type (chain) */
+
+#define IP_FW_FWD 0
+#define IP_FW_IN 1
+#define IP_FW_OUT 2
+#define IP_FW_ACCT 3
+#define IP_FW_CHAINS 4 /* total number of ip_fw chains */
+#define IP_FW_MASQ 5
+
+#define IP_FW_INSERT (IP_FW_BASE_CTL)
+#define IP_FW_APPEND (IP_FW_BASE_CTL+1)
+#define IP_FW_DELETE (IP_FW_BASE_CTL+2)
+#define IP_FW_FLUSH (IP_FW_BASE_CTL+3)
+#define IP_FW_ZERO (IP_FW_BASE_CTL+4)
+#define IP_FW_POLICY (IP_FW_BASE_CTL+5)
+#define IP_FW_CHECK (IP_FW_BASE_CTL+6)
+#define IP_FW_MASQ_TIMEOUTS (IP_FW_BASE_CTL+7)
+
+#define IP_FW_INSERT_FWD (IP_FW_INSERT | (IP_FW_FWD << IP_FW_SHIFT))
+#define IP_FW_APPEND_FWD (IP_FW_APPEND | (IP_FW_FWD << IP_FW_SHIFT))
+#define IP_FW_DELETE_FWD (IP_FW_DELETE | (IP_FW_FWD << IP_FW_SHIFT))
+#define IP_FW_FLUSH_FWD (IP_FW_FLUSH | (IP_FW_FWD << IP_FW_SHIFT))
+#define IP_FW_ZERO_FWD (IP_FW_ZERO | (IP_FW_FWD << IP_FW_SHIFT))
+#define IP_FW_POLICY_FWD (IP_FW_POLICY | (IP_FW_FWD << IP_FW_SHIFT))
+#define IP_FW_CHECK_FWD (IP_FW_CHECK | (IP_FW_FWD << IP_FW_SHIFT))
+
+#define IP_FW_INSERT_IN (IP_FW_INSERT | (IP_FW_IN << IP_FW_SHIFT))
+#define IP_FW_APPEND_IN (IP_FW_APPEND | (IP_FW_IN << IP_FW_SHIFT))
+#define IP_FW_DELETE_IN (IP_FW_DELETE | (IP_FW_IN << IP_FW_SHIFT))
+#define IP_FW_FLUSH_IN (IP_FW_FLUSH | (IP_FW_IN << IP_FW_SHIFT))
+#define IP_FW_ZERO_IN (IP_FW_ZERO | (IP_FW_IN << IP_FW_SHIFT))
+#define IP_FW_POLICY_IN (IP_FW_POLICY | (IP_FW_IN << IP_FW_SHIFT))
+#define IP_FW_CHECK_IN (IP_FW_CHECK | (IP_FW_IN << IP_FW_SHIFT))
+
+#define IP_FW_INSERT_OUT (IP_FW_INSERT | (IP_FW_OUT << IP_FW_SHIFT))
+#define IP_FW_APPEND_OUT (IP_FW_APPEND | (IP_FW_OUT << IP_FW_SHIFT))
+#define IP_FW_DELETE_OUT (IP_FW_DELETE | (IP_FW_OUT << IP_FW_SHIFT))
+#define IP_FW_FLUSH_OUT (IP_FW_FLUSH | (IP_FW_OUT << IP_FW_SHIFT))
+#define IP_FW_ZERO_OUT (IP_FW_ZERO | (IP_FW_OUT << IP_FW_SHIFT))
+#define IP_FW_POLICY_OUT (IP_FW_POLICY | (IP_FW_OUT << IP_FW_SHIFT))
+#define IP_FW_CHECK_OUT (IP_FW_CHECK | (IP_FW_OUT << IP_FW_SHIFT))
+
+#define IP_ACCT_INSERT (IP_FW_INSERT | (IP_FW_ACCT << IP_FW_SHIFT))
+#define IP_ACCT_APPEND (IP_FW_APPEND | (IP_FW_ACCT << IP_FW_SHIFT))
+#define IP_ACCT_DELETE (IP_FW_DELETE | (IP_FW_ACCT << IP_FW_SHIFT))
+#define IP_ACCT_FLUSH (IP_FW_FLUSH | (IP_FW_ACCT << IP_FW_SHIFT))
+#define IP_ACCT_ZERO (IP_FW_ZERO | (IP_FW_ACCT << IP_FW_SHIFT))
+
+#define IP_FW_MASQ_INSERT (IP_FW_INSERT | (IP_FW_MASQ << IP_FW_SHIFT))
+#define IP_FW_MASQ_ADD (IP_FW_APPEND | (IP_FW_MASQ << IP_FW_SHIFT))
+#define IP_FW_MASQ_DEL (IP_FW_DELETE | (IP_FW_MASQ << IP_FW_SHIFT))
+#define IP_FW_MASQ_FLUSH (IP_FW_FLUSH | (IP_FW_MASQ << IP_FW_SHIFT))
+
+#define IP_FW_MASQ_INSERT (IP_FW_INSERT | (IP_FW_MASQ << IP_FW_SHIFT))
+#define IP_FW_MASQ_ADD (IP_FW_APPEND | (IP_FW_MASQ << IP_FW_SHIFT))
+#define IP_FW_MASQ_DEL (IP_FW_DELETE | (IP_FW_MASQ << IP_FW_SHIFT))
+#define IP_FW_MASQ_FLUSH (IP_FW_FLUSH | (IP_FW_MASQ << IP_FW_SHIFT))
+
+struct ip_fwpkt
+{
+ struct iphdr fwp_iph; /* IP header */
+ union {
+ struct tcphdr fwp_tcph; /* TCP header or */
+ struct udphdr fwp_udph; /* UDP header */
+ struct icmphdr fwp_icmph; /* ICMP header */
+ } fwp_protoh;
+ struct in_addr fwp_via; /* interface address */
+ char fwp_vianame[IFNAMSIZ]; /* interface name */
+};
+
+#define IP_FW_MASQCTL_MAX 256
+#define IP_MASQ_MOD_NMAX 32
+
+struct ip_fw_masqctl
+{
+ int mctl_action;
+ union {
+ struct {
+ char name[IP_MASQ_MOD_NMAX];
+ char data[1];
+ } mod;
+ } u;
+};
+
+/*
+ * timeouts for ip masquerading
+ */
+
+struct ip_fw_masq;
+
+/*
+ * Main firewall chains definitions and global var's definitions.
+ */
+
+#ifdef __KERNEL__
+
+/* Modes used in the ip_fw_chk() routine. */
+#define IP_FW_MODE_FW 0x00 /* kernel firewall check */
+#define IP_FW_MODE_ACCT_IN 0x01 /* accounting (incoming) */
+#define IP_FW_MODE_ACCT_OUT 0x02 /* accounting (outgoing) */
+#define IP_FW_MODE_CHK 0x04 /* check requested by user */
+
+#include <linux/config.h>
+#ifdef CONFIG_IP_FIREWALL
+extern struct ip_fw *ip_fw_in_chain;
+extern struct ip_fw *ip_fw_out_chain;
+extern struct ip_fw *ip_fw_fwd_chain;
+extern int ip_fw_in_policy;
+extern int ip_fw_out_policy;
+extern int ip_fw_fwd_policy;
+extern int ip_fw_ctl(int, void *, int);
+#endif
+#ifdef CONFIG_IP_ACCT
+extern struct ip_fw *ip_acct_chain;
+extern int ip_acct_ctl(int, void *, int);
+#endif
+#ifdef CONFIG_IP_MASQUERADE
+extern int ip_masq_ctl(int, void *, int);
+#endif
+#ifdef CONFIG_IP_MASQUERADE
+extern int ip_masq_ctl(int, void *, int);
+#endif
+
+extern int ip_fw_masq_timeouts(void *user, int len);
+
+extern int ip_fw_chk(struct iphdr *, struct net_device *, __u16 *,
+ struct ip_fw *, int, int);
+#endif /* KERNEL */
+#endif /* _IP_FW_H */
diff --git a/include/linux/netfilter_ipv4/ipt_LOG.h b/include/linux/netfilter_ipv4/ipt_LOG.h
new file mode 100644
index 000000000..481e12846
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_LOG.h
@@ -0,0 +1,15 @@
+#ifndef _IPT_LOG_H
+#define _IPT_LOG_H
+
+#define IPT_LOG_TCPSEQ 0x01 /* Log TCP sequence numbers */
+#define IPT_LOG_TCPOPT 0x02 /* Log TCP options */
+#define IPT_LOG_IPOPT 0x04 /* Log IP options */
+#define IPT_LOG_MASK 0x07
+
+struct ipt_log_info {
+ unsigned char level;
+ unsigned char logflags;
+ char prefix[30];
+};
+
+#endif /*_IPT_LOG_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_MARK.h b/include/linux/netfilter_ipv4/ipt_MARK.h
new file mode 100644
index 000000000..cc57ae7a3
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_MARK.h
@@ -0,0 +1,8 @@
+#ifndef _IPT_MARK_H_target
+#define _IPT_MARK_H_target
+
+struct ipt_mark_target_info {
+ unsigned long mark;
+};
+
+#endif /*_IPT_MARK_H_target*/
diff --git a/include/linux/netfilter_ipv4/ipt_REJECT.h b/include/linux/netfilter_ipv4/ipt_REJECT.h
new file mode 100644
index 000000000..1ceebe211
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_REJECT.h
@@ -0,0 +1,17 @@
+#ifndef _IPT_REJECT_H
+#define _IPT_REJECT_H
+
+enum ipt_reject_with {
+ IPT_ICMP_NET_UNREACHABLE,
+ IPT_ICMP_HOST_UNREACHABLE,
+ IPT_ICMP_PROT_UNREACHABLE,
+ IPT_ICMP_PORT_UNREACHABLE,
+ IPT_ICMP_ECHOREPLY,
+ IPT_TCP_RESET,
+};
+
+struct ipt_reject_info {
+ enum ipt_reject_with with; /* reject type */
+};
+
+#endif /*_IPT_REJECT_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_TOS.h b/include/linux/netfilter_ipv4/ipt_TOS.h
new file mode 100644
index 000000000..6bf9e1fdf
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_TOS.h
@@ -0,0 +1,12 @@
+#ifndef _IPT_TOS_H_target
+#define _IPT_TOS_H_target
+
+#ifndef IPTOS_NORMALSVC
+#define IPTOS_NORMALSVC 0
+#endif
+
+struct ipt_tos_target_info {
+ u_int8_t tos;
+};
+
+#endif /*_IPT_TOS_H_target*/
diff --git a/include/linux/netfilter_ipv4/ipt_limit.h b/include/linux/netfilter_ipv4/ipt_limit.h
new file mode 100644
index 000000000..256453409
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_limit.h
@@ -0,0 +1,21 @@
+#ifndef _IPT_RATE_H
+#define _IPT_RATE_H
+
+/* timings are in milliseconds. */
+#define IPT_LIMIT_SCALE 10000
+
+/* 1/10,000 sec period => max of 10,000/sec. Min rate is then 429490
+ seconds, or one every 59 hours. */
+struct ipt_rateinfo {
+ u_int32_t avg; /* Average secs between packets * scale */
+ u_int32_t burst; /* Period multiplier for upper limit. */
+
+ /* Used internally by the kernel */
+ unsigned long prev;
+ u_int32_t credit;
+ u_int32_t credit_cap, cost;
+
+ /* Ugly, ugly fucker. */
+ struct ipt_rateinfo *master;
+};
+#endif /*_IPT_RATE_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_mac.h b/include/linux/netfilter_ipv4/ipt_mac.h
new file mode 100644
index 000000000..f8d5b8e7c
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_mac.h
@@ -0,0 +1,8 @@
+#ifndef _IPT_MAC_H
+#define _IPT_MAC_H
+
+struct ipt_mac_info {
+ unsigned char srcaddr[ETH_ALEN];
+ int invert;
+};
+#endif /*_IPT_MAC_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_mark.h b/include/linux/netfilter_ipv4/ipt_mark.h
new file mode 100644
index 000000000..f3952b563
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_mark.h
@@ -0,0 +1,9 @@
+#ifndef _IPT_MARK_H
+#define _IPT_MARK_H
+
+struct ipt_mark_info {
+ unsigned long mark, mask;
+ u_int8_t invert;
+};
+
+#endif /*_IPT_MARK_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_multiport.h b/include/linux/netfilter_ipv4/ipt_multiport.h
new file mode 100644
index 000000000..f6e50ae92
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_multiport.h
@@ -0,0 +1,21 @@
+#ifndef _IPT_MULTIPORT_H
+#define _IPT_MULTIPORT_H
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+enum ipt_multiport_flags
+{
+ IPT_MULTIPORT_SOURCE,
+ IPT_MULTIPORT_DESTINATION,
+ IPT_MULTIPORT_EITHER
+};
+
+#define IPT_MULTI_PORTS 15
+
+/* Must fit inside union ipt_matchinfo: 16 bytes */
+struct ipt_multiport
+{
+ u_int8_t flags; /* Type of comparison */
+ u_int8_t count; /* Number of ports */
+ u_int16_t ports[IPT_MULTI_PORTS]; /* Ports */
+};
+#endif /*_IPT_MULTIPORT_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_owner.h b/include/linux/netfilter_ipv4/ipt_owner.h
new file mode 100644
index 000000000..b014ef8b3
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_owner.h
@@ -0,0 +1,18 @@
+#ifndef _IPT_OWNER_H
+#define _IPT_OWNER_H
+
+/* match and invert flags */
+#define IPT_OWNER_UID 0x01
+#define IPT_OWNER_GID 0x02
+#define IPT_OWNER_PID 0x04
+#define IPT_OWNER_SID 0x08
+
+struct ipt_owner_info {
+ uid_t uid;
+ gid_t gid;
+ pid_t pid;
+ pid_t sid;
+ u_int8_t match, invert; /* flags */
+};
+
+#endif /*_IPT_OWNER_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_state.h b/include/linux/netfilter_ipv4/ipt_state.h
new file mode 100644
index 000000000..ad11d316a
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_state.h
@@ -0,0 +1,12 @@
+#ifndef _IPT_STATE_H
+#define _IPT_STATE_H
+
+#define _IPT_STATE_BIT(ctinfo) (1 << ((ctinfo)+1))
+#define IPT_STATE_BIT(ctinfo) ((ctinfo) >= IP_CT_IS_REPLY ? _IPT_STATE_BIT((ctinfo)-IP_CT_IS_REPLY) : _IPT_STATE_BIT(ctinfo))
+#define IPT_STATE_INVALID (1 << 0)
+
+struct ipt_state_info
+{
+ unsigned int statemask;
+};
+#endif /*_IPT_STATE_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_tos.h b/include/linux/netfilter_ipv4/ipt_tos.h
new file mode 100644
index 000000000..a21f5df23
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_tos.h
@@ -0,0 +1,13 @@
+#ifndef _IPT_TOS_H
+#define _IPT_TOS_H
+
+struct ipt_tos_info {
+ u_int8_t tos;
+ u_int8_t invert;
+};
+
+#ifndef IPTOS_NORMALSVC
+#define IPTOS_NORMALSVC 0
+#endif
+
+#endif /*_IPT_TOS_H*/
diff --git a/include/linux/netfilter_ipv4/listhelp.h b/include/linux/netfilter_ipv4/listhelp.h
new file mode 100644
index 000000000..e64a0eeb8
--- /dev/null
+++ b/include/linux/netfilter_ipv4/listhelp.h
@@ -0,0 +1,115 @@
+#ifndef _LISTHELP_H
+#define _LISTHELP_H
+#include <linux/config.h>
+#include <linux/list.h>
+#include <linux/netfilter_ipv4/lockhelp.h>
+
+/* Header to do more comprehensive job than linux/list.h; assume list
+ is first entry in structure. */
+
+/* Return pointer to first true entry, if any, or NULL. A macro
+ required to allow inlining of cmpfn. */
+#define LIST_FIND(head, cmpfn, type, args...) \
+({ \
+ const struct list_head *__i = (head); \
+ \
+ ASSERT_READ_LOCK(head); \
+ do { \
+ __i = __i->next; \
+ if (__i == (head)) { \
+ __i = NULL; \
+ break; \
+ } \
+ } while (!cmpfn((const type)__i , ## args)); \
+ (type)__i; \
+})
+
+#define LIST_FIND_W(head, cmpfn, type, args...) \
+({ \
+ const struct list_head *__i = (head); \
+ \
+ ASSERT_WRITE_LOCK(head); \
+ do { \
+ __i = __i->next; \
+ if (__i == (head)) { \
+ __i = NULL; \
+ break; \
+ } \
+ } while (!cmpfn((type)__i , ## args)); \
+ (type)__i; \
+})
+
+extern inline int
+__list_cmp_same(const void *p1, const void *p2) { return p1 == p2; }
+
+/* Is this entry in the list? */
+extern inline int
+list_inlist(struct list_head *head, const void *entry)
+{
+ return LIST_FIND(head, __list_cmp_same, void *, entry) != NULL;
+}
+
+/* Delete from list. */
+#ifdef CONFIG_NETFILTER_DEBUG
+#define LIST_DELETE(head, oldentry) \
+do { \
+ ASSERT_WRITE_LOCK(head); \
+ if (!list_inlist(head, oldentry)) \
+ printk("LIST_DELETE: %s:%u `%s'(%p) not in %s.\n", \
+ __FILE__, __LINE__, #oldentry, oldentry, #head); \
+ else list_del((struct list_head *)oldentry); \
+} while(0)
+#else
+#define LIST_DELETE(head, oldentry) list_del((struct list_head *)oldentry)
+#endif
+
+/* Append. */
+extern inline void
+list_append(struct list_head *head, void *new)
+{
+ ASSERT_WRITE_LOCK(head);
+ list_add((new), (head)->prev);
+}
+
+/* Prepend. */
+extern inline void
+list_prepend(struct list_head *head, void *new)
+{
+ ASSERT_WRITE_LOCK(head);
+ list_add(new, head);
+}
+
+/* Insert according to ordering function; insert before first true. */
+#define LIST_INSERT(head, new, cmpfn) \
+do { \
+ struct list_head *__i; \
+ ASSERT_WRITE_LOCK(head); \
+ for (__i = (head)->next; \
+ !cmpfn((new), (typeof (new))__i) && __i != (head); \
+ __i = __i->next); \
+ list_add((struct list_head *)(new), __i->prev); \
+} while(0)
+
+/* If the field after the list_head is a nul-terminated string, you
+ can use these functions. */
+extern inline int __list_cmp_name(const void *i, const char *name)
+{
+ return strcmp(name, i+sizeof(struct list_head)) == 0;
+}
+
+/* Returns false if same name already in list, otherwise does insert. */
+extern inline int
+list_named_insert(struct list_head *head, void *new)
+{
+ if (LIST_FIND(head, __list_cmp_name, void *,
+ new + sizeof(struct list_head)))
+ return 0;
+ list_prepend(head, new);
+ return 1;
+}
+
+/* Find this named element in the list. */
+#define list_named_find(head, name) \
+LIST_FIND(head, __list_cmp_name, void *, name)
+
+#endif /*_LISTHELP_H*/
diff --git a/include/linux/netfilter_ipv4/lockhelp.h b/include/linux/netfilter_ipv4/lockhelp.h
new file mode 100644
index 000000000..89dd63f9f
--- /dev/null
+++ b/include/linux/netfilter_ipv4/lockhelp.h
@@ -0,0 +1,129 @@
+#ifndef _LOCKHELP_H
+#define _LOCKHELP_H
+#include <linux/config.h>
+
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+#include <linux/interrupt.h>
+#include <linux/smp.h>
+
+/* Header to do help in lock debugging. */
+
+#ifdef CONFIG_NETFILTER_DEBUG
+struct spinlock_debug
+{
+ spinlock_t l;
+ atomic_t locked_by;
+};
+
+struct rwlock_debug
+{
+ rwlock_t l;
+ int read_locked_map;
+ int write_locked_map;
+};
+
+#define DECLARE_LOCK(l) \
+struct spinlock_debug l = { SPIN_LOCK_UNLOCKED, ATOMIC_INIT(-1) }
+#define DECLARE_LOCK_EXTERN(l) \
+extern struct spinlock_debug l
+#define DECLARE_RWLOCK(l) \
+struct rwlock_debug l = { RW_LOCK_UNLOCKED, 0, 0 }
+#define DECLARE_RWLOCK_EXTERN(l) \
+extern struct rwlock_debug l
+
+#define MUST_BE_LOCKED(l) \
+do { if (atomic_read(&(l)->locked_by) != smp_processor_id()) \
+ printk("ASSERT %s:%u %s unlocked\n", __FILE__, __LINE__, #l); \
+} while(0)
+
+#define MUST_BE_UNLOCKED(l) \
+do { if (atomic_read(&(l)->locked_by) == smp_processor_id()) \
+ printk("ASSERT %s:%u %s locked\n", __FILE__, __LINE__, #l); \
+} while(0)
+
+/* Write locked OK as well. */ \
+#define MUST_BE_READ_LOCKED(l) \
+do { if (!((l)->read_locked_map & (1 << smp_processor_id())) \
+ && !((l)->write_locked_map & (1 << smp_processor_id()))) \
+ printk("ASSERT %s:%u %s not readlocked\n", __FILE__, __LINE__, #l); \
+} while(0)
+
+#define MUST_BE_WRITE_LOCKED(l) \
+do { if (!((l)->write_locked_map & (1 << smp_processor_id()))) \
+ printk("ASSERT %s:%u %s not writelocked\n", __FILE__, __LINE__, #l); \
+} while(0)
+
+#define MUST_BE_READ_WRITE_UNLOCKED(l) \
+do { if ((l)->read_locked_map & (1 << smp_processor_id())) \
+ printk("ASSERT %s:%u %s readlocked\n", __FILE__, __LINE__, #l); \
+ else if ((l)->write_locked_map & (1 << smp_processor_id())) \
+ printk("ASSERT %s:%u %s writelocked\n", __FILE__, __LINE__, #l); \
+} while(0)
+
+#define LOCK_BH(lk) \
+do { \
+ MUST_BE_UNLOCKED(lk); \
+ spin_lock_bh(&(lk)->l); \
+ atomic_set(&(lk)->locked_by, smp_processor_id()); \
+} while(0)
+
+#define UNLOCK_BH(lk) \
+do { \
+ MUST_BE_LOCKED(lk); \
+ atomic_set(&(lk)->locked_by, -1); \
+ spin_unlock_bh(&(lk)->l); \
+} while(0)
+
+#define READ_LOCK(lk) \
+do { \
+ MUST_BE_READ_WRITE_UNLOCKED(lk); \
+ read_lock_bh(&(lk)->l); \
+ set_bit(smp_processor_id(), &(lk)->read_locked_map); \
+} while(0)
+
+#define WRITE_LOCK(lk) \
+do { \
+ MUST_BE_READ_WRITE_UNLOCKED(lk); \
+ write_lock_bh(&(lk)->l); \
+ set_bit(smp_processor_id(), &(lk)->write_locked_map); \
+} while(0)
+
+#define READ_UNLOCK(lk) \
+do { \
+ if (!((lk)->read_locked_map & (1 << smp_processor_id()))) \
+ printk("ASSERT: %s:%u %s not readlocked\n", \
+ __FILE__, __LINE__, #lk); \
+ clear_bit(smp_processor_id(), &(lk)->read_locked_map); \
+ read_unlock_bh(&(lk)->l); \
+} while(0)
+
+#define WRITE_UNLOCK(lk) \
+do { \
+ MUST_BE_WRITE_LOCKED(lk); \
+ clear_bit(smp_processor_id(), &(lk)->write_locked_map); \
+ write_unlock_bh(&(lk)->l); \
+} while(0)
+
+#else
+#define DECLARE_LOCK(l) spinlock_t l = SPIN_LOCK_UNLOCKED
+#define DECLARE_LOCK_EXTERN(l) extern spinlock_t l
+#define DECLARE_RWLOCK(l) rwlock_t l = RW_LOCK_UNLOCKED
+#define DECLARE_RWLOCK_EXTERN(l) extern rwlock_t l
+
+#define MUST_BE_LOCKED(l)
+#define MUST_BE_UNLOCKED(l)
+#define MUST_BE_READ_LOCKED(l)
+#define MUST_BE_WRITE_LOCKED(l)
+#define MUST_BE_READ_WRITE_UNLOCKED(l)
+
+#define LOCK_BH(l) spin_lock_bh(l)
+#define UNLOCK_BH(l) spin_unlock_bh(l)
+
+#define READ_LOCK(l) read_lock_bh(l)
+#define WRITE_LOCK(l) write_lock_bh(l)
+#define READ_UNLOCK(l) read_unlock_bh(l)
+#define WRITE_UNLOCK(l) write_unlock_bh(l)
+#endif /*CONFIG_NETFILTER_DEBUG*/
+
+#endif /* _LOCKHELP_H */
diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h
index a2f4897fc..42663e79b 100644
--- a/include/linux/nfsd/nfsd.h
+++ b/include/linux/nfsd/nfsd.h
@@ -172,6 +172,9 @@ void nfsd_lockd_unexport(struct svc_client *);
#define nfserr_badtype __constant_htonl(NFSERR_BADTYPE)
#define nfserr_jukebox __constant_htonl(NFSERR_JUKEBOX)
+/* Check for dir entries '.' and '..' */
+#define isdotent(n, l) (l < 3 && n[0] == '.' && (l == 1 || n[1] == '.'))
+
/*
* Time of server startup
*/
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 413def0a9..472c9d87e 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -293,8 +293,6 @@
#define PCI_DMA_FROMDEVICE 2
#define PCI_DMA_NONE 3
-#include <asm/pci.h>
-
#define DEVICE_COUNT_COMPATIBLE 4
#define DEVICE_COUNT_IRQ 2
#define DEVICE_COUNT_DMA 2
@@ -542,6 +540,10 @@ void pci_remove_device(struct pci_dev *);
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);
+/* Include architecture-dependent settings and functions */
+
+#include <asm/pci.h>
+
/*
* If the system does not have PCI, clearly these return errors. Define
* these as simple inline functions to avoid hair in drivers.
@@ -580,6 +582,7 @@ extern inline int pci_module_init(struct pci_driver *drv) { return -ENODEV; }
extern inline int pci_assign_resource(struct pci_dev *dev, int i) { return -EBUSY;}
extern inline int pci_register_driver(struct pci_driver *drv) { return 0;}
extern inline void pci_unregister_driver(struct pci_driver *drv) { }
+extern inline int scsi_to_pci_dma_dir(unsigned char scsi_dir) { return scsi_dir; }
#else
diff --git a/include/linux/ppp_channel.h b/include/linux/ppp_channel.h
index fdcfb8f75..d9ba188a0 100644
--- a/include/linux/ppp_channel.h
+++ b/include/linux/ppp_channel.h
@@ -61,8 +61,8 @@ extern int ppp_register_channel(struct ppp_channel *);
/* Detach a channel from its PPP unit (e.g. on hangup). */
extern void ppp_unregister_channel(struct ppp_channel *);
-/* Get the channel number for a channel */
-extern int ppp_channel_index(struct ppp_channel *);
+/* Get the unit number associated with a channel */
+extern int ppp_unit_number(struct ppp_channel *);
/*
* SMP locking notes:
diff --git a/include/linux/prctl.h b/include/linux/prctl.h
index ca7a8cd8b..c6c42a55b 100644
--- a/include/linux/prctl.h
+++ b/include/linux/prctl.h
@@ -16,4 +16,8 @@
# define PR_UNALIGN_NOPRINT 1 /* silently fix up unaligned user accesses */
# define PR_UNALIGN_SIGBUS 2 /* generate SIGBUS on unaligned user access */
+/* Get/set whether or not to drop capabilities on setuid() away from uid 0 */
+#define PR_GET_KEEPCAPS 7
+#define PR_SET_KEEPCAPS 8
+
#endif /* _LINUX_PRCTL_H */
diff --git a/include/linux/quota.h b/include/linux/quota.h
index ad2380019..9324b9a02 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -174,7 +174,7 @@ struct dquot {
int dq_count; /* Reference count */
/* fields after this point are cleared when invalidating */
- struct vfsmount *dq_mnt; /* VFS_mount_point this applies to */
+ struct super_block *dq_sb; /* superblock this applies to */
unsigned int dq_id; /* ID this applies to (uid, gid) */
kdev_t dq_dev; /* Device this applies to */
short dq_type; /* Type of quota */
diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h
index 17371e58c..14ad1b7ca 100644
--- a/include/linux/quotaops.h
+++ b/include/linux/quotaops.h
@@ -20,7 +20,7 @@
extern void dquot_initialize(struct inode *inode, short type);
extern void dquot_drop(struct inode *inode);
extern void invalidate_dquots(kdev_t dev, short type);
-extern int quota_off(kdev_t dev, short type);
+extern int quota_off(struct super_block *sb, short type);
extern int sync_dquots(kdev_t dev, short type);
extern int dquot_alloc_block(const struct inode *inode, unsigned long number, char prealloc);
@@ -103,7 +103,7 @@ extern __inline__ int DQUOT_TRANSFER(struct dentry *dentry, struct iattr *iattr)
}
#define DQUOT_SYNC(dev) sync_dquots(dev, -1)
-#define DQUOT_OFF(dev) quota_off(dev, -1)
+#define DQUOT_OFF(sb) quota_off(sb, -1)
#else
@@ -118,7 +118,7 @@ extern __inline__ int DQUOT_TRANSFER(struct dentry *dentry, struct iattr *iattr)
#define DQUOT_FREE_BLOCK(sb, inode, nr) do { } while(0)
#define DQUOT_FREE_INODE(sb, inode) do { } while(0)
#define DQUOT_SYNC(dev) do { } while(0)
-#define DQUOT_OFF(dev) do { } while(0)
+#define DQUOT_OFF(sb) do { } while(0)
/*
* Special case expands to a simple notify_change.
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 48050ccb0..75d010ca4 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -326,6 +326,7 @@ struct task_struct {
int ngroups;
gid_t groups[NGROUPS];
kernel_cap_t cap_effective, cap_inheritable, cap_permitted;
+ int keep_capabilities:1;
struct user_struct *user;
/* limits */
struct rlimit rlim[RLIM_NLIMITS];
@@ -414,6 +415,7 @@ struct task_struct {
cap_effective: CAP_INIT_EFF_SET, \
cap_inheritable: CAP_INIT_INH_SET, \
cap_permitted: CAP_FULL_SET, \
+ keep_capabilities: 0, \
rlim: INIT_RLIMITS, \
comm: "swapper", \
thread: INIT_THREAD, \
diff --git a/include/linux/shm.h b/include/linux/shm.h
index c1ab5240b..bc56c5e20 100644
--- a/include/linux/shm.h
+++ b/include/linux/shm.h
@@ -11,8 +11,8 @@
#define SHMMAX 0x2000000 /* max shared seg size (bytes) */
#define SHMMIN 0 /* min shared seg size (bytes) */
-#define SHMMNI 128 /* max num of segs system wide */
-#define SHMALL (SHMMAX/PAGE_SIZE*SHMMNI) /* max shm system wide (pages) */
+#define SHMMNI 4096 /* max num of segs system wide */
+#define SHMALL (SHMMAX/PAGE_SIZE*(SHMMNI/16)) /* max shm system wide (pages) */
#define SHMSEG SHMMNI /* max shared segs per process */
#include <asm/shmparam.h>
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 6081b0890..429089cc5 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -122,8 +122,6 @@ struct sk_buff {
#ifdef CONFIG_NETFILTER
/* Can be used for communication between hooks. */
unsigned long nfmark;
- /* Reason for doing this to the packet (see netfilter.h) */
- __u32 nfreason;
/* Cache info */
__u32 nfcache;
/* Associated connection, if any */
diff --git a/include/linux/soundcard.h b/include/linux/soundcard.h
index 2d5128dc4..ca8ad780d 100644
--- a/include/linux/soundcard.h
+++ b/include/linux/soundcard.h
@@ -552,6 +552,7 @@ typedef struct {
# define AFMT_U16_LE 0x00000080 /* Little endian U16 */
# define AFMT_U16_BE 0x00000100 /* Big endian U16 */
# define AFMT_MPEG 0x00000200 /* MPEG (2) audio */
+# define AFMT_AC3 0x00000400 /* Dolby Digital AC3 */
/*
* Buffer status queries.
@@ -581,11 +582,14 @@ typedef struct audio_buf_info {
/* but usually not */
# define DSP_CAP_TRIGGER 0x00001000 /* Supports SETTRIGGER */
# define DSP_CAP_MMAP 0x00002000 /* Supports mmap() */
+# define DSP_CAP_MULTI 0x00004000 /* support multiple open */
+# define DSP_CAP_BIND 0x00008000 /* channel binding to front/rear/cneter/lfe */
+
#define SNDCTL_DSP_GETTRIGGER _SIOR ('P',16, int)
#define SNDCTL_DSP_SETTRIGGER _SIOW ('P',16, int)
# define PCM_ENABLE_INPUT 0x00000001
-# define PCM_ENABLE_OUTPUT 0x00000002
+# define PCM_ENABLE_OUTPUT 0x00000002
typedef struct count_info {
int bytes; /* Total # of bytes processed */
@@ -606,6 +610,19 @@ typedef struct buffmem_desc {
#define SNDCTL_DSP_SETDUPLEX _SIO ('P', 22)
#define SNDCTL_DSP_GETODELAY _SIOR ('P', 23, int)
+#define SNDCTL_DSP_GETCHANNELMASK _SIOWR('P', 64, int)
+#define SNDCTL_DSP_BIND_CHANNEL _SIOWR('P', 65, int)
+# define DSP_BIND_QUERY 0x00000000
+# define DSP_BIND_FRONT 0x00000001
+# define DSP_BIND_SURR 0x00000002
+# define DSP_BIND_CENTER_LFE 0x00000004
+# define DSP_BIND_HANDSET 0x00000008
+# define DSP_BIND_MIC 0x00000010
+# define DSP_BIND_MODEM1 0x00000020
+# define DSP_BIND_MODEM2 0x00000040
+# define DSP_BIND_I2S 0x00000080
+# define DSP_BIND_SPDIF 0x00000100
+
/*
* Application's profile defines the way how playback underrun situations should be handled.
*
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 569b8b6ea..dacdfa2a7 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -669,6 +669,8 @@ extern void tcp_v4_send_check(struct sock *sk,
struct tcphdr *th, int len,
struct sk_buff *skb);
+extern void tcp_v4_send_reset(struct sk_buff *skb);
+
extern int tcp_v4_conn_request(struct sock *sk,
struct sk_buff *skb);
diff --git a/ipc/shm.c b/ipc/shm.c
index cb7e2c33a..196460c6f 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -18,13 +18,19 @@
* 1) It only can handle one directory.
* 2) Because the directory is represented by the SYSV shm array it
* can only be mounted one time.
- * 3) This again leads to SYSV shm not working properly in a chrooted
- * environment
- * 4) Read and write are not implemented (should they?)
- * 5) No special nodes are supported
+ * 3) Read and write are not implemented (should they?)
+ * 4) No special nodes are supported
+ *
+ * There are the following mount options:
+ * - nr_blocks (^= shmall) is the number of blocks of size PAGE_SIZE
+ * we are allowed to allocate
+ * - nr_inodes (^= shmmni) is the number of files we are allowed to
+ * allocate
+ * - mode is the mode for the root directory (default S_IRWXUGO | S_ISVTX)
*/
#include <linux/config.h>
+#include <linux/module.h>
#include <linux/malloc.h>
#include <linux/shm.h>
#include <linux/swap.h>
@@ -48,7 +54,7 @@ static void shm_put_super (struct super_block *);
static int shm_remount_fs (struct super_block *, int *, char *);
static void shm_read_inode (struct inode *);
static void shm_write_inode(struct inode *);
-static int shm_statfs (struct super_block *, struct statfs *);
+static int shm_statfs (struct super_block *, struct statfs *);
static int shm_create (struct inode *,struct dentry *,int);
static struct dentry *shm_lookup (struct inode *,struct dentry *);
static int shm_unlink (struct inode *,struct dentry *);
@@ -57,8 +63,6 @@ static void shm_delete (struct inode *);
static int shm_mmap (struct file *, struct vm_area_struct *);
static int shm_readdir (struct file *, void *, filldir_t);
-char shm_path[256] = "/var/shm";
-
#define SHM_NAME_LEN NAME_MAX
#define SHM_FMT ".IPC_%08x"
#define SHM_FMT_LEN 13
@@ -78,6 +82,7 @@ struct shmid_kernel /* private to the kernel */
time_t ctime;
pid_t cpid;
pid_t lpid;
+ int unlinked;
int nlen;
char nm[0];
} shmem;
@@ -95,6 +100,7 @@ struct shmid_kernel /* private to the kernel */
#define shm_lprid permap.shmem.lpid
#define shm_namelen permap.shmem.nlen
#define shm_name permap.shmem.nm
+#define shm_unlinked permap.shmem.unlinked
#define zsem permap.zero.sema
#define zero_list permap.zero.list
@@ -105,7 +111,6 @@ static struct ipc_ids shm_ids;
#define shm_lockall() ipc_lockall(&shm_ids)
#define shm_unlockall() ipc_unlockall(&shm_ids)
#define shm_get(id) ((struct shmid_kernel*)ipc_get(&shm_ids,id))
-#define shm_rmid(id) ((struct shmid_kernel*)ipc_rmid(&shm_ids,id))
#define shm_checkid(s, id) \
ipc_checkid(&shm_ids,&s->shm_perm,id)
#define shm_buildid(id, seq) \
@@ -115,6 +120,7 @@ static int newseg (key_t key, const char *name, int namelen, int shmflg, size_t
static void killseg_core(struct shmid_kernel *shp, int doacc);
static void shm_open (struct vm_area_struct *shmd);
static void shm_close (struct vm_area_struct *shmd);
+static int shm_remove_name(int id);
static struct page * shm_nopage(struct vm_area_struct *, unsigned long, int);
static int shm_swapout(struct page *, struct file *);
#ifdef CONFIG_PROC_FS
@@ -266,7 +272,7 @@ static struct super_block *shm_read_super(struct super_block *s,void *data,
struct inode * root_inode;
if (shm_sb) {
- printk ("shm fs already mounted\n");
+ printk(KERN_ERR "shm fs already mounted\n");
return NULL;
}
@@ -274,7 +280,7 @@ static struct super_block *shm_read_super(struct super_block *s,void *data,
shm_ctlmni = SHMMNI;
shm_mode = S_IRWXUGO | S_ISVTX;
if (shm_parse_options (data)) {
- printk ("shm fs invalid option\n");
+ printk(KERN_ERR "shm fs invalid option\n");
goto out_unlock;
}
@@ -297,7 +303,7 @@ static struct super_block *shm_read_super(struct super_block *s,void *data,
return s;
out_no_root:
- printk("proc_read_super: get root inode failed\n");
+ printk(KERN_ERR "proc_read_super: get root inode failed\n");
iput(root_inode);
out_unlock:
return NULL;
@@ -310,6 +316,16 @@ static int shm_remount_fs (struct super_block *sb, int *flags, char *data)
return 0;
}
+static inline struct shmid_kernel *shm_rmid(int id)
+{
+ return (struct shmid_kernel *)ipc_rmid(&shm_ids,id);
+}
+
+static __inline__ int shm_addid(struct shmid_kernel *shp)
+{
+ return ipc_addid(&shm_ids, &shp->shm_perm, shm_ctlmni+1);
+}
+
static void shm_put_super(struct super_block *sb)
{
struct super_block **p = &shm_sb;
@@ -329,7 +345,7 @@ static void shm_put_super(struct super_block *sb)
if (!(shp = shm_lock (i)))
continue;
if (shp->shm_nattch)
- printk ("shm_nattch = %ld\n", shp->shm_nattch);
+ printk(KERN_DEBUG "shm_nattch = %ld\n", shp->shm_nattch);
shp = shm_rmid(i);
shm_unlock(i);
killseg_core(shp, 1);
@@ -439,7 +455,7 @@ static int shm_readdir (struct file *filp, void *dirent, filldir_t filldir)
continue;
if (!(shp = shm_get (nr-2)))
continue;
- if (shp->shm_perm.mode & SHM_DEST)
+ if (shp->shm_unlinked)
continue;
if (filldir(dirent, shp->shm_name, shp->shm_namelen, nr, nr) < 0 )
break;;
@@ -468,7 +484,7 @@ static struct dentry *shm_lookup (struct inode *dir, struct dentry *dent)
continue;
if (!(shp = shm_lock(i)))
continue;
- if (!(shp->shm_perm.mode & SHM_DEST) &&
+ if (!(shp->shm_unlinked) &&
dent->d_name.len == shp->shm_namelen &&
strncmp(dent->d_name.name, shp->shm_name, shp->shm_namelen) == 0)
goto found;
@@ -506,12 +522,21 @@ static int shm_unlink (struct inode *dir, struct dentry *dent)
down (&shm_ids.sem);
if (!(shp = shm_lock (inode->i_ino)))
BUG();
+ shp->shm_unlinked = 1;
shp->shm_perm.mode |= SHM_DEST;
shp->shm_perm.key = IPC_PRIVATE; /* Do not find it any more */
shm_unlock (inode->i_ino);
up (&shm_ids.sem);
inode->i_nlink -= 1;
- d_delete (dent);
+ /*
+ * If it's a reserved name we have to drop the dentry instead
+ * of creating a negative dentry
+ */
+ if (dent->d_name.len == SHM_FMT_LEN &&
+ memcmp (SHM_FMT, dent->d_name.name, SHM_FMT_LEN - 8) == 0)
+ d_drop (dent);
+ else
+ d_delete (dent);
return 0;
}
@@ -637,16 +662,18 @@ set_attr:
static inline struct shmid_kernel *newseg_alloc(int numpages, size_t namelen)
{
struct shmid_kernel *shp;
+ pte_t **dir;
shp = (struct shmid_kernel *) kmalloc (sizeof (*shp) + namelen, GFP_KERNEL);
if (!shp)
- return 0;
+ return ERR_PTR(-ENOMEM);
- shp->shm_dir = shm_alloc (numpages);
- if (!shp->shm_dir) {
+ dir = shm_alloc (numpages);
+ if (IS_ERR(dir)) {
kfree(shp);
- return 0;
+ return ERR_PTR(PTR_ERR(dir));
}
+ shp->shm_dir = dir;
shp->shm_npages = numpages;
shp->shm_nattch = 0;
shp->shm_namelen = namelen;
@@ -665,12 +692,13 @@ static int newseg (key_t key, const char *name, int namelen,
if (size > shm_ctlmax)
return -EINVAL;
+
if (shm_tot + numpages >= shm_ctlall)
return -ENOSPC;
if (!(shp = newseg_alloc(numpages, namelen ? namelen : SHM_FMT_LEN + 1)))
return -ENOMEM;
- id = ipc_addid(&shm_ids, &shp->shm_perm, shm_ctlmni+1);
+ id = shm_addid(shp);
if(id == -1) {
shm_free(shp->shm_dir,numpages);
kfree(shp);
@@ -684,6 +712,7 @@ static int newseg (key_t key, const char *name, int namelen,
shp->shm_atim = shp->shm_dtim = 0;
shp->shm_ctim = CURRENT_TIME;
shp->id = shm_buildid(id,shp->shm_perm.seq);
+ shp->shm_unlinked = 0;
if (namelen != 0) {
shp->shm_namelen = namelen;
memcpy (shp->shm_name, name, namelen);
@@ -877,18 +906,6 @@ static inline unsigned long copy_shminfo_to_user(void *buf, struct shminfo64 *in
}
}
-char * shm_getname(int id)
-{
- char *result;
-
- result = __getname ();
- if (IS_ERR(result))
- return result;
-
- sprintf (result, "%s/" SHM_FMT, shm_path, id);
- return result;
-}
-
asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds *buf)
{
struct shm_setbuf setbuf;
@@ -1018,18 +1035,50 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds *buf)
}
case IPC_RMID:
{
- char *name;
- if ((shmid % SEQ_MULTIPLIER)== zero_id)
+ /*
+ * We cannot simply remove the file. The SVID states
+ * that the block remains until the last person
+ * detaches from it, then is deleted. A shmat() on
+ * an RMID segment is legal in older Linux and if
+ * we change it apps break...
+ *
+ * Instead we set a destroyed flag, and then blow
+ * the name away when the usage hits zero.
+ */
+ if ((shmid % SEQ_MULTIPLIER) == zero_id)
return -EINVAL;
- name = shm_getname(shmid);
- if (IS_ERR(name))
- return PTR_ERR(name);
lock_kernel();
- err = do_unlink (name);
+ down(&shm_ids.sem);
+ shp = shm_lock(shmid);
+ if (shp == NULL) {
+ up(&shm_ids.sem);
+ unlock_kernel();
+ return -EINVAL;
+ }
+ err = -EIDRM;
+ if (shm_checkid(shp, shmid) == 0) {
+ if (shp->shm_nattch == 0) {
+ int id=shp->id;
+ shm_unlock(shmid);
+ up(&shm_ids.sem);
+ /* The kernel lock prevents new attaches from
+ * being happening. We can't hold shm_lock here
+ * else we will deadlock in shm_lookup when we
+ * try to recursively grab it.
+ */
+ err = shm_remove_name(id);
+ unlock_kernel();
+ return err;
+ }
+ /* Do not find me any more */
+ shp->shm_perm.mode |= SHM_DEST;
+ shp->shm_perm.key = IPC_PRIVATE; /* Do not find it any more */
+ err = 0;
+ }
+ /* Unlock */
+ shm_unlock(shmid);
+ up(&shm_ids.sem);
unlock_kernel();
- putname (name);
- if (err == -ENOENT)
- err = -EINVAL;
return err;
}
@@ -1108,7 +1157,7 @@ asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr)
struct file * file;
int err;
int flags;
- char *name;
+ char name[SHM_FMT_LEN+1];
if (!shm_sb || (shmid % SEQ_MULTIPLIER) == zero_id)
return -EINVAL;
@@ -1125,17 +1174,11 @@ asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr)
} else
flags = MAP_SHARED;
- name = shm_getname(shmid);
- if (IS_ERR (name))
- return PTR_ERR (name);
-
+ sprintf (name, SHM_FMT, shmid);
lock_kernel();
- file = filp_open (name, O_RDWR, 0);
- putname (name);
- if (IS_ERR (file)) {
- unlock_kernel();
+ file = filp_open(name, O_RDWR, 0, dget(shm_sb->s_root));
+ if (IS_ERR (file))
goto bad_file;
- }
*raddr = do_mmap (file, addr, file->f_dentry->d_inode->i_size,
(shmflg & SHM_RDONLY ? PROT_READ :
PROT_READ | PROT_WRITE), flags, 0);
@@ -1148,6 +1191,7 @@ asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr)
return err;
bad_file:
+ unlock_kernel();
if ((err = PTR_ERR(file)) == -ENOENT)
return -EINVAL;
return err;
@@ -1160,6 +1204,17 @@ static void shm_open (struct vm_area_struct *shmd)
}
/*
+ * Remove a name. Must be called with lock_kernel
+ */
+
+static int shm_remove_name(int id)
+{
+ char name[SHM_FMT_LEN+1];
+ sprintf (name, SHM_FMT, id);
+ return do_unlink (name, dget(shm_sb->s_root));
+}
+
+/*
* remove the attach descriptor shmd.
* free memory for segment if it is marked destroyed.
* The descriptor has already been removed from the current->mm->mmap list
@@ -1170,13 +1225,33 @@ static void shm_close (struct vm_area_struct *shmd)
int id = shmd->vm_file->f_dentry->d_inode->i_ino;
struct shmid_kernel *shp;
+ lock_kernel();
+
/* remove from the list of attaches of the shm segment */
if(!(shp = shm_lock(id)))
BUG();
shp->shm_lprid = current->pid;
shp->shm_dtim = CURRENT_TIME;
shp->shm_nattch--;
- shm_unlock(id);
+ if(shp->shm_nattch == 0 && shp->shm_perm.mode & SHM_DEST) {
+ int pid=shp->id;
+ int err;
+ shm_unlock(id);
+
+ /* The kernel lock prevents new attaches from
+ * being happening. We can't hold shm_lock here
+ * else we will deadlock in shm_lookup when we
+ * try to recursively grab it.
+ */
+ err = shm_remove_name(pid);
+ if(err && err != -ENOENT)
+ printk(KERN_ERR "Unlink of SHM id %d failed (%d).\n", pid, err);
+
+ } else {
+ shm_unlock(id);
+ }
+
+ unlock_kernel();
}
/*
@@ -1214,13 +1289,13 @@ static int shm_swapout(struct page * page, struct file *file)
/*
* page not present ... go through shm_dir
*/
-static struct page * shm_nopage_core(struct shmid_kernel *shp, unsigned int idx, int *swp, int *rss)
+static struct page * shm_nopage_core(struct shmid_kernel *shp, unsigned int idx, int *swp, int *rss, unsigned long address)
{
pte_t pte;
struct page * page;
if (idx >= shp->shm_npages)
- goto sigbus;
+ return NOPAGE_SIGBUS;
pte = SHM_ENTRY(shp,idx);
if (!pte_present(pte)) {
@@ -1232,7 +1307,7 @@ static struct page * shm_nopage_core(struct shmid_kernel *shp, unsigned int idx,
page = alloc_page(GFP_HIGHUSER);
if (!page)
goto oom;
- clear_highpage(page);
+ clear_user_highpage(page, address);
if ((shp != shm_lock(shp->id)) && (shp->id != zero_id))
BUG();
} else {
@@ -1267,9 +1342,8 @@ static struct page * shm_nopage_core(struct shmid_kernel *shp, unsigned int idx,
return pte_page(pte);
oom:
+ shm_lock(shp->id);
return NOPAGE_OOM;
-sigbus:
- return NOPAGE_SIGBUS;
}
static struct page * shm_nopage(struct vm_area_struct * shmd, unsigned long address, int no_share)
@@ -1285,7 +1359,7 @@ static struct page * shm_nopage(struct vm_area_struct * shmd, unsigned long addr
down(&inode->i_sem);
if(!(shp = shm_lock(inode->i_ino)))
BUG();
- page = shm_nopage_core(shp, idx, &shm_swp, &shm_rss);
+ page = shm_nopage_core(shp, idx, &shm_swp, &shm_rss, address);
shm_unlock(inode->i_ino);
up(&inode->i_sem);
return(page);
@@ -1482,8 +1556,8 @@ static int sysvipc_shm_read_proc(char *buffer, char **start, off_t offset, int l
continue;
shp = shm_lock(i);
if(shp!=NULL) {
-#define SMALL_STRING "%10d %10d %4o %10u %5u %5u %5d %5u %5u %5u %5u %10lu %10lu %10lu %.*s\n"
-#define BIG_STRING "%10d %10d %4o %21u %5u %5u %5d %5u %5u %5u %5u %10lu %10lu %10lu %.*s\n"
+#define SMALL_STRING "%10d %10d %4o %10u %5u %5u %5d %5u %5u %5u %5u %10lu %10lu %10lu %.*s%s\n"
+#define BIG_STRING "%10d %10d %4o %21u %5u %5u %5d %5u %5u %5u %5u %10lu %10lu %10lu %.*s%s\n"
char *format;
if (sizeof(size_t) <= sizeof(int))
@@ -1506,7 +1580,8 @@ static int sysvipc_shm_read_proc(char *buffer, char **start, off_t offset, int l
shp->shm_dtim,
shp->shm_ctim,
shp->shm_namelen,
- shp->shm_name);
+ shp->shm_name,
+ shp->shm_unlinked ? " (deleted)" : "");
shm_unlock(i);
pos += len;
@@ -1595,8 +1670,8 @@ int map_zero_setup(struct vm_area_struct *vma)
if (!vm_enough_memory((vma->vm_end - vma->vm_start) >> PAGE_SHIFT))
return -ENOMEM;
- if (!(shp = newseg_alloc((vma->vm_end - vma->vm_start) / PAGE_SIZE, 0)))
- return -ENOMEM;
+ if (IS_ERR(shp = newseg_alloc((vma->vm_end - vma->vm_start) / PAGE_SIZE, 0)))
+ return PTR_ERR(shp);
if ((filp = file_setup(vma->vm_file, shp)) == 0) {
killseg_core(shp, 0);
return -ENOMEM;
@@ -1657,7 +1732,7 @@ static struct page * shmzero_nopage(struct vm_area_struct * shmd, unsigned long
shp = VMA_TO_SHP(shmd);
down(&shp->zsem);
shm_lock(zero_id);
- page = shm_nopage_core(shp, idx, &dummy, &zshm_rss);
+ page = shm_nopage_core(shp, idx, &dummy, &zshm_rss, address);
shm_unlock(zero_id);
up(&shp->zsem);
return(page);
diff --git a/kernel/acct.c b/kernel/acct.c
index 63e66a558..f15b6971f 100644
--- a/kernel/acct.c
+++ b/kernel/acct.c
@@ -154,7 +154,7 @@ asmlinkage long sys_acct(const char *name)
if (IS_ERR(tmp))
goto out;
/* Difference from BSD - they don't do O_APPEND */
- file = filp_open(tmp, O_WRONLY|O_APPEND, 0);
+ file = filp_open(tmp, O_WRONLY|O_APPEND, 0, NULL);
putname(tmp);
if (IS_ERR(file)) {
error = PTR_ERR(file);
diff --git a/kernel/ksyms.c b/kernel/ksyms.c
index 78b40185d..a6748e70c 100644
--- a/kernel/ksyms.c
+++ b/kernel/ksyms.c
@@ -141,7 +141,8 @@ EXPORT_SYMBOL(iget4);
EXPORT_SYMBOL(iput);
EXPORT_SYMBOL(__namei);
EXPORT_SYMBOL(lookup_dentry);
-EXPORT_SYMBOL(open_namei);
+EXPORT_SYMBOL(lookup_one);
+EXPORT_SYMBOL(__open_namei);
EXPORT_SYMBOL(sys_close);
EXPORT_SYMBOL(d_alloc_root);
EXPORT_SYMBOL(d_delete);
@@ -360,6 +361,7 @@ EXPORT_SYMBOL(free_kiovec);
EXPORT_SYMBOL(expand_kiobuf);
EXPORT_SYMBOL(map_user_kiobuf);
+EXPORT_SYMBOL(unmap_kiobuf);
EXPORT_SYMBOL(lock_kiovec);
EXPORT_SYMBOL(unlock_kiovec);
EXPORT_SYMBOL(brw_kiovec);
@@ -506,3 +508,6 @@ EXPORT_SYMBOL(tasklet_kill);
/* init task, for moving kthread roots - ought to export a function ?? */
EXPORT_SYMBOL(init_task_union);
+
+EXPORT_SYMBOL(tasklist_lock);
+EXPORT_SYMBOL(pidhash);
diff --git a/kernel/sys.c b/kernel/sys.c
index 1748d8afd..6e30f0347 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -336,12 +336,23 @@ asmlinkage long sys_setgid(gid_t gid)
* never happen.
*
* -astor
+ *
+ * cevans - New behaviour, Oct '99
+ * A process may, via prctl(), elect to keep its capabilities when it
+ * calls setuid() and switches away from uid==0. Both permitted and
+ * effective sets will be retained.
+ * Without this change, it was impossible for a daemon to drop only some
+ * of its privilege. The call to setuid(!=0) would drop all privileges!
+ * Keeping uid 0 is not an option because uid 0 owns too many vital
+ * files..
+ * Thanks to Olaf Kirch and Peter Benie for spotting this.
*/
extern inline void cap_emulate_setxuid(int old_ruid, int old_euid,
int old_suid)
{
if ((old_ruid == 0 || old_euid == 0 || old_suid == 0) &&
- (current->uid != 0 && current->euid != 0 && current->suid != 0)) {
+ (current->uid != 0 && current->euid != 0 && current->suid != 0) &&
+ !current->keep_capabilities) {
cap_clear(current->cap_permitted);
cap_clear(current->cap_effective);
}
@@ -1080,6 +1091,17 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3,
#endif
break;
+ case PR_GET_KEEPCAPS:
+ if (current->keep_capabilities)
+ error = 1;
+ break;
+ case PR_SET_KEEPCAPS:
+ if (arg2 != 0 && arg2 != 1) {
+ error = -EINVAL;
+ break;
+ }
+ current->keep_capabilities = arg2;
+ break;
default:
error = -EINVAL;
break;
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 0001a1473..4e654fa3a 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -57,7 +57,6 @@ extern int sg_big_buff;
#endif
#ifdef CONFIG_SYSVIPC
extern size_t shm_ctlmax;
-extern char shm_path[];
extern int msg_ctlmax;
extern int msg_ctlmnb;
extern int msg_ctlmni;
@@ -199,8 +198,6 @@ static ctl_table kern_table[] = {
{KERN_RTSIGMAX, "rtsig-max", &max_queued_signals, sizeof(int),
0644, NULL, &proc_dointvec},
#ifdef CONFIG_SYSVIPC
- {KERN_SHMPATH, "shmpath", &shm_path, 256,
- 0644, NULL, &proc_dostring, &sysctl_string },
{KERN_SHMMAX, "shmmax", &shm_ctlmax, sizeof (size_t),
0644, NULL, &proc_doulongvec_minmax},
{KERN_MSGMAX, "msgmax", &msg_ctlmax, sizeof (int),
diff --git a/mm/filemap.c b/mm/filemap.c
index 3fb7d011c..1b9f9ed6a 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -108,14 +108,15 @@ void invalidate_inode_pages(struct inode * inode)
curr = curr->next;
/* We cannot invalidate a locked page */
- if (PageLocked(page))
+ if (TryLockPage(page))
continue;
lru_cache_del(page);
-
remove_page_from_inode_queue(page);
remove_page_from_hash_queue(page);
page->mapping = NULL;
+ UnlockPage(page);
+
page_cache_release(page);
}
spin_unlock(&pagecache_lock);
@@ -1413,7 +1414,7 @@ success:
struct page *new_page = page_cache_alloc();
if (new_page) {
- copy_highpage(new_page, old_page);
+ copy_user_highpage(new_page, old_page, address);
flush_page_to_ram(new_page);
} else
new_page = NOPAGE_OOM;
diff --git a/mm/memory.c b/mm/memory.c
index dbcdd0052..9d1a131ff 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -61,10 +61,10 @@ struct page *highmem_start_page;
static inline void copy_cow_page(struct page * from, struct page * to, unsigned long address)
{
if (from == ZERO_PAGE(address)) {
- clear_highpage(to);
+ clear_user_highpage(to, address);
return;
}
- copy_highpage(to, from);
+ copy_user_highpage(to, from, address);
}
mem_map_t * mem_map = NULL;
@@ -1073,7 +1073,7 @@ static int do_anonymous_page(struct task_struct * tsk, struct vm_area_struct * v
return -1;
if (PageHighMem(page))
high = 1;
- clear_highpage(page);
+ clear_user_highpage(page, addr);
entry = pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
vma->vm_mm->rss++;
tsk->min_flt++;
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 0204cf141..1205ab835 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -58,6 +58,8 @@ static int zone_balance_max[MAX_NR_ZONES] = { 255 , 255, 255, };
*/
#define BAD_RANGE(zone,x) (((zone) != (x)->zone) || (((x)-mem_map) < (zone)->offset) || (((x)-mem_map) >= (zone)->offset+(zone)->size))
+#if 0
+
static inline unsigned long classfree(zone_t *zone)
{
unsigned long free = 0;
@@ -71,6 +73,8 @@ static inline unsigned long classfree(zone_t *zone)
return(free);
}
+#endif
+
/*
* Buddy system. Hairy. You really aren't expected to understand this
*
@@ -148,8 +152,10 @@ void __free_pages_ok (struct page *page, unsigned long order)
spin_unlock_irqrestore(&zone->lock, flags);
- if (classfree(zone) > zone->pages_high)
+ if (zone->free_pages > zone->pages_high) {
zone->zone_wake_kswapd = 0;
+ zone->low_on_memory = 0;
+ }
}
#define MARK_USED(index, order, area) \
@@ -176,7 +182,8 @@ static inline struct page * expand (zone_t *zone, struct page *page,
return page;
}
-static inline struct page * rmqueue (zone_t *zone, unsigned long order)
+static FASTCALL(struct page * rmqueue(zone_t *zone, unsigned long order));
+static struct page * rmqueue(zone_t *zone, unsigned long order)
{
free_area_t * area = zone->free_area + order;
unsigned long curr_order = order;
@@ -216,19 +223,43 @@ static inline struct page * rmqueue (zone_t *zone, unsigned long order)
return NULL;
}
-static inline int zone_balance_memory (zone_t *zone, int gfp_mask)
+static int zone_balance_memory(zonelist_t *zonelist)
{
- int freed;
+ int tried = 0, freed = 0;
+ zone_t **zone;
+ int gfp_mask = zonelist->gfp_mask;
+ extern wait_queue_head_t kswapd_wait;
- /*
- * In the atomic allocation case we only 'kick' the
- * state machine, but do not try to free pages
- * ourselves.
- */
- freed = try_to_free_pages(gfp_mask, zone);
+ zone = zonelist->zones;
+ for (;;) {
+ zone_t *z = *(zone++);
+ if (!z)
+ break;
+ if (z->free_pages > z->pages_low)
+ continue;
- if (!freed && !(gfp_mask & __GFP_HIGH))
- return 0;
+ z->zone_wake_kswapd = 1;
+ wake_up_interruptible(&kswapd_wait);
+
+ /* Are we reaching the critical stage? */
+ if (!z->low_on_memory) {
+ /* Not yet critical, so let kswapd handle it.. */
+ if (z->free_pages > z->pages_min)
+ continue;
+ z->low_on_memory = 1;
+ }
+ /*
+ * In the atomic allocation case we only 'kick' the
+ * state machine, but do not try to free pages
+ * ourselves.
+ */
+ tried = 1;
+ freed |= try_to_free_pages(gfp_mask, z);
+ }
+ if (tried && !freed) {
+ if (!(gfp_mask & __GFP_HIGH))
+ return 0;
+ }
return 1;
}
@@ -237,9 +268,7 @@ static inline int zone_balance_memory (zone_t *zone, int gfp_mask)
*/
struct page * __alloc_pages (zonelist_t *zonelist, unsigned long order)
{
- zone_t **zone, *z;
- struct page *page;
- int gfp_mask;
+ zone_t **zone = zonelist->zones;
/*
* (If anyone calls gfp from interrupts nonatomically then it
@@ -248,10 +277,8 @@ struct page * __alloc_pages (zonelist_t *zonelist, unsigned long order)
* We are falling back to lower-level zones if allocation
* in a higher zone fails.
*/
- zone = zonelist->zones;
- gfp_mask = zonelist->gfp_mask;
for (;;) {
- z = *(zone++);
+ zone_t *z = *(zone++);
if (!z)
break;
if (!z->size)
@@ -261,23 +288,10 @@ struct page * __alloc_pages (zonelist_t *zonelist, unsigned long order)
* do our best to just allocate things without
* further thought.
*/
- if (!(current->flags & PF_MEMALLOC))
- {
- unsigned long free = classfree(z);
-
- if (free <= z->pages_high)
- {
- extern wait_queue_head_t kswapd_wait;
-
- z->zone_wake_kswapd = 1;
- wake_up_interruptible(&kswapd_wait);
-
- if (free <= z->pages_min)
- z->low_on_memory = 1;
-
- if (z->low_on_memory)
- goto balance;
- }
+ if (!(current->flags & PF_MEMALLOC)) {
+ /* Are we low on memory? */
+ if (z->free_pages <= z->pages_low)
+ continue;
}
/*
* This is an optimization for the 'higher order zone
@@ -287,24 +301,30 @@ struct page * __alloc_pages (zonelist_t *zonelist, unsigned long order)
* we do not take the spinlock and it's not exact for
* the higher order case, but will do it for most things.)
*/
-ready:
if (z->free_pages) {
- page = rmqueue(z, order);
+ struct page *page = rmqueue(z, order);
if (page)
return page;
}
}
-
-nopage:
+ if (zone_balance_memory(zonelist)) {
+ zone = zonelist->zones;
+ for (;;) {
+ zone_t *z = *(zone++);
+ if (!z)
+ break;
+ if (z->free_pages) {
+ struct page *page = rmqueue(z, order);
+ if (page)
+ return page;
+ }
+ }
+ }
return NULL;
/*
* The main chunk of the balancing code is in this offline branch:
*/
-balance:
- if (!zone_balance_memory(z, gfp_mask))
- goto nopage;
- goto ready;
}
/*
@@ -549,7 +569,7 @@ void __init free_area_init_core(int nid, pg_data_t *pgdat, struct page **gmap,
zone->offset = offset;
cumulative += size;
- mask = (cumulative / zone_balance_ratio[j]);
+ mask = (size / zone_balance_ratio[j]);
if (mask < zone_balance_min[j])
mask = zone_balance_min[j];
else if (mask > zone_balance_max[j])
diff --git a/net/Config.in b/net/Config.in
index 624885478..ce5b6faa9 100644
--- a/net/Config.in
+++ b/net/Config.in
@@ -13,9 +13,9 @@ if [ "$CONFIG_NETLINK" = "y" ]; then
tristate ' Netlink device emulation' CONFIG_NETLINK_DEV
fi
bool 'Network packet filtering (replaces ipchains)' CONFIG_NETFILTER
-if [ "$CONFIG_NETFILTER" = "y" ]; then
- bool ' Network packet filtering debugging' CONFIG_NETFILTER_DEBUG
-fi
+#if [ "$CONFIG_NETFILTER" = "y" ]; then
+# bool ' Network packet filtering debugging' CONFIG_NETFILTER_DEBUG
+#fi
bool 'Socket Filtering' CONFIG_FILTER
tristate 'Unix domain sockets' CONFIG_UNIX
bool 'TCP/IP networking' CONFIG_INET
diff --git a/net/Makefile b/net/Makefile
index bf234eae1..44b34d799 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -20,6 +20,10 @@ endif
ifeq ($(CONFIG_INET),y)
SUB_DIRS += ipv4
+ifeq ($(CONFIG_NETFILTER),y)
+SUB_DIRS += ipv4/netfilter
+MOD_SUB_DIRS += ipv4/netfilter
+endif
endif
ifeq ($(CONFIG_UNIX),y)
@@ -198,7 +202,7 @@ endif
endif
L_TARGET := network.a
-L_OBJS := $(SOCK) protocols.o $(join $(SUB_DIRS),$(SUB_DIRS:%=/%.o))
+L_OBJS := $(SOCK) protocols.o $(join $(SUB_DIRS), $(patsubst %,/%.o,$(notdir $(SUB_DIRS))))
M_OBJS :=
diff --git a/net/core/netfilter.c b/net/core/netfilter.c
index 18f697755..02c3bc989 100644
--- a/net/core/netfilter.c
+++ b/net/core/netfilter.c
@@ -4,9 +4,10 @@
* Thanks to Rob `CmdrTaco' Malda for not influencing this code in any
* way.
*
- * Rusty Russell (C)1998 -- This code is GPL.
+ * Rusty Russell (C)2000 -- This code is GPL.
*
* February 2000: Modified by James Morris to have 1 queue per protocol.
+ * 15-Mar-2000: Added NF_REPEAT --RR.
*/
#include <linux/config.h>
#include <linux/netfilter.h>
@@ -56,8 +57,6 @@ int nf_register_hook(struct nf_hook_ops *reg)
{
struct list_head *i;
- NFDEBUG("nf_register_hook: pf=%i hook=%u.\n", reg->pf, reg->hooknum);
-
br_write_lock_bh(BR_NETPROTO_LOCK);
for (i = nf_hooks[reg->pf][reg->hooknum].next;
i != &nf_hooks[reg->pf][reg->hooknum];
@@ -119,7 +118,16 @@ out:
void nf_unregister_sockopt(struct nf_sockopt_ops *reg)
{
/* No point being interruptible: we're probably in cleanup_module() */
+ restart:
down(&nf_sockopt_mutex);
+ if (reg->use != 0) {
+ /* To be woken by nf_sockopt call... */
+ reg->cleanup_task = current;
+ up(&nf_sockopt_mutex);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule();
+ goto restart;
+ }
list_del(&reg->list);
up(&nf_sockopt_mutex);
}
@@ -178,7 +186,7 @@ void nf_dump_skb(int pf, struct sk_buff *skb)
dst_port = ntohs(tcp->dest);
}
- printk("PROTO=%d %ld.%ld.%ld.%ld:%hu %ld.%ld.%ld.%ld:%hu"
+ printk("PROTO=%d %d.%d.%d.%d:%hu %d.%d.%d.%d:%hu"
" L=%hu S=0x%2.2hX I=%hu F=0x%4.4hX T=%hu",
ip->protocol,
(ntohl(ip->saddr)>>24)&0xFF,
@@ -261,9 +269,16 @@ void nf_debug_ip_finish_output2(struct sk_buff *skb)
if (skb->nf_debug != ((1 << NF_IP_PRE_ROUTING)
| (1 << NF_IP_FORWARD)
| (1 << NF_IP_POST_ROUTING))) {
- printk("ip_finish_output: bad unowned skb = %p: ",skb);
- debug_print_hooks_ip(skb->nf_debug);
- nf_dump_skb(PF_INET, skb);
+ /* Fragments will have no owners, but still
+ may be local */
+ if (!(skb->nh.iph->frag_off & htons(IP_MF|IP_OFFSET))
+ || skb->nf_debug != ((1 << NF_IP_LOCAL_OUT)
+ | (1 << NF_IP_POST_ROUTING))){
+ printk("ip_finish_output:"
+ " bad unowned skb = %p: ",skb);
+ debug_print_hooks_ip(skb->nf_debug);
+ nf_dump_skb(PF_INET, skb);
+ }
}
}
}
@@ -274,31 +289,42 @@ static int nf_sockopt(struct sock *sk, int pf, int val,
char *opt, int *len, int get)
{
struct list_head *i;
+ struct nf_sockopt_ops *ops;
int ret;
if (down_interruptible(&nf_sockopt_mutex) != 0)
return -EINTR;
for (i = nf_sockopts.next; i != &nf_sockopts; i = i->next) {
- struct nf_sockopt_ops *ops = (struct nf_sockopt_ops *)i;
+ ops = (struct nf_sockopt_ops *)i;
if (ops->pf == pf) {
if (get) {
if (val >= ops->get_optmin
&& val < ops->get_optmax) {
+ ops->use++;
+ up(&nf_sockopt_mutex);
ret = ops->get(sk, val, opt, len);
goto out;
}
} else {
if (val >= ops->set_optmin
&& val < ops->set_optmax) {
+ ops->use++;
+ up(&nf_sockopt_mutex);
ret = ops->set(sk, val, opt, *len);
goto out;
}
}
}
}
- ret = -ENOPROTOOPT;
+ up(&nf_sockopt_mutex);
+ return -ENOPROTOOPT;
+
out:
+ down(&nf_sockopt_mutex);
+ ops->use--;
+ if (ops->cleanup_task)
+ wake_up_process(ops->cleanup_task);
up(&nf_sockopt_mutex);
return ret;
}
@@ -334,6 +360,10 @@ static unsigned int nf_iterate(struct list_head *head,
case NF_DROP:
return NF_DROP;
+ case NF_REPEAT:
+ *i = (*i)->prev;
+ break;
+
#ifdef CONFIG_NETFILTER_DEBUG
case NF_ACCEPT:
break;
@@ -367,7 +397,6 @@ int nf_register_queue_handler(int pf, nf_queue_outfn_t outfn, void *data)
/* The caller must flush their queue before this */
int nf_unregister_queue_handler(int pf)
{
- NFDEBUG("Unregistering Netfilter queue handler for pf=%d\n", pf);
br_write_lock_bh(BR_NETPROTO_LOCK);
queue_handler[pf].outfn = NULL;
queue_handler[pf].data = NULL;
@@ -390,7 +419,6 @@ static void nf_queue(struct sk_buff *skb,
struct nf_info *info;
if (!queue_handler[pf].outfn) {
- NFDEBUG("nf_queue: noone wants the packet, dropping it.\n");
kfree_skb(skb);
return;
}
@@ -432,6 +460,14 @@ int nf_hook_slow(int pf, unsigned int hook, struct sk_buff *skb,
unsigned int verdict;
int ret = 0;
+#ifdef CONFIG_NETFILTER_DEBUG
+ if (skb->nf_debug & (1 << hook)) {
+ printk("nf_hook: hook %i already set.\n", hook);
+ nf_dump_skb(pf, skb);
+ }
+ skb->nf_debug |= (1 << hook);
+#endif
+
elem = &nf_hooks[pf][hook];
verdict = nf_iterate(&nf_hooks[pf][hook], &skb, hook, indev,
outdev, &elem, okfn);
@@ -473,6 +509,11 @@ void nf_reinject(struct sk_buff *skb, struct nf_info *info,
}
/* Continue traversal iff userspace said ok... */
+ if (verdict == NF_REPEAT) {
+ elem = elem->prev;
+ verdict = NF_ACCEPT;
+ }
+
if (verdict == NF_ACCEPT) {
verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
&skb, info->hook,
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index ecda47d7a..dad1f3925 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -4,7 +4,7 @@
* Authors: Alan Cox <iiitac@pyr.swan.ac.uk>
* Florian La Roche <rzsfl@rz.uni-sb.de>
*
- * Version: $Id: skbuff.c,v 1.69 2000/03/06 03:47:58 davem Exp $
+ * Version: $Id: skbuff.c,v 1.70 2000/03/17 14:41:39 davem Exp $
*
* Fixes:
* Alan Cox : Fixed the worst of the load balancer bugs.
@@ -203,7 +203,7 @@ static inline void skb_headerinit(void *p, kmem_cache_t *cache,
skb->dst = NULL;
skb->rx_dev = NULL;
#ifdef CONFIG_NETFILTER
- skb->nfmark = skb->nfreason = skb->nfcache = 0;
+ skb->nfmark = skb->nfcache = 0;
skb->nfct = NULL;
#ifdef CONFIG_NETFILTER_DEBUG
skb->nf_debug = 0;
@@ -319,7 +319,6 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
new->security=old->security;
#ifdef CONFIG_NETFILTER
new->nfmark=old->nfmark;
- new->nfreason=old->nfreason;
new->nfcache=old->nfcache;
new->nfct=old->nfct;
nf_conntrack_get(new->nfct);
diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c
index 5e54a6fa8..2ba5f2f6c 100644
--- a/net/decnet/dn_route.c
+++ b/net/decnet/dn_route.c
@@ -805,10 +805,7 @@ non_local_input:
key.scope = RT_SCOPE_UNIVERSE;
#ifdef CONFIG_DECNET_ROUTE_FWMASK
- if (skb->nfreason == NF_REASON_FOR_ROUTING)
- key.fwmark = skb->fwmark;
- else
- key.fwmark = 0;
+ key.fwmark = skb->fwmark;
#else
key.fwmark = 0;
#endif
@@ -886,9 +883,7 @@ int dn_route_input(struct sk_buff *skb)
(rt->key.daddr == cb->dst) &&
(rt->key.oif == 0) &&
#ifdef CONFIG_DECNET_ROUTE_FWMASK
- (rt->key.fwmark == (skb->nfreason ==
- NF_REASON_FOR_ROUTING
- ? skb->nfmark : 0)) &&
+ (rt->key.fwmark == skb->nfmark) &&
#endif
(rt->key.iif == cb->iif)) {
rt->u.dst.lastuse = jiffies;
diff --git a/net/ipv4/Config.in b/net/ipv4/Config.in
index 32e2aca16..68fea0272 100644
--- a/net/ipv4/Config.in
+++ b/net/ipv4/Config.in
@@ -9,7 +9,7 @@ if [ "$CONFIG_IP_ADVANCED_ROUTER" = "y" ]; then
bool ' IP: policy routing' CONFIG_IP_MULTIPLE_TABLES
if [ "$CONFIG_IP_MULTIPLE_TABLES" = "y" ]; then
if [ "$CONFIG_NETFILTER" = "y" ]; then
- bool ' IP: use FWMARK value as routing key' CONFIG_IP_ROUTE_FWMARK
+ bool ' IP: use netfilter MARK value as routing key' CONFIG_IP_ROUTE_FWMARK
fi
bool ' IP: fast network address translation' CONFIG_IP_ROUTE_NAT
fi
@@ -53,3 +53,6 @@ bool ' IP: Allow large windows (not recommended if <16Mb of memory)' CONFIG_SKB
#if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
# bool ' IP: support checksum copy to user for UDP (EXPERIMENTAL)' CONFIG_UDP_DELAY_CSUM
#fi
+if [ "$CONFIG_NETFILTER" != "n" ]; then
+ source net/ipv4/netfilter/Config.in
+fi
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index d7da63f4e..7561e190b 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -3,7 +3,7 @@
*
* Alan Cox, <alan@redhat.com>
*
- * Version: $Id: icmp.c,v 1.65 2000/02/22 23:54:25 davem Exp $
+ * Version: $Id: icmp.c,v 1.66 2000/03/17 14:41:50 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -332,20 +332,6 @@ struct icmp_control
static struct icmp_control icmp_pointers[NR_ICMP_TYPES+1];
/*
- * Build xmit assembly blocks
- */
-
-struct icmp_bxm
-{
- void *data_ptr;
- int data_len;
- struct icmphdr icmph;
- unsigned long csum;
- struct ip_options replyopts;
- unsigned char optbuf[40];
-};
-
-/*
* The ICMP socket. This is the most convenient way to flow control
* our ICMP output as well as maintain a clean interface throughout
* all layers. All Socketless IP sends will soon be gone.
@@ -508,7 +494,7 @@ static int icmp_glue_bits(const void *p, char *to, unsigned int offset, unsigned
* Driving logic for building and sending ICMP messages.
*/
-static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
+void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
{
struct sock *sk=icmp_socket->sk;
struct ipcm_cookie ipc;
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 8d651b042..01a39b6e4 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -599,6 +599,10 @@ int ipgre_rcv(struct sk_buff *skb, unsigned short len)
skb->dev = tunnel->dev;
dst_release(skb->dst);
skb->dst = NULL;
+#ifdef CONFIG_NETFILTER
+ nf_conntrack_put(skb->nfct);
+ skb->nfct = NULL;
+#endif
netif_rx(skb);
read_unlock(&ipgre_lock);
return(0);
@@ -818,6 +822,11 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
ip_select_ident(iph, &rt->u.dst);
ip_send_check(iph);
+#ifdef CONFIG_NETFILTER
+ nf_conntrack_put(skb->nfct);
+ skb->nfct = NULL;
+#endif
+
stats->tx_bytes += skb->len;
stats->tx_packets++;
ip_send(skb);
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 26d025d32..f3013ca57 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -5,7 +5,7 @@
*
* The Internet Protocol (IP) output module.
*
- * Version: $Id: ip_output.c,v 1.81 2000/03/06 03:48:01 davem Exp $
+ * Version: $Id: ip_output.c,v 1.82 2000/03/17 14:41:50 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -894,6 +894,9 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
/* Connection association is same as pre-frag packet */
skb2->nfct = skb->nfct;
nf_conntrack_get(skb2->nfct);
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb2->nf_debug = skb->nf_debug;
+#endif
#endif
/*
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index 5518ec1cb..2823c2c7e 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -1,7 +1,7 @@
/*
* Linux NET3: IP/IP protocol decoder.
*
- * Version: $Id: ipip.c,v 1.30 2000/01/06 00:41:55 davem Exp $
+ * Version: $Id: ipip.c,v 1.31 2000/03/17 14:41:51 davem Exp $
*
* Authors:
* Sam Lantinga (slouken@cs.ucdavis.edu) 02/01/95
@@ -93,6 +93,7 @@
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/sched.h>
@@ -483,6 +484,10 @@ int ipip_rcv(struct sk_buff *skb, unsigned short len)
skb->dev = tunnel->dev;
dst_release(skb->dst);
skb->dst = NULL;
+#ifdef CONFIG_NETFILTER
+ nf_conntrack_put(skb->nfct);
+ skb->nfct = NULL;
+#endif
netif_rx(skb);
read_unlock(&ipip_lock);
return 0;
@@ -619,6 +624,11 @@ static int ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
ip_select_ident(iph, &rt->u.dst);
ip_send_check(iph);
+#ifdef CONFIG_NETFILTER
+ nf_conntrack_put(skb->nfct);
+ skb->nfct = NULL;
+#endif
+
stats->tx_bytes += skb->len;
stats->tx_packets++;
ip_send(skb);
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index fce5a43f8..1e33ec4ca 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -9,7 +9,7 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
- * Version: $Id: ipmr.c,v 1.50 2000/01/09 02:19:32 davem Exp $
+ * Version: $Id: ipmr.c,v 1.51 2000/03/17 14:41:52 davem Exp $
*
* Fixes:
* Michael Chastain : Incorrect size of copying.
@@ -1100,6 +1100,10 @@ static void ip_encap(struct sk_buff *skb, u32 saddr, u32 daddr)
skb->h.ipiph = skb->nh.iph;
skb->nh.iph = iph;
+#ifdef CONFIG_NETFILTER
+ nf_conntrack_put(skb->nfct);
+ skb->nfct = NULL;
+#endif
}
static inline int ipmr_forward_finish(struct sk_buff *skb)
@@ -1433,6 +1437,10 @@ int pim_rcv_v1(struct sk_buff * skb, unsigned short len)
skb->dst = NULL;
((struct net_device_stats*)reg_dev->priv)->rx_bytes += skb->len;
((struct net_device_stats*)reg_dev->priv)->rx_packets++;
+#ifdef CONFIG_NETFILTER
+ nf_conntrack_put(skb->nfct);
+ skb->nfct = NULL;
+#endif
netif_rx(skb);
dev_put(reg_dev);
return 0;
@@ -1488,6 +1496,10 @@ int pim_rcv(struct sk_buff * skb, unsigned short len)
((struct net_device_stats*)reg_dev->priv)->rx_bytes += skb->len;
((struct net_device_stats*)reg_dev->priv)->rx_packets++;
skb->dst = NULL;
+#ifdef CONFIG_NETFILTER
+ nf_conntrack_put(skb->nfct);
+ skb->nfct = NULL;
+#endif
netif_rx(skb);
dev_put(reg_dev);
return 0;
diff --git a/net/ipv4/netfilter/Config.in b/net/ipv4/netfilter/Config.in
new file mode 100644
index 000000000..bf2a28269
--- /dev/null
+++ b/net/ipv4/netfilter/Config.in
@@ -0,0 +1,64 @@
+#
+# IP netfilter configuration
+#
+mainmenu_option next_comment
+comment ' IP: Netfilter Configuration'
+
+tristate 'Connection tracking (required for masq/NAT)' CONFIG_IP_NF_CONNTRACK
+if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then
+ dep_tristate ' FTP protocol support' CONFIG_IP_NF_FTP $CONFIG_IP_NF_CONNTRACK
+fi
+
+if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_NETLINK" = "y" ]; then
+ tristate 'Userspace queueing via NETLINK (EXPERIMENTAL)' CONFIG_IP_NF_QUEUE
+fi
+tristate 'IP tables support (required for filtering/masq/NAT)' CONFIG_IP_NF_IPTABLES
+if [ "$CONFIG_IP_NF_IPTABLES" != "n" ]; then
+# The simple matches.
+ dep_tristate ' limit match support' CONFIG_IP_NF_MATCH_LIMIT $CONFIG_IP_NF_IPTABLES
+ dep_tristate ' MAC address match support' CONFIG_IP_NF_MATCH_MAC $CONFIG_IP_NF_IPTABLES
+ dep_tristate ' netfilter MARK match support' CONFIG_IP_NF_MATCH_MARK $CONFIG_IP_NF_IPTABLES
+ dep_tristate ' Multiple port match support' CONFIG_IP_NF_MATCH_MULTIPORT $CONFIG_IP_NF_IPTABLES
+ dep_tristate ' TOS match support' CONFIG_IP_NF_MATCH_TOS $CONFIG_IP_NF_IPTABLES
+ if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then
+ dep_tristate ' Connection state match support' CONFIG_IP_NF_MATCH_STATE $CONFIG_IP_NF_CONNTRACK $CONFIG_IP_NF_IPTABLES
+ fi
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ dep_tristate ' Unclean match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_UNCLEAN $CONFIG_IP_NF_IPTABLES
+ dep_tristate ' Owner match support (EXPERIMENTAL)' CONFIG_IP_NF_MATCH_OWNER $CONFIG_IP_NF_IPTABLES
+ fi
+# The targets
+ dep_tristate ' Packet filtering' CONFIG_IP_NF_FILTER $CONFIG_IP_NF_IPTABLES
+ if [ "$CONFIG_IP_NF_FILTER" != "n" ]; then
+ dep_tristate ' REJECT target support' CONFIG_IP_NF_TARGET_REJECT $CONFIG_IP_NF_FILTER
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ dep_tristate ' MIRROR target support (EXPERIMENTAL)' CONFIG_IP_NF_TARGET_MIRROR $CONFIG_IP_NF_FILTER
+ fi
+ fi
+
+ if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then
+ dep_tristate ' Full NAT' CONFIG_IP_NF_NAT $CONFIG_IP_NF_IPTABLES
+ if [ "$CONFIG_IP_NF_NAT" != "n" ]; then
+ dep_tristate ' MASQUERADE target support' CONFIG_IP_NF_TARGET_MASQUERADE $CONFIG_IP_NF_NAT
+ dep_tristate ' REDIRECT target support' CONFIG_IP_NF_TARGET_REDIRECT $CONFIG_IP_NF_NAT
+ fi
+ fi
+
+ dep_tristate ' Packet mangling' CONFIG_IP_NF_MANGLE $CONFIG_IP_NF_IPTABLES
+ if [ "$CONFIG_IP_NF_MANGLE" != "n" ]; then
+ dep_tristate ' TOS target support' CONFIG_IP_NF_TARGET_TOS $CONFIG_IP_NF_MANGLE
+ dep_tristate ' MARK target support' CONFIG_IP_NF_TARGET_MARK $CONFIG_IP_NF_MANGLE
+ fi
+ dep_tristate ' LOG target support' CONFIG_IP_NF_TARGET_LOG $CONFIG_IP_NF_IPTABLES
+fi
+
+# Backwards compatibility modules: only if you don't build in the others.
+if [ "$CONFIG_IP_NF_CONNTRACK" != "y" ]; then
+ if [ "$CONFIG_IP_NF_IPTABLES" != "y" ]; then
+ tristate 'ipchains (2.2-style) support' CONFIG_IP_NF_COMPAT_IPCHAINS
+ if [ "$CONFIG_IP_NF_COMPAT_IPCHAINS" != "y" ]; then
+ tristate 'ipfwadm (2.0-style) support' CONFIG_IP_NF_COMPAT_IPFWADM
+ fi
+ fi
+fi
+endmenu
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile
new file mode 100644
index 000000000..41a61e010
--- /dev/null
+++ b/net/ipv4/netfilter/Makefile
@@ -0,0 +1,234 @@
+#
+# Makefile for the netfilter modules on top of IPv4.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+O_TARGET := netfilter.o
+MOD_LIST_NAME := IPV4_MODULES
+M_OBJS :=
+
+IP_NF_CONNTRACK_OBJ:=ip_conntrack_core.o ip_conntrack_proto_generic.o ip_conntrack_proto_tcp.o ip_conntrack_proto_udp.o ip_conntrack_proto_icmp.o
+
+IP_NF_NAT_OBJ:=ip_nat_core.o ip_nat_proto_unknown.o ip_nat_proto_tcp.o ip_nat_proto_udp.o ip_nat_proto_icmp.o
+
+# Link order matters here.
+ifeq ($(CONFIG_IP_NF_CONNTRACK),y)
+OX_OBJS += ip_conntrack_standalone.o
+O_OBJS += $(IP_NF_CONNTRACK_OBJ)
+else
+ ifeq ($(CONFIG_IP_NF_CONNTRACK),m)
+ M_OBJS += ip_conntrack.o
+ endif
+endif
+
+ifeq ($(CONFIG_IP_NF_QUEUE),y)
+O_OBJS += ip_queue.o
+else
+ ifeq ($(CONFIG_IP_NF_QUEUE),m)
+ M_OBJS += ip_queue.o
+ endif
+endif
+
+ifeq ($(CONFIG_IP_NF_FTP),y)
+OX_OBJS += ip_conntrack_ftp.o
+else
+ ifeq ($(CONFIG_IP_NF_FTP),m)
+ M_OBJS += ip_conntrack_ftp.o
+ endif
+endif
+
+ifeq ($(CONFIG_IP_NF_IPTABLES),y)
+O_OBJS += ip_tables.o
+else
+ ifeq ($(CONFIG_IP_NF_IPTABLES),m)
+ M_OBJS += ip_tables.o
+ endif
+endif
+
+ifeq ($(CONFIG_IP_NF_MATCH_LIMIT),y)
+O_OBJS += ipt_limit.o
+else
+ ifeq ($(CONFIG_IP_NF_MATCH_LIMIT),m)
+ M_OBJS += ipt_limit.o
+ endif
+endif
+
+ifeq ($(CONFIG_IP_NF_MATCH_MARK),y)
+O_OBJS += ipt_mark.o
+else
+ ifeq ($(CONFIG_IP_NF_MATCH_MARK),m)
+ M_OBJS += ipt_mark.o
+ endif
+endif
+
+ifeq ($(CONFIG_IP_NF_MATCH_MAC),y)
+O_OBJS += ipt_mac.o
+else
+ ifeq ($(CONFIG_IP_NF_MATCH_MAC),m)
+ M_OBJS += ipt_mac.o
+ endif
+endif
+
+ifeq ($(CONFIG_IP_NF_MATCH_MULTIPORT),y)
+O_OBJS += ipt_multiport.o
+else
+ ifeq ($(CONFIG_IP_NF_MATCH_MULTIPORT),m)
+ M_OBJS += ipt_multiport.o
+ endif
+endif
+
+ifeq ($(CONFIG_IP_NF_MATCH_OWNER),y)
+O_OBJS += ipt_owner.o
+else
+ ifeq ($(CONFIG_IP_NF_MATCH_OWNER),m)
+ M_OBJS += ipt_owner.o
+ endif
+endif
+
+ifeq ($(CONFIG_IP_NF_MATCH_TOS),y)
+O_OBJS += ipt_tos.o
+else
+ ifeq ($(CONFIG_IP_NF_MATCH_TOS),m)
+ M_OBJS += ipt_tos.o
+ endif
+endif
+
+ifeq ($(CONFIG_IP_NF_MATCH_STATE),y)
+O_OBJS += ipt_state.o
+else
+ ifeq ($(CONFIG_IP_NF_MATCH_STATE),m)
+ M_OBJS += ipt_state.o
+ endif
+endif
+
+ifeq ($(CONFIG_IP_NF_MATCH_UNCLEAN),y)
+O_OBJS += ipt_unclean.o
+else
+ ifeq ($(CONFIG_IP_NF_MATCH_UNCLEAN),m)
+ M_OBJS += ipt_unclean.o
+ endif
+endif
+
+ifeq ($(CONFIG_IP_NF_FILTER),y)
+O_OBJS += iptable_filter.o
+else
+ ifeq ($(CONFIG_IP_NF_FILTER),m)
+ M_OBJS += iptable_filter.o
+ endif
+endif
+
+ifeq ($(CONFIG_IP_NF_NAT),y)
+OX_OBJS += ip_nat_standalone.o
+O_OBJS += ip_nat_rule.o $(IP_NF_NAT_OBJ)
+ ifeq ($(CONFIG_IP_NF_FTP),y)
+ O_OBJS += ip_nat_ftp.o
+ endif
+else
+ ifeq ($(CONFIG_IP_NF_NAT),m)
+ M_OBJS += iptable_nat.o
+ ifeq ($(CONFIG_IP_NF_FTP),m)
+ M_OBJS += ip_nat_ftp.o
+ endif
+ endif
+endif
+
+ifeq ($(CONFIG_IP_NF_MANGLE),y)
+O_OBJS += iptable_mangle.o
+else
+ ifeq ($(CONFIG_IP_NF_MANGLE),m)
+ M_OBJS += iptable_mangle.o
+ endif
+endif
+
+ifeq ($(CONFIG_IP_NF_TARGET_REJECT),y)
+O_OBJS += ipt_REJECT.o
+else
+ ifeq ($(CONFIG_IP_NF_TARGET_REJECT),m)
+ M_OBJS += ipt_REJECT.o
+ endif
+endif
+
+ifeq ($(CONFIG_IP_NF_TARGET_MIRROR),y)
+O_OBJS += ipt_MIRROR.o
+else
+ ifeq ($(CONFIG_IP_NF_TARGET_MIRROR),m)
+ M_OBJS += ipt_MIRROR.o
+ endif
+endif
+
+ifeq ($(CONFIG_IP_NF_TARGET_TOS),y)
+O_OBJS += ipt_TOS.o
+else
+ ifeq ($(CONFIG_IP_NF_TARGET_TOS),m)
+ M_OBJS += ipt_TOS.o
+ endif
+endif
+
+ifeq ($(CONFIG_IP_NF_TARGET_MARK),y)
+O_OBJS += ipt_MARK.o
+else
+ ifeq ($(CONFIG_IP_NF_TARGET_MARK),m)
+ M_OBJS += ipt_MARK.o
+ endif
+endif
+
+ifeq ($(CONFIG_IP_NF_TARGET_MASQUERADE),y)
+O_OBJS += ipt_MASQUERADE.o
+else
+ ifeq ($(CONFIG_IP_NF_TARGET_MASQUERADE),m)
+ M_OBJS += ipt_MASQUERADE.o
+ endif
+endif
+
+ifeq ($(CONFIG_IP_NF_TARGET_REDIRECT),y)
+O_OBJS += ipt_REDIRECT.o
+else
+ ifeq ($(CONFIG_IP_NF_TARGET_REDIRECT),m)
+ M_OBJS += ipt_REDIRECT.o
+ endif
+endif
+
+ifeq ($(CONFIG_IP_NF_TARGET_LOG),y)
+O_OBJS += ipt_LOG.o
+else
+ ifeq ($(CONFIG_IP_NF_TARGET_LOG),m)
+ M_OBJS += ipt_LOG.o
+ endif
+endif
+
+ifeq ($(CONFIG_IP_NF_COMPAT_IPCHAINS),y)
+O_OBJS += ipchains.o
+else
+ ifeq ($(CONFIG_IP_NF_COMPAT_IPCHAINS),m)
+ M_OBJS += ipchains.o
+ endif
+endif
+
+ifeq ($(CONFIG_IP_NF_COMPAT_IPFWADM),y)
+O_OBJS += ipfwadm.o
+else
+ ifeq ($(CONFIG_IP_NF_COMPAT_IPFWADM),m)
+ M_OBJS += ipfwadm.o
+ endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+ip_conntrack.o: ip_conntrack_standalone.o $(IP_NF_CONNTRACK_OBJ)
+ $(LD) -r -o $@ $(IP_NF_CONNTRACK_OBJ) ip_conntrack_standalone.o
+
+iptable_nat.o: ip_nat_standalone.o ip_nat_rule.o $(IP_NF_NAT_OBJ)
+ $(LD) -r -o $@ ip_nat_standalone.o ip_nat_rule.o $(IP_NF_NAT_OBJ)
+
+# All the parts of conntrack and NAT required for compatibility layer.
+IP_NF_COMPAT_LAYER:=ip_fw_compat.o ip_fw_compat_redir.o ip_fw_compat_masq.o $(IP_NF_CONNTRACK_OBJ) $(IP_NF_NAT_OBJ)
+
+ipfwadm.o: ipfwadm_core.o $(IP_NF_COMPAT_LAYER)
+ $(LD) -r -o $@ ipfwadm_core.o $(IP_NF_COMPAT_LAYER)
+
+ipchains.o: ipchains_core.o $(IP_NF_COMPAT_LAYER)
+ $(LD) -r -o $@ ipchains_core.o $(IP_NF_COMPAT_LAYER)
diff --git a/net/ipv4/netfilter/ip_conntrack_core.c b/net/ipv4/netfilter/ip_conntrack_core.c
new file mode 100644
index 000000000..9007cdc89
--- /dev/null
+++ b/net/ipv4/netfilter/ip_conntrack_core.c
@@ -0,0 +1,891 @@
+/* Connection state tracking for netfilter. This is separated from,
+ but required by, the NAT layer; it can also be used by an iptables
+ extension. */
+
+/* (c) 1999 Paul `Rusty' Russell. Licenced under the GNU General
+ Public Licence. */
+
+#ifdef MODULE
+#define __NO_VERSION__
+#endif
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/ip.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/vmalloc.h>
+#include <linux/brlock.h>
+#include <net/checksum.h>
+#include <linux/stddef.h>
+#include <linux/sysctl.h>
+
+/* This rwlock protects the main hash table, protocol/helper/expected
+ registrations, conntrack timers*/
+#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_conntrack_lock)
+#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_conntrack_lock)
+
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
+#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
+#include <linux/netfilter_ipv4/ip_conntrack_core.h>
+#include <linux/netfilter_ipv4/listhelp.h>
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+DECLARE_RWLOCK(ip_conntrack_lock);
+
+void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack) = NULL;
+static LIST_HEAD(expect_list);
+static LIST_HEAD(protocol_list);
+static LIST_HEAD(helpers);
+unsigned int ip_conntrack_htable_size = 0;
+static int ip_conntrack_max = 0;
+static atomic_t ip_conntrack_count = ATOMIC_INIT(0);
+struct list_head *ip_conntrack_hash;
+
+extern struct ip_conntrack_protocol ip_conntrack_generic_protocol;
+
+static inline int proto_cmpfn(const struct ip_conntrack_protocol *curr,
+ u_int8_t protocol)
+{
+ return protocol == curr->proto;
+}
+
+struct ip_conntrack_protocol *__find_proto(u_int8_t protocol)
+{
+ struct ip_conntrack_protocol *p;
+
+ MUST_BE_READ_LOCKED(&ip_conntrack_lock);
+ p = LIST_FIND(&protocol_list, proto_cmpfn,
+ struct ip_conntrack_protocol *, protocol);
+ if (!p)
+ p = &ip_conntrack_generic_protocol;
+
+ return p;
+}
+
+struct ip_conntrack_protocol *find_proto(u_int8_t protocol)
+{
+ struct ip_conntrack_protocol *p;
+
+ READ_LOCK(&ip_conntrack_lock);
+ p = __find_proto(protocol);
+ READ_UNLOCK(&ip_conntrack_lock);
+ return p;
+}
+
+static inline void ip_conntrack_put(struct ip_conntrack *ct)
+{
+ IP_NF_ASSERT(ct);
+ IP_NF_ASSERT(ct->infos[0].master);
+ /* nf_conntrack_put wants to go via an info struct, so feed it
+ one at random. */
+ nf_conntrack_put(&ct->infos[0]);
+}
+
+static inline u_int32_t
+hash_conntrack(const struct ip_conntrack_tuple *tuple)
+{
+#if 0
+ dump_tuple(tuple);
+#endif
+#ifdef CONFIG_NETFILTER_DEBUG
+ if (tuple->src.pad)
+ DEBUGP("Tuple %p has non-zero padding.\n", tuple);
+#endif
+ /* ntohl because more differences in low bits. */
+ /* To ensure that halves of the same connection don't hash
+ clash, we add the source per-proto again. */
+ return (ntohl(tuple->src.ip + tuple->dst.ip
+ + tuple->src.u.all + tuple->dst.u.all
+ + tuple->dst.protonum)
+ + ntohs(tuple->src.u.all))
+ % ip_conntrack_htable_size;
+}
+
+inline int
+get_tuple(const struct iphdr *iph, size_t len,
+ struct ip_conntrack_tuple *tuple,
+ struct ip_conntrack_protocol *protocol)
+{
+ int ret;
+
+ /* Can only happen when extracting tuples from inside ICMP
+ packets */
+ if (iph->frag_off & htons(IP_OFFSET)) {
+ if (net_ratelimit())
+ printk("ip_conntrack_core: Frag of proto %u.\n",
+ iph->protocol);
+ return 0;
+ }
+ /* Guarantee 8 protocol bytes: if more wanted, use len param */
+ else if (iph->ihl * 4 + 8 > len)
+ return 0;
+
+ tuple->src.ip = iph->saddr;
+ tuple->src.pad = 0;
+ tuple->dst.ip = iph->daddr;
+ tuple->dst.protonum = iph->protocol;
+
+ ret = protocol->pkt_to_tuple((u_int32_t *)iph + iph->ihl,
+ len - 4*iph->ihl,
+ tuple);
+ return ret;
+}
+
+static int
+invert_tuple(struct ip_conntrack_tuple *inverse,
+ const struct ip_conntrack_tuple *orig,
+ const struct ip_conntrack_protocol *protocol)
+{
+ inverse->src.ip = orig->dst.ip;
+ inverse->src.pad = 0;
+ inverse->dst.ip = orig->src.ip;
+ inverse->dst.protonum = orig->dst.protonum;
+
+ return protocol->invert_tuple(inverse, orig);
+}
+
+static void
+destroy_conntrack(struct nf_conntrack *nfct)
+{
+ struct ip_conntrack *ct = (struct ip_conntrack *)nfct;
+
+ IP_NF_ASSERT(atomic_read(&nfct->use) == 0);
+ IP_NF_ASSERT(!timer_pending(&ct->timeout));
+
+ if (ct->master.master)
+ nf_conntrack_put(&ct->master);
+
+ if (ip_conntrack_destroyed)
+ ip_conntrack_destroyed(ct);
+ kfree(ct);
+ atomic_dec(&ip_conntrack_count);
+}
+
+static void death_by_timeout(unsigned long ul_conntrack)
+{
+ struct ip_conntrack *ct = (void *)ul_conntrack;
+
+ WRITE_LOCK(&ip_conntrack_lock);
+ /* Remove from both hash lists */
+ LIST_DELETE(&ip_conntrack_hash
+ [hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple)],
+ &ct->tuplehash[IP_CT_DIR_ORIGINAL]);
+ LIST_DELETE(&ip_conntrack_hash
+ [hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple)],
+ &ct->tuplehash[IP_CT_DIR_REPLY]);
+ /* If our expected is in the list, take it out. */
+ if (ct->expected.expectant) {
+ IP_NF_ASSERT(list_inlist(&expect_list, &ct->expected));
+ IP_NF_ASSERT(ct->expected.expectant == ct);
+ LIST_DELETE(&expect_list, &ct->expected);
+ }
+ WRITE_UNLOCK(&ip_conntrack_lock);
+ ip_conntrack_put(ct);
+}
+
+static inline int
+conntrack_tuple_cmp(const struct ip_conntrack_tuple_hash *i,
+ const struct ip_conntrack_tuple *tuple,
+ const struct ip_conntrack *ignored_conntrack)
+{
+ MUST_BE_READ_LOCKED(&ip_conntrack_lock);
+ return i->ctrack != ignored_conntrack
+ && memcmp(tuple, &i->tuple, sizeof(*tuple)) == 0;
+}
+
+static struct ip_conntrack_tuple_hash *
+__ip_conntrack_find(const struct ip_conntrack_tuple *tuple,
+ const struct ip_conntrack *ignored_conntrack)
+{
+ struct ip_conntrack_tuple_hash *h;
+
+ MUST_BE_READ_LOCKED(&ip_conntrack_lock);
+ h = LIST_FIND(&ip_conntrack_hash[hash_conntrack(tuple)],
+ conntrack_tuple_cmp,
+ struct ip_conntrack_tuple_hash *,
+ tuple, ignored_conntrack);
+ return h;
+}
+
+/* Find a connection corresponding to a tuple. */
+struct ip_conntrack_tuple_hash *
+ip_conntrack_find_get(const struct ip_conntrack_tuple *tuple,
+ const struct ip_conntrack *ignored_conntrack)
+{
+ struct ip_conntrack_tuple_hash *h;
+
+ READ_LOCK(&ip_conntrack_lock);
+ h = __ip_conntrack_find(tuple, ignored_conntrack);
+ if (h)
+ atomic_inc(&h->ctrack->ct_general.use);
+ READ_UNLOCK(&ip_conntrack_lock);
+
+ return h;
+}
+
+/* Returns true if a connection correspondings to the tuple (required
+ for NAT). */
+int
+ip_conntrack_tuple_taken(const struct ip_conntrack_tuple *tuple,
+ const struct ip_conntrack *ignored_conntrack)
+{
+ struct ip_conntrack_tuple_hash *h;
+
+ READ_LOCK(&ip_conntrack_lock);
+ h = __ip_conntrack_find(tuple, ignored_conntrack);
+ READ_UNLOCK(&ip_conntrack_lock);
+
+ return h != NULL;
+}
+
+/* Returns TRUE if it dealt with ICMP, and filled in skb fields */
+int icmp_error_track(struct sk_buff *skb)
+{
+ const struct iphdr *iph = skb->nh.iph;
+ struct icmphdr *hdr = (struct icmphdr *)((u_int32_t *)iph + iph->ihl);
+ struct ip_conntrack_tuple innertuple, origtuple;
+ struct iphdr *inner = (struct iphdr *)(hdr + 1);
+ size_t datalen = skb->len - iph->ihl*4 - sizeof(*hdr);
+ struct ip_conntrack_protocol *innerproto;
+ struct ip_conntrack_tuple_hash *h;
+ enum ip_conntrack_info ctinfo;
+
+ if (iph->protocol != IPPROTO_ICMP)
+ return 0;
+
+ if (skb->len < iph->ihl * 4 + sizeof(struct icmphdr)) {
+ DEBUGP("icmp_error_track: too short\n");
+ return 1;
+ }
+
+ if (hdr->type != ICMP_DEST_UNREACH
+ && hdr->type != ICMP_SOURCE_QUENCH
+ && hdr->type != ICMP_TIME_EXCEEDED
+ && hdr->type != ICMP_PARAMETERPROB
+ && hdr->type != ICMP_REDIRECT)
+ return 0;
+
+ /* Ignore it if the checksum's bogus. */
+ if (ip_compute_csum((unsigned char *)hdr, sizeof(*hdr) + datalen)) {
+ DEBUGP("icmp_error_track: bad csum\n");
+ return 1;
+ }
+
+ innerproto = find_proto(inner->protocol);
+ /* Are they talking about one of our connections? */
+ if (inner->ihl * 4 + 8 > datalen
+ || !get_tuple(inner, datalen, &origtuple, innerproto)) {
+ DEBUGP("icmp_error: ! get_tuple p=%u (%u*4+%u dlen=%u)\n",
+ inner->protocol, inner->ihl, 8,
+ datalen);
+ return 1;
+ }
+
+ /* Ordinarily, we'd expect the inverted tupleproto, but it's
+ been preserved inside the ICMP. */
+ if (!invert_tuple(&innertuple, &origtuple, innerproto)) {
+ DEBUGP("icmp_error_track: Can't invert tuple\n");
+ return 1;
+ }
+ h = ip_conntrack_find_get(&innertuple, NULL);
+ if (!h) {
+ DEBUGP("icmp_error_track: no match\n");
+ return 1;
+ }
+
+ ctinfo = IP_CT_RELATED;
+ if (DIRECTION(h) == IP_CT_DIR_REPLY)
+ ctinfo += IP_CT_IS_REPLY;
+
+ /* Update skb to refer to this connection */
+ skb->nfct = &h->ctrack->infos[ctinfo];
+ return 1;
+}
+
+static inline int helper_cmp(const struct ip_conntrack_helper *i,
+ const struct ip_conntrack_tuple *rtuple)
+{
+ return i->will_help(rtuple);
+}
+
+/* Compare all but src per-proto part. */
+static int expect_cmp(const struct ip_conntrack_expect *i,
+ const struct ip_conntrack_tuple *tuple)
+{
+ return (tuple->src.ip == i->tuple.src.ip
+ && tuple->dst.ip == i->tuple.dst.ip
+ && tuple->dst.u.all == i->tuple.dst.u.all
+ && tuple->dst.protonum == i->tuple.dst.protonum);
+}
+
+/* Allocate a new conntrack; we set everything up, then grab write
+ lock and see if we lost a race. If we lost it we return 0,
+ indicating the controlling code should look again. */
+static int
+init_conntrack(const struct ip_conntrack_tuple *tuple,
+ struct ip_conntrack_protocol *protocol,
+ struct sk_buff *skb)
+{
+ struct ip_conntrack *conntrack;
+ struct ip_conntrack_tuple repl_tuple;
+ size_t hash, repl_hash;
+ struct ip_conntrack_expect *expected;
+ enum ip_conntrack_info ctinfo;
+ int i;
+
+ if (!invert_tuple(&repl_tuple, tuple, protocol)) {
+ DEBUGP("Can't invert tuple.\n");
+ return 1;
+ }
+
+ if(ip_conntrack_max &&
+ (atomic_read(&ip_conntrack_count) >= ip_conntrack_max)) {
+ if (net_ratelimit())
+ printk(KERN_WARNING "ip_conntrack: maximum limit of %d entries exceeded\n", ip_conntrack_max);
+ return 1;
+ }
+
+ conntrack = kmalloc(sizeof(struct ip_conntrack), GFP_ATOMIC);
+ if (!conntrack) {
+ DEBUGP("Can't allocate conntrack.\n");
+ return 1;
+ }
+ hash = hash_conntrack(tuple);
+ repl_hash = hash_conntrack(&repl_tuple);
+
+ memset(conntrack, 0, sizeof(struct ip_conntrack));
+ atomic_set(&conntrack->ct_general.use, 1);
+ conntrack->ct_general.destroy = destroy_conntrack;
+ conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *tuple;
+ conntrack->tuplehash[IP_CT_DIR_ORIGINAL].ctrack = conntrack;
+ conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = repl_tuple;
+ conntrack->tuplehash[IP_CT_DIR_REPLY].ctrack = conntrack;
+ for(i=0; i < IP_CT_NUMBER; i++)
+ conntrack->infos[i].master = &conntrack->ct_general;
+
+ if (!protocol->new(conntrack, skb->nh.iph, skb->len)) {
+ kfree(conntrack);
+ return 1;
+ }
+
+ /* Sew in at head of hash list. */
+ WRITE_LOCK(&ip_conntrack_lock);
+ /* Check noone else beat us in the race... */
+ if (__ip_conntrack_find(tuple, NULL)) {
+ WRITE_UNLOCK(&ip_conntrack_lock);
+ printk("ip_conntrack: Wow someone raced us!\n");
+ kfree(conntrack);
+ return 0;
+ }
+ conntrack->helper = LIST_FIND(&helpers, helper_cmp,
+ struct ip_conntrack_helper *,
+ &repl_tuple);
+ /* Need finding and deleting of expected ONLY if we win race */
+ expected = LIST_FIND(&expect_list, expect_cmp,
+ struct ip_conntrack_expect *, tuple);
+ if (expected) {
+ /* Welcome, Mr. Bond. We've been expecting you... */
+ conntrack->status = IPS_EXPECTED;
+ conntrack->master.master = &expected->expectant->ct_general;
+ IP_NF_ASSERT(conntrack->master.master);
+ LIST_DELETE(&expect_list, expected);
+ expected->expectant = NULL;
+ nf_conntrack_get(&conntrack->master);
+ ctinfo = IP_CT_RELATED;
+ } else {
+ ctinfo = IP_CT_NEW;
+ }
+ list_prepend(&ip_conntrack_hash[hash],
+ &conntrack->tuplehash[IP_CT_DIR_ORIGINAL]);
+ list_prepend(&ip_conntrack_hash[repl_hash],
+ &conntrack->tuplehash[IP_CT_DIR_REPLY]);
+ WRITE_UNLOCK(&ip_conntrack_lock);
+
+ /* Update skb to refer to this connection */
+ skb->nfct = &conntrack->infos[ctinfo];
+
+ atomic_inc(&ip_conntrack_count);
+ return 1;
+}
+
+static void
+resolve_normal_ct(struct sk_buff *skb)
+{
+ struct ip_conntrack_tuple tuple;
+ struct ip_conntrack_tuple_hash *h;
+ struct ip_conntrack_protocol *proto;
+ enum ip_conntrack_info ctinfo;
+
+ proto = find_proto(skb->nh.iph->protocol);
+ if (!get_tuple(skb->nh.iph, skb->len, &tuple, proto))
+ return;
+
+ /* Loop around search/insert race */
+ do {
+ /* look for tuple match */
+ h = ip_conntrack_find_get(&tuple, NULL);
+ if (!h && init_conntrack(&tuple, proto, skb))
+ return;
+ } while (!h);
+
+ /* It exists; we have (non-exclusive) reference. */
+ if (DIRECTION(h) == IP_CT_DIR_REPLY) {
+ ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY;
+ h->ctrack->status |= IPS_SEEN_REPLY;
+ } else {
+ /* Once we've had two way comms, always ESTABLISHED. */
+ if (h->ctrack->status & IPS_SEEN_REPLY) {
+ DEBUGP("ip_conntrack_in: normal packet for %p\n",
+ h->ctrack);
+ ctinfo = IP_CT_ESTABLISHED;
+ } else if (h->ctrack->status & IPS_EXPECTED) {
+ DEBUGP("ip_conntrack_in: related packet for %p\n",
+ h->ctrack);
+ ctinfo = IP_CT_RELATED;
+ } else {
+ DEBUGP("ip_conntrack_in: new packet for %p\n",
+ h->ctrack);
+ ctinfo = IP_CT_NEW;
+ }
+ }
+ skb->nfct = &h->ctrack->infos[ctinfo];
+}
+
+/* Return conntrack and conntrack_info a given skb */
+struct ip_conntrack *
+ip_conntrack_get(struct sk_buff *skb, enum ip_conntrack_info *ctinfo)
+{
+ if (!skb->nfct) {
+ /* It may be an icmp error... */
+ if (!icmp_error_track(skb))
+ resolve_normal_ct(skb);
+ }
+
+ if (skb->nfct) {
+ struct ip_conntrack *ct
+ = (struct ip_conntrack *)skb->nfct->master;
+
+ /* ctinfo is the index of the nfct inside the conntrack */
+ *ctinfo = skb->nfct - ct->infos;
+ IP_NF_ASSERT(*ctinfo >= 0 && *ctinfo < IP_CT_NUMBER);
+ return ct;
+ }
+ return NULL;
+}
+
+/* Netfilter hook itself. */
+unsigned int ip_conntrack_in(unsigned int hooknum,
+ struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ struct ip_conntrack *ct;
+ enum ip_conntrack_info ctinfo;
+ struct ip_conntrack_protocol *proto;
+ int ret;
+
+ /* FIXME: Do this right please. --RR */
+ (*pskb)->nfcache |= NFC_UNKNOWN;
+
+ /* Previously seen (loopback)? Ignore. Do this before
+ fragment check. */
+ if ((*pskb)->nfct)
+ return NF_ACCEPT;
+
+ /* Gather fragments. */
+ if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
+ *pskb = ip_ct_gather_frags(*pskb);
+ if (!*pskb)
+ return NF_STOLEN;
+ }
+
+ ct = ip_conntrack_get(*pskb, &ctinfo);
+ if (!ct)
+ /* Not valid part of a connection */
+ return NF_ACCEPT;
+
+ proto = find_proto((*pskb)->nh.iph->protocol);
+ /* If this is new, this is first time timer will be set */
+ ret = proto->packet(ct, (*pskb)->nh.iph, (*pskb)->len, ctinfo);
+
+ if (ret == -1) {
+ /* Invalid */
+ nf_conntrack_put((*pskb)->nfct);
+ (*pskb)->nfct = NULL;
+ return NF_ACCEPT;
+ }
+
+ if (ret != NF_DROP && ct->helper) {
+ ret = ct->helper->help((*pskb)->nh.iph, (*pskb)->len,
+ ct, ctinfo);
+ if (ret == -1) {
+ /* Invalid */
+ nf_conntrack_put((*pskb)->nfct);
+ (*pskb)->nfct = NULL;
+ return NF_ACCEPT;
+ }
+ }
+
+ return ret;
+}
+
+int invert_tuplepr(struct ip_conntrack_tuple *inverse,
+ const struct ip_conntrack_tuple *orig)
+{
+ return invert_tuple(inverse, orig, find_proto(orig->dst.protonum));
+}
+
+/* Add a related connection. */
+int ip_conntrack_expect_related(struct ip_conntrack *related_to,
+ const struct ip_conntrack_tuple *tuple)
+{
+ WRITE_LOCK(&ip_conntrack_lock);
+ related_to->expected.tuple = *tuple;
+
+ if (!related_to->expected.expectant) {
+ list_prepend(&expect_list, &related_to->expected);
+ related_to->expected.expectant = related_to;
+ } else {
+ IP_NF_ASSERT(list_inlist(&expect_list, &related_to->expected));
+ IP_NF_ASSERT(related_to->expected.expectant
+ == related_to);
+ }
+ WRITE_UNLOCK(&ip_conntrack_lock);
+
+ return 0;
+}
+
+/* Alter reply tuple (maybe alter helper). If it's already taken,
+ return 0 and don't do alteration. */
+int ip_conntrack_alter_reply(struct ip_conntrack *conntrack,
+ const struct ip_conntrack_tuple *newreply)
+{
+ unsigned int newindex = hash_conntrack(newreply);
+
+ WRITE_LOCK(&ip_conntrack_lock);
+ if (__ip_conntrack_find(newreply, conntrack)) {
+ WRITE_UNLOCK(&ip_conntrack_lock);
+ return 0;
+ }
+ DEBUGP("Altering reply tuple of %p to ", conntrack);
+ DUMP_TUPLE(newreply);
+
+ LIST_DELETE(&ip_conntrack_hash
+ [hash_conntrack(&conntrack->tuplehash[IP_CT_DIR_REPLY]
+ .tuple)],
+ &conntrack->tuplehash[IP_CT_DIR_REPLY]);
+ conntrack->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply;
+ list_prepend(&ip_conntrack_hash[newindex],
+ &conntrack->tuplehash[IP_CT_DIR_REPLY]);
+ conntrack->helper = LIST_FIND(&helpers, helper_cmp,
+ struct ip_conntrack_helper *,
+ newreply);
+ WRITE_UNLOCK(&ip_conntrack_lock);
+ return 1;
+}
+
+int ip_conntrack_helper_register(struct ip_conntrack_helper *me)
+{
+ MOD_INC_USE_COUNT;
+
+ WRITE_LOCK(&ip_conntrack_lock);
+ list_prepend(&helpers, me);
+ WRITE_UNLOCK(&ip_conntrack_lock);
+
+ return 0;
+}
+
+static inline int unhelp(struct ip_conntrack_tuple_hash *i,
+ const struct ip_conntrack_helper *me)
+{
+ if (i->ctrack->helper == me) {
+ i->ctrack->helper = NULL;
+ /* Get rid of any expected. */
+ if (i->ctrack->expected.expectant) {
+ IP_NF_ASSERT(i->ctrack->expected.expectant
+ == i->ctrack);
+ LIST_DELETE(&expect_list, &i->ctrack->expected);
+ i->ctrack->expected.expectant = NULL;
+ }
+ }
+ return 0;
+}
+
+void ip_conntrack_helper_unregister(struct ip_conntrack_helper *me)
+{
+ unsigned int i;
+
+ /* Need write lock here, to delete helper. */
+ WRITE_LOCK(&ip_conntrack_lock);
+ LIST_DELETE(&helpers, me);
+
+ /* Get rid of expecteds, set helpers to NULL. */
+ for (i = 0; i < ip_conntrack_htable_size; i++)
+ LIST_FIND_W(&ip_conntrack_hash[i], unhelp,
+ struct ip_conntrack_tuple_hash *, me);
+ WRITE_UNLOCK(&ip_conntrack_lock);
+
+ /* Someone could be still looking at the helper in a bh. */
+ br_write_lock_bh(BR_NETPROTO_LOCK);
+ br_write_unlock_bh(BR_NETPROTO_LOCK);
+
+ MOD_DEC_USE_COUNT;
+}
+
+/* Refresh conntrack for this many jiffies: if noone calls this,
+ conntrack will vanish with current skb. */
+void ip_ct_refresh(struct ip_conntrack *ct, unsigned long extra_jiffies)
+{
+ WRITE_LOCK(&ip_conntrack_lock);
+ /* If this hasn't had a timer before, it's still being set up */
+ if (ct->timeout.data == 0) {
+ ct->timeout.data = (unsigned long)ct;
+ ct->timeout.function = death_by_timeout;
+ ct->timeout.expires = jiffies + extra_jiffies;
+ atomic_inc(&ct->ct_general.use);
+ add_timer(&ct->timeout);
+ } else {
+ /* Need del_timer for race avoidance (may already be dying). */
+ if (del_timer(&ct->timeout)) {
+ ct->timeout.expires = jiffies + extra_jiffies;
+ add_timer(&ct->timeout);
+ }
+ }
+ WRITE_UNLOCK(&ip_conntrack_lock);
+}
+
+/* Returns new sk_buff, or NULL */
+struct sk_buff *
+ip_ct_gather_frags(struct sk_buff *skb)
+{
+ struct sock *sk = skb->sk;
+#ifdef CONFIG_NETFILTER_DEBUG
+ unsigned int olddebug = skb->nf_debug;
+#endif
+ if (sk) sock_hold(sk);
+ skb = ip_defrag(skb);
+ if (!skb) {
+ if (sk) sock_put(sk);
+ return skb;
+ }
+ if (sk) {
+ skb_set_owner_w(skb, sk);
+ sock_put(sk);
+ }
+
+ ip_send_check(skb->nh.iph);
+ skb->nfcache |= NFC_ALTERED;
+#ifdef CONFIG_NETFILTER_DEBUG
+ /* Packet path as if nothing had happened. */
+ skb->nf_debug = olddebug;
+#endif
+ return skb;
+}
+
+static inline int
+do_kill(const struct ip_conntrack_tuple_hash *i,
+ int (*kill)(const struct ip_conntrack *i, void *data),
+ void *data)
+{
+ return kill(i->ctrack, data);
+}
+
+/* Bring out ya dead! */
+static struct ip_conntrack_tuple_hash *
+get_next_corpse(int (*kill)(const struct ip_conntrack *i, void *data),
+ void *data)
+{
+ struct ip_conntrack_tuple_hash *h = NULL;
+ unsigned int i;
+
+ READ_LOCK(&ip_conntrack_lock);
+ for (i = 0; !h && i < ip_conntrack_htable_size; i++) {
+ h = LIST_FIND(&ip_conntrack_hash[i], do_kill,
+ struct ip_conntrack_tuple_hash *, kill, data);
+ }
+ if (h)
+ atomic_inc(&h->ctrack->ct_general.use);
+ READ_UNLOCK(&ip_conntrack_lock);
+
+ return h;
+}
+
+void
+ip_ct_selective_cleanup(int (*kill)(const struct ip_conntrack *i, void *data),
+ void *data)
+{
+ struct ip_conntrack_tuple_hash *h;
+
+ /* This is order n^2, by the way. */
+ while ((h = get_next_corpse(kill, data)) != NULL) {
+ /* Time to push up daises... */
+ if (del_timer(&h->ctrack->timeout))
+ death_by_timeout((unsigned long)h->ctrack);
+ /* ... else the timer will get him soon. */
+
+ ip_conntrack_put(h->ctrack);
+ }
+}
+
+/* Fast function for those who don't want to parse /proc (and I don't
+ blame them). */
+/* Reversing the socket's dst/src point of view gives us the reply
+ mapping. */
+static int
+getorigdst(struct sock *sk, int optval, void *user, int *len)
+{
+ struct ip_conntrack_tuple_hash *h;
+ struct ip_conntrack_tuple tuple = { { sk->rcv_saddr, { sk->sport },
+ 0 },
+ { sk->daddr, { sk->dport },
+ IPPROTO_TCP } };
+
+ /* We only do TCP at the moment: is there a better way? */
+ if (strcmp(sk->prot->name, "TCP") != 0) {
+ DEBUGP("SO_ORIGINAL_DST: Not a TCP socket\n");
+ return -ENOPROTOOPT;
+ }
+
+ if (*len != sizeof(struct sockaddr_in)) {
+ DEBUGP("SO_ORIGINAL_DST: len %u not %u\n",
+ *len, sizeof(struct sockaddr_in));
+ return -EINVAL;
+ }
+
+ h = ip_conntrack_find_get(&tuple, NULL);
+ if (h) {
+ struct sockaddr_in sin;
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = h->ctrack->tuplehash[IP_CT_DIR_ORIGINAL]
+ .tuple.dst.u.tcp.port;
+ sin.sin_addr.s_addr = h->ctrack->tuplehash[IP_CT_DIR_ORIGINAL]
+ .tuple.dst.ip;
+
+ DEBUGP("SO_ORIGINAL_DST: %u.%u.%u.%u %u\n",
+ IP_PARTS(sin.sin_addr.s_addr), ntohs(sin.sin_port));
+ ip_conntrack_put(h->ctrack);
+ if (copy_to_user(user, &sin, sizeof(sin)) != 0)
+ return -EFAULT;
+ else
+ return 0;
+ }
+ DEBUGP("SO_ORIGINAL_DST: Can't find %u.%u.%u.%u/%u-%u.%u.%u.%u/%u.\n",
+ IP_PARTS(tuple.src.ip), ntohs(tuple.src.u.tcp.port),
+ IP_PARTS(tuple.dst.ip), ntohs(tuple.dst.u.tcp.port));
+ return -ENOENT;
+}
+
+static struct nf_sockopt_ops so_getorigdst
+= { { NULL, NULL }, PF_INET,
+ 0, 0, NULL, /* Setsockopts */
+ SO_ORIGINAL_DST, SO_ORIGINAL_DST+1, &getorigdst,
+ 0, NULL };
+
+#define NET_IP_CONNTRACK_MAX 2089
+#define NET_IP_CONNTRACK_MAX_NAME "ip_conntrack_max"
+
+static struct ctl_table_header *ip_conntrack_sysctl_header;
+
+static ctl_table ip_conntrack_table[] = {
+ { NET_IP_CONNTRACK_MAX, NET_IP_CONNTRACK_MAX_NAME, &ip_conntrack_max,
+ sizeof(ip_conntrack_max), 0644, NULL, proc_dointvec },
+ { 0 }
+};
+
+static ctl_table ip_conntrack_dir_table[] = {
+ {NET_IPV4, "ipv4", NULL, 0, 0555, ip_conntrack_table, 0, 0, 0, 0, 0},
+ { 0 }
+};
+
+static ctl_table ip_conntrack_root_table[] = {
+ {CTL_NET, "net", NULL, 0, 0555, ip_conntrack_dir_table, 0, 0, 0, 0, 0},
+ { 0 }
+};
+
+static int kill_all(const struct ip_conntrack *i, void *data)
+{
+ return 1;
+}
+
+/* Mishearing the voices in his head, our hero wonders how he's
+ supposed to kill the mall. */
+void ip_conntrack_cleanup(void)
+{
+ unregister_sysctl_table(ip_conntrack_sysctl_header);
+ ip_ct_selective_cleanup(kill_all, NULL);
+ vfree(ip_conntrack_hash);
+ nf_unregister_sockopt(&so_getorigdst);
+}
+
+int __init ip_conntrack_init(void)
+{
+ unsigned int i;
+ int ret;
+
+ /* Idea from tcp.c: use 1/16384 of memory. On i386: 32MB
+ * machine has 256 buckets. 1GB machine has 8192 buckets. */
+ ip_conntrack_htable_size
+ = (((num_physpages << PAGE_SHIFT) / 16384)
+ / sizeof(struct list_head));
+ ip_conntrack_max = 8 * ip_conntrack_htable_size;
+
+ printk("ip_conntrack (%u buckets, %d max)\n",
+ ip_conntrack_htable_size, ip_conntrack_max);
+
+ ret = nf_register_sockopt(&so_getorigdst);
+ if (ret != 0)
+ return ret;
+
+ ip_conntrack_hash = vmalloc(sizeof(struct list_head)
+ * ip_conntrack_htable_size);
+ if (!ip_conntrack_hash) {
+ nf_unregister_sockopt(&so_getorigdst);
+ return -ENOMEM;
+ }
+
+ /* Don't NEED lock here, but good form anyway. */
+ WRITE_LOCK(&ip_conntrack_lock);
+ /* Sew in builtin protocols. */
+ list_append(&protocol_list, &ip_conntrack_protocol_tcp);
+ list_append(&protocol_list, &ip_conntrack_protocol_udp);
+ list_append(&protocol_list, &ip_conntrack_protocol_icmp);
+ WRITE_UNLOCK(&ip_conntrack_lock);
+
+ for (i = 0; i < ip_conntrack_htable_size; i++)
+ INIT_LIST_HEAD(&ip_conntrack_hash[i]);
+
+/* This is fucking braindead. There is NO WAY of doing this without
+ the CONFIG_SYSCTL unless you don't want to detect errors.
+ Grrr... --RR */
+#ifdef CONFIG_SYSCTL
+ ip_conntrack_sysctl_header
+ = register_sysctl_table(ip_conntrack_root_table, 0);
+ if (ip_conntrack_sysctl_header == NULL) {
+ vfree(ip_conntrack_hash);
+ nf_unregister_sockopt(&so_getorigdst);
+ return -ENOMEM;
+ }
+#endif /*CONFIG_SYSCTL*/
+
+ ret = ip_conntrack_protocol_tcp_init();
+ if (ret != 0) {
+ unregister_sysctl_table(ip_conntrack_sysctl_header);
+ vfree(ip_conntrack_hash);
+ nf_unregister_sockopt(&so_getorigdst);
+ }
+
+ return ret;
+}
+
diff --git a/net/ipv4/netfilter/ip_conntrack_ftp.c b/net/ipv4/netfilter/ip_conntrack_ftp.c
new file mode 100644
index 000000000..9137d13ea
--- /dev/null
+++ b/net/ipv4/netfilter/ip_conntrack_ftp.c
@@ -0,0 +1,251 @@
+/* FTP extension for IP connection tracking. */
+#ifdef MODULE
+#define EXPORT_SYMTAB
+#endif
+#include <linux/module.h>
+#include <linux/netfilter.h>
+#include <linux/ip.h>
+#include <net/checksum.h>
+#include <net/tcp.h>
+
+#include <linux/netfilter_ipv4/lockhelp.h>
+#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
+#include <linux/netfilter_ipv4/ip_conntrack_ftp.h>
+
+DECLARE_LOCK(ip_ftp_lock);
+
+#define SERVER_STRING "227 Entering Passive Mode ("
+#define CLIENT_STRING "PORT "
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+#define IP_PARTS_NATIVE(n) \
+(unsigned int)((n)>>24)&0xFF, \
+(unsigned int)((n)>>16)&0xFF, \
+(unsigned int)((n)>>8)&0xFF, \
+(unsigned int)((n)&0xFF)
+
+#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
+
+static struct {
+ const char *pattern;
+ size_t plen;
+ char term;
+} search[2] = {
+ [IP_CT_FTP_PORT] { CLIENT_STRING, sizeof(CLIENT_STRING) - 1, '\r' },
+ [IP_CT_FTP_PASV] { SERVER_STRING, sizeof(SERVER_STRING) - 1, ')' }
+};
+
+/* Returns 0, or length of numbers */
+static int try_number(const char *data, size_t dlen, u_int32_t array[6],
+ char term)
+{
+ u_int32_t i, len;
+
+ /* Keep data pointing at next char. */
+ for (i = 0, len = 0; len < dlen; len++, data++) {
+ if (*data >= '0' && *data <= '9') {
+ array[i] = array[i]*10 + *data - '0';
+ }
+ else if (*data == ',')
+ i++;
+ else {
+ /* Unexpected character; true if it's the
+ terminator and we're finished. */
+ if (*data == term && i == 5)
+ return len;
+
+ DEBUGP("Char %u (got %u nums) `%u' unexpected\n",
+ len, i, *data);
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+/* Return 1 for match, 0 for accept, -1 for partial. */
+static int find_pattern(const char *data, size_t dlen,
+ const char *pattern, size_t plen,
+ char term,
+ unsigned int *numoff,
+ unsigned int *numlen,
+ u_int32_t array[6])
+{
+ if (dlen == 0)
+ return 0;
+
+ if (dlen < plen) {
+ /* Short packet: try for partial? */
+ if (strnicmp(data, pattern, dlen) == 0)
+ return -1;
+ else return 0;
+ }
+
+ if (strnicmp(data, pattern, plen) != 0) {
+#if 0
+ size_t i;
+
+ DEBUGP("ftp: string mismatch\n");
+ for (i = 0; i < plen; i++) {
+ DEBUGFTP("ftp:char %u `%c'(%u) vs `%c'(%u)\n",
+ i, data[i], data[i],
+ pattern[i], pattern[i]);
+ }
+#endif
+ return 0;
+ }
+
+ *numoff = plen;
+ *numlen = try_number(data + plen, dlen - plen, array, term);
+ if (!*numlen)
+ return -1;
+
+ return 1;
+}
+
+/* FIXME: This should be in userspace. Later. */
+static int help(const struct iphdr *iph, size_t len,
+ struct ip_conntrack *ct,
+ enum ip_conntrack_info ctinfo)
+{
+ /* tcplen not negative guarenteed by ip_conntrack_tcp.c */
+ struct tcphdr *tcph = (void *)iph + iph->ihl * 4;
+ const char *data = (const char *)tcph + tcph->doff * 4;
+ unsigned int tcplen = len - iph->ihl * 4;
+ unsigned int datalen = tcplen - tcph->doff * 4;
+ u_int32_t old_seq_aft_nl;
+ int old_seq_aft_nl_set;
+ u_int32_t array[6] = { 0 };
+ int dir = CTINFO2DIR(ctinfo);
+ unsigned int matchlen, matchoff;
+ struct ip_conntrack_tuple t;
+ struct ip_ct_ftp *info = &ct->help.ct_ftp_info;
+
+ /* Can't track connections formed before we registered */
+ if (!info)
+ return NF_ACCEPT;
+
+ /* Until there's been traffic both ways, don't look in packets. */
+ if (ctinfo != IP_CT_ESTABLISHED
+ && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) {
+ DEBUGP("ftp: Conntrackinfo = %u\n", ctinfo);
+ return NF_ACCEPT;
+ }
+
+ /* Not whole TCP header? */
+ if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff*4) {
+ DEBUGP("ftp: tcplen = %u\n", (unsigned)tcplen);
+ return NF_ACCEPT;
+ }
+
+ /* Checksum invalid? Ignore. */
+ /* FIXME: Source route IP option packets --RR */
+ if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
+ csum_partial((char *)tcph, tcplen, 0))) {
+ DEBUGP("ftp_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
+ tcph, tcplen, IP_PARTS(iph->saddr),
+ IP_PARTS(iph->daddr));
+ return NF_ACCEPT;
+ }
+
+ LOCK_BH(&ip_ftp_lock);
+ old_seq_aft_nl_set = info->seq_aft_nl_set[dir];
+ old_seq_aft_nl = info->seq_aft_nl[dir];
+
+ DEBUGP("conntrack_ftp: datalen %u\n", datalen);
+ if ((datalen > 0) && (data[datalen-1] == '\n')) {
+ DEBUGP("conntrack_ftp: datalen %u ends in \\n\n", datalen);
+ if (!old_seq_aft_nl_set
+ || after(ntohl(tcph->seq) + datalen, old_seq_aft_nl)) {
+ DEBUGP("conntrack_ftp: updating nl to %u\n",
+ ntohl(tcph->seq) + datalen);
+ info->seq_aft_nl[dir] = ntohl(tcph->seq) + datalen;
+ info->seq_aft_nl_set[dir] = 1;
+ }
+ }
+ UNLOCK_BH(&ip_ftp_lock);
+
+ if(!old_seq_aft_nl_set ||
+ (ntohl(tcph->seq) != old_seq_aft_nl)) {
+ DEBUGP("ip_conntrack_ftp_help: wrong seq pos %s(%u)\n",
+ old_seq_aft_nl_set ? "":"(UNSET) ", old_seq_aft_nl);
+ return NF_ACCEPT;
+ }
+
+ switch (find_pattern(data, datalen,
+ search[dir].pattern,
+ search[dir].plen, search[dir].term,
+ &matchoff, &matchlen,
+ array)) {
+ case -1: /* partial */
+ /* We don't usually drop packets. After all, this is
+ connection tracking, not packet filtering.
+ However, it is neccessary for accurate tracking in
+ this case. */
+ DEBUGP("conntrack_ftp: partial `%.*s'\n",
+ (int)datalen, data);
+ return NF_DROP;
+
+ case 0: /* no match */
+ DEBUGP("ip_conntrack_ftp_help: no match\n");
+ return NF_ACCEPT;
+ }
+
+ DEBUGP("conntrack_ftp: match `%.*s' (%u bytes at %u)\n",
+ (int)matchlen, data + matchoff,
+ matchlen, ntohl(tcph->seq) + matchoff);
+
+ /* Update the ftp info */
+ LOCK_BH(&ip_ftp_lock);
+ info->is_ftp = 1;
+ info->seq = ntohl(tcph->seq) + matchoff;
+ info->len = matchlen;
+ info->ftptype = dir;
+ info->port = array[4] << 8 | array[5];
+
+ t = ((struct ip_conntrack_tuple)
+ { { ct->tuplehash[!dir].tuple.src.ip,
+ { 0 }, 0 },
+ { htonl((array[0] << 24) | (array[1] << 16)
+ | (array[2] << 8) | array[3]),
+ { htons(array[4] << 8 | array[5]) },
+ IPPROTO_TCP }});
+ ip_conntrack_expect_related(ct, &t);
+ UNLOCK_BH(&ip_ftp_lock);
+
+ return NF_ACCEPT;
+}
+
+/* Returns TRUE if it wants to help this connection (tuple is the
+ tuple of REPLY packets from server). */
+static int ftp_will_help(const struct ip_conntrack_tuple *rtuple)
+{
+ return (rtuple->dst.protonum == IPPROTO_TCP
+ && rtuple->src.u.tcp.port == __constant_htons(21));
+}
+
+static struct ip_conntrack_helper ftp = { { NULL, NULL },
+ ftp_will_help,
+ help };
+
+static int __init init(void)
+{
+ return ip_conntrack_helper_register(&ftp);
+}
+
+static void __exit fini(void)
+{
+ ip_conntrack_helper_unregister(&ftp);
+}
+
+struct module *ip_conntrack_ftp = THIS_MODULE;
+EXPORT_SYMBOL(ip_conntrack_ftp);
+EXPORT_SYMBOL(ip_ftp_lock);
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/netfilter/ip_conntrack_proto_generic.c b/net/ipv4/netfilter/ip_conntrack_proto_generic.c
new file mode 100644
index 000000000..77a491e34
--- /dev/null
+++ b/net/ipv4/netfilter/ip_conntrack_proto_generic.c
@@ -0,0 +1,60 @@
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
+
+#define GENERIC_TIMEOUT (3600*HZ)
+
+static int generic_pkt_to_tuple(const void *datah, size_t datalen,
+ struct ip_conntrack_tuple *tuple)
+{
+ tuple->src.u.all = 0;
+ tuple->dst.u.all = 0;
+
+ return 1;
+}
+
+static int generic_invert_tuple(struct ip_conntrack_tuple *tuple,
+ const struct ip_conntrack_tuple *orig)
+{
+ tuple->src.u.all = 0;
+ tuple->dst.u.all = 0;
+
+ return 1;
+}
+
+/* Print out the per-protocol part of the tuple. */
+static unsigned int generic_print_tuple(char *buffer,
+ const struct ip_conntrack_tuple *tuple)
+{
+ return 0;
+}
+
+/* Print out the private part of the conntrack. */
+static unsigned int generic_print_conntrack(char *buffer,
+ const struct ip_conntrack *state)
+{
+ return 0;
+}
+
+/* Returns verdict for packet, or -1 for invalid. */
+static int established(struct ip_conntrack *conntrack,
+ struct iphdr *iph, size_t len,
+ enum ip_conntrack_info conntrackinfo)
+{
+ ip_ct_refresh(conntrack, GENERIC_TIMEOUT);
+ return NF_ACCEPT;
+}
+
+/* Called when a new connection for this protocol found. */
+static int new(struct ip_conntrack *conntrack, struct iphdr *iph, size_t len)
+{
+ return 1;
+}
+
+struct ip_conntrack_protocol ip_conntrack_generic_protocol
+= { { NULL, NULL }, 0, "unknown",
+ generic_pkt_to_tuple, generic_invert_tuple, generic_print_tuple,
+ generic_print_conntrack, established, new, NULL };
+
diff --git a/net/ipv4/netfilter/ip_conntrack_proto_icmp.c b/net/ipv4/netfilter/ip_conntrack_proto_icmp.c
new file mode 100644
index 000000000..1d1256be5
--- /dev/null
+++ b/net/ipv4/netfilter/ip_conntrack_proto_icmp.c
@@ -0,0 +1,111 @@
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/netfilter.h>
+#include <linux/icmp.h>
+#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
+
+#define ICMP_TIMEOUT (30*HZ)
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+static int icmp_pkt_to_tuple(const void *datah, size_t datalen,
+ struct ip_conntrack_tuple *tuple)
+{
+ const struct icmphdr *hdr = datah;
+
+ tuple->dst.u.icmp.type = hdr->type;
+ tuple->src.u.icmp.id = hdr->un.echo.id;
+ tuple->dst.u.icmp.code = hdr->code;
+
+ return 1;
+}
+
+static int icmp_invert_tuple(struct ip_conntrack_tuple *tuple,
+ const struct ip_conntrack_tuple *orig)
+{
+ /* Add 1; spaces filled with 0. */
+ static u_int8_t invmap[]
+ = { [ICMP_ECHO] = ICMP_ECHOREPLY + 1,
+ [ICMP_ECHOREPLY] = ICMP_ECHO + 1,
+ [ICMP_TIMESTAMP] = ICMP_TIMESTAMPREPLY + 1,
+ [ICMP_TIMESTAMPREPLY] = ICMP_TIMESTAMP + 1,
+ [ICMP_INFO_REQUEST] = ICMP_INFO_REPLY + 1,
+ [ICMP_INFO_REPLY] = ICMP_INFO_REQUEST + 1,
+ [ICMP_ADDRESS] = ICMP_ADDRESSREPLY + 1,
+ [ICMP_ADDRESSREPLY] = ICMP_ADDRESS + 1};
+
+ if (orig->dst.u.icmp.type >= sizeof(invmap)
+ || !invmap[orig->dst.u.icmp.type])
+ return 0;
+
+ tuple->src.u.icmp.id = orig->src.u.icmp.id;
+ tuple->dst.u.icmp.type = invmap[orig->dst.u.icmp.type] - 1;
+ tuple->dst.u.icmp.code = orig->dst.u.icmp.code;
+ return 1;
+}
+
+/* Print out the per-protocol part of the tuple. */
+static unsigned int icmp_print_tuple(char *buffer,
+ const struct ip_conntrack_tuple *tuple)
+{
+ return sprintf(buffer, "type=%u code=%u id=%u ",
+ tuple->dst.u.icmp.type,
+ tuple->dst.u.icmp.code,
+ ntohs(tuple->src.u.icmp.id));
+}
+
+/* Print out the private part of the conntrack. */
+static unsigned int icmp_print_conntrack(char *buffer,
+ const struct ip_conntrack *conntrack)
+{
+ return 0;
+}
+
+/* Returns verdict for packet, or -1 for invalid. */
+static int icmp_packet(struct ip_conntrack *ct,
+ struct iphdr *iph, size_t len,
+ enum ip_conntrack_info ctinfo)
+{
+ /* FIXME: Should keep count of orig - reply packets: if == 0,
+ destroy --RR */
+ /* Delete connection immediately on reply: won't actually
+ vanish as we still have skb */
+ if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) {
+ if (del_timer(&ct->timeout))
+ ct->timeout.function((unsigned long)ct);
+ } else
+ ip_ct_refresh(ct, ICMP_TIMEOUT);
+
+ return NF_ACCEPT;
+}
+
+/* Called when a new connection for this protocol found. */
+static int icmp_new(struct ip_conntrack *conntrack,
+ struct iphdr *iph, size_t len)
+{
+ static u_int8_t valid_new[]
+ = { [ICMP_ECHO] = 1,
+ [ICMP_TIMESTAMP] = 1,
+ [ICMP_INFO_REQUEST] = 1,
+ [ICMP_ADDRESS] = 1 };
+
+ if (conntrack->tuplehash[0].tuple.dst.u.icmp.type >= sizeof(valid_new)
+ || !valid_new[conntrack->tuplehash[0].tuple.dst.u.icmp.type]) {
+ /* Can't create a new ICMP `conn' with this. */
+ DEBUGP("icmp: can't create new conn with type %u\n",
+ conntrack->tuplehash[0].tuple.dst.u.icmp.type);
+ DUMP_TUPLE(&conntrack->tuplehash[0].tuple);
+ return 0;
+ }
+ return 1;
+}
+
+struct ip_conntrack_protocol ip_conntrack_protocol_icmp
+= { { NULL, NULL }, IPPROTO_ICMP, "icmp",
+ icmp_pkt_to_tuple, icmp_invert_tuple, icmp_print_tuple,
+ icmp_print_conntrack, icmp_packet, icmp_new, NULL };
diff --git a/net/ipv4/netfilter/ip_conntrack_proto_tcp.c b/net/ipv4/netfilter/ip_conntrack_proto_tcp.c
new file mode 100644
index 000000000..3dd448252
--- /dev/null
+++ b/net/ipv4/netfilter/ip_conntrack_proto_tcp.c
@@ -0,0 +1,227 @@
+#define __NO_VERSION__
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/netfilter.h>
+#include <linux/module.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
+#include <linux/netfilter_ipv4/lockhelp.h>
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+/* Protects conntrack->proto.tcp_state */
+static DECLARE_RWLOCK(tcp_lock);
+
+/* FIXME: Examine ipfilter's timeouts and conntrack transitions more
+ closely. They're more complex. --RR */
+
+/* Actually, I believe that neither ipmasq (where this code is stolen
+ from) nor ipfilter do it exactly right. A new conntrack machine taking
+ into account packet loss (which creates uncertainty as to exactly
+ the conntrack of the connection) is required. RSN. --RR */
+enum tcp_conntrack {
+ TCP_CONNTRACK_NONE,
+ TCP_CONNTRACK_ESTABLISHED,
+ TCP_CONNTRACK_SYN_SENT,
+ TCP_CONNTRACK_SYN_RECV,
+ TCP_CONNTRACK_FIN_WAIT,
+ TCP_CONNTRACK_TIME_WAIT,
+ TCP_CONNTRACK_CLOSE,
+ TCP_CONNTRACK_CLOSE_WAIT,
+ TCP_CONNTRACK_LAST_ACK,
+ TCP_CONNTRACK_LISTEN,
+ TCP_CONNTRACK_MAX
+};
+
+static const char *tcp_conntrack_names[] = {
+ "NONE",
+ "ESTABLISHED",
+ "SYN_SENT",
+ "SYN_RECV",
+ "FIN_WAIT",
+ "TIME_WAIT",
+ "CLOSE",
+ "CLOSE_WAIT",
+ "LAST_ACK",
+ "LISTEN"
+};
+
+#define SECS *HZ
+#define MINS * 60 SECS
+#define HOURS * 60 MINS
+#define DAYS * 24 HOURS
+
+
+static unsigned long tcp_timeouts[]
+= { 30 MINS, /* TCP_CONNTRACK_NONE, */
+ 5 DAYS, /* TCP_CONNTRACK_ESTABLISHED, */
+ 2 MINS, /* TCP_CONNTRACK_SYN_SENT, */
+ 60 SECS, /* TCP_CONNTRACK_SYN_RECV, */
+ 2 MINS, /* TCP_CONNTRACK_FIN_WAIT, */
+ 2 MINS, /* TCP_CONNTRACK_TIME_WAIT, */
+ 10 SECS, /* TCP_CONNTRACK_CLOSE, */
+ 60 SECS, /* TCP_CONNTRACK_CLOSE_WAIT, */
+ 30 SECS, /* TCP_CONNTRACK_LAST_ACK, */
+ 2 MINS, /* TCP_CONNTRACK_LISTEN, */
+};
+
+#define sNO TCP_CONNTRACK_NONE
+#define sES TCP_CONNTRACK_ESTABLISHED
+#define sSS TCP_CONNTRACK_SYN_SENT
+#define sSR TCP_CONNTRACK_SYN_RECV
+#define sFW TCP_CONNTRACK_FIN_WAIT
+#define sTW TCP_CONNTRACK_TIME_WAIT
+#define sCL TCP_CONNTRACK_CLOSE
+#define sCW TCP_CONNTRACK_CLOSE_WAIT
+#define sLA TCP_CONNTRACK_LAST_ACK
+#define sLI TCP_CONNTRACK_LISTEN
+#define sIV TCP_CONNTRACK_MAX
+
+static enum tcp_conntrack tcp_conntracks[2][5][TCP_CONNTRACK_MAX] = {
+ {
+/* ORIGINAL */
+/* sNO, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sLI */
+/*syn*/ {sSS, sES, sSS, sES, sSS, sSS, sSS, sSS, sSS, sLI },
+/*fin*/ {sTW, sFW, sSS, sTW, sFW, sTW, sCL, sTW, sLA, sLI },
+/*ack*/ {sES, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sES },
+/*rst*/ {sCL, sCL, sSS, sCL, sCL, sTW, sCL, sCL, sCL, sCL },
+/*none*/{sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }
+ },
+ {
+/* REPLY */
+/* sNO, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sLI */
+/*syn*/ {sSR, sES, sES, sSR, sSR, sSR, sSR, sSR, sSR, sSR },
+/*fin*/ {sCL, sCW, sSS, sTW, sTW, sTW, sCL, sCW, sLA, sLI },
+/*ack*/ {sCL, sES, sSS, sSR, sFW, sTW, sCL, sCW, sCL, sLI },
+/*rst*/ {sCL, sCL, sCL, sSR, sCL, sCL, sCL, sCL, sLA, sLI },
+/*none*/{sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }
+ }
+};
+
+static int tcp_pkt_to_tuple(const void *datah, size_t datalen,
+ struct ip_conntrack_tuple *tuple)
+{
+ const struct tcphdr *hdr = datah;
+
+ tuple->src.u.tcp.port = hdr->source;
+ tuple->dst.u.tcp.port = hdr->dest;
+
+ return 1;
+}
+
+static int tcp_invert_tuple(struct ip_conntrack_tuple *tuple,
+ const struct ip_conntrack_tuple *orig)
+{
+ tuple->src.u.tcp.port = orig->dst.u.tcp.port;
+ tuple->dst.u.tcp.port = orig->src.u.tcp.port;
+ return 1;
+}
+
+/* Print out the per-protocol part of the tuple. */
+static unsigned int tcp_print_tuple(char *buffer,
+ const struct ip_conntrack_tuple *tuple)
+{
+ return sprintf(buffer, "sport=%hu dport=%hu ",
+ ntohs(tuple->src.u.tcp.port),
+ ntohs(tuple->dst.u.tcp.port));
+}
+
+/* Print out the private part of the conntrack. */
+static unsigned int tcp_print_conntrack(char *buffer,
+ const struct ip_conntrack *conntrack)
+{
+ enum tcp_conntrack state;
+
+ READ_LOCK(&tcp_lock);
+ state = conntrack->proto.tcp_state;
+ READ_UNLOCK(&tcp_lock);
+
+ return sprintf(buffer, "%s ", tcp_conntrack_names[state]);
+}
+
+static unsigned int get_conntrack_index(const struct tcphdr *tcph)
+{
+ if (tcph->rst) return 3;
+ else if (tcph->syn) return 0;
+ else if (tcph->fin) return 1;
+ else if (tcph->ack) return 2;
+ else return 4;
+}
+
+/* Returns verdict for packet, or -1 for invalid. */
+static int tcp_packet(struct ip_conntrack *conntrack,
+ struct iphdr *iph, size_t len,
+ enum ip_conntrack_info ctinfo)
+{
+ enum tcp_conntrack newconntrack;
+ struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl);
+
+ /* We're guaranteed to have the base header, but maybe not the
+ options. */
+ if (len < (iph->ihl + tcph->doff) * 4) {
+ DEBUGP("ip_conntrack_tcp: Truncated packet.\n");
+ return -1;
+ }
+
+ WRITE_LOCK(&tcp_lock);
+ newconntrack
+ = tcp_conntracks
+ [CTINFO2DIR(ctinfo)]
+ [get_conntrack_index(tcph)][conntrack->proto.tcp_state];
+
+ /* Invalid */
+ if (newconntrack == TCP_CONNTRACK_MAX) {
+ DEBUGP("ip_conntrack_tcp: Invalid dir=%i index=%u conntrack=%u\n",
+ CTINFO2DIR(ctinfo), get_conntrack_index(tcph),
+ conntrack->proto.tcp_state);
+ WRITE_UNLOCK(&tcp_lock);
+ return -1;
+ }
+
+ conntrack->proto.tcp_state = newconntrack;
+ WRITE_UNLOCK(&tcp_lock);
+
+ /* Refresh: need write lock to write to conntrack. */
+ ip_ct_refresh(conntrack, tcp_timeouts[conntrack->proto.tcp_state]);
+ return NF_ACCEPT;
+}
+
+/* Called when a new connection for this protocol found. */
+static int tcp_new(struct ip_conntrack *conntrack,
+ struct iphdr *iph, size_t len)
+{
+ enum tcp_conntrack newconntrack;
+ struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl);
+
+ /* Don't need lock here: this conntrack not in circulation yet */
+ newconntrack
+ = tcp_conntracks[0][get_conntrack_index(tcph)]
+ [TCP_CONNTRACK_NONE];
+
+ /* Invalid: delete conntrack */
+ if (newconntrack == TCP_CONNTRACK_MAX) {
+ DEBUGP("ip_conntrack_tcp: invalid new deleting.\n");
+ return 0;
+ } else {
+ conntrack->proto.tcp_state = newconntrack;
+ ip_ct_refresh(conntrack, tcp_timeouts[conntrack->proto.tcp_state]);
+ }
+ return 1;
+}
+
+struct ip_conntrack_protocol ip_conntrack_protocol_tcp
+= { { NULL, NULL }, IPPROTO_TCP, "tcp",
+ tcp_pkt_to_tuple, tcp_invert_tuple, tcp_print_tuple, tcp_print_conntrack,
+ tcp_packet, tcp_new, NULL };
+
+int __init ip_conntrack_protocol_tcp_init(void)
+{
+ return 0;
+}
diff --git a/net/ipv4/netfilter/ip_conntrack_proto_udp.c b/net/ipv4/netfilter/ip_conntrack_proto_udp.c
new file mode 100644
index 000000000..688ae10fb
--- /dev/null
+++ b/net/ipv4/netfilter/ip_conntrack_proto_udp.c
@@ -0,0 +1,65 @@
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/netfilter.h>
+#include <linux/udp.h>
+#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
+
+#define UDP_TIMEOUT (60*HZ)
+
+static int udp_pkt_to_tuple(const void *datah, size_t datalen,
+ struct ip_conntrack_tuple *tuple)
+{
+ const struct udphdr *hdr = datah;
+
+ tuple->src.u.udp.port = hdr->source;
+ tuple->dst.u.udp.port = hdr->dest;
+
+ return 1;
+}
+
+static int udp_invert_tuple(struct ip_conntrack_tuple *tuple,
+ const struct ip_conntrack_tuple *orig)
+{
+ tuple->src.u.udp.port = orig->dst.u.udp.port;
+ tuple->dst.u.udp.port = orig->src.u.udp.port;
+ return 1;
+}
+
+/* Print out the per-protocol part of the tuple. */
+static unsigned int udp_print_tuple(char *buffer,
+ const struct ip_conntrack_tuple *tuple)
+{
+ return sprintf(buffer, "sport=%hu dport=%hu ",
+ ntohs(tuple->src.u.udp.port),
+ ntohs(tuple->dst.u.udp.port));
+}
+
+/* Print out the private part of the conntrack. */
+static unsigned int udp_print_conntrack(char *buffer,
+ const struct ip_conntrack *conntrack)
+{
+ return 0;
+}
+
+/* Returns verdict for packet, and may modify conntracktype */
+static int udp_packet(struct ip_conntrack *conntrack,
+ struct iphdr *iph, size_t len,
+ enum ip_conntrack_info conntrackinfo)
+{
+ /* Refresh. */
+ ip_ct_refresh(conntrack, UDP_TIMEOUT);
+ return NF_ACCEPT;
+}
+
+/* Called when a new connection for this protocol found. */
+static int udp_new(struct ip_conntrack *conntrack,
+ struct iphdr *iph, size_t len)
+{
+ return 1;
+}
+
+struct ip_conntrack_protocol ip_conntrack_protocol_udp
+= { { NULL, NULL }, IPPROTO_UDP, "udp",
+ udp_pkt_to_tuple, udp_invert_tuple, udp_print_tuple, udp_print_conntrack,
+ udp_packet, udp_new, NULL };
diff --git a/net/ipv4/netfilter/ip_conntrack_standalone.c b/net/ipv4/netfilter/ip_conntrack_standalone.c
new file mode 100644
index 000000000..ce79c3263
--- /dev/null
+++ b/net/ipv4/netfilter/ip_conntrack_standalone.c
@@ -0,0 +1,297 @@
+/* This file contains all the functions required for the standalone
+ ip_conntrack module.
+
+ These are not required by the compatibility layer.
+*/
+
+/* (c) 1999 Paul `Rusty' Russell. Licenced under the GNU General
+ Public Licence. */
+
+#ifdef MODULE
+#define EXPORT_SYMTAB
+#endif
+#include <linux/types.h>
+#include <linux/ip.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/version.h>
+#include <net/checksum.h>
+
+#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_conntrack_lock)
+#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_conntrack_lock)
+
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
+#include <linux/netfilter_ipv4/ip_conntrack_core.h>
+#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
+#include <linux/netfilter_ipv4/listhelp.h>
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+struct module *ip_conntrack_module = THIS_MODULE;
+
+static unsigned int
+print_tuple(char *buffer, const struct ip_conntrack_tuple *tuple,
+ struct ip_conntrack_protocol *proto)
+{
+ int len;
+
+ len = sprintf(buffer, "src=%u.%u.%u.%u dst=%u.%u.%u.%u ",
+ NIPQUAD(tuple->src.ip), NIPQUAD(tuple->dst.ip));
+
+ len += proto->print_tuple(buffer + len, tuple);
+
+ return len;
+}
+
+/* FIXME: Don't print source proto part. --RR */
+static unsigned int
+print_expect(char *buffer, const struct ip_conntrack_expect *expect)
+{
+ unsigned int len;
+
+ len = sprintf(buffer, "EXPECTING: proto=%u ",
+ expect->tuple.dst.protonum);
+ len += print_tuple(buffer + len, &expect->tuple,
+ __find_proto(expect->tuple.dst.protonum));
+ len += sprintf(buffer + len, "\n");
+ return len;
+}
+
+static unsigned int
+print_conntrack(char *buffer, const struct ip_conntrack *conntrack)
+{
+ unsigned int len;
+ struct ip_conntrack_protocol *proto
+ = __find_proto(conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
+ .tuple.dst.protonum);
+
+ len = sprintf(buffer, "%-8s %u %lu ",
+ proto->name,
+ conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
+ .tuple.dst.protonum,
+ timer_pending(&conntrack->timeout)
+ ? (conntrack->timeout.expires - jiffies)/HZ : 0);
+
+ len += proto->print_conntrack(buffer + len, conntrack);
+ len += print_tuple(buffer + len,
+ &conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
+ proto);
+ if (!(conntrack->status & IPS_SEEN_REPLY))
+ len += sprintf(buffer + len, "[UNREPLIED] ");
+ len += print_tuple(buffer + len,
+ &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple,
+ proto);
+ len += sprintf(buffer + len, "\n");
+
+ return len;
+}
+
+/* Returns true when finished. */
+static int
+conntrack_iterate(const struct ip_conntrack_tuple_hash *hash,
+ char *buffer, off_t offset, off_t *upto,
+ unsigned int *len, unsigned int maxlen)
+{
+ unsigned int newlen;
+ IP_NF_ASSERT(hash->ctrack);
+
+ MUST_BE_READ_LOCKED(&ip_conntrack_lock);
+
+ /* Only count originals */
+ if (DIRECTION(hash))
+ return 0;
+
+ if ((*upto)++ < offset)
+ return 0;
+
+ newlen = print_conntrack(buffer + *len, hash->ctrack);
+ if (*len + newlen > maxlen)
+ return 1;
+ else *len += newlen;
+
+ return 0;
+}
+
+static int
+list_conntracks(char *buffer, char **start, off_t offset, int length)
+{
+ unsigned int i;
+ unsigned int len = 0;
+ off_t upto = 0;
+ struct list_head *e;
+
+ READ_LOCK(&ip_conntrack_lock);
+ /* Traverse hash; print originals then reply. */
+ for (i = 0; i < ip_conntrack_htable_size; i++) {
+ if (LIST_FIND(&ip_conntrack_hash[i], conntrack_iterate,
+ struct ip_conntrack_tuple_hash *,
+ buffer, offset, &upto, &len, length))
+ goto finished;
+ }
+
+ /* Now iterate through expecteds. */
+ for (e = expect_list.next; e != &expect_list; e = e->next) {
+ unsigned int last_len;
+ struct ip_conntrack_expect *expect
+ = (struct ip_conntrack_expect *)e;
+ if (upto++ < offset) continue;
+
+ last_len = len;
+ len += print_expect(buffer + len, expect);
+ if (len > length) {
+ len = last_len;
+ goto finished;
+ }
+ }
+
+ finished:
+ READ_UNLOCK(&ip_conntrack_lock);
+
+ /* `start' hack - see fs/proc/generic.c line ~165 */
+ *start = (char *)((unsigned int)upto - offset);
+ return len;
+}
+
+static unsigned int ip_refrag(unsigned int hooknum,
+ struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ struct rtable *rt = (struct rtable *)(*pskb)->dst;
+
+ /* Local packets are never produced too large for their
+ interface. We degfragment them at LOCAL_OUT, however,
+ so we have to refragment them here. */
+ if ((*pskb)->len > rt->u.dst.pmtu) {
+ DEBUGP("ip_conntrack: refragm %p (size %u) to %u (okfn %p)\n",
+ *pskb, (*pskb)->len, rt->u.dst.pmtu, okfn);
+ /* No hook can be after us, so this should be OK. */
+ ip_fragment(*pskb, okfn);
+ return NF_STOLEN;
+ }
+ return NF_ACCEPT;
+}
+
+/* Connection tracking may drop packets, but never alters them, so
+ make it the first hook. */
+static struct nf_hook_ops ip_conntrack_in_ops
+= { { NULL, NULL }, ip_conntrack_in, PF_INET, NF_IP_PRE_ROUTING,
+ NF_IP_PRI_CONNTRACK };
+static struct nf_hook_ops ip_conntrack_local_out_ops
+= { { NULL, NULL }, ip_conntrack_in, PF_INET, NF_IP_LOCAL_OUT,
+ NF_IP_PRI_CONNTRACK };
+/* Refragmenter; last chance. */
+static struct nf_hook_ops ip_conntrack_out_ops
+= { { NULL, NULL }, ip_refrag, PF_INET, NF_IP_POST_ROUTING, NF_IP_PRI_LAST };
+
+static int init_or_cleanup(int init)
+{
+ int ret = 0;
+
+ if (!init) goto cleanup;
+
+ ret = ip_conntrack_init();
+ if (ret < 0)
+ goto cleanup_nothing;
+
+ proc_net_create("ip_conntrack",0,list_conntracks);
+ ret = nf_register_hook(&ip_conntrack_in_ops);
+ if (ret < 0) {
+ printk("ip_conntrack: can't register in hook.\n");
+ goto cleanup_init;
+ }
+ ret = nf_register_hook(&ip_conntrack_local_out_ops);
+ if (ret < 0) {
+ printk("ip_conntrack: can't register local out hook.\n");
+ goto cleanup_inops;
+ }
+ ret = nf_register_hook(&ip_conntrack_out_ops);
+ if (ret < 0) {
+ printk("ip_conntrack: can't register post-routing hook.\n");
+ goto cleanup_inandlocalops;
+ }
+
+ return ret;
+
+ cleanup:
+ nf_unregister_hook(&ip_conntrack_out_ops);
+ cleanup_inandlocalops:
+ nf_unregister_hook(&ip_conntrack_local_out_ops);
+ cleanup_inops:
+ nf_unregister_hook(&ip_conntrack_in_ops);
+ cleanup_init:
+ proc_net_remove("ip_conntrack");
+ ip_conntrack_cleanup();
+ cleanup_nothing:
+ return ret;
+}
+
+/* FIXME: Allow NULL functions and sub in pointers to generic for
+ them. --RR */
+int ip_conntrack_protocol_register(struct ip_conntrack_protocol *proto)
+{
+ int ret = 0;
+ struct list_head *i;
+
+ WRITE_LOCK(&ip_conntrack_lock);
+ for (i = protocol_list.next; i != &protocol_list; i = i->next) {
+ if (((struct ip_conntrack_protocol *)i)->proto
+ == proto->proto) {
+ ret = -EBUSY;
+ goto out;
+ }
+ }
+
+ list_prepend(&protocol_list, proto);
+ MOD_INC_USE_COUNT;
+
+ out:
+ WRITE_UNLOCK(&ip_conntrack_lock);
+ return ret;
+}
+
+/* FIXME: Implement this --RR */
+#if 0
+void ip_conntrack_protocol_unregister(struct ip_conntrack_protocol *proto)
+{
+}
+#endif
+
+static int __init init(void)
+{
+ return init_or_cleanup(1);
+}
+
+static void __exit fini(void)
+{
+ init_or_cleanup(0);
+}
+
+module_init(init);
+module_exit(fini);
+
+EXPORT_SYMBOL(ip_conntrack_protocol_register);
+EXPORT_SYMBOL(invert_tuplepr);
+EXPORT_SYMBOL(ip_conntrack_alter_reply);
+EXPORT_SYMBOL(ip_conntrack_destroyed);
+EXPORT_SYMBOL(ip_conntrack_get);
+EXPORT_SYMBOL(ip_conntrack_module);
+EXPORT_SYMBOL(ip_conntrack_helper_register);
+EXPORT_SYMBOL(ip_conntrack_helper_unregister);
+EXPORT_SYMBOL(ip_conntrack_lock);
+EXPORT_SYMBOL(find_proto);
+EXPORT_SYMBOL(get_tuple);
+EXPORT_SYMBOL(ip_ct_selective_cleanup);
+EXPORT_SYMBOL(ip_ct_refresh);
+EXPORT_SYMBOL(ip_conntrack_expect_related);
+EXPORT_SYMBOL(ip_conntrack_tuple_taken);
+EXPORT_SYMBOL(ip_ct_gather_frags);
diff --git a/net/ipv4/netfilter/ip_fw_compat.c b/net/ipv4/netfilter/ip_fw_compat.c
new file mode 100644
index 000000000..72dc3d816
--- /dev/null
+++ b/net/ipv4/netfilter/ip_fw_compat.c
@@ -0,0 +1,238 @@
+/* Compatibility framework for ipchains and ipfwadm support; designed
+ to look as much like the 2.2 infrastructure as possible. */
+struct notifier_block;
+
+#include <linux/netfilter_ipv4.h>
+#include <linux/ip.h>
+#include <net/icmp.h>
+#include <linux/if.h>
+#include <linux/inetdevice.h>
+#include <linux/netdevice.h>
+#include <linux/module.h>
+#include <net/ip.h>
+#include <net/route.h>
+#include <linux/netfilter_ipv4/compat_firewall.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+
+EXPORT_NO_SYMBOLS;
+
+static struct firewall_ops *fwops;
+
+/* From ip_fw_compat_redir.c */
+extern unsigned int
+do_redirect(struct sk_buff *skb,
+ const struct net_device *dev,
+ u_int16_t redirpt);
+
+extern void
+check_for_redirect(struct sk_buff *skb);
+
+extern void
+check_for_unredirect(struct sk_buff *skb);
+
+/* From ip_fw_compat_masq.c */
+extern unsigned int
+do_masquerade(struct sk_buff **pskb, const struct net_device *dev);
+
+extern unsigned int
+check_for_demasq(struct sk_buff **pskb);
+
+extern int __init masq_init(void);
+extern void masq_cleanup(void);
+
+/* They call these; we do what they want. */
+int register_firewall(int pf, struct firewall_ops *fw)
+{
+ if (pf != PF_INET) {
+ printk("Attempt to register non-IP firewall module.\n");
+ return -EINVAL;
+ }
+ if (fwops) {
+ printk("Attempt to register multiple firewall modules.\n");
+ return -EBUSY;
+ }
+
+ fwops = fw;
+ return 0;
+}
+
+int unregister_firewall(int pf, struct firewall_ops *fw)
+{
+ fwops = NULL;
+ return 0;
+}
+
+static unsigned int
+fw_in(unsigned int hooknum,
+ struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ int ret = FW_BLOCK;
+ u_int16_t redirpt;
+
+ (*pskb)->nfcache |= NFC_UNKNOWN;
+ (*pskb)->ip_summed = CHECKSUM_NONE;
+
+ switch (hooknum) {
+ case NF_IP_PRE_ROUTING:
+ if (fwops->fw_acct_in)
+ fwops->fw_acct_in(fwops, PF_INET,
+ (struct net_device *)in,
+ (*pskb)->nh.raw, &redirpt, pskb);
+
+ if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
+ *pskb = ip_ct_gather_frags(*pskb);
+
+ if (!*pskb)
+ return NF_STOLEN;
+ }
+
+ ret = fwops->fw_input(fwops, PF_INET, (struct net_device *)in,
+ (*pskb)->nh.raw, &redirpt, pskb);
+ break;
+
+ case NF_IP_FORWARD:
+ /* Connection will only be set if it was
+ demasqueraded: if so, skip forward chain. */
+ if ((*pskb)->nfct)
+ ret = FW_ACCEPT;
+ else ret = fwops->fw_forward(fwops, PF_INET,
+ (struct net_device *)out,
+ (*pskb)->nh.raw, &redirpt, pskb);
+ break;
+
+ case NF_IP_POST_ROUTING:
+ ret = fwops->fw_output(fwops, PF_INET,
+ (struct net_device *)out,
+ (*pskb)->nh.raw, &redirpt, pskb);
+ if (fwops->fw_acct_out && (ret == FW_ACCEPT || ret == FW_SKIP))
+ fwops->fw_acct_out(fwops, PF_INET,
+ (struct net_device *)in,
+ (*pskb)->nh.raw, &redirpt, pskb);
+ break;
+ }
+
+ switch (ret) {
+ case FW_REJECT: {
+ /* Alexey says:
+ *
+ * Generally, routing is THE FIRST thing to make, when
+ * packet enters IP stack. Before packet is routed you
+ * cannot call any service routines from IP stack. */
+ struct iphdr *iph = (*pskb)->nh.iph;
+
+ if ((*pskb)->dst != NULL
+ || ip_route_input(*pskb, iph->daddr, iph->saddr, iph->tos,
+ (struct net_device *)in) == 0)
+ icmp_send(*pskb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH,
+ 0);
+ return NF_DROP;
+ }
+
+ case FW_ACCEPT:
+ case FW_SKIP:
+ if (hooknum == NF_IP_PRE_ROUTING) {
+ check_for_demasq(pskb);
+ check_for_redirect(*pskb);
+ } else if (hooknum == NF_IP_POST_ROUTING)
+ check_for_unredirect(*pskb);
+
+ return NF_ACCEPT;
+
+ case FW_MASQUERADE:
+ if (hooknum == NF_IP_FORWARD)
+ return do_masquerade(pskb, out);
+ else return NF_ACCEPT;
+
+ case FW_REDIRECT:
+ if (hooknum == NF_IP_PRE_ROUTING)
+ return do_redirect(*pskb, in, redirpt);
+ else return NF_ACCEPT;
+
+ default:
+ /* FW_BLOCK */
+ return NF_DROP;
+ }
+}
+
+extern int ip_fw_ctl(int optval, void *user, unsigned int len);
+
+static int sock_fn(struct sock *sk, int optval, void *user, unsigned int len)
+{
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ return -ip_fw_ctl(optval, user, len);
+}
+
+static struct nf_hook_ops preroute_ops
+= { { NULL, NULL }, fw_in, PF_INET, NF_IP_PRE_ROUTING, NF_IP_PRI_FILTER };
+
+static struct nf_hook_ops postroute_ops
+= { { NULL, NULL }, fw_in, PF_INET, NF_IP_POST_ROUTING, NF_IP_PRI_FILTER };
+
+static struct nf_hook_ops forward_ops
+= { { NULL, NULL }, fw_in, PF_INET, NF_IP_FORWARD, NF_IP_PRI_FILTER };
+
+static struct nf_sockopt_ops sock_ops
+= { { NULL, NULL }, PF_INET, 64, 64 + 1024 + 1, &sock_fn, 0, 0, NULL,
+ 0, NULL };
+
+extern int ipfw_init_or_cleanup(int init);
+
+static int init_or_cleanup(int init)
+{
+ int ret = 0;
+
+ if (!init) goto cleanup;
+
+ ret = nf_register_sockopt(&sock_ops);
+
+ if (ret < 0)
+ goto cleanup_nothing;
+
+ ret = ipfw_init_or_cleanup(1);
+ if (ret < 0)
+ goto cleanup_sockopt;
+
+ ret = masq_init();
+ if (ret < 0)
+ goto cleanup_ipfw;
+
+ nf_register_hook(&preroute_ops);
+ nf_register_hook(&postroute_ops);
+ nf_register_hook(&forward_ops);
+
+ return ret;
+
+ cleanup:
+ nf_unregister_hook(&preroute_ops);
+ nf_unregister_hook(&postroute_ops);
+ nf_unregister_hook(&forward_ops);
+
+ masq_cleanup();
+
+ cleanup_ipfw:
+ ipfw_init_or_cleanup(0);
+
+ cleanup_sockopt:
+ nf_unregister_sockopt(&sock_ops);
+
+ cleanup_nothing:
+ return ret;
+}
+
+static int __init init(void)
+{
+ return init_or_cleanup(1);
+}
+
+static void __exit fini(void)
+{
+ init_or_cleanup(0);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/netfilter/ip_fw_compat_masq.c b/net/ipv4/netfilter/ip_fw_compat_masq.c
new file mode 100644
index 000000000..e0074c1e2
--- /dev/null
+++ b/net/ipv4/netfilter/ip_fw_compat_masq.c
@@ -0,0 +1,288 @@
+/* Masquerading compatibility layer.
+
+ Note that there are no restrictions on other programs binding to
+ ports 61000:65095 (in 2.0 and 2.2 they get EADDRINUSE). Just DONT
+ DO IT.
+ */
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/icmp.h>
+#include <linux/udp.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/version.h>
+#include <net/route.h>
+
+#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_conntrack_lock)
+#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_conntrack_lock)
+
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_conntrack_core.h>
+#include <linux/netfilter_ipv4/ip_nat.h>
+#include <linux/netfilter_ipv4/ip_nat_core.h>
+#include <linux/netfilter_ipv4/listhelp.h>
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+unsigned int
+do_masquerade(struct sk_buff **pskb, const struct net_device *dev)
+{
+ struct iphdr *iph = (*pskb)->nh.iph;
+ struct ip_nat_info *info;
+ enum ip_conntrack_info ctinfo;
+ struct ip_conntrack *ct;
+ unsigned int ret;
+
+ /* Sorry, only ICMP, TCP and UDP. */
+ if (iph->protocol != IPPROTO_ICMP
+ && iph->protocol != IPPROTO_TCP
+ && iph->protocol != IPPROTO_UDP)
+ return NF_DROP;
+
+ /* Feed it to connection tracking; in fact we're in NF_IP_FORWARD,
+ but connection tracking doesn't expect that */
+ ret = ip_conntrack_in(NF_IP_POST_ROUTING, pskb, dev, NULL, NULL);
+ if (ret != NF_ACCEPT) {
+ DEBUGP("ip_conntrack_in returned %u.\n", ret);
+ return ret;
+ }
+
+ ct = ip_conntrack_get(*pskb, &ctinfo);
+
+ if (!ct) {
+ DEBUGP("ip_conntrack_in set to invalid conntrack.\n");
+ return NF_DROP;
+ }
+
+ info = &ct->nat.info;
+
+ WRITE_LOCK(&ip_nat_lock);
+ /* Setup the masquerade, if not already */
+ if (!info->initialized) {
+ u_int32_t newsrc;
+ struct rtable *rt;
+ struct ip_nat_multi_range range;
+
+ /* Pass 0 instead of saddr, since it's going to be changed
+ anyway. */
+ if (ip_route_output(&rt, iph->daddr, 0, 0, 0) != 0) {
+ DEBUGP("ipnat_rule_masquerade: Can't reroute.\n");
+ return NF_DROP;
+ }
+ newsrc = inet_select_addr(rt->u.dst.dev, rt->rt_gateway,
+ RT_SCOPE_UNIVERSE);
+ ip_rt_put(rt);
+ range = ((struct ip_nat_multi_range)
+ { 1,
+ {{IP_NAT_RANGE_MAP_IPS|IP_NAT_RANGE_PROTO_SPECIFIED,
+ newsrc, newsrc,
+ { htons(61000) }, { htons(65095) } } } });
+
+ ip_nat_setup_info(ct, &range, NF_IP_POST_ROUTING);
+ place_in_hashes(ct, info);
+ info->initialized = 1;
+ } else
+ DEBUGP("Masquerading already done on this conn.\n");
+ WRITE_UNLOCK(&ip_nat_lock);
+
+ return do_bindings(ct, ctinfo, info, NF_IP_POST_ROUTING, pskb);
+}
+
+unsigned int
+check_for_demasq(struct sk_buff **pskb)
+{
+ struct ip_conntrack_tuple tuple;
+ struct iphdr *iph = (*pskb)->nh.iph;
+ struct ip_conntrack_protocol *protocol;
+ struct ip_conntrack_tuple_hash *h;
+ enum ip_conntrack_info ctinfo;
+ int ret;
+
+ protocol = find_proto(iph->protocol);
+
+ /* We don't feed packets to conntrack system unless we know
+ they're part of an connection already established by an
+ explicit masq command. */
+ switch (iph->protocol) {
+ case IPPROTO_ICMP:
+ /* ICMP errors. */
+ if (icmp_error_track(*pskb)) {
+ /* If it is valid, tranlsate it */
+ if ((*pskb)->nfct) {
+ struct ip_conntrack *ct
+ = (struct ip_conntrack *)
+ (*pskb)->nfct->master;
+ enum ip_conntrack_dir dir;
+
+ if ((*pskb)->nfct-ct->infos >= IP_CT_IS_REPLY)
+ dir = IP_CT_DIR_REPLY;
+ else
+ dir = IP_CT_DIR_ORIGINAL;
+
+ icmp_reply_translation(*pskb,
+ ct,
+ NF_IP_PRE_ROUTING,
+ dir);
+ }
+ return NF_ACCEPT;
+ }
+ /* Fall thru... */
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ if (!get_tuple(iph, (*pskb)->len, &tuple, protocol)) {
+ printk("ip_fw_compat_masq: Couldn't get tuple\n");
+ return NF_ACCEPT;
+ }
+ break;
+
+ default:
+ /* Not ours... */
+ return NF_ACCEPT;
+ }
+ h = ip_conntrack_find_get(&tuple, NULL);
+
+ /* MUST be found, and MUST be reply. */
+ if (h && DIRECTION(h) == 1) {
+ ret = ip_conntrack_in(NF_IP_PRE_ROUTING, pskb,
+ NULL, NULL, NULL);
+
+ /* Put back the reference gained from find_get */
+ nf_conntrack_put(&h->ctrack->infos[0]);
+ if (ret == NF_ACCEPT) {
+ struct ip_conntrack *ct;
+ ct = ip_conntrack_get(*pskb, &ctinfo);
+
+ if (ct) {
+ struct ip_nat_info *info = &ct->nat.info;
+
+ do_bindings(ct, ctinfo, info,
+ NF_IP_PRE_ROUTING,
+ pskb);
+ } else
+ printk("ip_fw_compat_masq: conntrack"
+ " didn't like\n");
+ }
+ } else {
+ if (h)
+ /* Put back the reference gained from find_get */
+ nf_conntrack_put(&h->ctrack->infos[0]);
+ ret = NF_ACCEPT;
+ }
+
+ return ret;
+}
+
+int ip_fw_masq_timeouts(void *user, int len)
+{
+ printk("Sorry: masquerading timeouts set 5DAYS/2MINS/60SECS\n");
+ return 0;
+}
+
+static const char *masq_proto_name(u_int16_t protonum)
+{
+ switch (protonum) {
+ case IPPROTO_TCP: return "TCP";
+ case IPPROTO_UDP: return "UDP";
+ case IPPROTO_ICMP: return "ICMP";
+ default: return "MORE-CAFFIENE-FOR-RUSTY";
+ }
+}
+
+static unsigned int
+print_masq(char *buffer, const struct ip_conntrack *conntrack)
+{
+ char temp[129];
+
+ /* This is for backwards compatibility, but ick!.
+ We should never export jiffies to userspace.
+ */
+ sprintf(temp,"%s %08X:%04X %08X:%04X %04X %08X %6d %6d %7lu",
+ masq_proto_name(conntrack->tuplehash[0].tuple.dst.protonum),
+ ntohl(conntrack->tuplehash[0].tuple.src.ip),
+ ntohs(conntrack->tuplehash[0].tuple.src.u.all),
+ ntohl(conntrack->tuplehash[0].tuple.dst.ip),
+ ntohs(conntrack->tuplehash[0].tuple.dst.u.all),
+ ntohs(conntrack->tuplehash[1].tuple.dst.u.all),
+ /* Sorry, no init_seq, delta or previous_delta (yet). */
+ 0, 0, 0,
+ conntrack->timeout.expires - jiffies);
+
+ return sprintf(buffer, "%-127s\n", temp);
+}
+
+/* Returns true when finished. */
+static int
+masq_iterate(const struct ip_conntrack_tuple_hash *hash,
+ char *buffer, off_t offset, off_t *upto,
+ unsigned int *len, unsigned int maxlen)
+{
+ unsigned int newlen;
+
+ IP_NF_ASSERT(hash->ctrack);
+
+ /* Only count originals */
+ if (DIRECTION(hash))
+ return 0;
+
+ if ((*upto)++ < offset)
+ return 0;
+
+ newlen = print_masq(buffer + *len, hash->ctrack);
+ if (*len + newlen > maxlen)
+ return 1;
+ else *len += newlen;
+
+ return 0;
+}
+
+/* Everything in the hash is masqueraded. */
+static int
+masq_procinfo(char *buffer, char **start, off_t offset, int length)
+{
+ unsigned int i;
+ int len = 0;
+ off_t upto = 0;
+
+ READ_LOCK(&ip_conntrack_lock);
+ /* Traverse hash; print originals then reply. */
+ for (i = 0; i < ip_conntrack_htable_size; i++) {
+ if (LIST_FIND(&ip_conntrack_hash[i], masq_iterate,
+ struct ip_conntrack_tuple_hash *,
+ buffer, offset, &upto, &len, length))
+ break;
+ }
+ READ_UNLOCK(&ip_conntrack_lock);
+
+ /* `start' hack - see fs/proc/generic.c line ~165 */
+ *start = (char *)((unsigned int)upto - offset);
+ return len;
+}
+
+int __init masq_init(void)
+{
+ int ret;
+
+ ret = ip_conntrack_init();
+ if (ret == 0) {
+ ret = ip_nat_init();
+ if (ret == 0)
+ proc_net_create("ip_masquerade", 0, masq_procinfo);
+ else
+ ip_conntrack_cleanup();
+ }
+
+ return ret;
+}
+
+void masq_cleanup(void)
+{
+ ip_nat_cleanup();
+ ip_conntrack_cleanup();
+ proc_net_remove("ip_masquerade");
+}
diff --git a/net/ipv4/netfilter/ip_fw_compat_redir.c b/net/ipv4/netfilter/ip_fw_compat_redir.c
new file mode 100644
index 000000000..d4d910e77
--- /dev/null
+++ b/net/ipv4/netfilter/ip_fw_compat_redir.c
@@ -0,0 +1,284 @@
+/* This is a file to handle the "simple" NAT cases (redirect and
+ masquerade) required for the compatibility layer.
+
+ `bind to foreign address' and `getpeername' hacks are not
+ supported.
+
+ FIXME: Timing is overly simplistic. If anyone complains, make it
+ use conntrack.
+*/
+#include <linux/config.h>
+#include <linux/netfilter.h>
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <linux/tcp.h>
+#include <net/checksum.h>
+#include <linux/timer.h>
+#include <linux/netdevice.h>
+#include <linux/if.h>
+#include <linux/in.h>
+
+#include <linux/netfilter_ipv4/lockhelp.h>
+
+static DECLARE_LOCK(redir_lock);
+#define ASSERT_READ_LOCK(x) MUST_BE_LOCKED(&redir_lock)
+#define ASSERT_WRITE_LOCK(x) MUST_BE_LOCKED(&redir_lock)
+
+#include <linux/netfilter_ipv4/listhelp.h>
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+#ifdef CONFIG_NETFILTER_DEBUG
+#define IP_NF_ASSERT(x) \
+do { \
+ if (!(x)) \
+ /* Wooah! I'm tripping my conntrack in a frenzy of \
+ netplay... */ \
+ printk("ASSERT: %s:%i(%s)\n", \
+ __FILE__, __LINE__, __FUNCTION__); \
+} while(0);
+#else
+#define IP_NF_ASSERT(x)
+#endif
+
+static u_int16_t
+cheat_check(u_int32_t oldvalinv, u_int32_t newval, u_int16_t oldcheck)
+{
+ u_int32_t diffs[] = { oldvalinv, newval };
+ return csum_fold(csum_partial((char *)diffs, sizeof(diffs),
+ oldcheck^0xFFFF));
+}
+
+struct redir_core {
+ u_int32_t orig_srcip, orig_dstip;
+ u_int16_t orig_sport, orig_dport;
+
+ u_int32_t new_dstip;
+ u_int16_t new_dport;
+};
+
+struct redir
+{
+ struct list_head list;
+ struct redir_core core;
+ struct timer_list destroyme;
+};
+
+static LIST_HEAD(redirs);
+
+static int
+redir_cmp(const struct redir *i,
+ u_int32_t orig_srcip, u_int32_t orig_dstip,
+ u_int16_t orig_sport, u_int16_t orig_dport)
+{
+ return (i->core.orig_srcip == orig_srcip
+ && i->core.orig_dstip == orig_dstip
+ && i->core.orig_sport == orig_sport
+ && i->core.orig_dport == orig_dport);
+}
+
+/* Search for an existing redirection of the TCP packet. */
+static struct redir *
+find_redir(u_int32_t orig_srcip, u_int32_t orig_dstip,
+ u_int16_t orig_sport, u_int16_t orig_dport)
+{
+ return LIST_FIND(&redirs, redir_cmp, struct redir *,
+ orig_srcip, orig_dstip, orig_sport, orig_dport);
+}
+
+static void do_tcp_redir(struct sk_buff *skb, struct redir *redir)
+{
+ struct iphdr *iph = skb->nh.iph;
+ struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph
+ + iph->ihl);
+
+ tcph->check = cheat_check(~redir->core.orig_dstip,
+ redir->core.new_dstip,
+ cheat_check(redir->core.orig_dport ^ 0xFFFF,
+ redir->core.new_dport,
+ tcph->check));
+ iph->check = cheat_check(~redir->core.orig_dstip,
+ redir->core.new_dstip, iph->check);
+ tcph->dest = redir->core.new_dport;
+ iph->daddr = redir->core.new_dstip;
+
+ skb->nfcache |= NFC_ALTERED;
+}
+
+static int
+unredir_cmp(const struct redir *i,
+ u_int32_t new_dstip, u_int32_t orig_srcip,
+ u_int16_t new_dport, u_int16_t orig_sport)
+{
+ return (i->core.orig_srcip == orig_srcip
+ && i->core.new_dstip == new_dstip
+ && i->core.orig_sport == orig_sport
+ && i->core.new_dport == new_dport);
+}
+
+/* Match reply packet against redir */
+static struct redir *
+find_unredir(u_int32_t new_dstip, u_int32_t orig_srcip,
+ u_int16_t new_dport, u_int16_t orig_sport)
+{
+ return LIST_FIND(&redirs, unredir_cmp, struct redir *,
+ new_dstip, orig_srcip, new_dport, orig_sport);
+}
+
+/* `unredir' a reply packet. */
+static void do_tcp_unredir(struct sk_buff *skb, struct redir *redir)
+{
+ struct iphdr *iph = skb->nh.iph;
+ struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph
+ + iph->ihl);
+
+ tcph->check = cheat_check(~redir->core.new_dstip,
+ redir->core.orig_dstip,
+ cheat_check(redir->core.new_dport ^ 0xFFFF,
+ redir->core.orig_dport,
+ tcph->check));
+ iph->check = cheat_check(~redir->core.new_dstip,
+ redir->core.orig_dstip,
+ iph->check);
+ tcph->source = redir->core.orig_dport;
+ iph->saddr = redir->core.orig_dstip;
+
+ skb->nfcache |= NFC_ALTERED;
+}
+
+/* REDIRECT a packet. */
+unsigned int
+do_redirect(struct sk_buff *skb,
+ const struct net_device *dev,
+ u_int16_t redirpt)
+{
+ struct iphdr *iph = skb->nh.iph;
+ u_int32_t newdst;
+
+ /* Figure out address: not loopback. */
+ if (!dev)
+ return NF_DROP;
+
+ /* Grab first address on interface. */
+ newdst = ((struct in_device *)dev->ip_ptr)->ifa_list->ifa_local;
+
+ switch (iph->protocol) {
+ case IPPROTO_UDP: {
+ /* Simple mangle. */
+ struct udphdr *udph = (struct udphdr *)((u_int32_t *)iph
+ + iph->ihl);
+
+ udph->check = cheat_check(~iph->daddr, newdst,
+ cheat_check(udph->dest ^ 0xFFFF,
+ redirpt,
+ udph->check));
+ iph->check = cheat_check(~iph->daddr, newdst, iph->check);
+ udph->dest = redirpt;
+ iph->daddr = newdst;
+
+ skb->nfcache |= NFC_ALTERED;
+ return NF_ACCEPT;
+ }
+ case IPPROTO_TCP: {
+ /* Mangle, maybe record. */
+ struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph
+ + iph->ihl);
+ struct redir *redir;
+ int ret;
+
+ DEBUGP("Doing tcp redirect. %08X:%u %08X:%u -> %08X:%u\n",
+ iph->saddr, tcph->source, iph->daddr, tcph->dest,
+ newdst, redirpt);
+ LOCK_BH(&redir_lock);
+ redir = find_redir(iph->saddr, iph->daddr,
+ tcph->source, tcph->dest);
+
+ if (!redir) {
+ redir = kmalloc(sizeof(struct redir), GFP_ATOMIC);
+ if (!redir) {
+ ret = NF_DROP;
+ goto out;
+ }
+ list_prepend(&redirs, redir);
+ init_timer(&redir->destroyme);
+ }
+ /* In case mangling has changed, rewrite this part. */
+ redir->core = ((struct redir_core)
+ { iph->saddr, iph->daddr,
+ tcph->source, tcph->dest,
+ newdst, redirpt });
+ do_tcp_redir(skb, redir);
+ ret = NF_ACCEPT;
+
+ out:
+ UNLOCK_BH(&redir_lock);
+ return ret;
+ }
+
+ default: /* give up if not TCP or UDP. */
+ return NF_DROP;
+ }
+}
+
+static void destroyme(unsigned long me)
+{
+ LOCK_BH(&redir_lock);
+ LIST_DELETE(&redirs, (struct redir *)me);
+ UNLOCK_BH(&redir_lock);
+}
+
+/* Incoming packet: is it a reply to a masqueraded connection, or
+ part of an already-redirected TCP connection? */
+void
+check_for_redirect(struct sk_buff *skb)
+{
+ struct iphdr *iph = skb->nh.iph;
+ struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph
+ + iph->ihl);
+ struct redir *redir;
+
+ if (iph->protocol != IPPROTO_TCP)
+ return;
+
+ LOCK_BH(&redir_lock);
+ redir = find_redir(iph->saddr, iph->daddr, tcph->source, tcph->dest);
+ if (redir) {
+ DEBUGP("Doing tcp redirect again.\n");
+ do_tcp_redir(skb, redir);
+ if (tcph->rst || tcph->fin) {
+ redir->destroyme.function = destroyme;
+ redir->destroyme.data = (unsigned long)redir;
+ mod_timer(&redir->destroyme, 75*HZ);
+ }
+ }
+ UNLOCK_BH(&redir_lock);
+}
+
+void
+check_for_unredirect(struct sk_buff *skb)
+{
+ struct iphdr *iph = skb->nh.iph;
+ struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph
+ + iph->ihl);
+ struct redir *redir;
+
+ if (iph->protocol != IPPROTO_TCP)
+ return;
+
+ LOCK_BH(&redir_lock);
+ redir = find_unredir(iph->saddr, iph->daddr, tcph->source, tcph->dest);
+ if (redir) {
+ DEBUGP("Doing tcp unredirect.\n");
+ do_tcp_unredir(skb, redir);
+ if (tcph->rst || tcph->fin) {
+ redir->destroyme.function = destroyme;
+ redir->destroyme.data = (unsigned long)redir;
+ mod_timer(&redir->destroyme, 75*HZ);
+ }
+ }
+ UNLOCK_BH(&redir_lock);
+}
diff --git a/net/ipv4/netfilter/ip_nat_core.c b/net/ipv4/netfilter/ip_nat_core.c
new file mode 100644
index 000000000..996e5a7ff
--- /dev/null
+++ b/net/ipv4/netfilter/ip_nat_core.c
@@ -0,0 +1,855 @@
+/* NAT for netfilter; shared with compatibility layer. */
+
+/* (c) 1999 Paul `Rusty' Russell. Licenced under the GNU General
+ Public Licence. */
+#ifdef MODULE
+#define __NO_VERSION__
+#endif
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/timer.h>
+#include <linux/skbuff.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/brlock.h>
+#include <net/checksum.h>
+#include <net/icmp.h>
+#include <net/ip.h>
+#include <net/tcp.h> /* For tcp_prot in getorigdst */
+
+#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_nat_lock)
+#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_nat_lock)
+
+#include <linux/netfilter_ipv4/ip_nat.h>
+#include <linux/netfilter_ipv4/ip_nat_protocol.h>
+#include <linux/netfilter_ipv4/ip_nat_core.h>
+#include <linux/netfilter_ipv4/ip_nat_helper.h>
+#include <linux/netfilter_ipv4/listhelp.h>
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+DECLARE_RWLOCK(ip_nat_lock);
+
+#define IP_NAT_HTABLE_SIZE 64
+
+static struct list_head bysource[IP_NAT_HTABLE_SIZE];
+static struct list_head byipsproto[IP_NAT_HTABLE_SIZE];
+LIST_HEAD(protos);
+static LIST_HEAD(helpers);
+
+extern struct ip_nat_protocol unknown_nat_protocol;
+
+/* We keep extra hashes for each conntrack, for fast searching. */
+static inline size_t
+hash_by_ipsproto(u_int32_t src, u_int32_t dst, u_int16_t proto)
+{
+ /* Modified src and dst, to ensure we don't create two
+ identical streams. */
+ return (src + dst + proto) % IP_NAT_HTABLE_SIZE;
+}
+
+static inline size_t
+hash_by_src(const struct ip_conntrack_manip *manip, u_int16_t proto)
+{
+ /* Original src, to ensure we map it consistently if poss. */
+ return (manip->ip + manip->u.all + proto) % IP_NAT_HTABLE_SIZE;
+}
+
+/* Noone using conntrack by the time this called. */
+static void ip_nat_cleanup_conntrack(struct ip_conntrack *conn)
+{
+ struct ip_nat_info *info = &conn->nat.info;
+
+ if (!info->initialized)
+ return;
+
+ IP_NF_ASSERT(info->bysource.conntrack);
+ IP_NF_ASSERT(info->byipsproto.conntrack);
+
+ WRITE_LOCK(&ip_nat_lock);
+ LIST_DELETE(&bysource[hash_by_src(&conn->tuplehash[IP_CT_DIR_ORIGINAL]
+ .tuple.src,
+ conn->tuplehash[IP_CT_DIR_ORIGINAL]
+ .tuple.dst.protonum)],
+ &info->bysource);
+
+ LIST_DELETE(&byipsproto
+ [hash_by_ipsproto(conn->tuplehash[IP_CT_DIR_REPLY]
+ .tuple.src.ip,
+ conn->tuplehash[IP_CT_DIR_REPLY]
+ .tuple.dst.ip,
+ conn->tuplehash[IP_CT_DIR_REPLY]
+ .tuple.dst.protonum)],
+ &info->byipsproto);
+ WRITE_UNLOCK(&ip_nat_lock);
+}
+
+/* We do checksum mangling, so if they were wrong before they're still
+ * wrong. Also works for incomplete packets (eg. ICMP dest
+ * unreachables.) */
+u_int16_t
+ip_nat_cheat_check(u_int32_t oldvalinv, u_int32_t newval, u_int16_t oldcheck)
+{
+ u_int32_t diffs[] = { oldvalinv, newval };
+ return csum_fold(csum_partial((char *)diffs, sizeof(diffs),
+ oldcheck^0xFFFF));
+}
+
+static inline int cmp_proto(const struct ip_nat_protocol *i, int proto)
+{
+ return i->protonum == proto;
+}
+
+struct ip_nat_protocol *
+find_nat_proto(u_int16_t protonum)
+{
+ struct ip_nat_protocol *i;
+
+ MUST_BE_READ_LOCKED(&ip_nat_lock);
+ i = LIST_FIND(&protos, cmp_proto, struct ip_nat_protocol *, protonum);
+ if (!i)
+ i = &unknown_nat_protocol;
+ return i;
+}
+
+/* Is this tuple already taken? (not by us) */
+int
+ip_nat_used_tuple(const struct ip_conntrack_tuple *tuple,
+ const struct ip_conntrack *ignored_conntrack)
+{
+ /* Conntrack tracking doesn't keep track of outgoing tuples; only
+ incoming ones. NAT means they don't have a fixed mapping,
+ so we invert the tuple and look for the incoming reply.
+
+ We could keep a separate hash if this proves too slow. */
+ struct ip_conntrack_tuple reply;
+
+ invert_tuplepr(&reply, tuple);
+ return ip_conntrack_tuple_taken(&reply, ignored_conntrack);
+}
+
+/* Does tuple + the source manip come within the range mr */
+static int
+in_range(const struct ip_conntrack_tuple *tuple,
+ const struct ip_conntrack_manip *manip,
+ const struct ip_nat_multi_range *mr)
+{
+ struct ip_nat_protocol *proto = find_nat_proto(tuple->dst.protonum);
+ unsigned int i;
+ struct ip_conntrack_tuple newtuple = { *manip, tuple->dst };
+
+ for (i = 0; i < mr->rangesize; i++) {
+ /* If we are allowed to map IPs, then we must be in the
+ range specified, otherwise we must be unchanged. */
+ if (mr->range[i].flags & IP_NAT_RANGE_MAP_IPS) {
+ if (ntohl(newtuple.src.ip) < ntohl(mr->range[i].min_ip)
+ || (ntohl(newtuple.src.ip)
+ > ntohl(mr->range[i].max_ip)))
+ continue;
+ } else {
+ if (newtuple.src.ip != tuple->src.ip)
+ continue;
+ }
+
+ if ((mr->range[i].flags & IP_NAT_RANGE_PROTO_SPECIFIED)
+ && proto->in_range(&newtuple, IP_NAT_MANIP_SRC,
+ &mr->range[i].min, &mr->range[i].max))
+ return 1;
+ }
+ return 0;
+}
+
+static inline int
+src_cmp(const struct ip_nat_hash *i,
+ const struct ip_conntrack_tuple *tuple,
+ const struct ip_nat_multi_range *mr)
+{
+ return (i->conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum
+ == tuple->dst.protonum
+ && i->conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip
+ == tuple->src.ip
+ && i->conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.all
+ == tuple->src.u.all
+ && in_range(tuple,
+ &i->conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
+ .tuple.src,
+ mr));
+}
+
+/* Only called for SRC manip */
+static struct ip_conntrack_manip *
+find_appropriate_src(const struct ip_conntrack_tuple *tuple,
+ const struct ip_nat_multi_range *mr)
+{
+ unsigned int h = hash_by_src(&tuple->src, tuple->dst.protonum);
+ struct ip_nat_hash *i;
+
+ MUST_BE_READ_LOCKED(&ip_nat_lock);
+ i = LIST_FIND(&bysource[h], src_cmp, struct ip_nat_hash *, tuple, mr);
+ if (i)
+ return &i->conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src;
+ else
+ return NULL;
+}
+
+/* If it's really a local destination manip, it may need to do a
+ source manip too. */
+static int
+do_extra_mangle(u_int32_t var_ip, u_int32_t *other_ipp)
+{
+ struct rtable *rt;
+
+ /* FIXME: IPTOS_TOS(iph->tos) --RR */
+ if (ip_route_output(&rt, var_ip, 0, 0, 0) != 0) {
+ DEBUGP("do_extra_mangle: Can't get route to %u.%u.%u.%u\n",
+ IP_PARTS(var_ip));
+ return 0;
+ }
+
+ *other_ipp = rt->rt_src;
+ ip_rt_put(rt);
+ return 1;
+}
+
+/* Simple way to iterate through all. */
+static inline int fake_cmp(const struct ip_nat_hash *i,
+ u_int32_t src, u_int32_t dst, u_int16_t protonum,
+ unsigned int *score,
+ const struct ip_conntrack *conntrack)
+{
+ /* Compare backwards: we're dealing with OUTGOING tuples, and
+ inside the conntrack is the REPLY tuple. Don't count this
+ conntrack. */
+ if (i->conntrack != conntrack
+ && i->conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip == dst
+ && i->conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip == src
+ && (i->conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.protonum
+ == protonum))
+ (*score)++;
+ return 0;
+}
+
+static inline unsigned int
+count_maps(u_int32_t src, u_int32_t dst, u_int16_t protonum,
+ const struct ip_conntrack *conntrack)
+{
+ unsigned int score = 0;
+
+ MUST_BE_READ_LOCKED(&ip_nat_lock);
+ LIST_FIND(&byipsproto[hash_by_ipsproto(src, dst, protonum)],
+ fake_cmp, struct ip_nat_hash *, src, dst, protonum, &score,
+ conntrack);
+
+ return score;
+}
+
+/* For [FUTURE] fragmentation handling, we want the least-used
+ src-ip/dst-ip/proto triple. Fairness doesn't come into it. Thus
+ if the range specifies 1.2.3.4 ports 10000-10005 and 1.2.3.5 ports
+ 1-65535, we don't do pro-rata allocation based on ports; we choose
+ the ip with the lowest src-ip/dst-ip/proto usage.
+
+ If an allocation then fails (eg. all 6 ports used in the 1.2.3.4
+ range), we eliminate that and try again. This is not the most
+ efficient approach, but if you're worried about that, don't hand us
+ ranges you don't really have. */
+static struct ip_nat_range *
+find_best_ips_proto(struct ip_conntrack_tuple *tuple,
+ const struct ip_nat_multi_range *mr,
+ const struct ip_conntrack *conntrack,
+ unsigned int hooknum)
+{
+ unsigned int i;
+ struct {
+ const struct ip_nat_range *range;
+ unsigned int score;
+ struct ip_conntrack_tuple tuple;
+ } best = { NULL, 0xFFFFFFFF };
+ u_int32_t *var_ipp, *other_ipp, saved_ip;
+
+ if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) {
+ var_ipp = &tuple->src.ip;
+ saved_ip = tuple->dst.ip;
+ other_ipp = &tuple->dst.ip;
+ } else {
+ var_ipp = &tuple->dst.ip;
+ saved_ip = tuple->src.ip;
+ other_ipp = &tuple->src.ip;
+ }
+
+ IP_NF_ASSERT(mr->rangesize >= 1);
+ for (i = 0; i < mr->rangesize; i++) {
+ u_int32_t minip, maxip;
+
+ /* Don't do ranges which are already eliminated. */
+ if (mr->range[i].flags & IP_NAT_RANGE_FULL) {
+ continue;
+ }
+
+ if (mr->range[i].flags & IP_NAT_RANGE_MAP_IPS) {
+ minip = mr->range[i].min_ip;
+ maxip = mr->range[i].max_ip;
+ } else
+ minip = maxip = *var_ipp;
+
+ for (*var_ipp = minip;
+ ntohl(*var_ipp) <= ntohl(maxip);
+ *var_ipp = htonl(ntohl(*var_ipp) + 1)) {
+ unsigned int score;
+
+ /* Reset the other ip in case it was mangled by
+ * do_extra_mangle last time. */
+ *other_ipp = saved_ip;
+
+ if (hooknum == NF_IP_LOCAL_OUT
+ && !do_extra_mangle(*var_ipp, other_ipp)) {
+ DEBUGP("Range %u %u.%u.%u.%u rt failed!\n",
+ i, IP_PARTS(*var_ipp));
+ /* Can't route? This whole range part is
+ * probably screwed, but keep trying
+ * anyway. */
+ continue;
+ }
+
+ /* Count how many others map onto this. */
+ score = count_maps(tuple->src.ip, tuple->dst.ip,
+ tuple->dst.protonum, conntrack);
+ if (score < best.score) {
+ /* Optimization: doesn't get any better than
+ this. */
+ if (score == 0)
+ return (struct ip_nat_range *)
+ &mr->range[i];
+
+ best.score = score;
+ best.tuple = *tuple;
+ best.range = &mr->range[i];
+ }
+ }
+ }
+ *tuple = best.tuple;
+
+ /* Discard const. */
+ return (struct ip_nat_range *)best.range;
+}
+
+static int
+get_unique_tuple(struct ip_conntrack_tuple *tuple,
+ const struct ip_conntrack_tuple *orig_tuple,
+ const struct ip_nat_multi_range *mrr,
+ struct ip_conntrack *conntrack,
+ unsigned int hooknum)
+{
+ struct ip_nat_protocol *proto
+ = find_nat_proto(orig_tuple->dst.protonum);
+ struct ip_nat_range *rptr;
+ unsigned int i;
+ int ret;
+
+ /* We temporarily use flags for marking full parts, but we
+ always clean up afterwards */
+ struct ip_nat_multi_range *mr = (void *)mrr;
+
+ /* 1) If this srcip/proto/src-proto-part is currently mapped,
+ and that same mapping gives a unique tuple within the given
+ range, use that.
+
+ This is only required for source (ie. NAT/masq) mappings.
+ So far, we don't do local source mappings, so multiple
+ manips not an issue. */
+ if (hooknum == NF_IP_POST_ROUTING) {
+ struct ip_conntrack_manip *manip;
+
+ manip = find_appropriate_src(orig_tuple, mr);
+ if (manip) {
+ /* Apply same source manipulation. */
+ *tuple = ((struct ip_conntrack_tuple)
+ { *manip, orig_tuple->dst });
+ DEBUGP("get_unique_tuple: Found current src map\n");
+ return 1;
+ }
+ }
+
+ /* 2) Select the least-used IP/proto combination in the given
+ range.
+ */
+ *tuple = *orig_tuple;
+ while ((rptr = find_best_ips_proto(tuple, mr, conntrack, hooknum))
+ != NULL) {
+ DEBUGP("Found best for "); DUMP_TUPLE(tuple);
+ /* 3) The per-protocol part of the manip is made to
+ map into the range to make a unique tuple. */
+
+ /* Only bother mapping if it's not already in range
+ and unique */
+ if ((!(rptr->flags & IP_NAT_RANGE_PROTO_SPECIFIED)
+ || proto->in_range(tuple, HOOK2MANIP(hooknum),
+ &rptr->min, &rptr->max))
+ && !ip_nat_used_tuple(tuple, conntrack)) {
+ ret = 1;
+ goto clear_fulls;
+ } else {
+ if (proto->unique_tuple(tuple, rptr,
+ HOOK2MANIP(hooknum),
+ conntrack)) {
+ /* Must be unique. */
+ IP_NF_ASSERT(!ip_nat_used_tuple(tuple,
+ conntrack));
+ ret = 1;
+ goto clear_fulls;
+ }
+ DEBUGP("Protocol can't get unique tuple.\n");
+ }
+
+ /* Eliminate that from range, and try again. */
+ rptr->flags |= IP_NAT_RANGE_FULL;
+ *tuple = *orig_tuple;
+ }
+
+ ret = 0;
+
+ clear_fulls:
+ /* Clear full flags. */
+ IP_NF_ASSERT(mr->rangesize >= 1);
+ for (i = 0; i < mr->rangesize; i++)
+ mr->range[i].flags &= ~IP_NAT_RANGE_FULL;
+
+ return ret;
+}
+
+static inline int
+helper_cmp(const struct ip_nat_helper *helper,
+ u_int16_t protocol,
+ u_int16_t protocol_dst)
+{
+ return (protocol == helper->protocol
+ && protocol_dst == helper->protocol_dst);
+}
+
+/* Where to manip the reply packets (will be reverse manip). */
+static unsigned int opposite_hook[NF_IP_NUMHOOKS]
+= { [NF_IP_PRE_ROUTING] = NF_IP_POST_ROUTING,
+ [NF_IP_POST_ROUTING] = NF_IP_PRE_ROUTING,
+ [NF_IP_LOCAL_OUT] = NF_IP_PRE_ROUTING
+};
+
+unsigned int
+ip_nat_setup_info(struct ip_conntrack *conntrack,
+ const struct ip_nat_multi_range *mr,
+ unsigned int hooknum)
+{
+ struct ip_conntrack_tuple new_tuple, inv_tuple, reply;
+ struct ip_conntrack_tuple orig_tp;
+ struct ip_nat_info *info = &conntrack->nat.info;
+
+ MUST_BE_WRITE_LOCKED(&ip_nat_lock);
+ IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING
+ || hooknum == NF_IP_POST_ROUTING
+ || hooknum == NF_IP_LOCAL_OUT);
+ IP_NF_ASSERT(info->num_manips < IP_NAT_MAX_MANIPS);
+
+ /* What we've got will look like inverse of reply. Normally
+ this is what is in the conntrack, except for prior
+ manipulations (future optimization: if num_manips == 0,
+ orig_tp =
+ conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple) */
+ invert_tuplepr(&orig_tp,
+ &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple);
+
+#if 0
+ {
+ unsigned int i;
+
+ DEBUGP("Hook %u (%s), ", hooknum,
+ HOOK2MANIP(hooknum)==IP_NAT_MANIP_SRC ? "SRC" : "DST");
+ DUMP_TUPLE(&orig_tp);
+ DEBUGP("Range %p: ", mr);
+ for (i = 0; i < mr->rangesize; i++) {
+ DEBUGP("%u:%s%s%s %u.%u.%u.%u - %u.%u.%u.%u %u - %u\n",
+ i,
+ (mr->range[i].flags & IP_NAT_RANGE_MAP_IPS)
+ ? " MAP_IPS" : "",
+ (mr->range[i].flags
+ & IP_NAT_RANGE_PROTO_SPECIFIED)
+ ? " PROTO_SPECIFIED" : "",
+ (mr->range[i].flags & IP_NAT_RANGE_FULL)
+ ? " FULL" : "",
+ IP_PARTS(mr->range[i].min_ip),
+ IP_PARTS(mr->range[i].max_ip),
+ mr->range[i].min.all,
+ mr->range[i].max.all);
+ }
+ }
+#endif
+
+ do {
+ if (!get_unique_tuple(&new_tuple, &orig_tp, mr, conntrack,
+ hooknum)) {
+ DEBUGP("ip_nat_setup_info: Can't get unique for %p.\n",
+ conntrack);
+ return NF_DROP;
+ }
+
+#if 0
+ DEBUGP("Hook %u (%s) %p\n", hooknum,
+ HOOK2MANIP(hooknum)==IP_NAT_MANIP_SRC ? "SRC" : "DST",
+ conntrack);
+ DEBUGP("Original: ");
+ DUMP_TUPLE(&orig_tp);
+ DEBUGP("New: ");
+ DUMP_TUPLE(&new_tuple);
+#endif
+
+ /* We now have two tuples (SRCIP/SRCPT/DSTIP/DSTPT):
+ the original (A/B/C/D') and the mangled one (E/F/G/H').
+
+ We're only allowed to work with the SRC per-proto
+ part, so we create inverses of both to start, then
+ derive the other fields we need. */
+
+ /* Reply connection: simply invert the new tuple
+ (G/H/E/F') */
+ invert_tuplepr(&reply, &new_tuple);
+
+ /* Alter conntrack table so it recognizes replies.
+ If fail this race (reply tuple now used), repeat. */
+ } while (!ip_conntrack_alter_reply(conntrack, &reply));
+
+ /* FIXME: We can simply used existing conntrack reply tuple
+ here --RR */
+ /* Create inverse of original: C/D/A/B' */
+ invert_tuplepr(&inv_tuple, &orig_tp);
+
+ /* Has source changed?. */
+ if (memcmp(&new_tuple.src, &orig_tp.src, sizeof(new_tuple.src))
+ != 0) {
+ /* In this direction, a source manip. */
+ info->manips[info->num_manips++] =
+ ((struct ip_nat_info_manip)
+ { IP_CT_DIR_ORIGINAL, hooknum,
+ IP_NAT_MANIP_SRC, new_tuple.src });
+
+ IP_NF_ASSERT(info->num_manips < IP_NAT_MAX_MANIPS);
+
+ /* In the reverse direction, a destination manip. */
+ info->manips[info->num_manips++] =
+ ((struct ip_nat_info_manip)
+ { IP_CT_DIR_REPLY, opposite_hook[hooknum],
+ IP_NAT_MANIP_DST, orig_tp.src });
+ IP_NF_ASSERT(info->num_manips <= IP_NAT_MAX_MANIPS);
+ }
+
+ /* Has destination changed? */
+ if (memcmp(&new_tuple.dst, &orig_tp.dst, sizeof(new_tuple.dst))
+ != 0) {
+ /* In this direction, a destination manip */
+ info->manips[info->num_manips++] =
+ ((struct ip_nat_info_manip)
+ { IP_CT_DIR_ORIGINAL, hooknum,
+ IP_NAT_MANIP_DST, reply.src });
+
+ IP_NF_ASSERT(info->num_manips < IP_NAT_MAX_MANIPS);
+
+ /* In the reverse direction, a source manip. */
+ info->manips[info->num_manips++] =
+ ((struct ip_nat_info_manip)
+ { IP_CT_DIR_REPLY, opposite_hook[hooknum],
+ IP_NAT_MANIP_SRC, inv_tuple.src });
+ IP_NF_ASSERT(info->num_manips <= IP_NAT_MAX_MANIPS);
+ }
+
+ /* If there's a helper, assign it; based on new tuple. */
+ info->helper = LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *,
+ new_tuple.dst.protonum,
+ new_tuple.dst.u.all);
+
+ /* It's done. */
+ info->initialized |= (1 << HOOK2MANIP(hooknum));
+ return NF_ACCEPT;
+}
+
+void replace_in_hashes(struct ip_conntrack *conntrack,
+ struct ip_nat_info *info)
+{
+ /* Source has changed, so replace in hashes. */
+ unsigned int srchash
+ = hash_by_src(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
+ .tuple.src,
+ conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
+ .tuple.dst.protonum);
+ /* We place packet as seen OUTGOUNG in byips_proto hash
+ (ie. reverse dst and src of reply packet. */
+ unsigned int ipsprotohash
+ = hash_by_ipsproto(conntrack->tuplehash[IP_CT_DIR_REPLY]
+ .tuple.dst.ip,
+ conntrack->tuplehash[IP_CT_DIR_REPLY]
+ .tuple.src.ip,
+ conntrack->tuplehash[IP_CT_DIR_REPLY]
+ .tuple.dst.protonum);
+
+ IP_NF_ASSERT(info->bysource.conntrack == conntrack);
+ MUST_BE_WRITE_LOCKED(&ip_nat_lock);
+
+ list_del(&info->bysource.list);
+ list_del(&info->byipsproto.list);
+
+ list_prepend(&bysource[srchash], &info->bysource);
+ list_prepend(&byipsproto[ipsprotohash], &info->byipsproto);
+}
+
+void place_in_hashes(struct ip_conntrack *conntrack,
+ struct ip_nat_info *info)
+{
+ unsigned int srchash
+ = hash_by_src(&conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
+ .tuple.src,
+ conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
+ .tuple.dst.protonum);
+ /* We place packet as seen OUTGOUNG in byips_proto hash
+ (ie. reverse dst and src of reply packet. */
+ unsigned int ipsprotohash
+ = hash_by_ipsproto(conntrack->tuplehash[IP_CT_DIR_REPLY]
+ .tuple.dst.ip,
+ conntrack->tuplehash[IP_CT_DIR_REPLY]
+ .tuple.src.ip,
+ conntrack->tuplehash[IP_CT_DIR_REPLY]
+ .tuple.dst.protonum);
+
+ IP_NF_ASSERT(!info->bysource.conntrack);
+
+ MUST_BE_WRITE_LOCKED(&ip_nat_lock);
+ info->byipsproto.conntrack = conntrack;
+ info->bysource.conntrack = conntrack;
+
+ list_prepend(&bysource[srchash], &info->bysource);
+ list_prepend(&byipsproto[ipsprotohash], &info->byipsproto);
+}
+
+static void
+manip_pkt(u_int16_t proto, struct iphdr *iph, size_t len,
+ const struct ip_conntrack_manip *manip,
+ enum ip_nat_manip_type maniptype)
+{
+ find_nat_proto(proto)->manip_pkt(iph, len, manip, maniptype);
+
+ if (maniptype == IP_NAT_MANIP_SRC) {
+ iph->check = ip_nat_cheat_check(~iph->saddr, manip->ip,
+ iph->check);
+ iph->saddr = manip->ip;
+ } else {
+ iph->check = ip_nat_cheat_check(~iph->daddr, manip->ip,
+ iph->check);
+ iph->daddr = manip->ip;
+ }
+#if 0
+ if (ip_fast_csum((u8 *)iph, iph->ihl) != 0)
+ DEBUGP("IP: checksum on packet bad.\n");
+
+ if (proto == IPPROTO_TCP) {
+ void *th = (u_int32_t *)iph + iph->ihl;
+ if (tcp_v4_check(th, len - 4*iph->ihl, iph->saddr, iph->daddr,
+ csum_partial((char *)th, len-4*iph->ihl, 0)))
+ DEBUGP("TCP: checksum on packet bad\n");
+ }
+#endif
+}
+
+/* Do packet manipulations according to binding. */
+unsigned int
+do_bindings(struct ip_conntrack *ct,
+ enum ip_conntrack_info ctinfo,
+ struct ip_nat_info *info,
+ unsigned int hooknum,
+ struct sk_buff **pskb)
+{
+ unsigned int i;
+ struct ip_nat_helper *helper;
+ enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
+
+ /* Need nat lock to protect against modification, but neither
+ conntrack (referenced) and helper (deleted with
+ synchronize_bh()) can vanish. */
+ READ_LOCK(&ip_nat_lock);
+ for (i = 0; i < info->num_manips; i++) {
+ if (info->manips[i].direction == dir
+ && info->manips[i].hooknum == hooknum) {
+ DEBUGP("Mangling %p: %s to %u.%u.%u.%u %u\n",
+ *pskb,
+ info->manips[i].maniptype == IP_NAT_MANIP_SRC
+ ? "SRC" : "DST",
+ IP_PARTS(info->manips[i].manip.ip),
+ htons(info->manips[i].manip.u.all));
+ manip_pkt((*pskb)->nh.iph->protocol,
+ (*pskb)->nh.iph,
+ (*pskb)->len,
+ &info->manips[i].manip,
+ info->manips[i].maniptype);
+ }
+ }
+ helper = info->helper;
+ READ_UNLOCK(&ip_nat_lock);
+
+ if (helper) {
+ /* Always defragged for helpers */
+ IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off
+ & __constant_htons(IP_MF|IP_OFFSET)));
+ return helper->help(ct, info, ctinfo, hooknum, pskb);
+ } else return NF_ACCEPT;
+}
+
+void
+icmp_reply_translation(struct sk_buff *skb,
+ struct ip_conntrack *conntrack,
+ unsigned int hooknum,
+ int dir)
+{
+ struct iphdr *iph = skb->nh.iph;
+ struct icmphdr *hdr = (struct icmphdr *)((u_int32_t *)iph + iph->ihl);
+ struct iphdr *inner = (struct iphdr *)(hdr + 1);
+ size_t datalen = skb->len - ((void *)inner - (void *)iph);
+ unsigned int i;
+ struct ip_nat_info *info = &conntrack->nat.info;
+
+ IP_NF_ASSERT(skb->len >= iph->ihl*4 + sizeof(struct icmphdr));
+
+ DEBUGP("icmp_reply_translation: translating error %p hook %u dir %s\n",
+ skb, hooknum, dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY");
+ /* Note: May not be from a NAT'd host, but probably safest to
+ do translation always as if it came from the host itself
+ (even though a "host unreachable" coming from the host
+ itself is a bit wierd).
+
+ More explanation: some people use NAT for anonomizing.
+ Also, CERT recommends dropping all packets from private IP
+ addresses (although ICMP errors from internal links with
+ such addresses are not too uncommon, as Alan Cox points
+ out) */
+
+ READ_LOCK(&ip_nat_lock);
+ for (i = 0; i < info->num_manips; i++) {
+ DEBUGP("icmp_reply: manip %u dir %s hook %u\n",
+ i, info->manips[i].direction == IP_CT_DIR_ORIGINAL ?
+ "ORIG" : "REPLY", info->manips[i].hooknum);
+ /* Mapping the inner packet is just like a normal
+ packet in the other direction, except it was never
+ src/dst reversed, so where we would normally apply
+ a dst manip, we reply a src, and vice versa. */
+ if (info->manips[i].direction != dir
+ && info->manips[i].hooknum == opposite_hook[hooknum]) {
+ DEBUGP("icmp_reply: inner %s -> %u.%u.%u.%u %u\n",
+ info->manips[i].maniptype == IP_NAT_MANIP_SRC
+ ? "DST" : "SRC",
+ IP_PARTS(info->manips[i].manip.ip),
+ ntohs(info->manips[i].manip.u.udp.port));
+ manip_pkt(inner->protocol, inner,
+ skb->len - ((void *)inner - (void *)iph),
+ &info->manips[i].manip,
+ !info->manips[i].maniptype);
+ }
+ /* Outer packet needs to have IP header NATed like
+ it's a reply. */
+ else if (info->manips[i].direction != dir
+ && info->manips[i].hooknum == hooknum) {
+ /* Use mapping to map outer packet: 0 give no
+ per-proto mapping */
+ DEBUGP("icmp_reply: outer %s %u.%u.%u.%u\n",
+ info->manips[i].maniptype == IP_NAT_MANIP_SRC
+ ? "SRC" : "DST",
+ IP_PARTS(info->manips[i].manip.ip));
+ manip_pkt(0, iph, skb->len,
+ &info->manips[i].manip,
+ info->manips[i].maniptype);
+ }
+ }
+ READ_UNLOCK(&ip_nat_lock);
+
+ /* Since we mangled inside ICMP packet, recalculate its
+ checksum from scratch. (Hence the handling of incorrect
+ checksums in conntrack, so we don't accidentally fix one.) */
+ hdr->checksum = 0;
+ hdr->checksum = ip_compute_csum((unsigned char *)hdr,
+ sizeof(*hdr) + datalen);
+}
+
+int ip_nat_helper_register(struct ip_nat_helper *me)
+{
+ int ret = 0;
+
+ WRITE_LOCK(&ip_nat_lock);
+ if (LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *,
+ me->protocol, me->protocol_dst))
+ ret = -EBUSY;
+ else {
+ list_prepend(&helpers, me);
+ MOD_INC_USE_COUNT;
+ }
+ WRITE_UNLOCK(&ip_nat_lock);
+
+ return ret;
+}
+
+static int
+kill_helper(const struct ip_conntrack *i, void *helper)
+{
+ int ret;
+
+ READ_LOCK(&ip_nat_lock);
+ ret = (i->nat.info.helper == helper);
+ READ_UNLOCK(&ip_nat_lock);
+
+ return ret;
+}
+
+void ip_nat_helper_unregister(struct ip_nat_helper *me)
+{
+ WRITE_LOCK(&ip_nat_lock);
+ LIST_DELETE(&helpers, me);
+ WRITE_UNLOCK(&ip_nat_lock);
+
+ /* Someone could be still looking at the helper in a bh. */
+ br_write_lock_bh(BR_NETPROTO_LOCK);
+ br_write_unlock_bh(BR_NETPROTO_LOCK);
+
+ /* Find anything using it, and umm, kill them. We can't turn
+ them into normal connections: if we've adjusted SYNs, then
+ they'll ackstorm. So we just drop it. We used to just
+ bump module count when a connection existed, but that
+ forces admins to gen fake RSTs or bounce box, either of
+ which is just a long-winded way of making things
+ worse. --RR */
+ ip_ct_selective_cleanup(kill_helper, me);
+
+ MOD_DEC_USE_COUNT;
+}
+
+int __init ip_nat_init(void)
+{
+ size_t i;
+
+ /* Sew in builtin protocols. */
+ WRITE_LOCK(&ip_nat_lock);
+ list_append(&protos, &ip_nat_protocol_tcp);
+ list_append(&protos, &ip_nat_protocol_udp);
+ list_append(&protos, &ip_nat_protocol_icmp);
+ WRITE_UNLOCK(&ip_nat_lock);
+
+ for (i = 0; i < IP_NAT_HTABLE_SIZE; i++) {
+ INIT_LIST_HEAD(&bysource[i]);
+ INIT_LIST_HEAD(&byipsproto[i]);
+ }
+
+ /* FIXME: Man, this is a hack. <SIGH> */
+ IP_NF_ASSERT(ip_conntrack_destroyed == NULL);
+ ip_conntrack_destroyed = &ip_nat_cleanup_conntrack;
+
+ return 0;
+}
+
+void ip_nat_cleanup(void)
+{
+ ip_conntrack_destroyed = NULL;
+}
diff --git a/net/ipv4/netfilter/ip_nat_ftp.c b/net/ipv4/netfilter/ip_nat_ftp.c
new file mode 100644
index 000000000..8252e6d9b
--- /dev/null
+++ b/net/ipv4/netfilter/ip_nat_ftp.c
@@ -0,0 +1,403 @@
+/* FTP extension for TCP NAT alteration. */
+#include <linux/module.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <net/tcp.h>
+#include <linux/netfilter_ipv4/ip_nat.h>
+#include <linux/netfilter_ipv4/ip_nat_helper.h>
+#include <linux/netfilter_ipv4/ip_nat_rule.h>
+#include <linux/netfilter_ipv4/ip_nat_ftp.h>
+#include <linux/netfilter_ipv4/ip_conntrack_ftp.h>
+#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
+
+EXPORT_NO_SYMBOLS;
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+/* FIXME: Time out? --RR */
+
+static int
+ftp_nat_expected(struct sk_buff **pskb,
+ unsigned int hooknum,
+ struct ip_conntrack *ct,
+ struct ip_nat_info *info,
+ struct ip_conntrack *master,
+ struct ip_nat_info *masterinfo,
+ unsigned int *verdict)
+{
+ struct ip_nat_multi_range mr;
+ u_int32_t newdstip, newsrcip, newip;
+ struct ip_ct_ftp *ftpinfo;
+
+ IP_NF_ASSERT(info);
+ IP_NF_ASSERT(master);
+ IP_NF_ASSERT(masterinfo);
+
+ IP_NF_ASSERT(!(info->initialized & (1<<HOOK2MANIP(hooknum))));
+
+ DEBUGP("nat_expected: We have a connection!\n");
+ /* Master must be an ftp connection */
+ ftpinfo = &master->help.ct_ftp_info;
+
+ LOCK_BH(&ip_ftp_lock);
+ if (!ftpinfo->is_ftp) {
+ UNLOCK_BH(&ip_ftp_lock);
+ DEBUGP("nat_expected: master not ftp\n");
+ return 0;
+ }
+
+ if (ftpinfo->ftptype == IP_CT_FTP_PORT) {
+ /* PORT command: make connection go to the client. */
+ newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
+ newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
+ DEBUGP("nat_expected: PORT cmd. %u.%u.%u.%u->%u.%u.%u.%u\n",
+ IP_PARTS(newsrcip), IP_PARTS(newdstip));
+ } else {
+ /* PASV command: make the connection go to the server */
+ newdstip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
+ newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
+ DEBUGP("nat_expected: PASV cmd. %u.%u.%u.%u->%u.%u.%u.%u\n",
+ IP_PARTS(newsrcip), IP_PARTS(newdstip));
+ }
+ UNLOCK_BH(&ip_ftp_lock);
+
+ if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)
+ newip = newsrcip;
+ else
+ newip = newdstip;
+
+ DEBUGP("nat_expected: IP to %u.%u.%u.%u\n", IP_PARTS(newip));
+
+ mr.rangesize = 1;
+ /* We don't want to manip the per-protocol, just the IPs. */
+ mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
+ mr.range[0].min_ip = mr.range[0].max_ip = newip;
+
+ *verdict = ip_nat_setup_info(ct, &mr, hooknum);
+
+ return 1;
+}
+
+/* This is interesting. We simply use the port given us by the client
+ or server. In practice it's extremely unlikely to clash; if it
+ does, the rule won't be able to get a unique tuple and will drop
+ the packets. */
+static int
+mangle_packet(struct sk_buff **pskb,
+ u_int32_t newip,
+ u_int16_t port,
+ unsigned int matchoff,
+ unsigned int matchlen,
+ struct ip_nat_ftp_info *this_way,
+ struct ip_nat_ftp_info *other_way)
+{
+ struct iphdr *iph = (*pskb)->nh.iph;
+ struct tcphdr *tcph;
+ unsigned char *data;
+ unsigned int tcplen, newlen, newtcplen;
+ char buffer[sizeof("nnn,nnn,nnn,nnn,nnn,nnn")];
+
+ MUST_BE_LOCKED(&ip_ftp_lock);
+ sprintf(buffer, "%u,%u,%u,%u,%u,%u",
+ IP_PARTS(newip), port>>8, port&0xFF);
+
+ tcplen = (*pskb)->len - iph->ihl * 4;
+ newtcplen = tcplen - matchlen + strlen(buffer);
+ newlen = iph->ihl*4 + newtcplen;
+
+ /* So there I am, in the middle of my `netfilter-is-wonderful'
+ talk in Sydney, and someone asks `What happens if you try
+ to enlarge a 64k packet here?'. I think I said something
+ eloquent like `fuck'. */
+ if (newlen > 65535) {
+ if (net_ratelimit())
+ printk("nat_ftp cheat: %u.%u.%u.%u->%u.%u.%u.%u %u\n",
+ NIPQUAD((*pskb)->nh.iph->saddr),
+ NIPQUAD((*pskb)->nh.iph->daddr),
+ (*pskb)->nh.iph->protocol);
+ return NF_DROP;
+ }
+
+ if (newlen > (*pskb)->len + skb_tailroom(*pskb)) {
+ struct sk_buff *newskb;
+ newskb = skb_copy_expand(*pskb, skb_headroom(*pskb), newlen,
+ GFP_ATOMIC);
+ if (!newskb) {
+ DEBUGP("ftp: oom\n");
+ return 0;
+ } else {
+ kfree_skb(*pskb);
+ *pskb = newskb;
+ iph = (*pskb)->nh.iph;
+ }
+ }
+
+ tcph = (void *)iph + iph->ihl*4;
+ data = (void *)tcph + tcph->doff*4;
+
+ DEBUGP("Mapping `%.*s' [%u %u %u] to new `%s' [%u]\n",
+ (int)matchlen, data+matchoff,
+ data[matchoff], data[matchoff+1],
+ matchlen, buffer, strlen(buffer));
+
+ /* SYN adjust. If it's uninitialized, or this is after last
+ correction, record it: we don't handle more than one
+ adjustment in the window, but do deal with common case of a
+ retransmit. */
+ if (this_way->syn_offset_before == this_way->syn_offset_after
+ || before(this_way->syn_correction_pos, ntohl(tcph->seq))) {
+ this_way->syn_correction_pos = ntohl(tcph->seq);
+ this_way->syn_offset_before = this_way->syn_offset_after;
+ this_way->syn_offset_after = (int32_t)
+ this_way->syn_offset_before + newlen - (*pskb)->len;
+ }
+
+ /* Move post-replacement */
+ memmove(data + matchoff + strlen(buffer),
+ data + matchoff + matchlen,
+ (*pskb)->tail - (data + matchoff + matchlen));
+ memcpy(data + matchoff, buffer, strlen(buffer));
+
+ /* Resize packet. */
+ if (newlen > (*pskb)->len) {
+ DEBUGP("ip_nat_ftp: Extending packet by %u to %u bytes\n",
+ newlen - (*pskb)->len, newlen);
+ skb_put(*pskb, newlen - (*pskb)->len);
+ } else {
+ DEBUGP("ip_nat_ftp: Shrinking packet from %u to %u bytes\n",
+ (*pskb)->len, newlen);
+ skb_trim(*pskb, newlen);
+ }
+
+ /* Fix checksums */
+ iph->tot_len = htons(newlen);
+ (*pskb)->csum = csum_partial((char *)tcph + tcph->doff*4,
+ newtcplen - tcph->doff*4, 0);
+ tcph->check = 0;
+ tcph->check = tcp_v4_check(tcph, newtcplen, iph->saddr, iph->daddr,
+ csum_partial((char *)tcph, tcph->doff*4,
+ (*pskb)->csum));
+ ip_send_check(iph);
+ return 1;
+}
+
+/* Grrr... SACK. Fuck me even harder. Don't want to fix it on the
+ fly, so blow it away. */
+static void
+delete_sack(struct sk_buff *skb, struct tcphdr *tcph)
+{
+ unsigned int i;
+ u_int8_t *opt = (u_int8_t *)tcph;
+
+ DEBUGP("Seeking SACKPERM in SYN packet (doff = %u).\n",
+ tcph->doff * 4);
+ for (i = sizeof(struct tcphdr); i < tcph->doff * 4;) {
+ DEBUGP("%u ", opt[i]);
+ switch (opt[i]) {
+ case TCPOPT_NOP:
+ case TCPOPT_EOL:
+ i++;
+ break;
+
+ case TCPOPT_SACK_PERM:
+ goto found_opt;
+
+ default:
+ /* Worst that can happen: it will take us over. */
+ i += opt[i+1] ?: 1;
+ }
+ }
+ DEBUGP("\n");
+ return;
+
+ found_opt:
+ DEBUGP("\n");
+ DEBUGP("Found SACKPERM at offset %u.\n", i);
+
+ /* Must be within TCP header, and valid SACK perm. */
+ if (i + opt[i+1] <= tcph->doff*4 && opt[i+1] == 2) {
+ /* Replace with NOPs. */
+ tcph->check
+ = ip_nat_cheat_check(*((u_int16_t *)(opt + i))^0xFFFF,
+ 0, tcph->check);
+ opt[i] = opt[i+1] = 0;
+ }
+ else DEBUGP("Something wrong with SACK_PERM.\n");
+}
+
+static int ftp_data_fixup(const struct ip_ct_ftp *ct_ftp_info,
+ struct ip_conntrack *ct,
+ struct ip_nat_ftp_info *ftp,
+ unsigned int datalen,
+ struct sk_buff **pskb)
+{
+ u_int32_t newip;
+ struct ip_conntrack_tuple t;
+ struct iphdr *iph = (*pskb)->nh.iph;
+ struct tcphdr *tcph = (void *)iph + iph->ihl*4;
+
+ MUST_BE_LOCKED(&ip_ftp_lock);
+ DEBUGP("FTP_NAT: seq %u + %u in %u + %u\n",
+ ct_ftp_info->seq, ct_ftp_info->len,
+ ntohl(tcph->seq), datalen);
+
+ /* Change address inside packet to match way we're mapping
+ this connection. */
+ if (ct_ftp_info->ftptype == IP_CT_FTP_PASV) {
+ /* PASV response: must be where client thinks server
+ is */
+ newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
+ } else {
+ /* PORT command: must be where server thinks client is */
+ newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
+ }
+
+ if (!mangle_packet(pskb, newip, ct_ftp_info->port,
+ ct_ftp_info->seq - ntohl(tcph->seq),
+ ct_ftp_info->len,
+ &ftp[ct_ftp_info->ftptype],
+ &ftp[!ct_ftp_info->ftptype]))
+ return 0;
+
+ /* Alter conntrack's expectations. */
+
+ /* We can read expect here without conntrack lock, since it's
+ only set in ip_conntrack_ftp, with ip_ftp_lock held
+ writable */
+ t = ct->expected.tuple;
+ t.dst.ip = newip;
+ ip_conntrack_expect_related(ct, &t);
+
+ return 1;
+}
+
+static unsigned int help(struct ip_conntrack *ct,
+ struct ip_nat_info *info,
+ enum ip_conntrack_info ctinfo,
+ unsigned int hooknum,
+ struct sk_buff **pskb)
+{
+ struct iphdr *iph = (*pskb)->nh.iph;
+ struct tcphdr *tcph = (void *)iph + iph->ihl*4;
+ u_int32_t newseq, newack;
+ unsigned int datalen;
+ int dir;
+ int score;
+ struct ip_ct_ftp *ct_ftp_info
+ = &ct->help.ct_ftp_info;
+ struct ip_nat_ftp_info *ftp
+ = &ct->nat.help.ftp_info[0];
+
+ /* Delete SACK_OK on initial TCP SYNs. */
+ if (tcph->syn && !tcph->ack)
+ delete_sack(*pskb, tcph);
+
+ /* Only mangle things once: original direction in POST_ROUTING
+ and reply direction on PRE_ROUTING. */
+ dir = CTINFO2DIR(ctinfo);
+ if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL)
+ || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) {
+ DEBUGP("nat_ftp: Not touching dir %s at hook %s\n",
+ dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
+ hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
+ : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
+ : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???");
+ return NF_ACCEPT;
+ }
+
+ datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4;
+ score = 0;
+ LOCK_BH(&ip_ftp_lock);
+ if (ct_ftp_info->len) {
+ /* If it's in the right range... */
+ score += between(ct_ftp_info->seq, ntohl(tcph->seq),
+ ntohl(tcph->seq) + datalen);
+ score += between(ct_ftp_info->seq + ct_ftp_info->len,
+ ntohl(tcph->seq),
+ ntohl(tcph->seq) + datalen);
+ if (score == 1) {
+ /* Half a match? This means a partial retransmisison.
+ It's a cracker being funky. */
+ if (net_ratelimit()) {
+ printk("FTP_NAT: partial packet %u/%u in %u/%u\n",
+ ct_ftp_info->seq, ct_ftp_info->len,
+ ntohl(tcph->seq),
+ ntohl(tcph->seq) + datalen);
+ }
+ UNLOCK_BH(&ip_ftp_lock);
+ return NF_DROP;
+ } else if (score == 2) {
+ if (!ftp_data_fixup(ct_ftp_info, ct, ftp, datalen,
+ pskb)) {
+ UNLOCK_BH(&ip_ftp_lock);
+ return NF_DROP;
+ }
+
+ /* skb may have been reallocated */
+ iph = (*pskb)->nh.iph;
+ tcph = (void *)iph + iph->ihl*4;
+ }
+ }
+
+ /* Sequence adjust */
+ if (after(ntohl(tcph->seq), ftp[dir].syn_correction_pos))
+ newseq = ntohl(tcph->seq) + ftp[dir].syn_offset_after;
+ else
+ newseq = ntohl(tcph->seq) + ftp[dir].syn_offset_before;
+ newseq = htonl(newseq);
+
+ /* Ack adjust */
+ if (after(ntohl(tcph->ack_seq), ftp[!dir].syn_correction_pos))
+ newack = ntohl(tcph->ack_seq) - ftp[!dir].syn_offset_after;
+ else
+ newack = ntohl(tcph->ack_seq) - ftp[!dir].syn_offset_before;
+ newack = htonl(newack);
+ UNLOCK_BH(&ip_ftp_lock);
+
+ tcph->check = ip_nat_cheat_check(~tcph->seq, newseq,
+ ip_nat_cheat_check(~tcph->ack_seq,
+ newack,
+ tcph->check));
+ tcph->seq = newseq;
+ tcph->ack_seq = newack;
+
+ return NF_ACCEPT;
+}
+
+static struct ip_nat_helper ftp
+= { { NULL, NULL }, IPPROTO_TCP, __constant_htons(21), help, "ftp" };
+static struct ip_nat_expect ftp_expect
+= { { NULL, NULL }, ftp_nat_expected };
+
+extern struct module *ip_conntrack_ftp;
+
+static int __init init(void)
+{
+ int ret;
+
+ ret = ip_nat_expect_register(&ftp_expect);
+ if (ret == 0) {
+ ret = ip_nat_helper_register(&ftp);
+
+ if (ret == 0)
+ __MOD_INC_USE_COUNT(ip_conntrack_ftp);
+ else
+ ip_nat_expect_unregister(&ftp_expect);
+ }
+ return ret;
+}
+
+static void __exit fini(void)
+{
+ __MOD_DEC_USE_COUNT(ip_conntrack_ftp);
+ ip_nat_helper_unregister(&ftp);
+ ip_nat_expect_unregister(&ftp_expect);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/netfilter/ip_nat_proto_icmp.c b/net/ipv4/netfilter/ip_nat_proto_icmp.c
new file mode 100644
index 000000000..9bc7427ce
--- /dev/null
+++ b/net/ipv4/netfilter/ip_nat_proto_icmp.c
@@ -0,0 +1,97 @@
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/netfilter.h>
+#include <linux/ip.h>
+#include <linux/icmp.h>
+#include <linux/if.h>
+
+#include <linux/netfilter_ipv4/ip_nat.h>
+#include <linux/netfilter_ipv4/ip_nat_rule.h>
+#include <linux/netfilter_ipv4/ip_nat_protocol.h>
+
+static int
+icmp_in_range(const struct ip_conntrack_tuple *tuple,
+ enum ip_nat_manip_type maniptype,
+ const union ip_conntrack_manip_proto *min,
+ const union ip_conntrack_manip_proto *max)
+{
+ return (tuple->src.u.icmp.id >= min->icmp.id
+ && tuple->src.u.icmp.id <= max->icmp.id);
+}
+
+static int
+icmp_unique_tuple(struct ip_conntrack_tuple *tuple,
+ const struct ip_nat_range *range,
+ enum ip_nat_manip_type maniptype,
+ const struct ip_conntrack *conntrack)
+{
+ static u_int16_t id = 0;
+ unsigned int range_size
+ = (unsigned int)range->max.icmp.id - range->min.icmp.id + 1;
+ unsigned int i;
+
+ /* If no range specified... */
+ if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED))
+ range_size = 0xFFFF;
+
+ for (i = 0; i < range_size; i++, id++) {
+ tuple->src.u.icmp.id = range->min.icmp.id + (id % range_size);
+ if (!ip_nat_used_tuple(tuple, conntrack))
+ return 1;
+ }
+ return 0;
+}
+
+static void
+icmp_manip_pkt(struct iphdr *iph, size_t len,
+ const struct ip_conntrack_manip *manip,
+ enum ip_nat_manip_type maniptype)
+{
+ struct icmphdr *hdr = (struct icmphdr *)((u_int32_t *)iph + iph->ihl);
+
+ hdr->checksum = ip_nat_cheat_check(hdr->un.echo.id ^ 0xFFFF,
+ manip->u.icmp.id,
+ hdr->checksum);
+ hdr->un.echo.id = manip->u.icmp.id;
+}
+
+static unsigned int
+icmp_print(char *buffer,
+ const struct ip_conntrack_tuple *match,
+ const struct ip_conntrack_tuple *mask)
+{
+ unsigned int len = 0;
+
+ if (mask->src.u.icmp.id)
+ len += sprintf(buffer + len, "id=%u ",
+ ntohs(match->src.u.icmp.id));
+
+ if (mask->dst.u.icmp.type)
+ len += sprintf(buffer + len, "type=%u ",
+ ntohs(match->dst.u.icmp.type));
+
+ if (mask->dst.u.icmp.code)
+ len += sprintf(buffer + len, "code=%u ",
+ ntohs(match->dst.u.icmp.code));
+
+ return len;
+}
+
+static unsigned int
+icmp_print_range(char *buffer, const struct ip_nat_range *range)
+{
+ if (range->min.icmp.id != 0 || range->max.icmp.id != 0xFFFF)
+ return sprintf(buffer, "id %u-%u ",
+ ntohs(range->min.icmp.id),
+ ntohs(range->max.icmp.id));
+ else return 0;
+}
+
+struct ip_nat_protocol ip_nat_protocol_icmp
+= { { NULL, NULL }, "ICMP", IPPROTO_ICMP,
+ icmp_manip_pkt,
+ icmp_in_range,
+ icmp_unique_tuple,
+ icmp_print,
+ icmp_print_range
+};
diff --git a/net/ipv4/netfilter/ip_nat_proto_tcp.c b/net/ipv4/netfilter/ip_nat_proto_tcp.c
new file mode 100644
index 000000000..7ff6ccb50
--- /dev/null
+++ b/net/ipv4/netfilter/ip_nat_proto_tcp.c
@@ -0,0 +1,143 @@
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/netfilter.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/if.h>
+
+#include <linux/netfilter_ipv4/ip_nat.h>
+#include <linux/netfilter_ipv4/ip_nat_rule.h>
+#include <linux/netfilter_ipv4/ip_nat_protocol.h>
+
+static int
+tcp_in_range(const struct ip_conntrack_tuple *tuple,
+ enum ip_nat_manip_type maniptype,
+ const union ip_conntrack_manip_proto *min,
+ const union ip_conntrack_manip_proto *max)
+{
+ u_int16_t port;
+
+ if (maniptype == IP_NAT_MANIP_SRC)
+ port = tuple->src.u.tcp.port;
+ else
+ port = tuple->dst.u.tcp.port;
+
+ return ntohs(port) >= ntohs(min->tcp.port)
+ && ntohs(port) <= ntohs(max->tcp.port);
+}
+
+static int
+tcp_unique_tuple(struct ip_conntrack_tuple *tuple,
+ const struct ip_nat_range *range,
+ enum ip_nat_manip_type maniptype,
+ const struct ip_conntrack *conntrack)
+{
+ static u_int16_t port = 0, *portptr;
+ unsigned int range_size, min, i;
+
+ if (maniptype == IP_NAT_MANIP_SRC)
+ portptr = &tuple->src.u.tcp.port;
+ else
+ portptr = &tuple->dst.u.tcp.port;
+
+ /* If no range specified... */
+ if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) {
+ /* If it's dst rewrite, can't change port */
+ if (maniptype == IP_NAT_MANIP_DST)
+ return 0;
+
+ /* Map privileged onto privileged. */
+ if (ntohs(*portptr) < 1024) {
+ /* Loose convention: >> 512 is credential passing */
+ if (ntohs(*portptr)<512) {
+ min = 1;
+ range_size = 511 - min + 1;
+ } else {
+ min = 600;
+ range_size = 1023 - min + 1;
+ }
+ } else {
+ min = 1024;
+ range_size = 65535 - 1024 + 1;
+ }
+ } else {
+ min = ntohs(range->min.tcp.port);
+ range_size = ntohs(range->max.tcp.port) - min + 1;
+ }
+
+ for (i = 0; i < range_size; i++, port++) {
+ *portptr = htons(min + port % range_size);
+ if (!ip_nat_used_tuple(tuple, conntrack)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void
+tcp_manip_pkt(struct iphdr *iph, size_t len,
+ const struct ip_conntrack_manip *manip,
+ enum ip_nat_manip_type maniptype)
+{
+ struct tcphdr *hdr = (struct tcphdr *)((u_int32_t *)iph + iph->ihl);
+ u_int32_t oldip;
+ u_int16_t *portptr;
+
+ if (maniptype == IP_NAT_MANIP_SRC) {
+ /* Get rid of src ip and src pt */
+ oldip = iph->saddr;
+ portptr = &hdr->source;
+ } else {
+ /* Get rid of dst ip and dst pt */
+ oldip = iph->daddr;
+ portptr = &hdr->dest;
+ }
+ hdr->check = ip_nat_cheat_check(~oldip, manip->ip,
+ ip_nat_cheat_check(*portptr ^ 0xFFFF,
+ manip->u.tcp.port,
+ hdr->check));
+ *portptr = manip->u.tcp.port;
+}
+
+static unsigned int
+tcp_print(char *buffer,
+ const struct ip_conntrack_tuple *match,
+ const struct ip_conntrack_tuple *mask)
+{
+ unsigned int len = 0;
+
+ if (mask->src.u.tcp.port)
+ len += sprintf(buffer + len, "srcpt=%u ",
+ ntohs(match->src.u.tcp.port));
+
+
+ if (mask->dst.u.tcp.port)
+ len += sprintf(buffer + len, "dstpt=%u ",
+ ntohs(match->dst.u.tcp.port));
+
+ return len;
+}
+
+static unsigned int
+tcp_print_range(char *buffer, const struct ip_nat_range *range)
+{
+ if (range->min.tcp.port != 0 || range->max.tcp.port != 0xFFFF) {
+ if (range->min.tcp.port == range->max.tcp.port)
+ return sprintf(buffer, "port %u ",
+ ntohs(range->min.tcp.port));
+ else
+ return sprintf(buffer, "ports %u-%u ",
+ ntohs(range->min.tcp.port),
+ ntohs(range->max.tcp.port));
+ }
+ else return 0;
+}
+
+struct ip_nat_protocol ip_nat_protocol_tcp
+= { { NULL, NULL }, "TCP", IPPROTO_TCP,
+ tcp_manip_pkt,
+ tcp_in_range,
+ tcp_unique_tuple,
+ tcp_print,
+ tcp_print_range
+};
diff --git a/net/ipv4/netfilter/ip_nat_proto_udp.c b/net/ipv4/netfilter/ip_nat_proto_udp.c
new file mode 100644
index 000000000..e0dc25910
--- /dev/null
+++ b/net/ipv4/netfilter/ip_nat_proto_udp.c
@@ -0,0 +1,141 @@
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/netfilter.h>
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <linux/if.h>
+
+#include <linux/netfilter_ipv4/ip_nat.h>
+#include <linux/netfilter_ipv4/ip_nat_rule.h>
+#include <linux/netfilter_ipv4/ip_nat_protocol.h>
+
+static int
+udp_in_range(const struct ip_conntrack_tuple *tuple,
+ enum ip_nat_manip_type maniptype,
+ const union ip_conntrack_manip_proto *min,
+ const union ip_conntrack_manip_proto *max)
+{
+ u_int16_t port;
+
+ if (maniptype == IP_NAT_MANIP_SRC)
+ port = tuple->src.u.udp.port;
+ else
+ port = tuple->dst.u.udp.port;
+
+ return ntohs(port) >= ntohs(min->udp.port)
+ && ntohs(port) <= ntohs(max->udp.port);
+}
+
+static int
+udp_unique_tuple(struct ip_conntrack_tuple *tuple,
+ const struct ip_nat_range *range,
+ enum ip_nat_manip_type maniptype,
+ const struct ip_conntrack *conntrack)
+{
+ static u_int16_t port = 0, *portptr;
+ unsigned int range_size, min, i;
+
+ if (maniptype == IP_NAT_MANIP_SRC)
+ portptr = &tuple->src.u.udp.port;
+ else
+ portptr = &tuple->dst.u.udp.port;
+
+ /* If no range specified... */
+ if (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)) {
+ /* If it's dst rewrite, can't change port */
+ if (maniptype == IP_NAT_MANIP_DST)
+ return 0;
+
+ if (ntohs(*portptr) < 1024) {
+ /* Loose convention: >> 512 is credential passing */
+ if (ntohs(*portptr)<512) {
+ min = 1;
+ range_size = 511 - min + 1;
+ } else {
+ min = 600;
+ range_size = 1023 - min + 1;
+ }
+ } else {
+ min = 1024;
+ range_size = 65535 - 1024 + 1;
+ }
+ } else {
+ min = ntohs(range->min.udp.port);
+ range_size = ntohs(range->max.udp.port) - min + 1;
+ }
+
+ for (i = 0; i < range_size; i++, port++) {
+ *portptr = htons(min + port % range_size);
+ if (!ip_nat_used_tuple(tuple, conntrack))
+ return 1;
+ }
+ return 0;
+}
+
+static void
+udp_manip_pkt(struct iphdr *iph, size_t len,
+ const struct ip_conntrack_manip *manip,
+ enum ip_nat_manip_type maniptype)
+{
+ struct udphdr *hdr = (struct udphdr *)((u_int32_t *)iph + iph->ihl);
+ u_int32_t oldip;
+ u_int16_t *portptr;
+
+ if (maniptype == IP_NAT_MANIP_SRC) {
+ /* Get rid of src ip and src pt */
+ oldip = iph->saddr;
+ portptr = &hdr->source;
+ } else {
+ /* Get rid of dst ip and dst pt */
+ oldip = iph->daddr;
+ portptr = &hdr->dest;
+ }
+ hdr->check = ip_nat_cheat_check(~oldip, manip->ip,
+ ip_nat_cheat_check(*portptr ^ 0xFFFF,
+ manip->u.udp.port,
+ hdr->check));
+ *portptr = manip->u.udp.port;
+}
+
+static unsigned int
+udp_print(char *buffer,
+ const struct ip_conntrack_tuple *match,
+ const struct ip_conntrack_tuple *mask)
+{
+ unsigned int len = 0;
+
+ if (mask->src.u.udp.port)
+ len += sprintf(buffer + len, "srcpt=%u ",
+ ntohs(match->src.u.udp.port));
+
+
+ if (mask->dst.u.udp.port)
+ len += sprintf(buffer + len, "dstpt=%u ",
+ ntohs(match->dst.u.udp.port));
+
+ return len;
+}
+
+static unsigned int
+udp_print_range(char *buffer, const struct ip_nat_range *range)
+{
+ if (range->min.udp.port != 0 || range->max.udp.port != 0xFFFF) {
+ if (range->min.udp.port == range->max.udp.port)
+ return sprintf(buffer, "port %u ",
+ ntohs(range->min.udp.port));
+ else
+ return sprintf(buffer, "ports %u-%u ",
+ ntohs(range->min.udp.port),
+ ntohs(range->max.udp.port));
+ }
+ else return 0;
+}
+
+struct ip_nat_protocol ip_nat_protocol_udp
+= { { NULL, NULL }, "UDP", IPPROTO_UDP,
+ udp_manip_pkt,
+ udp_in_range,
+ udp_unique_tuple,
+ udp_print,
+ udp_print_range
+};
diff --git a/net/ipv4/netfilter/ip_nat_proto_unknown.c b/net/ipv4/netfilter/ip_nat_proto_unknown.c
new file mode 100644
index 000000000..0e3907036
--- /dev/null
+++ b/net/ipv4/netfilter/ip_nat_proto_unknown.c
@@ -0,0 +1,61 @@
+/* The "unknown" protocol. This is what is used for protocols we
+ * don't understand. It's returned by find_proto().
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/netfilter.h>
+#include <linux/if.h>
+
+#include <linux/netfilter_ipv4/ip_nat.h>
+#include <linux/netfilter_ipv4/ip_nat_rule.h>
+#include <linux/netfilter_ipv4/ip_nat_protocol.h>
+
+static int unknown_in_range(const struct ip_conntrack_tuple *tuple,
+ enum ip_nat_manip_type manip_type,
+ const union ip_conntrack_manip_proto *min,
+ const union ip_conntrack_manip_proto *max)
+{
+ return 1;
+}
+
+static int unknown_unique_tuple(struct ip_conntrack_tuple *tuple,
+ const struct ip_nat_range *range,
+ enum ip_nat_manip_type maniptype,
+ const struct ip_conntrack *conntrack)
+{
+ /* Sorry: we can't help you; if it's not unique, we can't frob
+ anything. */
+ return 0;
+}
+
+static void
+unknown_manip_pkt(struct iphdr *iph, size_t len,
+ const struct ip_conntrack_manip *manip,
+ enum ip_nat_manip_type maniptype)
+{
+ return;
+}
+
+static unsigned int
+unknown_print(char *buffer,
+ const struct ip_conntrack_tuple *match,
+ const struct ip_conntrack_tuple *mask)
+{
+ return 0;
+}
+
+static unsigned int
+unknown_print_range(char *buffer, const struct ip_nat_range *range)
+{
+ return 0;
+}
+
+struct ip_nat_protocol unknown_nat_protocol = {
+ { NULL, NULL }, "unknown", 0,
+ unknown_manip_pkt,
+ unknown_in_range,
+ unknown_unique_tuple,
+ unknown_print,
+ unknown_print_range
+};
diff --git a/net/ipv4/netfilter/ip_nat_rule.c b/net/ipv4/netfilter/ip_nat_rule.c
new file mode 100644
index 000000000..74516687b
--- /dev/null
+++ b/net/ipv4/netfilter/ip_nat_rule.c
@@ -0,0 +1,327 @@
+/* Everything about the rules for NAT. */
+#define __NO_VERSION__
+#include <linux/types.h>
+#include <linux/ip.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <net/checksum.h>
+#include <linux/bitops.h>
+#include <linux/version.h>
+
+#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_nat_lock)
+#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_nat_lock)
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ip_nat.h>
+#include <linux/netfilter_ipv4/ip_nat_core.h>
+#include <linux/netfilter_ipv4/ip_nat_rule.h>
+#include <linux/netfilter_ipv4/listhelp.h>
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+#define NAT_VALID_HOOKS ((1<<NF_IP_PRE_ROUTING) | (1<<NF_IP_POST_ROUTING) | (1<<NF_IP_LOCAL_OUT))
+
+/* Standard entry. */
+struct ipt_standard
+{
+ struct ipt_entry entry;
+ struct ipt_standard_target target;
+};
+
+struct ipt_error_target
+{
+ struct ipt_entry_target target;
+ char errorname[IPT_FUNCTION_MAXNAMELEN];
+};
+
+struct ipt_error
+{
+ struct ipt_entry entry;
+ struct ipt_error_target target;
+};
+
+static struct
+{
+ struct ipt_replace repl;
+ struct ipt_standard entries[3];
+ struct ipt_error term;
+} nat_initial_table __initdata
+= { { "nat", NAT_VALID_HOOKS, 4,
+ sizeof(struct ipt_standard) * 3 + sizeof(struct ipt_error),
+ { [NF_IP_PRE_ROUTING] 0,
+ [NF_IP_POST_ROUTING] sizeof(struct ipt_standard),
+ [NF_IP_LOCAL_OUT] sizeof(struct ipt_standard) * 2 },
+ { [NF_IP_PRE_ROUTING] 0,
+ [NF_IP_POST_ROUTING] sizeof(struct ipt_standard),
+ [NF_IP_LOCAL_OUT] sizeof(struct ipt_standard) * 2 },
+ 0, NULL, { } },
+ {
+ /* PRE_ROUTING */
+ { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
+ 0,
+ sizeof(struct ipt_entry),
+ sizeof(struct ipt_standard),
+ 0, { 0, 0 }, { } },
+ { { sizeof(struct ipt_standard_target), { "" }, { } },
+ -NF_ACCEPT - 1 } },
+ /* POST_ROUTING */
+ { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
+ 0,
+ sizeof(struct ipt_entry),
+ sizeof(struct ipt_standard),
+ 0, { 0, 0 }, { } },
+ { { sizeof(struct ipt_standard_target), { "" }, { } },
+ -NF_ACCEPT - 1 } },
+ /* LOCAL_OUT */
+ { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
+ 0,
+ sizeof(struct ipt_entry),
+ sizeof(struct ipt_standard),
+ 0, { 0, 0 }, { } },
+ { { sizeof(struct ipt_standard_target), { "" }, { } },
+ -NF_ACCEPT - 1 } }
+ },
+ /* ERROR */
+ { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
+ 0,
+ sizeof(struct ipt_entry),
+ sizeof(struct ipt_error),
+ 0, { 0, 0 }, { } },
+ { { sizeof(struct ipt_error_target), { IPT_ERROR_TARGET },
+ { } },
+ "ERROR"
+ }
+ }
+};
+
+static struct ipt_table nat_table
+= { { NULL, NULL }, "nat", &nat_initial_table.repl,
+ NAT_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL };
+
+LIST_HEAD(nat_expect_list);
+
+/* Source NAT */
+static unsigned int ipt_snat_target(struct sk_buff **pskb,
+ unsigned int hooknum,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *targinfo,
+ void *userinfo)
+{
+ struct ip_conntrack *ct;
+ enum ip_conntrack_info ctinfo;
+
+ IP_NF_ASSERT(hooknum == NF_IP_POST_ROUTING);
+
+ ct = ip_conntrack_get(*pskb, &ctinfo);
+
+ /* Connection must be valid and new. */
+ IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));
+ IP_NF_ASSERT(out);
+
+ return ip_nat_setup_info(ct, targinfo, hooknum);
+}
+
+static unsigned int ipt_dnat_target(struct sk_buff **pskb,
+ unsigned int hooknum,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *targinfo,
+ void *userinfo)
+{
+ struct ip_conntrack *ct;
+ enum ip_conntrack_info ctinfo;
+
+ IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING
+ || hooknum == NF_IP_LOCAL_OUT);
+
+ ct = ip_conntrack_get(*pskb, &ctinfo);
+
+ /* Connection must be valid and new. */
+ IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));
+
+ return ip_nat_setup_info(ct, targinfo, hooknum);
+}
+
+static int ipt_snat_checkentry(const char *tablename,
+ const struct ipt_entry *e,
+ void *targinfo,
+ unsigned int targinfosize,
+ unsigned int hook_mask)
+{
+ struct ip_nat_multi_range *mr = targinfo;
+
+ /* Must be a valid range */
+ if (targinfosize < sizeof(struct ip_nat_multi_range)) {
+ DEBUGP("SNAT: Target size %u too small\n", targinfosize);
+ return 0;
+ }
+
+ if (targinfosize != IPT_ALIGN((sizeof(struct ip_nat_multi_range)
+ + (sizeof(struct ip_nat_range)
+ * (mr->rangesize - 1))))) {
+ DEBUGP("SNAT: Target size %u wrong for %u ranges\n",
+ targinfosize, mr->rangesize);
+ return 0;
+ }
+
+ if (hook_mask & ~(1 << NF_IP_POST_ROUTING)) {
+ DEBUGP("SNAT: hook mask 0x%x bad\n", hook_mask);
+ return 0;
+ }
+ return 1;
+}
+
+static int ipt_dnat_checkentry(const char *tablename,
+ const struct ipt_entry *e,
+ void *targinfo,
+ unsigned int targinfosize,
+ unsigned int hook_mask)
+{
+ struct ip_nat_multi_range *mr = targinfo;
+
+ /* Must be a valid range */
+ if (targinfosize < sizeof(struct ip_nat_multi_range)) {
+ DEBUGP("DNAT: Target size %u too small\n", targinfosize);
+ return 0;
+ }
+
+ if (targinfosize != IPT_ALIGN((sizeof(struct ip_nat_multi_range)
+ + (sizeof(struct ip_nat_range)
+ * (mr->rangesize - 1))))) {
+ DEBUGP("DNAT: Target size %u wrong for %u ranges\n",
+ targinfosize, mr->rangesize);
+ return 0;
+ }
+
+ if (hook_mask & ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_OUT))) {
+ DEBUGP("DNAT: hook mask 0x%x bad\n", hook_mask);
+ return 0;
+ }
+ return 1;
+}
+
+static inline unsigned int
+alloc_null_binding(struct ip_conntrack *conntrack,
+ struct ip_nat_info *info,
+ unsigned int hooknum)
+{
+ /* Force range to this IP; let proto decide mapping for
+ per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED).
+ Use reply in case it's already been mangled (eg local packet).
+ */
+ u_int32_t ip
+ = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC
+ ? conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip
+ : conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip);
+ struct ip_nat_multi_range mr
+ = { 1, { { IP_NAT_RANGE_MAP_IPS, ip, ip, { 0 }, { 0 } } } };
+
+ DEBUGP("Allocating NULL binding for %p (%u.%u.%u.%u)\n", conntrack,
+ IP_PARTS(ip));
+ return ip_nat_setup_info(conntrack, &mr, hooknum);
+}
+
+static inline int call_expect(const struct ip_nat_expect *i,
+ struct sk_buff **pskb,
+ unsigned int hooknum,
+ struct ip_conntrack *ct,
+ struct ip_nat_info *info,
+ struct ip_conntrack *master,
+ struct ip_nat_info *masterinfo,
+ unsigned int *verdict)
+{
+ return i->expect(pskb, hooknum, ct, info, master, masterinfo,
+ verdict);
+}
+
+int ip_nat_rule_find(struct sk_buff **pskb,
+ unsigned int hooknum,
+ const struct net_device *in,
+ const struct net_device *out,
+ struct ip_conntrack *ct,
+ struct ip_nat_info *info)
+{
+ int ret;
+
+ /* Master won't vanish while this ctrack still alive */
+ if (ct->master.master) {
+ struct ip_conntrack *master;
+
+ master = (struct ip_conntrack *)ct->master.master;
+ if (LIST_FIND(&nat_expect_list,
+ call_expect,
+ struct ip_nat_expect *,
+ pskb, hooknum, ct, info,
+ master, &master->nat.info, &ret))
+ return ret;
+ }
+ ret = ipt_do_table(pskb, hooknum, in, out, &nat_table, NULL);
+ if (ret == NF_ACCEPT) {
+ if (!(info->initialized & (1 << HOOK2MANIP(hooknum))))
+ /* NUL mapping */
+ ret = alloc_null_binding(ct, info, hooknum);
+ }
+ return ret;
+}
+
+int ip_nat_expect_register(struct ip_nat_expect *expect)
+{
+ WRITE_LOCK(&ip_nat_lock);
+ list_prepend(&nat_expect_list, expect);
+ WRITE_UNLOCK(&ip_nat_lock);
+
+ return 0;
+}
+
+void ip_nat_expect_unregister(struct ip_nat_expect *expect)
+{
+ WRITE_LOCK(&ip_nat_lock);
+ LIST_DELETE(&nat_expect_list, expect);
+ WRITE_UNLOCK(&ip_nat_lock);
+}
+
+static struct ipt_target ipt_snat_reg
+= { { NULL, NULL }, "SNAT", ipt_snat_target, ipt_snat_checkentry, NULL };
+static struct ipt_target ipt_dnat_reg
+= { { NULL, NULL }, "DNAT", ipt_dnat_target, ipt_dnat_checkentry, NULL };
+
+int __init ip_nat_rule_init(void)
+{
+ int ret;
+
+ ret = ipt_register_table(&nat_table);
+ if (ret != 0)
+ return ret;
+ ret = ipt_register_target(&ipt_snat_reg);
+ if (ret != 0)
+ goto unregister_table;
+
+ ret = ipt_register_target(&ipt_dnat_reg);
+ if (ret != 0)
+ goto unregister_snat;
+
+ return ret;
+
+ unregister_snat:
+ ipt_unregister_target(&ipt_snat_reg);
+ unregister_table:
+ ipt_unregister_table(&nat_table);
+
+ return ret;
+}
+
+void ip_nat_rule_cleanup(void)
+{
+ ipt_unregister_target(&ipt_dnat_reg);
+ ipt_unregister_target(&ipt_snat_reg);
+ ipt_unregister_table(&nat_table);
+}
diff --git a/net/ipv4/netfilter/ip_nat_standalone.c b/net/ipv4/netfilter/ip_nat_standalone.c
new file mode 100644
index 000000000..bf278d6f9
--- /dev/null
+++ b/net/ipv4/netfilter/ip_nat_standalone.c
@@ -0,0 +1,273 @@
+/* This file contains all the functions required for the standalone
+ ip_nat module.
+
+ These are not required by the compatibility layer.
+*/
+
+/* (c) 1999 Paul `Rusty' Russell. Licenced under the GNU General
+ Public Licence. */
+
+#ifdef MODULE
+#define EXPORT_SYMTAB
+#endif
+#include <linux/types.h>
+#include <linux/ip.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <net/checksum.h>
+#include <linux/spinlock.h>
+#include <linux/version.h>
+#include <linux/brlock.h>
+
+#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_nat_lock)
+#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_nat_lock)
+
+#include <linux/netfilter_ipv4/ip_nat.h>
+#include <linux/netfilter_ipv4/ip_nat_rule.h>
+#include <linux/netfilter_ipv4/ip_nat_protocol.h>
+#include <linux/netfilter_ipv4/ip_nat_core.h>
+#include <linux/netfilter_ipv4/ip_nat_helper.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ip_conntrack_core.h>
+#include <linux/netfilter_ipv4/listhelp.h>
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+#define HOOKNAME(hooknum) ((hooknum) == NF_IP_POST_ROUTING ? "POST_ROUTING" \
+ : ((hooknum) == NF_IP_PRE_ROUTING ? "PRE_ROUTING" \
+ : ((hooknum) == NF_IP_LOCAL_OUT ? "LOCAL_OUT" \
+ : "*ERROR*")))
+
+static unsigned int
+ip_nat_fn(unsigned int hooknum,
+ struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ struct ip_conntrack *ct;
+ enum ip_conntrack_info ctinfo;
+ struct ip_nat_info *info;
+ /* maniptype == SRC for postrouting. */
+ enum ip_nat_manip_type maniptype = HOOK2MANIP(hooknum);
+
+ /* We never see fragments: conntrack defrags on pre-routing
+ and local-out, and ip_nat_out protects post-routing. */
+ IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off
+ & __constant_htons(IP_MF|IP_OFFSET)));
+
+ /* FIXME: One day, fill in properly. --RR */
+ (*pskb)->nfcache |= NFC_UNKNOWN | NFC_ALTERED;
+
+ /* If we had a hardware checksum before, it's now invalid */
+ if ((*pskb)->pkt_type != PACKET_LOOPBACK)
+ (*pskb)->ip_summed = CHECKSUM_NONE;
+
+ ct = ip_conntrack_get(*pskb, &ctinfo);
+ /* Can't track? Maybe out of memory: this would make NAT
+ unreliable. */
+ if (!ct)
+ return NF_DROP;
+
+ switch (ctinfo) {
+ case IP_CT_RELATED:
+ case IP_CT_RELATED+IP_CT_IS_REPLY:
+ if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) {
+ icmp_reply_translation(*pskb, ct, hooknum,
+ CTINFO2DIR(ctinfo));
+ return NF_ACCEPT;
+ }
+ /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */
+ case IP_CT_NEW:
+ info = &ct->nat.info;
+
+ WRITE_LOCK(&ip_nat_lock);
+ /* Seen it before? This can happen for loopback, retrans,
+ or local packets.. */
+ if (!(info->initialized & (1 << maniptype))) {
+ int in_hashes = info->initialized;
+ unsigned int ret;
+
+ ret = ip_nat_rule_find(pskb, hooknum, in, out,
+ ct, info);
+ if (ret != NF_ACCEPT) {
+ WRITE_UNLOCK(&ip_nat_lock);
+ return ret;
+ }
+
+ if (in_hashes) {
+ IP_NF_ASSERT(info->bysource.conntrack);
+ replace_in_hashes(ct, info);
+ } else {
+ place_in_hashes(ct, info);
+ }
+ } else
+ DEBUGP("Already setup manip %s for ct %p\n",
+ maniptype == IP_NAT_MANIP_SRC ? "SRC" : "DST",
+ ct);
+ WRITE_UNLOCK(&ip_nat_lock);
+ break;
+
+ default:
+ /* ESTABLISHED */
+ IP_NF_ASSERT(ctinfo == IP_CT_ESTABLISHED
+ || ctinfo == (IP_CT_ESTABLISHED+IP_CT_IS_REPLY));
+ info = &ct->nat.info;
+ }
+
+ IP_NF_ASSERT(info);
+ return do_bindings(ct, ctinfo, info, hooknum, pskb);
+}
+
+static unsigned int
+ip_nat_out(unsigned int hooknum,
+ struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ /* We can hit fragment here; forwarded packets get
+ defragmented by connection tracking coming in, then
+ fragmented (grr) by the forward code.
+
+ In future: If we have nfct != NULL, AND we have NAT
+ initialized, AND there is no helper, then we can do full
+ NAPT on the head, and IP-address-only NAT on the rest.
+
+ I'm starting to have nightmares about fragments. */
+
+ if ((*pskb)->nh.iph->frag_off & __constant_htons(IP_MF|IP_OFFSET)) {
+ *pskb = ip_ct_gather_frags(*pskb);
+
+ if (!*pskb)
+ return NF_STOLEN;
+ }
+
+ return ip_nat_fn(hooknum, pskb, in, out, okfn);
+}
+
+/* We must be after connection tracking and before packet filtering. */
+
+/* Before packet filtering, change destination */
+static struct nf_hook_ops ip_nat_in_ops
+= { { NULL, NULL }, ip_nat_fn, PF_INET, NF_IP_PRE_ROUTING, NF_IP_PRI_NAT_DST };
+/* After packet filtering, change source */
+static struct nf_hook_ops ip_nat_out_ops
+= { { NULL, NULL }, ip_nat_out, PF_INET, NF_IP_POST_ROUTING, NF_IP_PRI_NAT_SRC};
+/* Before packet filtering, change destination */
+static struct nf_hook_ops ip_nat_local_out_ops
+= { { NULL, NULL }, ip_nat_fn, PF_INET, NF_IP_LOCAL_OUT, NF_IP_PRI_NAT_DST };
+
+/* Protocol registration. */
+int ip_nat_protocol_register(struct ip_nat_protocol *proto)
+{
+ int ret = 0;
+ struct list_head *i;
+
+ WRITE_LOCK(&ip_nat_lock);
+ for (i = protos.next; i != &protos; i = i->next) {
+ if (((struct ip_nat_protocol *)i)->protonum
+ == proto->protonum) {
+ ret = -EBUSY;
+ goto out;
+ }
+ }
+
+ list_prepend(&protos, proto);
+ MOD_INC_USE_COUNT;
+
+ out:
+ WRITE_UNLOCK(&ip_nat_lock);
+ return ret;
+}
+
+/* Noone stores the protocol anywhere; simply delete it. */
+void ip_nat_protocol_unregister(struct ip_nat_protocol *proto)
+{
+ WRITE_LOCK(&ip_nat_lock);
+ LIST_DELETE(&protos, proto);
+ WRITE_UNLOCK(&ip_nat_lock);
+
+ /* Someone could be still looking at the proto in a bh. */
+ br_write_lock_bh(BR_NETPROTO_LOCK);
+ br_write_unlock_bh(BR_NETPROTO_LOCK);
+
+ MOD_DEC_USE_COUNT;
+}
+
+static int init_or_cleanup(int init)
+{
+ int ret = 0;
+
+ if (!init) goto cleanup;
+
+ ret = ip_nat_rule_init();
+ if (ret < 0) {
+ printk("ip_nat_init: can't setup rules.\n");
+ goto cleanup_nothing;
+ }
+ ret = ip_nat_init();
+ if (ret < 0) {
+ printk("ip_nat_init: can't setup rules.\n");
+ goto cleanup_rule_init;
+ }
+ ret = nf_register_hook(&ip_nat_in_ops);
+ if (ret < 0) {
+ printk("ip_nat_init: can't register in hook.\n");
+ goto cleanup_nat;
+ }
+ ret = nf_register_hook(&ip_nat_out_ops);
+ if (ret < 0) {
+ printk("ip_nat_init: can't register out hook.\n");
+ goto cleanup_inops;
+ }
+ ret = nf_register_hook(&ip_nat_local_out_ops);
+ if (ret < 0) {
+ printk("ip_nat_init: can't register local out hook.\n");
+ goto cleanup_outops;
+ }
+ __MOD_INC_USE_COUNT(ip_conntrack_module);
+ return ret;
+
+ cleanup:
+ __MOD_DEC_USE_COUNT(ip_conntrack_module);
+ nf_unregister_hook(&ip_nat_local_out_ops);
+ cleanup_outops:
+ nf_unregister_hook(&ip_nat_out_ops);
+ cleanup_inops:
+ nf_unregister_hook(&ip_nat_in_ops);
+ cleanup_nat:
+ ip_nat_cleanup();
+ cleanup_rule_init:
+ ip_nat_rule_cleanup();
+ cleanup_nothing:
+ MUST_BE_READ_WRITE_UNLOCKED(&ip_nat_lock);
+ return ret;
+}
+
+static int __init init(void)
+{
+ return init_or_cleanup(1);
+}
+
+static void __exit fini(void)
+{
+ init_or_cleanup(0);
+}
+
+module_init(init);
+module_exit(fini);
+
+EXPORT_SYMBOL(ip_nat_setup_info);
+EXPORT_SYMBOL(ip_nat_helper_register);
+EXPORT_SYMBOL(ip_nat_helper_unregister);
+EXPORT_SYMBOL(ip_nat_expect_register);
+EXPORT_SYMBOL(ip_nat_expect_unregister);
+EXPORT_SYMBOL(ip_nat_cheat_check);
diff --git a/net/ipv4/netfilter/ip_queue.c b/net/ipv4/netfilter/ip_queue.c
new file mode 100644
index 000000000..d5ca01aa6
--- /dev/null
+++ b/net/ipv4/netfilter/ip_queue.c
@@ -0,0 +1,752 @@
+/*
+ * This is a module which is used for queueing IPv4 packets and
+ * communicating with userspace via netlink.
+ *
+ * (C) 2000 James Morris
+ */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/ip.h>
+#include <linux/notifier.h>
+#include <linux/netdevice.h>
+#include <linux/netfilter.h>
+#include <linux/netlink.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/rtnetlink.h>
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+#include <net/sock.h>
+
+#include <linux/netfilter_ipv4/ip_queue.h>
+
+EXPORT_NO_SYMBOLS;
+
+#define IPQ_THR_NAME "kipq"
+#define IPQ_NAME "ip_queue"
+#define IPQ_QMAX_DEFAULT 1024
+
+#define IPQ_PROC_FS_NAME "ip_queue"
+
+#define NET_IPQ_QMAX 2088
+#define NET_IPQ_QMAX_NAME "ip_queue_maxlen"
+
+typedef struct ipq_queue_element {
+ struct list_head list; /* Links element into queue */
+ unsigned char state; /* State of this element */
+ int verdict; /* Current verdict */
+ struct nf_info *info; /* Extra info from netfilter */
+ struct sk_buff *skb; /* Packet inside */
+} ipq_queue_element_t;
+
+typedef int (*ipq_send_cb_t)(ipq_queue_element_t *e);
+
+typedef struct ipq_peer {
+ pid_t pid; /* PID of userland peer */
+ unsigned char died; /* We think the peer died */
+ unsigned char copy_mode; /* Copy packet as well as metadata? */
+ size_t copy_range; /* Range past metadata to copy */
+ ipq_send_cb_t send; /* Callback for sending data to peer */
+} ipq_peer_t;
+
+typedef struct ipq_thread {
+ pid_t pid; /* PID of kernel thread */
+ unsigned char terminate; /* Termination flag */
+ unsigned char running; /* Running flag */
+ wait_queue_head_t wq; /* I/O wait queue */
+ void (*process)(void *data); /* Queue processing function */
+} ipq_thread_t;
+
+typedef struct ipq_queue {
+ int len; /* Current queue len */
+ int *maxlen; /* Maximum queue len, via sysctl */
+ unsigned char state; /* Current queue state */
+ struct list_head list; /* Head of packet queue */
+ spinlock_t lock; /* Queue spinlock */
+ ipq_peer_t peer; /* Userland peer */
+ ipq_thread_t thread; /* Thread context */
+} ipq_queue_t;
+
+
+/****************************************************************************
+*
+* Kernel thread
+*
+****************************************************************************/
+
+static void ipq_thread_init(char *thread_name)
+{
+ lock_kernel();
+ exit_files(current);
+ daemonize();
+ strcpy(current->comm, thread_name);
+ unlock_kernel();
+ spin_lock_irq(&current->sigmask_lock);
+ flush_signals(current);
+ sigfillset(&current->blocked);
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+}
+
+static int ipq_thread_start(void *data)
+{
+ ipq_queue_t *q = (ipq_queue_t *)data;
+
+ q->thread.running = 1;
+ ipq_thread_init(IPQ_THR_NAME);
+ q->thread.pid = current->pid;
+ while (!q->thread.terminate) {
+ interruptible_sleep_on(&q->thread.wq);
+ q->thread.process(q);
+ }
+ q->thread.running = 0;
+ return 0;
+}
+
+static void ipq_thread_stop(ipq_queue_t *q)
+{
+ if (!(q->thread.pid || q->thread.running))
+ return;
+ q->state = IPQ_QS_FLUSH;
+ q->thread.terminate = 1;
+ wake_up_interruptible(&q->thread.wq);
+ current->state = TASK_INTERRUPTIBLE;
+ while (q->thread.running) {
+ schedule_timeout(HZ/10);
+ current->state = TASK_RUNNING;
+ }
+}
+
+static int ipq_thread_create(ipq_queue_t *q)
+{
+ int status = kernel_thread(ipq_thread_start, q, 0);
+ return (status < 0) ? status : 0;
+}
+
+
+/****************************************************************************
+ *
+ * Packet queue
+ *
+ ****************************************************************************/
+
+/* Must be called under spinlock */
+static __inline__ void
+ipq_dequeue(ipq_queue_t *q,
+ ipq_queue_element_t *e)
+{
+ list_del(&e->list);
+ nf_reinject(e->skb, e->info, e->verdict);
+ kfree(e);
+ q->len--;
+}
+
+/* Must be called under spinlock */
+static __inline__ void
+ipq_queue_drop(ipq_queue_t *q,
+ ipq_queue_element_t *e)
+{
+ e->verdict = NF_DROP;
+ ipq_dequeue(q, e);
+}
+
+static int
+ipq_notify_peer(ipq_queue_t *q,
+ ipq_queue_element_t *e)
+{
+ int status = q->peer.send(e);
+
+ if (status >= 0) {
+ e->state = IPQ_PS_WAITING;
+ return status;
+ }
+ if (status == -ERESTARTSYS || status == -EAGAIN)
+ return 0;
+ printk(KERN_INFO "%s: error notifying peer %d, resetting "
+ "state and flushing queue\n", IPQ_NAME, q->peer.pid);
+ q->state = IPQ_QS_FLUSH;
+ q->peer.died = 1;
+ q->peer.pid = 0;
+ q->peer.copy_mode = IPQ_COPY_META;
+ q->peer.copy_range = 0;
+ return status;
+}
+
+static void
+ipq_queue_process(void *data)
+{
+ struct list_head *i;
+ ipq_queue_t *q = (ipq_queue_t *)data;
+
+restart:
+ if (q->state == IPQ_QS_HOLD)
+ return;
+ spin_lock_bh(&q->lock);
+ for (i = q->list.prev; i != &q->list; i = i->prev) {
+ ipq_queue_element_t *e = (ipq_queue_element_t *)i;
+
+ if (q->state == IPQ_QS_FLUSH) {
+ QDEBUG("flushing packet %p\n", e);
+ ipq_queue_drop(q, e);
+ continue;
+ }
+ switch (e->state) {
+ case IPQ_PS_NEW: {
+ int status = ipq_notify_peer(q, e);
+ if (status < 0) {
+ spin_unlock_bh(&q->lock);
+ goto restart;
+ }
+ break;
+ }
+ case IPQ_PS_VERDICT:
+ ipq_dequeue(q, e);
+ break;
+ case IPQ_PS_WAITING:
+ break;
+ default:
+ printk(KERN_INFO "%s: dropping stuck packet %p "
+ "with ps=%d qs=%d\n", IPQ_NAME,
+ e, e->state, q->state);
+ ipq_queue_drop(q, e);
+ }
+ }
+ spin_unlock_bh(&q->lock);
+ if (q->state == IPQ_QS_FLUSH)
+ q->state = IPQ_QS_HOLD;
+}
+
+static ipq_queue_t *
+ipq_queue_create(nf_queue_outfn_t outfn,
+ ipq_send_cb_t send_cb,
+ int *errp,
+ int *sysctl_qmax)
+{
+ int status;
+ ipq_queue_t *q;
+
+ *errp = 0;
+ q = kmalloc(sizeof(ipq_queue_t), GFP_KERNEL);
+ if (q == NULL) {
+ *errp = -ENOMEM;
+ return NULL;
+ }
+ q->thread.terminate = 0;
+ q->thread.running = 0;
+ q->thread.process = ipq_queue_process;
+ init_waitqueue_head(&q->thread.wq);
+ q->peer.pid = 0;
+ q->peer.died = 0;
+ q->peer.copy_mode = IPQ_COPY_META;
+ q->peer.copy_range = 0;
+ q->peer.send = send_cb;
+ q->len = 0;
+ q->maxlen = sysctl_qmax;
+ q->state = IPQ_QS_HOLD;
+ INIT_LIST_HEAD(&q->list);
+ spin_lock_init(&q->lock);
+ status = nf_register_queue_handler(PF_INET, outfn, q);
+ if (status < 0) {
+ *errp = -EBUSY;
+ kfree(q);
+ return NULL;
+ }
+ status = ipq_thread_create(q);
+ if (status < 0) {
+ nf_unregister_queue_handler(PF_INET);
+ *errp = status;
+ kfree(q);
+ return NULL;
+ }
+ return q;
+}
+
+static int
+ipq_enqueue(ipq_queue_t *q,
+ struct sk_buff *skb,
+ struct nf_info *info)
+{
+ ipq_queue_element_t *e = NULL;
+
+ e = kmalloc(sizeof(*e), GFP_ATOMIC);
+ if (e == NULL) {
+ printk(KERN_ERR "%s: out of memory in %s\n",
+ IPQ_NAME, __FUNCTION__);
+ return -ENOMEM;
+ }
+ e->state = IPQ_PS_NEW;
+ e->verdict = NF_DROP;
+ e->info = info;
+ e->skb = skb;
+ spin_lock_bh(&q->lock);
+ if (q->len >= *q->maxlen) {
+ spin_unlock_bh(&q->lock);
+ printk(KERN_WARNING "%s: queue full at %d entries, "
+ "dropping packet.\n", IPQ_NAME, q->len);
+ kfree(e);
+ nf_reinject(skb, info, NF_DROP);
+ return 0;
+ }
+ list_add(&e->list, &q->list);
+ q->len++;
+ spin_unlock_bh(&q->lock);
+ wake_up_interruptible(&q->thread.wq);
+ return 0;
+}
+
+/* FIXME: need to find a way to notify user during module unload */
+static void
+ipq_queue_destroy(ipq_queue_t *q)
+{
+ ipq_thread_stop(q);
+ nf_unregister_queue_handler(PF_INET);
+ kfree(q);
+}
+
+static int
+ipq_queue_mangle_ipv4(unsigned char *buf,
+ ipq_verdict_msg_t *v,
+ ipq_queue_element_t *e)
+{
+ struct iphdr *user_iph = (struct iphdr *)buf;
+
+ if (v->data_len < sizeof(*user_iph))
+ return 0;
+
+ if (e->skb->nh.iph->check != user_iph->check) {
+ int diff = v->data_len - e->skb->len;
+
+ if (diff < 0)
+ skb_trim(e->skb, v->data_len);
+ else if (diff > 0) {
+ if (v->data_len > 0xFFFF) {
+ e->verdict = NF_DROP;
+ return -EINVAL;
+ }
+ if (diff > skb_tailroom(e->skb)) {
+ struct sk_buff *newskb;
+
+ /* Ack, we waste a memcpy() of data here */
+ newskb = skb_copy_expand(e->skb,
+ skb_headroom(e->skb),
+ diff,
+ GFP_ATOMIC);
+ if (newskb == NULL) {
+ printk(KERN_WARNING "%s: OOM in %s, "
+ "dropping packet\n",
+ IPQ_THR_NAME, __FUNCTION__);
+ e->verdict = NF_DROP;
+ return -ENOMEM;
+ }
+ kfree_skb(e->skb);
+ e->skb = newskb;
+ }
+ skb_put(e->skb, diff);
+ }
+ memcpy(e->skb->data, buf, v->data_len);
+ e->skb->nfcache |= NFC_ALTERED;
+ }
+ return 0;
+}
+
+static int
+ipq_queue_set_verdict(ipq_queue_t *q,
+ ipq_verdict_msg_t *v,
+ unsigned char *buf,
+ unsigned int len)
+{
+ struct list_head *i;
+
+ if (v->value < 0 || v->value > NF_MAX_VERDICT)
+ return -EINVAL;
+ spin_lock_bh(&q->lock);
+ for (i = q->list.next; i != &q->list; i = i->next) {
+ ipq_queue_element_t *e = (ipq_queue_element_t *)i;
+
+ if (v->id == (unsigned long )e) {
+ int status = 0;
+ e->state = IPQ_PS_VERDICT;
+ e->verdict = v->value;
+
+ if (buf && v->data_len == len)
+ status = ipq_queue_mangle_ipv4(buf, v, e);
+ spin_unlock_bh(&q->lock);
+ return status;
+ }
+ }
+ spin_unlock_bh(&q->lock);
+ return -ENOENT;
+}
+
+static int
+ipq_receive_peer(ipq_queue_t *q,
+ ipq_peer_msg_t *m,
+ unsigned char type,
+ unsigned int len)
+{
+ if (q->state == IPQ_QS_FLUSH)
+ return -EBUSY;
+
+ if (len < sizeof(ipq_peer_msg_t))
+ return -EINVAL;
+
+ switch (type) {
+ case IPQM_MODE:
+ switch (m->msg.mode.value) {
+ case IPQ_COPY_NONE:
+ q->peer.copy_mode = IPQ_COPY_NONE;
+ q->peer.copy_range = 0;
+ q->state = IPQ_QS_FLUSH;
+ break;
+ case IPQ_COPY_META:
+ if (q->state == IPQ_QS_FLUSH)
+ return -EAGAIN;
+ q->peer.copy_mode = IPQ_COPY_META;
+ q->peer.copy_range = 0;
+ q->state = IPQ_QS_COPY;
+ break;
+ case IPQ_COPY_PACKET:
+ if (q->state == IPQ_QS_FLUSH)
+ return -EAGAIN;
+ q->peer.copy_mode = IPQ_COPY_PACKET;
+ q->peer.copy_range = m->msg.mode.range;
+ q->state = IPQ_QS_COPY;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IPQM_VERDICT: {
+ int status;
+ unsigned char *data = NULL;
+
+ if (m->msg.verdict.value > NF_MAX_VERDICT)
+ return -EINVAL;
+ if (m->msg.verdict.data_len)
+ data = (unsigned char *)m + sizeof(*m);
+ status = ipq_queue_set_verdict(q, &m->msg.verdict,
+ data, len - sizeof(*m));
+ if (status < 0)
+ return status;
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+ wake_up_interruptible(&q->thread.wq);
+ return 0;
+}
+
+
+/****************************************************************************
+ *
+ * Netfilter interface
+ *
+ ****************************************************************************/
+
+/*
+ * Packets arrive here from netfilter for queuing to userspace.
+ * All of them must be fed back via nf_reinject() or Alexey will kill Rusty.
+ */
+static int
+receive_netfilter(struct sk_buff *skb,
+ struct nf_info *info,
+ void *data)
+{
+ ipq_queue_t *q = (ipq_queue_t *)data;
+
+ if (q->state == IPQ_QS_FLUSH)
+ return -EBUSY;
+ return ipq_enqueue(q, skb, info);
+}
+
+/****************************************************************************
+ *
+ * Netlink interface.
+ *
+ ****************************************************************************/
+
+static struct sk_buff *
+netlink_build_message(ipq_queue_element_t *e,
+ int *errp);
+
+extern __inline__ void
+receive_user_skb(struct sk_buff *skb);
+
+static int
+netlink_send_peer(ipq_queue_element_t *e);
+
+static struct sock *nfnl = NULL;
+ipq_queue_t *nlq = NULL;
+
+static int
+netlink_send_peer(ipq_queue_element_t *e)
+{
+ int status = 0;
+ struct sk_buff *skb;
+
+ if (!nlq->peer.pid)
+ return -EINVAL;
+ skb = netlink_build_message(e, &status);
+ if (skb == NULL)
+ return status;
+ return netlink_unicast(nfnl, skb, nlq->peer.pid, 0);
+}
+
+static struct sk_buff *
+netlink_build_message(ipq_queue_element_t *e,
+ int *errp)
+{
+ unsigned char *old_tail;
+ size_t size = 0;
+ size_t data_len = 0;
+ struct sk_buff *skb;
+ ipq_packet_msg_t *pm;
+ struct nlmsghdr *nlh;
+
+ switch (nlq->peer.copy_mode) {
+ size_t copy_range;
+
+ case IPQ_COPY_META:
+ size = NLMSG_SPACE(sizeof(*pm));
+ data_len = 0;
+ break;
+ case IPQ_COPY_PACKET:
+ copy_range = nlq->peer.copy_range;
+ if (copy_range == 0 || copy_range > e->skb->len)
+ data_len = e->skb->len;
+ else
+ data_len = copy_range;
+ size = NLMSG_SPACE(sizeof(*pm) + data_len);
+ break;
+ case IPQ_COPY_NONE:
+ default:
+ *errp = -EINVAL;
+ return NULL;
+ }
+ skb = alloc_skb(size, GFP_ATOMIC);
+ if (!skb)
+ goto nlmsg_failure;
+ old_tail = skb->tail;
+ nlh = NLMSG_PUT(skb, 0, 0, IPQM_PACKET, size - sizeof(*nlh));
+ pm = NLMSG_DATA(nlh);
+ memset(pm, 0, sizeof(*pm));
+ pm->packet_id = (unsigned long )e;
+ pm->data_len = data_len;
+ pm->timestamp_sec = e->skb->stamp.tv_sec;
+ pm->timestamp_usec = e->skb->stamp.tv_usec;
+ pm->hook = e->info->hook;
+ if (e->info->indev) strcpy(pm->indev_name, e->info->indev->name);
+ else pm->indev_name[0] = '\0';
+ if (e->info->outdev) strcpy(pm->outdev_name, e->info->outdev->name);
+ else pm->outdev_name[0] = '\0';
+ if (data_len)
+ memcpy(++pm, e->skb->data, data_len);
+ nlh->nlmsg_len = skb->tail - old_tail;
+ NETLINK_CB(skb).dst_groups = 0;
+ return skb;
+nlmsg_failure:
+ if (skb)
+ kfree(skb);
+ *errp = 0;
+ printk(KERN_ERR "%s: error creating netlink message\n", IPQ_NAME);
+ return NULL;
+}
+
+#define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0);
+/*
+ * FIXME: ping old peer if we detect a new peer then resend.
+ */
+extern __inline__ void
+receive_user_skb(struct sk_buff *skb)
+{
+ int status, type;
+ struct nlmsghdr *nlh;
+
+ nlh = (struct nlmsghdr *)skb->data;
+ if (nlh->nlmsg_len < sizeof(*nlh)
+ || skb->len < nlh->nlmsg_len
+ || nlh->nlmsg_pid <= 0
+ || !(nlh->nlmsg_flags & NLM_F_REQUEST)
+ || nlh->nlmsg_flags & NLM_F_MULTI)
+ RCV_SKB_FAIL(-EINVAL);
+ if (nlh->nlmsg_flags & MSG_TRUNC)
+ RCV_SKB_FAIL(-ECOMM);
+ type = nlh->nlmsg_type;
+ if (type < NLMSG_NOOP || type >= IPQM_MAX)
+ RCV_SKB_FAIL(-EINVAL);
+ if (type <= IPQM_BASE)
+ return;
+ if(!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN))
+ RCV_SKB_FAIL(-EPERM);
+ if (nlq->peer.pid && !nlq->peer.died
+ && (nlq->peer.pid != nlh->nlmsg_pid))
+ printk(KERN_WARNING "%s: peer pid changed from %d to %d\n",
+ IPQ_NAME, nlq->peer.pid, nlh->nlmsg_pid);
+ nlq->peer.pid = nlh->nlmsg_pid;
+ nlq->peer.died = 0;
+ status = ipq_receive_peer(nlq, NLMSG_DATA(nlh),
+ type, skb->len - NLMSG_LENGTH(0));
+ if (status < 0)
+ RCV_SKB_FAIL(status);
+ if (nlh->nlmsg_flags & NLM_F_ACK)
+ netlink_ack(skb, nlh, 0);
+ return;
+}
+
+/* Note: we are only dealing with single part messages at the moment. */
+static void
+receive_user_sk(struct sock *sk,
+ int len)
+{
+ do {
+ struct sk_buff *skb;
+
+ if (rtnl_shlock_nowait())
+ return;
+ while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
+ receive_user_skb(skb);
+ kfree_skb(skb);
+ }
+ up(&rtnl_sem);
+ } while (nfnl && nfnl->receive_queue.qlen);
+}
+
+
+/****************************************************************************
+ *
+ * System events
+ *
+ ****************************************************************************/
+
+static int
+receive_event(struct notifier_block *this,
+ unsigned long event,
+ void *ptr)
+{
+ if (event == NETDEV_UNREGISTER)
+ if (nlq)
+ ipq_thread_stop(nlq);
+ return NOTIFY_DONE;
+}
+
+struct notifier_block ipq_dev_notifier = {
+ receive_event,
+ NULL,
+ 0
+};
+
+
+/****************************************************************************
+ *
+ * Sysctl - queue tuning.
+ *
+ ****************************************************************************/
+
+static int sysctl_maxlen = IPQ_QMAX_DEFAULT;
+
+static struct ctl_table_header *ipq_sysctl_header;
+
+static ctl_table ipq_table[] = {
+ { NET_IPQ_QMAX, NET_IPQ_QMAX_NAME, &sysctl_maxlen,
+ sizeof(sysctl_maxlen), 0644, NULL, proc_dointvec },
+ { 0 }
+};
+
+static ctl_table ipq_dir_table[] = {
+ {NET_IPV4, "ipv4", NULL, 0, 0555, ipq_table, 0, 0, 0, 0, 0},
+ { 0 }
+};
+
+static ctl_table ipq_root_table[] = {
+ {CTL_NET, "net", NULL, 0, 0555, ipq_dir_table, 0, 0, 0, 0, 0},
+ { 0 }
+};
+
+/****************************************************************************
+ *
+ * Procfs - debugging info.
+ *
+ ****************************************************************************/
+
+static int
+ipq_get_info(char *buffer, char **start, off_t offset, int length)
+{
+ int len;
+
+ spin_lock_bh(&nlq->lock);
+ len = sprintf(buffer,
+ "Thread pid : %d\n"
+ "Thread terminate : %d\n"
+ "Thread running : %d\n"
+ "Peer pid : %d\n"
+ "Peer died : %d\n"
+ "Peer copy mode : %d\n"
+ "Peer copy range : %d\n"
+ "Queue length : %d\n"
+ "Queue max. length : %d\n"
+ "Queue state : %d\n",
+ nlq->thread.pid,
+ nlq->thread.terminate,
+ nlq->thread.running,
+ nlq->peer.pid,
+ nlq->peer.died,
+ nlq->peer.copy_mode,
+ nlq->peer.copy_range,
+ nlq->len,
+ *nlq->maxlen,
+ nlq->state);
+ spin_unlock_bh(&nlq->lock);
+ *start = buffer + offset;
+ len -= offset;
+ if (len > length)
+ len = length;
+ else if (len < 0)
+ len = 0;
+ return len;
+}
+
+/****************************************************************************
+ *
+ * Module stuff.
+ *
+ ****************************************************************************/
+
+static int __init init(void)
+{
+ int status = 0;
+
+ nfnl = netlink_kernel_create(NETLINK_FIREWALL, receive_user_sk);
+ if (nfnl == NULL) {
+ printk(KERN_ERR "%s: initialisation failed: unable to "
+ "create kernel netlink socket\n", IPQ_NAME);
+ return -ENOMEM;
+ }
+ nlq = ipq_queue_create(receive_netfilter,
+ netlink_send_peer, &status, &sysctl_maxlen);
+ if (nlq == NULL) {
+ printk(KERN_ERR "%s: initialisation failed: unable to "
+ "initialise queue\n", IPQ_NAME);
+ sock_release(nfnl->socket);
+ return status;
+ }
+ register_netdevice_notifier(&ipq_dev_notifier);
+ proc_net_create(IPQ_PROC_FS_NAME, 0, ipq_get_info);
+ ipq_sysctl_header = register_sysctl_table(ipq_root_table, 0);
+ return status;
+}
+
+static void __exit fini(void)
+{
+ unregister_sysctl_table(ipq_sysctl_header);
+ proc_net_remove(IPQ_PROC_FS_NAME);
+ unregister_netdevice_notifier(&ipq_dev_notifier);
+ ipq_queue_destroy(nlq);
+ sock_release(nfnl->socket);
+}
+
+MODULE_DESCRIPTION("IPv4 packet queue handler");
+module_init(init);
+module_exit(fini);
+
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
new file mode 100644
index 000000000..8cc8c24ac
--- /dev/null
+++ b/net/ipv4/netfilter/ip_tables.c
@@ -0,0 +1,1664 @@
+/*
+ * Packet matching code.
+ *
+ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ */
+#include <linux/config.h>
+#include <linux/skbuff.h>
+#include <linux/kmod.h>
+#include <linux/vmalloc.h>
+#include <linux/netdevice.h>
+#include <linux/module.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/icmp.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+#ifndef IP_OFFSET
+#define IP_OFFSET 0x1FFF
+#endif
+
+/*#define DEBUG_IP_FIREWALL*/
+/*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
+/*#define DEBUG_IP_FIREWALL_USER*/
+
+#ifdef DEBUG_IP_FIREWALL
+#define dprintf(format, args...) printk(format , ## args)
+#else
+#define dprintf(format, args...)
+#endif
+
+#ifdef DEBUG_IP_FIREWALL_USER
+#define duprintf(format, args...) printk(format , ## args)
+#else
+#define duprintf(format, args...)
+#endif
+
+#ifdef CONFIG_NETFILTER_DEBUG
+#define IP_NF_ASSERT(x) \
+do { \
+ if (!(x)) \
+ printk("IPT_ASSERT: %s:%s:%u\n", \
+ __FUNCTION__, __FILE__, __LINE__); \
+} while(0)
+#else
+#define IP_NF_ASSERT(x)
+#endif
+#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
+
+/* Mutex protects lists (only traversed in user context). */
+static DECLARE_MUTEX(ipt_mutex);
+
+/* Must have mutex */
+#define ASSERT_READ_LOCK(x) IP_NF_ASSERT(down_trylock(&ipt_mutex) != 0)
+#define ASSERT_WRITE_LOCK(x) IP_NF_ASSERT(down_trylock(&ipt_mutex) != 0)
+#include <linux/netfilter_ipv4/lockhelp.h>
+#include <linux/netfilter_ipv4/listhelp.h>
+
+#if 0
+/* All the better to debug you with... */
+#define static
+#define inline
+#endif
+
+/* Locking is simple: we assume at worst case there will be one packet
+ in user context and one from bottom halves (or soft irq if Alexey's
+ softnet patch was applied).
+
+ We keep a set of rules for each CPU, so we can avoid write-locking
+ them; doing a readlock_bh() stops packets coming through if we're
+ in user context.
+
+ To be cache friendly on SMP, we arrange them like so:
+ [ n-entries ]
+ ... cache-align padding ...
+ [ n-entries ]
+
+ Hence the start of any table is given by get_table() below. */
+
+/* The table itself */
+struct ipt_table_info
+{
+ /* Size per table */
+ unsigned int size;
+ /* Number of entries: FIXME. --RR */
+ unsigned int number;
+
+ /* Entry points and underflows */
+ unsigned int hook_entry[NF_IP_NUMHOOKS];
+ unsigned int underflow[NF_IP_NUMHOOKS];
+
+ char padding[SMP_ALIGN((NF_IP_NUMHOOKS*2+2)*sizeof(unsigned int))];
+
+ /* ipt_entry tables: one per CPU */
+ char entries[0];
+};
+
+static LIST_HEAD(ipt_target);
+static LIST_HEAD(ipt_match);
+static LIST_HEAD(ipt_tables);
+#define ADD_COUNTER(c,b,p) do { (c).bcnt += (b); (c).pcnt += (p); } while(0)
+
+#ifdef CONFIG_SMP
+#define TABLE_OFFSET(t,p) (SMP_ALIGN((t)->size)*cpu_number_map(p))
+#else
+#define TABLE_OFFSET(t,p) 0
+#endif
+
+#if 0
+#define down(x) do { printk("DOWN:%u:" #x "\n", __LINE__); down(x); } while(0)
+#define down_interruptible(x) ({ int __r; printk("DOWNi:%u:" #x "\n", __LINE__); __r = down_interruptible(x); if (__r != 0) printk("ABORT-DOWNi:%u\n", __LINE__); __r; })
+#define up(x) do { printk("UP:%u:" #x "\n", __LINE__); up(x); } while(0)
+#endif
+
+/* Returns whether matches rule or not. */
+static inline int
+ip_packet_match(const struct iphdr *ip,
+ const char *indev,
+ const char *outdev,
+ const struct ipt_ip *ipinfo,
+ int isfrag)
+{
+ size_t i;
+ unsigned long ret;
+
+#define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
+
+ if (FWINV((ip->saddr&ipinfo->smsk.s_addr) != ipinfo->src.s_addr,
+ IPT_INV_SRCIP)
+ || FWINV((ip->daddr&ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr,
+ IPT_INV_DSTIP)) {
+ dprintf("Source or dest mismatch.\n");
+
+ dprintf("SRC: %u.%u.%u.%u. Mask: %u.%u.%u.%u. Target: %u.%u.%u.%u.%s\n",
+ NIPQUAD(ip->saddr),
+ NIPQUAD(ipinfo->smsk.s_addr),
+ NIPQUAD(ipinfo->src.s_addr),
+ ipinfo->invflags & IPT_INV_SRCIP ? " (INV)" : "");
+ dprintf("DST: %u.%u.%u.%u Mask: %u.%u.%u.%u Target: %u.%u.%u.%u.%s\n",
+ NIPQUAD(ip->daddr),
+ NIPQUAD(ipinfo->dmsk.s_addr),
+ NIPQUAD(ipinfo->dst.s_addr),
+ ipinfo->invflags & IPT_INV_DSTIP ? " (INV)" : "");
+ return 0;
+ }
+
+ /* Look for ifname matches; this should unroll nicely. */
+ for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
+ ret |= (((const unsigned long *)indev)[i]
+ ^ ((const unsigned long *)ipinfo->iniface)[i])
+ & ((const unsigned long *)ipinfo->iniface_mask)[i];
+ }
+
+ if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
+ dprintf("VIA in mismatch (%s vs %s).%s\n",
+ indev, ipinfo->iniface,
+ ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
+ return 0;
+ }
+
+ for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
+ ret |= (((const unsigned long *)outdev)[i]
+ ^ ((const unsigned long *)ipinfo->outiface)[i])
+ & ((const unsigned long *)ipinfo->outiface_mask)[i];
+ }
+
+ if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
+ dprintf("VIA out mismatch (%s vs %s).%s\n",
+ outdev, ipinfo->outiface,
+ ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
+ return 0;
+ }
+
+ /* Check specific protocol */
+ if (ipinfo->proto
+ && FWINV(ip->protocol != ipinfo->proto, IPT_INV_PROTO)) {
+ dprintf("Packet protocol %hi does not match %hi.%s\n",
+ ip->protocol, ipinfo->proto,
+ ipinfo->invflags&IPT_INV_PROTO ? " (INV)":"");
+ return 0;
+ }
+
+ /* If we have a fragment rule but the packet is not a fragment
+ * then we return zero */
+ if (FWINV((ipinfo->flags&IPT_F_FRAG) && !isfrag, IPT_INV_FRAG)) {
+ dprintf("Fragment rule but not fragment.%s\n",
+ ipinfo->invflags & IPT_INV_FRAG ? " (INV)" : "");
+ return 0;
+ }
+
+ return 1;
+}
+
+static inline int
+ip_checkentry(const struct ipt_ip *ip)
+{
+ if (ip->flags & ~IPT_F_MASK) {
+ duprintf("Unknown flag bits set: %08X\n",
+ ip->flags & ~IPT_F_MASK);
+ return 0;
+ }
+ if (ip->invflags & ~IPT_INV_MASK) {
+ duprintf("Unknown invflag bits set: %08X\n",
+ ip->invflags & ~IPT_INV_MASK);
+ return 0;
+ }
+ return 1;
+}
+
+static unsigned int
+ipt_error(struct sk_buff **pskb,
+ unsigned int hooknum,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *targinfo,
+ void *userinfo)
+{
+ if (net_ratelimit())
+ printk("ip_tables: error: `%s'\n", (char *)targinfo);
+
+ return NF_DROP;
+}
+
+static inline
+int do_match(struct ipt_entry_match *m,
+ const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int offset,
+ const void *hdr,
+ u_int16_t datalen,
+ int *hotdrop)
+{
+ /* Stop iteration if it doesn't match */
+ if (!m->u.match->match(skb, in, out, m->data,
+ offset, hdr, datalen, hotdrop))
+ return 1;
+ else
+ return 0;
+}
+
+static inline struct ipt_entry *
+get_entry(void *base, unsigned int offset)
+{
+ return (struct ipt_entry *)(base + offset);
+}
+
+/* Returns one of the generic firewall policies, like NF_ACCEPT. */
+unsigned int
+ipt_do_table(struct sk_buff **pskb,
+ unsigned int hook,
+ const struct net_device *in,
+ const struct net_device *out,
+ struct ipt_table *table,
+ void *userdata)
+{
+ static const char nulldevname[IFNAMSIZ] = { 0 };
+ u_int16_t offset;
+ struct iphdr *ip;
+ void *protohdr;
+ u_int16_t datalen;
+ int hotdrop = 0;
+ /* Initializing verdict to NF_DROP keeps gcc happy. */
+ unsigned int verdict = NF_DROP;
+ const char *indev, *outdev;
+ void *table_base;
+ struct ipt_entry *e, *back;
+
+ /* Initialization */
+ ip = (*pskb)->nh.iph;
+ protohdr = (u_int32_t *)ip + ip->ihl;
+ datalen = (*pskb)->len - ip->ihl * 4;
+ indev = in ? in->name : nulldevname;
+ outdev = out ? out->name : nulldevname;
+ /* We handle fragments by dealing with the first fragment as
+ * if it was a normal packet. All other fragments are treated
+ * normally, except that they will NEVER match rules that ask
+ * things we don't know, ie. tcp syn flag or ports). If the
+ * rule is also a fragment-specific rule, non-fragments won't
+ * match it. */
+ offset = ntohs(ip->frag_off) & IP_OFFSET;
+
+ read_lock_bh(&table->lock);
+ IP_NF_ASSERT(table->valid_hooks & (1 << hook));
+ table_base = (void *)table->private->entries
+ + TABLE_OFFSET(table->private, smp_processor_id());
+ e = get_entry(table_base, table->private->hook_entry[hook]);
+
+ /* Check noone else using our table */
+ IP_NF_ASSERT(((struct ipt_entry *)table_base)->comefrom == 0xdead57ac);
+#ifdef CONFIG_NETFILTER_DEBUG
+ ((struct ipt_entry *)table_base)->comefrom = 0x57acc001;
+#endif
+
+ /* For return from builtin chain */
+ back = get_entry(table_base, table->private->underflow[hook]);
+
+ do {
+ IP_NF_ASSERT(e);
+ IP_NF_ASSERT(back);
+ (*pskb)->nfcache |= e->nfcache;
+ if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
+ struct ipt_entry_target *t;
+
+ if (IPT_MATCH_ITERATE(e, do_match,
+ *pskb, in, out,
+ offset, protohdr,
+ datalen, &hotdrop) != 0)
+ goto no_match;
+
+ ADD_COUNTER(e->counters, ntohs(ip->tot_len), 1);
+
+ t = ipt_get_target(e);
+ IP_NF_ASSERT(t->u.target);
+ /* Standard target? */
+ if (!t->u.target->target) {
+ int v;
+
+ v = ((struct ipt_standard_target *)t)->verdict;
+ if (v < 0) {
+ /* Pop from stack? */
+ if (v != IPT_RETURN) {
+ verdict = (unsigned)(-v) - 1;
+ break;
+ }
+ e = back;
+ back = get_entry(table_base,
+ back->comefrom);
+ continue;
+ }
+ if (table_base + v
+ != (void *)e + e->next_offset) {
+ /* Save old back ptr in next entry */
+ struct ipt_entry *next
+ = (void *)e + e->next_offset;
+ next->comefrom
+ = (void *)back - table_base;
+ /* set back pointer to next entry */
+ back = next;
+ }
+
+ e = get_entry(table_base, v);
+ } else {
+ verdict = t->u.target->target(pskb, hook,
+ in, out,
+ t->data,
+ userdata);
+
+ /* Target might have changed stuff. */
+ ip = (*pskb)->nh.iph;
+ protohdr = (u_int32_t *)ip + ip->ihl;
+ datalen = (*pskb)->len - ip->ihl * 4;
+
+ if (verdict == IPT_CONTINUE)
+ e = (void *)e + e->next_offset;
+ else
+ /* Verdict */
+ break;
+ }
+ } else {
+
+ no_match:
+ e = (void *)e + e->next_offset;
+ }
+ } while (!hotdrop);
+
+#ifdef CONFIG_NETFILTER_DEBUG
+ ((struct ipt_entry *)table_base)->comefrom = 0xdead57ac;
+#endif
+ read_unlock_bh(&table->lock);
+
+#ifdef DEBUG_ALLOW_ALL
+ return NF_ACCEPT;
+#else
+ if (hotdrop)
+ return NF_DROP;
+ else return verdict;
+#endif
+}
+
+/* If it succeeds, returns element and locks mutex */
+static inline void *
+find_inlist_lock_noload(struct list_head *head,
+ const char *name,
+ int *error,
+ struct semaphore *mutex)
+{
+ void *ret;
+
+#if 0
+ duprintf("find_inlist: searching for `%s' in %s.\n",
+ name, head == &ipt_target ? "ipt_target"
+ : head == &ipt_match ? "ipt_match"
+ : head == &ipt_tables ? "ipt_tables" : "UNKNOWN");
+#endif
+
+ *error = down_interruptible(mutex);
+ if (*error != 0)
+ return NULL;
+
+ ret = list_named_find(head, name);
+ if (!ret) {
+ *error = -ENOENT;
+ up(mutex);
+ }
+ return ret;
+}
+
+#ifndef CONFIG_KMOD
+#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
+#else
+static void *
+find_inlist_lock(struct list_head *head,
+ const char *name,
+ const char *prefix,
+ int *error,
+ struct semaphore *mutex)
+{
+ void *ret;
+
+ ret = find_inlist_lock_noload(head, name, error, mutex);
+ if (!ret) {
+ char modulename[IPT_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
+ strcpy(modulename, prefix);
+ strcat(modulename, name);
+ duprintf("find_inlist: loading `%s'.\n", modulename);
+ request_module(modulename);
+ ret = find_inlist_lock_noload(head, name, error, mutex);
+ }
+
+ return ret;
+}
+#endif
+
+static inline struct ipt_table *
+find_table_lock(const char *name, int *error, struct semaphore *mutex)
+{
+ return find_inlist_lock(&ipt_tables, name, "iptable_", error, mutex);
+}
+
+static inline struct ipt_match *
+find_match_lock(const char *name, int *error, struct semaphore *mutex)
+{
+ return find_inlist_lock(&ipt_match, name, "ipt_", error, mutex);
+}
+
+static inline struct ipt_target *
+find_target_lock(const char *name, int *error, struct semaphore *mutex)
+{
+ return find_inlist_lock(&ipt_target, name, "ipt_", error, mutex);
+}
+
+/* All zeroes == unconditional rule. */
+static inline int
+unconditional(const struct ipt_ip *ip)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof(*ip)/sizeof(__u32); i++)
+ if (((__u32 *)ip)[i])
+ return 0;
+
+ return 1;
+}
+
+/* Figures out from what hook each rule can be called: returns 0 if
+ there are loops. Puts hook bitmask in comefrom. */
+static int
+mark_source_chains(struct ipt_table_info *newinfo, unsigned int valid_hooks)
+{
+ unsigned int hook;
+
+ /* No recursion; use packet counter to save back ptrs (reset
+ to 0 as we leave), and comefrom to save source hook bitmask */
+ for (hook = 0; hook < NF_IP_NUMHOOKS; hook++) {
+ unsigned int pos = newinfo->hook_entry[hook];
+ struct ipt_entry *e
+ = (struct ipt_entry *)(newinfo->entries + pos);
+
+ if (!(valid_hooks & (1 << hook)))
+ continue;
+
+ /* Set initial back pointer. */
+ e->counters.pcnt = pos;
+
+ for (;;) {
+ struct ipt_standard_target *t
+ = (void *)ipt_get_target(e);
+
+ if (e->comefrom & (1 << NF_IP_NUMHOOKS)) {
+ printk("iptables: loop hook %u pos %u %08X.\n",
+ hook, pos, e->comefrom);
+ return 0;
+ }
+ e->comefrom
+ |= ((1 << hook) | (1 << NF_IP_NUMHOOKS));
+
+ /* Unconditional return/END. */
+ if (e->target_offset == sizeof(struct ipt_entry)
+ && (strcmp(t->target.u.name, IPT_STANDARD_TARGET)
+ == 0)
+ && t->verdict < 0
+ && unconditional(&e->ip)) {
+ unsigned int oldpos, size;
+
+ /* Return: backtrack through the last
+ big jump. */
+ do {
+ e->comefrom ^= (1<<NF_IP_NUMHOOKS);
+#ifdef DEBUG_IP_FIREWALL_USER
+ if (e->comefrom
+ & (1 << NF_IP_NUMHOOKS)) {
+ duprintf("Back unset "
+ "on hook %u "
+ "rule %u\n",
+ hook, pos);
+ }
+#endif
+ oldpos = pos;
+ pos = e->counters.pcnt;
+ e->counters.pcnt = 0;
+
+ /* We're at the start. */
+ if (pos == oldpos)
+ goto next;
+
+ e = (struct ipt_entry *)
+ (newinfo->entries + pos);
+ } while (oldpos == pos + e->next_offset);
+
+ /* Move along one */
+ size = e->next_offset;
+ e = (struct ipt_entry *)
+ (newinfo->entries + pos + size);
+ e->counters.pcnt = pos;
+ pos += size;
+ } else {
+ int newpos = t->verdict;
+
+ if (strcmp(t->target.u.name,
+ IPT_STANDARD_TARGET) == 0
+ && newpos >= 0) {
+ /* This a jump; chase it. */
+ duprintf("Jump rule %u -> %u\n",
+ pos, newpos);
+ } else {
+ /* ... this is a fallthru */
+ newpos = pos + e->next_offset;
+ }
+ e = (struct ipt_entry *)
+ (newinfo->entries + newpos);
+ e->counters.pcnt = pos;
+ pos = newpos;
+ }
+ }
+ next:
+ duprintf("Finished chain %u\n", hook);
+ }
+ return 1;
+}
+
+static inline int
+cleanup_match(struct ipt_entry_match *m, unsigned int *i)
+{
+ if (i && (*i)-- == 0)
+ return 1;
+
+ if (m->u.match->me)
+ __MOD_DEC_USE_COUNT(m->u.match->me);
+
+ return 0;
+}
+
+static inline int
+standard_check(const struct ipt_entry_target *t,
+ unsigned int max_offset)
+{
+ struct ipt_standard_target *targ = (void *)t;
+
+ /* Check standard info. */
+ if (t->target_size != sizeof(struct ipt_standard_target)) {
+ duprintf("standard_check: target size %u != %u\n",
+ t->target_size, sizeof(struct ipt_standard_target));
+ return 0;
+ }
+
+ if (targ->verdict >= 0
+ && targ->verdict > max_offset - sizeof(struct ipt_entry)) {
+ duprintf("ipt_standard_check: bad verdict (%i)\n",
+ targ->verdict);
+ return 0;
+ }
+
+ if (targ->verdict < -NF_MAX_VERDICT - 1) {
+ duprintf("ipt_standard_check: bad negative verdict (%i)\n",
+ targ->verdict);
+ return 0;
+ }
+ return 1;
+}
+
+static inline int
+check_match(struct ipt_entry_match *m,
+ const char *name,
+ const struct ipt_ip *ip,
+ unsigned int hookmask,
+ unsigned int *i)
+{
+ int ret;
+ struct ipt_match *match;
+
+ match = find_match_lock(m->u.name, &ret, &ipt_mutex);
+ if (!match) {
+ duprintf("check_match: `%s' not found\n", m->u.name);
+ return ret;
+ }
+ if (match->me)
+ __MOD_INC_USE_COUNT(match->me);
+ m->u.match = match;
+ up(&ipt_mutex);
+
+ if (m->u.match->checkentry
+ && !m->u.match->checkentry(name, ip, m->data,
+ m->match_size - sizeof(*m),
+ hookmask)) {
+ if (m->u.match->me)
+ __MOD_DEC_USE_COUNT(m->u.match->me);
+ duprintf("ip_tables: check failed for `%s'.\n",
+ m->u.match->name);
+ return -EINVAL;
+ }
+
+ (*i)++;
+ return 0;
+}
+
+static struct ipt_target ipt_standard_target;
+
+static inline int
+check_entry(struct ipt_entry *e, const char *name, unsigned int size,
+ unsigned int *i)
+{
+ struct ipt_entry_target *t;
+ struct ipt_target *target;
+ int ret;
+ unsigned int j;
+
+ if (!ip_checkentry(&e->ip)) {
+ duprintf("ip_tables: ip check failed %p %s.\n", e, name);
+ return -EINVAL;
+ }
+
+ j = 0;
+ ret = IPT_MATCH_ITERATE(e, check_match, name, &e->ip, e->comefrom, &j);
+ if (ret != 0)
+ goto cleanup_matches;
+
+ t = ipt_get_target(e);
+ target = find_target_lock(t->u.name, &ret, &ipt_mutex);
+ if (!target) {
+ duprintf("check_entry: `%s' not found\n", t->u.name);
+ up(&ipt_mutex);
+ return ret;
+ }
+ if (target->me)
+ __MOD_INC_USE_COUNT(target->me);
+ t->u.target = target;
+ up(&ipt_mutex);
+
+ if (t->u.target == &ipt_standard_target) {
+ if (!standard_check(t, size)) {
+ ret = -EINVAL;
+ goto cleanup_matches;
+ }
+ } else if (t->u.target->checkentry
+ && !t->u.target->checkentry(name, e, t->data,
+ t->target_size - sizeof(*t),
+ e->comefrom)) {
+ if (t->u.target->me)
+ __MOD_DEC_USE_COUNT(t->u.target->me);
+ duprintf("ip_tables: check failed for `%s'.\n",
+ t->u.target->name);
+ ret = -EINVAL;
+ goto cleanup_matches;
+ }
+
+ (*i)++;
+ return 0;
+
+ cleanup_matches:
+ IPT_MATCH_ITERATE(e, cleanup_match, &j);
+ return ret;
+}
+
+static inline int
+check_entry_size_and_hooks(struct ipt_entry *e,
+ struct ipt_table_info *newinfo,
+ unsigned char *base,
+ unsigned char *limit,
+ const unsigned int *hook_entries,
+ const unsigned int *underflows,
+ unsigned int *i)
+{
+ unsigned int h;
+
+ if ((unsigned long)e % __alignof__(struct ipt_entry) != 0
+ || (unsigned char *)e + sizeof(struct ipt_entry) >= limit) {
+ duprintf("Bad offset %p\n", e);
+ return -EINVAL;
+ }
+
+ if (e->next_offset
+ < sizeof(struct ipt_entry) + sizeof(struct ipt_entry_target)) {
+ duprintf("checking: element %p size %u\n",
+ e, e->next_offset);
+ return -EINVAL;
+ }
+
+ /* Check hooks & underflows */
+ for (h = 0; h < NF_IP_NUMHOOKS; h++) {
+ if ((unsigned char *)e - base == hook_entries[h])
+ newinfo->hook_entry[h] = hook_entries[h];
+ if ((unsigned char *)e - base == underflows[h])
+ newinfo->underflow[h] = underflows[h];
+ }
+
+ /* FIXME: underflows must be unconditional, standard verdicts
+ < 0 (not IPT_RETURN). --RR */
+
+ /* Clear counters and comefrom */
+ e->counters = ((struct ipt_counters) { 0, 0 });
+ e->comefrom = 0;
+
+ (*i)++;
+ return 0;
+}
+
+static inline int
+cleanup_entry(struct ipt_entry *e, unsigned int *i)
+{
+ struct ipt_entry_target *t;
+
+ if (i && (*i)-- == 0)
+ return 1;
+
+ /* Cleanup all matches */
+ IPT_MATCH_ITERATE(e, cleanup_match, NULL);
+ t = ipt_get_target(e);
+ if (t->u.target->me)
+ __MOD_DEC_USE_COUNT(t->u.target->me);
+
+ return 0;
+}
+
+/* Checks and translates the user-supplied table segment (held in
+ newinfo) */
+static int
+translate_table(const char *name,
+ unsigned int valid_hooks,
+ struct ipt_table_info *newinfo,
+ unsigned int size,
+ unsigned int number,
+ const unsigned int *hook_entries,
+ const unsigned int *underflows)
+{
+ unsigned int i;
+ int ret;
+
+ newinfo->size = size;
+ newinfo->number = number;
+
+ /* Init all hooks to impossible value. */
+ for (i = 0; i < NF_IP_NUMHOOKS; i++) {
+ newinfo->hook_entry[i] = 0xFFFFFFFF;
+ newinfo->underflow[i] = 0xFFFFFFFF;
+ }
+
+ duprintf("translate_table: size %u\n", newinfo->size);
+ i = 0;
+ /* Walk through entries, checking offsets. */
+ ret = IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
+ check_entry_size_and_hooks,
+ newinfo,
+ newinfo->entries,
+ newinfo->entries + size,
+ hook_entries, underflows, &i);
+ if (ret != 0)
+ return ret;
+
+ if (i != number) {
+ duprintf("translate_table: %u not %u entries\n",
+ i, number);
+ return -EINVAL;
+ }
+
+ /* Check hooks all assigned */
+ for (i = 0; i < NF_IP_NUMHOOKS; i++) {
+ /* Only hooks which are valid */
+ if (!(valid_hooks & (1 << i)))
+ continue;
+ if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
+ duprintf("Invalid hook entry %u %u\n",
+ i, hook_entries[i]);
+ return -EINVAL;
+ }
+ if (newinfo->underflow[i] == 0xFFFFFFFF) {
+ duprintf("Invalid underflow %u %u\n",
+ i, underflows[i]);
+ return -EINVAL;
+ }
+ }
+
+ if (!mark_source_chains(newinfo, valid_hooks))
+ return -ELOOP;
+
+ /* Finally, each sanity check must pass */
+ i = 0;
+ ret = IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
+ check_entry, name, size, &i);
+
+ if (ret != 0) {
+ IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size,
+ cleanup_entry, &i);
+ return ret;
+ }
+
+ /* And one copy for every other CPU */
+ for (i = 1; i < smp_num_cpus; i++) {
+ memcpy(newinfo->entries + SMP_ALIGN(newinfo->size*i),
+ newinfo->entries,
+ SMP_ALIGN(newinfo->size));
+ }
+
+ return ret;
+}
+
+static struct ipt_table_info *
+replace_table(struct ipt_table *table,
+ unsigned int num_counters,
+ struct ipt_table_info *newinfo,
+ int *error)
+{
+ struct ipt_table_info *oldinfo;
+
+#ifdef CONFIG_NETFILTER_DEBUG
+ {
+ struct ipt_entry *table_base;
+ unsigned int i;
+
+ for (i = 0; i < smp_num_cpus; i++) {
+ table_base =
+ (void *)newinfo->entries
+ + TABLE_OFFSET(newinfo, i);
+
+ table_base->comefrom = 0xdead57ac;
+ }
+ }
+#endif
+
+ /* Do the substitution. */
+ write_lock_bh(&table->lock);
+ /* Check inside lock: is the old number correct? */
+ if (num_counters != table->private->number) {
+ duprintf("num_counters != table->private->number (%u/%u)\n",
+ num_counters, table->private->number);
+ write_unlock_bh(&table->lock);
+ *error = -EAGAIN;
+ return NULL;
+ }
+ oldinfo = table->private;
+ table->private = newinfo;
+ write_unlock_bh(&table->lock);
+
+ return oldinfo;
+}
+
+/* Gets counters. */
+static inline int
+add_entry_to_counter(const struct ipt_entry *e,
+ struct ipt_counters total[],
+ unsigned int *i)
+{
+ ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
+
+ (*i)++;
+ return 0;
+}
+
+static void
+get_counters(const struct ipt_table_info *t,
+ struct ipt_counters counters[])
+{
+ unsigned int cpu;
+ unsigned int i;
+
+ for (cpu = 0; cpu < smp_num_cpus; cpu++) {
+ i = 0;
+ IPT_ENTRY_ITERATE(t->entries + TABLE_OFFSET(t, cpu),
+ t->size,
+ add_entry_to_counter,
+ counters,
+ &i);
+ }
+}
+
+static int
+copy_entries_to_user(unsigned int total_size,
+ struct ipt_table *table,
+ void *userptr)
+{
+ unsigned int off, num, countersize;
+ struct ipt_entry *e;
+ struct ipt_counters *counters;
+ int ret = 0;
+
+ /* We need atomic snapshot of counters: rest doesn't change
+ (other than comefrom, which userspace doesn't care
+ about). */
+ countersize = sizeof(struct ipt_counters) * table->private->number;
+ counters = vmalloc(countersize);
+
+ if (counters == NULL)
+ return -ENOMEM;
+
+ /* First, sum counters... */
+ memset(counters, 0, countersize);
+ write_lock_bh(&table->lock);
+ get_counters(table->private, counters);
+ write_unlock_bh(&table->lock);
+
+ /* ... then copy entire thing from CPU 0... */
+ if (copy_to_user(userptr, table->private->entries, total_size) != 0) {
+ ret = -EFAULT;
+ goto free_counters;
+ }
+
+ /* FIXME: use iterator macros --RR */
+ /* ... then go back and fix counters and names */
+ for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
+ unsigned int i;
+ struct ipt_entry_match *m;
+ struct ipt_entry_target *t;
+
+ e = (struct ipt_entry *)(table->private->entries + off);
+ if (copy_to_user(userptr + off
+ + offsetof(struct ipt_entry, counters),
+ &counters[num],
+ sizeof(counters[num])) != 0) {
+ ret = -EFAULT;
+ goto free_counters;
+ }
+
+ for (i = sizeof(struct ipt_entry);
+ i < e->target_offset;
+ i += m->match_size) {
+ m = (void *)e + i;
+
+ if (copy_to_user(userptr + off + i
+ + offsetof(struct ipt_entry_match,
+ u.name),
+ m->u.match->name,
+ strlen(m->u.match->name)+1) != 0) {
+ ret = -EFAULT;
+ goto free_counters;
+ }
+ }
+
+ t = ipt_get_target(e);
+ if (copy_to_user(userptr + off + e->target_offset
+ + offsetof(struct ipt_entry_target,
+ u.name),
+ t->u.target->name,
+ strlen(t->u.target->name)+1) != 0) {
+ ret = -EFAULT;
+ goto free_counters;
+ }
+ }
+
+ free_counters:
+ vfree(counters);
+ return ret;
+}
+
+static int
+get_entries(const struct ipt_get_entries *entries,
+ struct ipt_get_entries *uptr)
+{
+ int ret;
+ struct ipt_table *t;
+
+ t = find_table_lock(entries->name, &ret, &ipt_mutex);
+ if (t) {
+ duprintf("t->private->number = %u\n",
+ t->private->number);
+ if (entries->size == t->private->size)
+ ret = copy_entries_to_user(t->private->size,
+ t, uptr->entries);
+ else {
+ duprintf("get_entries: I've got %u not %u!\n",
+ t->private->size,
+ entries->size);
+ ret = -EINVAL;
+ }
+ up(&ipt_mutex);
+ } else
+ duprintf("get_entries: Can't find %s!\n",
+ entries->name);
+
+ return ret;
+}
+
+static int
+do_replace(void *user, unsigned int len)
+{
+ int ret;
+ struct ipt_replace tmp;
+ struct ipt_table *t;
+ struct ipt_table_info *newinfo, *oldinfo;
+ struct ipt_counters *counters;
+
+ if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
+ return -EFAULT;
+
+ newinfo = vmalloc(sizeof(struct ipt_table_info)
+ + SMP_ALIGN(tmp.size) * smp_num_cpus);
+ if (!newinfo)
+ return -ENOMEM;
+
+ if (copy_from_user(newinfo->entries, user + sizeof(tmp),
+ tmp.size) != 0) {
+ ret = -EFAULT;
+ goto free_newinfo;
+ }
+
+ counters = vmalloc(tmp.num_counters * sizeof(struct ipt_counters));
+ if (!counters) {
+ ret = -ENOMEM;
+ goto free_newinfo;
+ }
+ memset(counters, 0, tmp.num_counters * sizeof(struct ipt_counters));
+
+ ret = translate_table(tmp.name, tmp.valid_hooks,
+ newinfo, tmp.size, tmp.num_entries,
+ tmp.hook_entry, tmp.underflow);
+ if (ret != 0)
+ goto free_newinfo_counters;
+
+ duprintf("ip_tables: Translated table\n");
+
+ t = find_table_lock(tmp.name, &ret, &ipt_mutex);
+ if (!t)
+ goto free_newinfo_counters_untrans;
+
+ /* You lied! */
+ if (tmp.valid_hooks != t->valid_hooks) {
+ duprintf("Valid hook crap: %08X vs %08X\n",
+ tmp.valid_hooks, t->valid_hooks);
+ ret = -EINVAL;
+ goto free_newinfo_counters_untrans_unlock;
+ }
+
+ oldinfo = replace_table(t, tmp.num_counters, newinfo, &ret);
+ if (!oldinfo)
+ goto free_newinfo_counters_untrans_unlock;
+
+ /* Get the old counters. */
+ get_counters(oldinfo, counters);
+ /* Decrease module usage counts and free resource */
+ IPT_ENTRY_ITERATE(oldinfo->entries, oldinfo->size, cleanup_entry,NULL);
+ vfree(oldinfo);
+ /* Silent error: too late now. */
+ copy_to_user(tmp.counters, counters,
+ sizeof(struct ipt_counters) * tmp.num_counters);
+
+ up(&ipt_mutex);
+ return 0;
+
+ free_newinfo_counters_untrans_unlock:
+ up(&ipt_mutex);
+ free_newinfo_counters_untrans:
+ IPT_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry,NULL);
+ free_newinfo_counters:
+ vfree(counters);
+ free_newinfo:
+ vfree(newinfo);
+ return ret;
+}
+
+/* We're lazy, and add to the first CPU; overflow works its fey magic
+ * and everything is OK. */
+static inline int
+add_counter_to_entry(struct ipt_entry *e,
+ const struct ipt_counters addme[],
+ unsigned int *i)
+{
+#if 0
+ duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
+ *i,
+ (long unsigned int)e->counters.pcnt,
+ (long unsigned int)e->counters.bcnt,
+ (long unsigned int)addme[*i].pcnt,
+ (long unsigned int)addme[*i].bcnt);
+#endif
+
+ ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
+
+ (*i)++;
+ return 0;
+}
+
+static int
+do_add_counters(void *user, unsigned int len)
+{
+ unsigned int i;
+ struct ipt_counters_info tmp, *paddc;
+ struct ipt_table *t;
+ int ret;
+
+ if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
+ return -EFAULT;
+
+ if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct ipt_counters))
+ return -EINVAL;
+
+ paddc = vmalloc(len);
+ if (!paddc)
+ return -ENOMEM;
+
+ if (copy_from_user(paddc, user, len) != 0) {
+ ret = -EFAULT;
+ goto free;
+ }
+
+ t = find_table_lock(tmp.name, &ret, &ipt_mutex);
+ if (!t)
+ goto free;
+
+ write_lock_bh(&t->lock);
+ if (t->private->number != paddc->num_counters) {
+ ret = -EINVAL;
+ goto unlock_up_free;
+ }
+
+ i = 0;
+ IPT_ENTRY_ITERATE(t->private->entries,
+ t->private->size,
+ add_counter_to_entry,
+ paddc->counters,
+ &i);
+ unlock_up_free:
+ write_unlock_bh(&t->lock);
+ up(&ipt_mutex);
+ free:
+ vfree(paddc);
+
+ return ret;
+}
+
+static int
+do_ipt_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
+{
+ int ret;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ switch (cmd) {
+ case IPT_SO_SET_REPLACE:
+ ret = do_replace(user, len);
+ break;
+
+ case IPT_SO_SET_ADD_COUNTERS:
+ ret = do_add_counters(user, len);
+ break;
+
+ default:
+ duprintf("do_ipt_set_ctl: unknown request %i\n", cmd);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int
+do_ipt_get_ctl(struct sock *sk, int cmd, void *user, int *len)
+{
+ int ret;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ switch (cmd) {
+ case IPT_SO_GET_INFO: {
+ char name[IPT_TABLE_MAXNAMELEN];
+ struct ipt_table *t;
+
+ if (*len != sizeof(struct ipt_getinfo)) {
+ duprintf("length %u != %u\n", *len,
+ sizeof(struct ipt_getinfo));
+ ret = -EINVAL;
+ break;
+ }
+
+ if (copy_from_user(name, user, sizeof(name)) != 0) {
+ ret = -EFAULT;
+ break;
+ }
+ t = find_table_lock(name, &ret, &ipt_mutex);
+ if (t) {
+ struct ipt_getinfo info;
+
+ info.valid_hooks = t->valid_hooks;
+ memcpy(info.hook_entry, t->private->hook_entry,
+ sizeof(info.hook_entry));
+ memcpy(info.underflow, t->private->underflow,
+ sizeof(info.underflow));
+ info.num_entries = t->private->number;
+ info.size = t->private->size;
+ strcpy(info.name, name);
+
+ if (copy_to_user(user, &info, *len) != 0)
+ ret = -EFAULT;
+ else
+ ret = 0;
+
+ up(&ipt_mutex);
+ }
+ }
+ break;
+
+ case IPT_SO_GET_ENTRIES: {
+ struct ipt_get_entries get;
+
+ if (*len < sizeof(get)) {
+ duprintf("get_entries: %u < %u\n", *len, sizeof(get));
+ ret = -EINVAL;
+ } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
+ ret = -EFAULT;
+ } else if (*len != sizeof(struct ipt_get_entries) + get.size) {
+ duprintf("get_entries: %u != %u\n", *len,
+ sizeof(struct ipt_get_entries) + get.size);
+ ret = -EINVAL;
+ } else
+ ret = get_entries(&get, user);
+ break;
+ }
+
+ default:
+ duprintf("do_ipt_get_ctl: unknown request %i\n", cmd);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/* Registration hooks for targets. */
+int
+ipt_register_target(struct ipt_target *target)
+{
+ int ret;
+
+ ret = down_interruptible(&ipt_mutex);
+ if (ret != 0)
+ return ret;
+
+ if (list_named_insert(&ipt_target, target)) {
+ MOD_INC_USE_COUNT;
+ ret = 0;
+ } else {
+ duprintf("ipt_register_target: `%s' already in list!\n",
+ target->name);
+ ret = -EINVAL;
+ }
+ up(&ipt_mutex);
+ return ret;
+}
+
+void
+ipt_unregister_target(struct ipt_target *target)
+{
+ down(&ipt_mutex);
+ LIST_DELETE(&ipt_target, target);
+ up(&ipt_mutex);
+ MOD_DEC_USE_COUNT;
+}
+
+int
+ipt_register_match(struct ipt_match *match)
+{
+ int ret;
+
+ ret = down_interruptible(&ipt_mutex);
+ if (ret != 0)
+ return ret;
+
+ if (list_named_insert(&ipt_match, match)) {
+ MOD_INC_USE_COUNT;
+ ret = 0;
+ } else {
+ duprintf("ipt_register_match: `%s' already in list!\n",
+ match->name);
+ ret = -EINVAL;
+ }
+ up(&ipt_mutex);
+
+ return ret;
+}
+
+void
+ipt_unregister_match(struct ipt_match *match)
+{
+ down(&ipt_mutex);
+ LIST_DELETE(&ipt_match, match);
+ up(&ipt_mutex);
+ MOD_DEC_USE_COUNT;
+}
+
+int ipt_register_table(struct ipt_table *table)
+{
+ int ret;
+ struct ipt_table_info *newinfo;
+ static struct ipt_table_info bootstrap
+ = { 0, 0, { 0 }, { 0 }, { }, { } };
+
+ newinfo = vmalloc(sizeof(struct ipt_table_info)
+ + SMP_ALIGN(table->table->size) * smp_num_cpus);
+ if (!newinfo) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ memcpy(newinfo->entries, table->table->entries, table->table->size);
+
+ ret = translate_table(table->name, table->valid_hooks,
+ newinfo, table->table->size,
+ table->table->num_entries,
+ table->table->hook_entry,
+ table->table->underflow);
+ if (ret != 0) {
+ vfree(newinfo);
+ return ret;
+ }
+
+ ret = down_interruptible(&ipt_mutex);
+ if (ret != 0) {
+ vfree(newinfo);
+ return ret;
+ }
+
+ /* Don't autoload: we'd eat our tail... */
+ if (list_named_find(&ipt_tables, table->name)) {
+ ret = -EEXIST;
+ goto free_unlock;
+ }
+
+ /* Simplifies replace_table code. */
+ table->private = &bootstrap;
+ if (!replace_table(table, 0, newinfo, &ret))
+ goto free_unlock;
+
+ duprintf("table->private->number = %u\n",
+ table->private->number);
+
+ table->lock = RW_LOCK_UNLOCKED;
+ list_prepend(&ipt_tables, table);
+ MOD_INC_USE_COUNT;
+
+ unlock:
+ up(&ipt_mutex);
+ return ret;
+
+ free_unlock:
+ vfree(newinfo);
+ goto unlock;
+}
+
+void ipt_unregister_table(struct ipt_table *table)
+{
+ down(&ipt_mutex);
+ LIST_DELETE(&ipt_tables, table);
+ up(&ipt_mutex);
+
+ /* Decrease module usage counts and free resources */
+ IPT_ENTRY_ITERATE(table->private->entries, table->private->size,
+ cleanup_entry, NULL);
+ vfree(table->private);
+ MOD_DEC_USE_COUNT;
+}
+
+/* Returns 1 if the port is matched by the range, 0 otherwise */
+static inline int
+port_match(u_int16_t min, u_int16_t max, u_int16_t port, int invert)
+{
+ int ret;
+
+ ret = (port >= min && port <= max) ^ invert;
+ return ret;
+}
+
+static int
+tcp_find_option(u_int8_t option,
+ const struct tcphdr *tcp,
+ u_int16_t datalen,
+ int invert,
+ int *hotdrop)
+{
+ unsigned int i = sizeof(struct tcphdr);
+ const u_int8_t *opt = (u_int8_t *)tcp;
+
+ duprintf("tcp_match: finding option\n");
+ /* If we don't have the whole header, drop packet. */
+ if (tcp->doff * 4 > datalen) {
+ *hotdrop = 1;
+ return 0;
+ }
+
+ while (i < tcp->doff * 4) {
+ if (opt[i] == option) return !invert;
+ if (opt[i] < 2) i++;
+ else i += opt[i+1]?:1;
+ }
+
+ return invert;
+}
+
+static int
+tcp_match(const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *matchinfo,
+ int offset,
+ const void *hdr,
+ u_int16_t datalen,
+ int *hotdrop)
+{
+ const struct tcphdr *tcp = hdr;
+ const struct ipt_tcp *tcpinfo = matchinfo;
+
+ /* To quote Alan:
+
+ Don't allow a fragment of TCP 8 bytes in. Nobody normal
+ causes this. Its a cracker trying to break in by doing a
+ flag overwrite to pass the direction checks.
+ */
+
+ if (offset == 1) {
+ duprintf("Dropping evil TCP offset=1 frag.\n");
+ *hotdrop = 1;
+ return 0;
+ } else if (offset == 0 && datalen < sizeof(struct tcphdr)) {
+ /* We've been asked to examine this packet, and we
+ can't. Hence, no choice but to drop. */
+ duprintf("Dropping evil TCP offset=0 tinygram.\n");
+ *hotdrop = 1;
+ return 0;
+ }
+
+ /* FIXME: Try tcp doff >> packet len against various stacks --RR */
+
+#define FWINVTCP(bool,invflg) ((bool) ^ !!(tcpinfo->invflags & invflg))
+
+ /* Must not be a fragment. */
+ return !offset
+ && port_match(tcpinfo->spts[0], tcpinfo->spts[1],
+ ntohs(tcp->source),
+ !!(tcpinfo->invflags & IPT_TCP_INV_SRCPT))
+ && port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
+ ntohs(tcp->dest),
+ !!(tcpinfo->invflags & IPT_TCP_INV_DSTPT))
+ && FWINVTCP((((unsigned char *)tcp)[13]
+ & tcpinfo->flg_mask)
+ == tcpinfo->flg_cmp,
+ IPT_TCP_INV_FLAGS)
+ && (!tcpinfo->option
+ || tcp_find_option(tcpinfo->option, tcp, datalen,
+ tcpinfo->invflags
+ & IPT_TCP_INV_OPTION,
+ hotdrop));
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+tcp_checkentry(const char *tablename,
+ const struct ipt_ip *ip,
+ void *matchinfo,
+ unsigned int matchsize,
+ unsigned int hook_mask)
+{
+ const struct ipt_tcp *tcpinfo = matchinfo;
+
+ /* Must specify proto == TCP, and no unknown invflags */
+ return ip->proto == IPPROTO_TCP
+ && !(ip->invflags & IPT_INV_PROTO)
+ && matchsize == IPT_ALIGN(sizeof(struct ipt_tcp))
+ && !(tcpinfo->invflags & ~IPT_TCP_INV_MASK);
+}
+
+static int
+udp_match(const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *matchinfo,
+ int offset,
+ const void *hdr,
+ u_int16_t datalen,
+ int *hotdrop)
+{
+ const struct udphdr *udp = hdr;
+ const struct ipt_udp *udpinfo = matchinfo;
+
+ if (offset == 0 && datalen < sizeof(struct udphdr)) {
+ /* We've been asked to examine this packet, and we
+ can't. Hence, no choice but to drop. */
+ duprintf("Dropping evil UDP tinygram.\n");
+ *hotdrop = 1;
+ return 0;
+ }
+
+ /* Must not be a fragment. */
+ return !offset
+ && port_match(udpinfo->spts[0], udpinfo->spts[1],
+ ntohs(udp->source),
+ !!(udpinfo->invflags & IPT_UDP_INV_SRCPT))
+ && port_match(udpinfo->dpts[0], udpinfo->dpts[1],
+ ntohs(udp->dest),
+ !!(udpinfo->invflags & IPT_UDP_INV_DSTPT));
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+udp_checkentry(const char *tablename,
+ const struct ipt_ip *ip,
+ void *matchinfo,
+ unsigned int matchinfosize,
+ unsigned int hook_mask)
+{
+ const struct ipt_udp *udpinfo = matchinfo;
+
+ /* Must specify proto == UDP, and no unknown invflags */
+ if (ip->proto != IPPROTO_UDP || (ip->invflags & IPT_INV_PROTO)) {
+ duprintf("ipt_udp: Protocol %u != %u\n", ip->proto,
+ IPPROTO_UDP);
+ return 0;
+ }
+ if (matchinfosize != IPT_ALIGN(sizeof(struct ipt_udp))) {
+ duprintf("ipt_udp: matchsize %u != %u\n",
+ matchinfosize, IPT_ALIGN(sizeof(struct ipt_udp)));
+ return 0;
+ }
+ if (udpinfo->invflags & ~IPT_UDP_INV_MASK) {
+ duprintf("ipt_udp: unknown flags %X\n",
+ udpinfo->invflags);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Returns 1 if the type and code is matched by the range, 0 otherwise */
+static inline int
+icmp_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
+ u_int8_t type, u_int8_t code,
+ int invert)
+{
+ return (type == test_type && code >= min_code && code <= max_code)
+ ^ invert;
+}
+
+static int
+icmp_match(const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *matchinfo,
+ int offset,
+ const void *hdr,
+ u_int16_t datalen,
+ int *hotdrop)
+{
+ const struct icmphdr *icmp = hdr;
+ const struct ipt_icmp *icmpinfo = matchinfo;
+
+ if (offset == 0 && datalen < 2) {
+ /* We've been asked to examine this packet, and we
+ can't. Hence, no choice but to drop. */
+ duprintf("Dropping evil ICMP tinygram.\n");
+ *hotdrop = 1;
+ return 0;
+ }
+
+ /* Must not be a fragment. */
+ return !offset
+ && icmp_type_code_match(icmpinfo->type,
+ icmpinfo->code[0],
+ icmpinfo->code[1],
+ icmp->type, icmp->code,
+ !!(icmpinfo->invflags&IPT_ICMP_INV));
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+icmp_checkentry(const char *tablename,
+ const struct ipt_ip *ip,
+ void *matchinfo,
+ unsigned int matchsize,
+ unsigned int hook_mask)
+{
+ const struct ipt_icmp *icmpinfo = matchinfo;
+
+ /* Must specify proto == ICMP, and no unknown invflags */
+ return ip->proto == IPPROTO_ICMP
+ && !(ip->invflags & IPT_INV_PROTO)
+ && matchsize == IPT_ALIGN(sizeof(struct ipt_icmp))
+ && !(icmpinfo->invflags & ~IPT_ICMP_INV);
+}
+
+/* The built-in targets: standard (NULL) and error. */
+static struct ipt_target ipt_standard_target
+= { { NULL, NULL }, IPT_STANDARD_TARGET, NULL, NULL, NULL };
+static struct ipt_target ipt_error_target
+= { { NULL, NULL }, IPT_ERROR_TARGET, ipt_error, NULL, NULL };
+
+static struct nf_sockopt_ops ipt_sockopts
+= { { NULL, NULL }, PF_INET, IPT_BASE_CTL, IPT_SO_SET_MAX+1, do_ipt_set_ctl,
+ IPT_BASE_CTL, IPT_SO_GET_MAX+1, do_ipt_get_ctl, 0, NULL };
+
+static struct ipt_match tcp_matchstruct
+= { { NULL, NULL }, "tcp", &tcp_match, &tcp_checkentry, NULL };
+static struct ipt_match udp_matchstruct
+= { { NULL, NULL }, "udp", &udp_match, &udp_checkentry, NULL };
+static struct ipt_match icmp_matchstruct
+= { { NULL, NULL }, "icmp", &icmp_match, &icmp_checkentry, NULL };
+
+static int __init init(void)
+{
+ int ret;
+
+ /* Noone else will be downing sem now, so we won't sleep */
+ down(&ipt_mutex);
+ list_append(&ipt_target, &ipt_standard_target);
+ list_append(&ipt_target, &ipt_error_target);
+ list_append(&ipt_match, &tcp_matchstruct);
+ list_append(&ipt_match, &udp_matchstruct);
+ list_append(&ipt_match, &icmp_matchstruct);
+ up(&ipt_mutex);
+
+ /* Register setsockopt */
+ ret = nf_register_sockopt(&ipt_sockopts);
+ if (ret < 0) {
+ duprintf("Unable to register sockopts.\n");
+ return ret;
+ }
+
+ printk("iptables: (c)2000 Netfilter core team\n");
+ return 0;
+}
+
+static void __exit fini(void)
+{
+ nf_unregister_sockopt(&ipt_sockopts);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/netfilter/ipchains_core.c b/net/ipv4/netfilter/ipchains_core.c
new file mode 100644
index 000000000..02bd7ad83
--- /dev/null
+++ b/net/ipv4/netfilter/ipchains_core.c
@@ -0,0 +1,1768 @@
+/* Minor modifications to fit on compatibility framework:
+ Rusty.Russell@rustcorp.com.au
+*/
+
+/*
+ * This code is heavily based on the code on the old ip_fw.c code; see below for
+ * copyrights and attributions of the old code. This code is basically GPL.
+ *
+ * 15-Aug-1997: Major changes to allow graphs for firewall rules.
+ * Paul Russell <Paul.Russell@rustcorp.com.au> and
+ * Michael Neuling <Michael.Neuling@rustcorp.com.au>
+ * 24-Aug-1997: Generalised protocol handling (not just TCP/UDP/ICMP).
+ * Added explicit RETURN from chains.
+ * Removed TOS mangling (done in ipchains 1.0.1).
+ * Fixed read & reset bug by reworking proc handling.
+ * Paul Russell <Paul.Russell@rustcorp.com.au>
+ * 28-Sep-1997: Added packet marking for net sched code.
+ * Removed fw_via comparisons: all done on device name now,
+ * similar to changes in ip_fw.c in DaveM's CVS970924 tree.
+ * Paul Russell <Paul.Russell@rustcorp.com.au>
+ * 2-Nov-1997: Moved types across to __u16, etc.
+ * Added inverse flags.
+ * Fixed fragment bug (in args to port_match).
+ * Changed mark to only one flag (MARKABS).
+ * 21-Nov-1997: Added ability to test ICMP code.
+ * 19-Jan-1998: Added wildcard interfaces.
+ * 6-Feb-1998: Merged 2.0 and 2.1 versions.
+ * Initialised ip_masq for 2.0.x version.
+ * Added explicit NETLINK option for 2.1.x version.
+ * Added packet and byte counters for policy matches.
+ * 26-Feb-1998: Fixed race conditions, added SMP support.
+ * 18-Mar-1998: Fix SMP, fix race condition fix.
+ * 1-May-1998: Remove caching of device pointer.
+ * 12-May-1998: Allow tiny fragment case for TCP/UDP.
+ * 15-May-1998: Treat short packets as fragments, don't just block.
+ * 3-Jan-1999: Fixed serious procfs security hole -- users should never
+ * be allowed to view the chains!
+ * Marc Santoro <ultima@snicker.emoti.com>
+ * 29-Jan-1999: Locally generated bogus IPs dealt with, rather than crash
+ * during dump_packet. --RR.
+ * 19-May-1999: Star Wars: The Phantom Menace opened. Rule num
+ * printed in log (modified from Michael Hasenstein's patch).
+ * Added SYN in log message. --RR
+ * 23-Jul-1999: Fixed small fragment security exposure opened on 15-May-1998.
+ * John McDonald <jm@dataprotect.com>
+ * Thomas Lopatic <tl@dataprotect.com>
+ */
+
+/*
+ *
+ * The origina Linux port was done Alan Cox, with changes/fixes from
+ * Pauline Middlelink, Jos Vos, Thomas Quinot, Wouter Gadeyne, Juan
+ * Jose Ciarlante, Bernd Eckenfels, Keith Owens and others.
+ *
+ * Copyright from the original FreeBSD version follows:
+ *
+ * Copyright (c) 1993 Daniel Boulet
+ * Copyright (c) 1994 Ugen J.S.Antsilevich
+ *
+ * Redistribution and use in source forms, with and without modification,
+ * are permitted provided that this entire comment appears intact.
+ *
+ * Redistribution in binary form may occur without any restrictions.
+ * Obviously, it would be nice if you gave credit where credit is due
+ * but requiring it would be too onerous.
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind. */
+
+#include <linux/config.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/icmp.h>
+#include <linux/udp.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <net/sock.h>
+#include <net/icmp.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4/compat_firewall.h>
+#include <linux/netfilter_ipv4/ipchains_core.h>
+
+#include <net/checksum.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+
+/* Understanding locking in this code: (thanks to Alan Cox for using
+ * little words to explain this to me). -- PR
+ *
+ * In UP, there can be two packets traversing the chains:
+ * 1) A packet from the current userspace context
+ * 2) A packet off the bh handlers (timer or net).
+ *
+ * For SMP (kernel v2.1+), multiply this by # CPUs.
+ *
+ * [Note that this in not correct for 2.2 - because the socket code always
+ * uses lock_kernel() to serialize, and bottom halves (timers and net_bhs)
+ * only run on one CPU at a time. This will probably change for 2.3.
+ * It is still good to use spinlocks because that avoids the global cli()
+ * for updating the tables, which is rather costly in SMP kernels -AK]
+ *
+ * This means counters and backchains can get corrupted if no precautions
+ * are taken.
+ *
+ * To actually alter a chain on UP, we need only do a cli(), as this will
+ * stop a bh handler firing, as we are in the current userspace context
+ * (coming from a setsockopt()).
+ *
+ * On SMP, we need a write_lock_irqsave(), which is a simple cli() in
+ * UP.
+ *
+ * For backchains and counters, we use an array, indexed by
+ * [cpu_number_map[smp_processor_id()]*2 + !in_interrupt()]; the array is of
+ * size [smp_num_cpus*2]. For v2.0, smp_num_cpus is effectively 1. So,
+ * confident of uniqueness, we modify counters even though we only
+ * have a read lock (to read the counters, you need a write lock,
+ * though). */
+
+/* Why I didn't use straight locking... -- PR
+ *
+ * The backchains can be separated out of the ip_chains structure, and
+ * allocated as needed inside ip_fw_check().
+ *
+ * The counters, however, can't. Trying to lock these means blocking
+ * interrupts every time we want to access them. This would suck HARD
+ * performance-wise. Not locking them leads to possible corruption,
+ * made worse on 32-bit machines (counters are 64-bit). */
+
+/*#define DEBUG_IP_FIREWALL*/
+/*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
+/*#define DEBUG_IP_FIREWALL_USER*/
+/*#define DEBUG_IP_FIREWALL_LOCKING*/
+
+static struct sock *ipfwsk;
+
+#ifdef CONFIG_SMP
+#define SLOT_NUMBER() (cpu_number_map(smp_processor_id())*2 + !in_interrupt())
+#else /* !SMP */
+#define SLOT_NUMBER() (!in_interrupt())
+#endif /* CONFIG_SMP */
+#define NUM_SLOTS (smp_num_cpus*2)
+
+#define SIZEOF_STRUCT_IP_CHAIN (sizeof(struct ip_chain) \
+ + NUM_SLOTS*sizeof(struct ip_reent))
+#define SIZEOF_STRUCT_IP_FW_KERNEL (sizeof(struct ip_fwkernel) \
+ + NUM_SLOTS*sizeof(struct ip_counters))
+
+#ifdef DEBUG_IP_FIREWALL_LOCKING
+static unsigned int fwc_rlocks, fwc_wlocks;
+#define FWC_DEBUG_LOCK(d) \
+do { \
+ FWC_DONT_HAVE_LOCK(d); \
+ d |= (1 << SLOT_NUMBER()); \
+} while (0)
+
+#define FWC_DEBUG_UNLOCK(d) \
+do { \
+ FWC_HAVE_LOCK(d); \
+ d &= ~(1 << SLOT_NUMBER()); \
+} while (0)
+
+#define FWC_DONT_HAVE_LOCK(d) \
+do { \
+ if ((d) & (1 << SLOT_NUMBER())) \
+ printk("%s:%i: Got lock on %i already!\n", \
+ __FILE__, __LINE__, SLOT_NUMBER()); \
+} while(0)
+
+#define FWC_HAVE_LOCK(d) \
+do { \
+ if (!((d) & (1 << SLOT_NUMBER()))) \
+ printk("%s:%i:No lock on %i!\n", \
+ __FILE__, __LINE__, SLOT_NUMBER()); \
+} while (0)
+
+#else
+#define FWC_DEBUG_LOCK(d) do { } while(0)
+#define FWC_DEBUG_UNLOCK(d) do { } while(0)
+#define FWC_DONT_HAVE_LOCK(d) do { } while(0)
+#define FWC_HAVE_LOCK(d) do { } while(0)
+#endif /*DEBUG_IP_FIRWALL_LOCKING*/
+
+#define FWC_READ_LOCK(l) do { FWC_DEBUG_LOCK(fwc_rlocks); read_lock(l); } while (0)
+#define FWC_WRITE_LOCK(l) do { FWC_DEBUG_LOCK(fwc_wlocks); write_lock(l); } while (0)
+#define FWC_READ_LOCK_IRQ(l,f) do { FWC_DEBUG_LOCK(fwc_rlocks); read_lock_irqsave(l,f); } while (0)
+#define FWC_WRITE_LOCK_IRQ(l,f) do { FWC_DEBUG_LOCK(fwc_wlocks); write_lock_irqsave(l,f); } while (0)
+#define FWC_READ_UNLOCK(l) do { FWC_DEBUG_UNLOCK(fwc_rlocks); read_unlock(l); } while (0)
+#define FWC_WRITE_UNLOCK(l) do { FWC_DEBUG_UNLOCK(fwc_wlocks); write_unlock(l); } while (0)
+#define FWC_READ_UNLOCK_IRQ(l,f) do { FWC_DEBUG_UNLOCK(fwc_rlocks); read_unlock_irqrestore(l,f); } while (0)
+#define FWC_WRITE_UNLOCK_IRQ(l,f) do { FWC_DEBUG_UNLOCK(fwc_wlocks); write_unlock_irqrestore(l,f); } while (0)
+
+struct ip_chain;
+
+struct ip_counters
+{
+ __u64 pcnt, bcnt; /* Packet and byte counters */
+};
+
+struct ip_fwkernel
+{
+ struct ip_fw ipfw;
+ struct ip_fwkernel *next; /* where to go next if current
+ * rule doesn't match */
+ struct ip_chain *branch; /* which branch to jump to if
+ * current rule matches */
+ int simplebranch; /* Use this if branch == NULL */
+ struct ip_counters counters[0]; /* Actually several of these */
+};
+
+struct ip_reent
+{
+ struct ip_chain *prevchain; /* Pointer to referencing chain */
+ struct ip_fwkernel *prevrule; /* Pointer to referencing rule */
+ struct ip_counters counters;
+};
+
+struct ip_chain
+{
+ ip_chainlabel label; /* Defines the label for each block */
+ struct ip_chain *next; /* Pointer to next block */
+ struct ip_fwkernel *chain; /* Pointer to first rule in block */
+ __u32 refcount; /* Number of refernces to block */
+ int policy; /* Default rule for chain. Only *
+ * used in built in chains */
+ struct ip_reent reent[0]; /* Actually several of these */
+};
+
+/*
+ * Implement IP packet firewall
+ */
+
+#ifdef DEBUG_IP_FIREWALL
+#define dprintf(format, args...) printk(format , ## args)
+#else
+#define dprintf(format, args...)
+#endif
+
+#ifdef DEBUG_IP_FIREWALL_USER
+#define duprintf(format, args...) printk(format , ## args)
+#else
+#define duprintf(format, args...)
+#endif
+
+/* Lock around ip_fw_chains linked list structure */
+rwlock_t ip_fw_lock = RW_LOCK_UNLOCKED;
+
+/* Head of linked list of fw rules */
+static struct ip_chain *ip_fw_chains;
+
+#define IP_FW_INPUT_CHAIN ip_fw_chains
+#define IP_FW_FORWARD_CHAIN (ip_fw_chains->next)
+#define IP_FW_OUTPUT_CHAIN (ip_fw_chains->next->next)
+
+/* Returns 1 if the port is matched by the range, 0 otherwise */
+extern inline int port_match(__u16 min, __u16 max, __u16 port,
+ int frag, int invert)
+{
+ if (frag) /* Fragments fail ANY port test. */
+ return (min == 0 && max == 0xFFFF);
+ else return (port >= min && port <= max) ^ invert;
+}
+
+/* Returns whether matches rule or not. */
+static int ip_rule_match(struct ip_fwkernel *f,
+ const char *ifname,
+ struct iphdr *ip,
+ char tcpsyn,
+ __u16 src_port, __u16 dst_port,
+ char isfrag)
+{
+#define FWINV(bool,invflg) ((bool) ^ !!(f->ipfw.fw_invflg & invflg))
+ /*
+ * This is a bit simpler as we don't have to walk
+ * an interface chain as you do in BSD - same logic
+ * however.
+ */
+
+ if (FWINV((ip->saddr&f->ipfw.fw_smsk.s_addr) != f->ipfw.fw_src.s_addr,
+ IP_FW_INV_SRCIP)
+ || FWINV((ip->daddr&f->ipfw.fw_dmsk.s_addr)!=f->ipfw.fw_dst.s_addr,
+ IP_FW_INV_DSTIP)) {
+ dprintf("Source or dest mismatch.\n");
+
+ dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
+ f->ipfw.fw_smsk.s_addr, f->ipfw.fw_src.s_addr,
+ f->ipfw.fw_invflg & IP_FW_INV_SRCIP ? " (INV)" : "");
+ dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
+ f->ipfw.fw_dmsk.s_addr, f->ipfw.fw_dst.s_addr,
+ f->ipfw.fw_invflg & IP_FW_INV_DSTIP ? " (INV)" : "");
+ return 0;
+ }
+
+ /*
+ * Look for a VIA device match
+ */
+ if (f->ipfw.fw_flg & IP_FW_F_WILDIF) {
+ if (FWINV(strncmp(ifname, f->ipfw.fw_vianame,
+ strlen(f->ipfw.fw_vianame)) != 0,
+ IP_FW_INV_VIA)) {
+ dprintf("Wildcard interface mismatch.%s\n",
+ f->ipfw.fw_invflg & IP_FW_INV_VIA ? " (INV)" : "");
+ return 0; /* Mismatch */
+ }
+ }
+ else if (FWINV(strcmp(ifname, f->ipfw.fw_vianame) != 0,
+ IP_FW_INV_VIA)) {
+ dprintf("Interface name does not match.%s\n",
+ f->ipfw.fw_invflg & IP_FW_INV_VIA
+ ? " (INV)" : "");
+ return 0; /* Mismatch */
+ }
+
+ /*
+ * Ok the chain addresses match.
+ */
+
+ /* If we have a fragment rule but the packet is not a fragment
+ * the we return zero */
+ if (FWINV((f->ipfw.fw_flg&IP_FW_F_FRAG) && !isfrag, IP_FW_INV_FRAG)) {
+ dprintf("Fragment rule but not fragment.%s\n",
+ f->ipfw.fw_invflg & IP_FW_INV_FRAG ? " (INV)" : "");
+ return 0;
+ }
+
+ /* Fragment NEVER passes a SYN test, even an inverted one. */
+ if (FWINV((f->ipfw.fw_flg&IP_FW_F_TCPSYN) && !tcpsyn, IP_FW_INV_SYN)
+ || (isfrag && (f->ipfw.fw_flg&IP_FW_F_TCPSYN))) {
+ dprintf("Rule requires SYN and packet has no SYN.%s\n",
+ f->ipfw.fw_invflg & IP_FW_INV_SYN ? " (INV)" : "");
+ return 0;
+ }
+
+ if (f->ipfw.fw_proto) {
+ /*
+ * Specific firewall - packet's protocol
+ * must match firewall's.
+ */
+
+ if (FWINV(ip->protocol!=f->ipfw.fw_proto, IP_FW_INV_PROTO)) {
+ dprintf("Packet protocol %hi does not match %hi.%s\n",
+ ip->protocol, f->ipfw.fw_proto,
+ f->ipfw.fw_invflg&IP_FW_INV_PROTO ? " (INV)":"");
+ return 0;
+ }
+
+ /* For non TCP/UDP/ICMP, port range is max anyway. */
+ if (!port_match(f->ipfw.fw_spts[0],
+ f->ipfw.fw_spts[1],
+ src_port, isfrag,
+ !!(f->ipfw.fw_invflg&IP_FW_INV_SRCPT))
+ || !port_match(f->ipfw.fw_dpts[0],
+ f->ipfw.fw_dpts[1],
+ dst_port, isfrag,
+ !!(f->ipfw.fw_invflg
+ &IP_FW_INV_DSTPT))) {
+ dprintf("Port match failed.\n");
+ return 0;
+ }
+ }
+
+ dprintf("Match succeeded.\n");
+ return 1;
+}
+
+static const char *branchname(struct ip_chain *branch,int simplebranch)
+{
+ if (branch)
+ return branch->label;
+ switch (simplebranch)
+ {
+ case FW_BLOCK: return IP_FW_LABEL_BLOCK;
+ case FW_ACCEPT: return IP_FW_LABEL_ACCEPT;
+ case FW_REJECT: return IP_FW_LABEL_REJECT;
+ case FW_REDIRECT: return IP_FW_LABEL_REDIRECT;
+ case FW_MASQUERADE: return IP_FW_LABEL_MASQUERADE;
+ case FW_SKIP: return "-";
+ case FW_SKIP+1: return IP_FW_LABEL_RETURN;
+ default:
+ return "UNKNOWN";
+ }
+}
+
+/*
+ * VERY ugly piece of code which actually
+ * makes kernel printf for matching packets...
+ */
+static void dump_packet(const struct iphdr *ip,
+ const char *ifname,
+ struct ip_fwkernel *f,
+ const ip_chainlabel chainlabel,
+ __u16 src_port,
+ __u16 dst_port,
+ unsigned int count,
+ int syn)
+{
+ __u32 *opt = (__u32 *) (ip + 1);
+ int opti;
+
+ if (f) {
+ printk(KERN_INFO "Packet log: %s ",chainlabel);
+ printk("%s ",branchname(f->branch,f->simplebranch));
+ if (f->simplebranch==FW_REDIRECT)
+ printk("%d ",f->ipfw.fw_redirpt);
+ }
+
+ printk("%s PROTO=%d %d.%d.%d.%d:%hu %d.%d.%d.%d:%hu"
+ " L=%hu S=0x%2.2hX I=%hu F=0x%4.4hX T=%hu",
+ ifname, ip->protocol,
+ (ntohl(ip->saddr)>>24)&0xFF,
+ (ntohl(ip->saddr)>>16)&0xFF,
+ (ntohl(ip->saddr)>>8)&0xFF,
+ (ntohl(ip->saddr))&0xFF,
+ src_port,
+ (ntohl(ip->daddr)>>24)&0xFF,
+ (ntohl(ip->daddr)>>16)&0xFF,
+ (ntohl(ip->daddr)>>8)&0xFF,
+ (ntohl(ip->daddr))&0xFF,
+ dst_port,
+ ntohs(ip->tot_len), ip->tos, ntohs(ip->id),
+ ntohs(ip->frag_off), ip->ttl);
+
+ for (opti = 0; opti < (ip->ihl - sizeof(struct iphdr) / 4); opti++)
+ printk(" O=0x%8.8X", *opt++);
+ printk(" %s(#%d)\n", syn ? "SYN " : /* "PENANCE" */ "", count);
+}
+
+/* function for checking chain labels for user space. */
+static int check_label(ip_chainlabel label)
+{
+ unsigned int i;
+ /* strlen must be < IP_FW_MAX_LABEL_LENGTH. */
+ for (i = 0; i < IP_FW_MAX_LABEL_LENGTH + 1; i++)
+ if (label[i] == '\0') return 1;
+
+ return 0;
+}
+
+/* This function returns a pointer to the first chain with a label
+ * that matches the one given. */
+static struct ip_chain *find_label(ip_chainlabel label)
+{
+ struct ip_chain *tmp;
+ FWC_HAVE_LOCK(fwc_rlocks | fwc_wlocks);
+ for (tmp = ip_fw_chains; tmp; tmp = tmp->next)
+ if (strcmp(tmp->label,label) == 0)
+ break;
+ return tmp;
+}
+
+/* This function returns a boolean which when true sets answer to one
+ of the FW_*. */
+static int find_special(ip_chainlabel label, int *answer)
+{
+ if (label[0] == '\0') {
+ *answer = FW_SKIP; /* => pass-through rule */
+ return 1;
+ } else if (strcmp(label,IP_FW_LABEL_ACCEPT) == 0) {
+ *answer = FW_ACCEPT;
+ return 1;
+ } else if (strcmp(label,IP_FW_LABEL_BLOCK) == 0) {
+ *answer = FW_BLOCK;
+ return 1;
+ } else if (strcmp(label,IP_FW_LABEL_REJECT) == 0) {
+ *answer = FW_REJECT;
+ return 1;
+ } else if (strcmp(label,IP_FW_LABEL_REDIRECT) == 0) {
+ *answer = FW_REDIRECT;
+ return 1;
+ } else if (strcmp(label,IP_FW_LABEL_MASQUERADE) == 0) {
+ *answer = FW_MASQUERADE;
+ return 1;
+ } else if (strcmp(label, IP_FW_LABEL_RETURN) == 0) {
+ *answer = FW_SKIP+1;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* This function cleans up the prevchain and prevrule. If the verbose
+ * flag is set then he names of the chains will be printed as it
+ * cleans up. */
+static void cleanup(struct ip_chain *chain,
+ const int verbose,
+ unsigned int slot)
+{
+ struct ip_chain *tmpchain = chain->reent[slot].prevchain;
+ if (verbose)
+ printk(KERN_ERR "Chain backtrace: ");
+ while (tmpchain) {
+ if (verbose)
+ printk("%s<-",chain->label);
+ chain->reent[slot].prevchain = NULL;
+ chain = tmpchain;
+ tmpchain = chain->reent[slot].prevchain;
+ }
+ if (verbose)
+ printk("%s\n",chain->label);
+}
+
+static inline int
+ip_fw_domatch(struct ip_fwkernel *f,
+ struct iphdr *ip,
+ const char *rif,
+ const ip_chainlabel label,
+ struct sk_buff *skb,
+ unsigned int slot,
+ __u16 src_port, __u16 dst_port,
+ unsigned int count,
+ int tcpsyn)
+{
+ f->counters[slot].bcnt+=ntohs(ip->tot_len);
+ f->counters[slot].pcnt++;
+ if (f->ipfw.fw_flg & IP_FW_F_PRN) {
+ dump_packet(ip,rif,f,label,src_port,dst_port,count,tcpsyn);
+ }
+ ip->tos = (ip->tos & f->ipfw.fw_tosand) ^ f->ipfw.fw_tosxor;
+
+/* This functionality is useless in stock 2.0.x series, but we don't
+ * discard the mark thing altogether, to avoid breaking ipchains (and,
+ * more importantly, the ipfwadm wrapper) --PR */
+ if (f->ipfw.fw_flg & IP_FW_F_MARKABS) {
+ skb->nfmark = f->ipfw.fw_mark;
+ } else {
+ skb->nfmark += f->ipfw.fw_mark;
+ }
+ if (f->ipfw.fw_flg & IP_FW_F_NETLINK) {
+#if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE)
+ size_t len = min(f->ipfw.fw_outputsize, ntohs(ip->tot_len))
+ + sizeof(__u32) + sizeof(skb->nfmark) + IFNAMSIZ;
+ struct sk_buff *outskb=alloc_skb(len, GFP_ATOMIC);
+
+ duprintf("Sending packet out NETLINK (length = %u).\n",
+ (unsigned int)len);
+ if (outskb) {
+ /* Prepend length, mark & interface */
+ skb_put(outskb, len);
+ *((__u32 *)outskb->data) = (__u32)len;
+ *((__u32 *)(outskb->data+sizeof(__u32))) = skb->nfmark;
+ strcpy(outskb->data+sizeof(__u32)*2, rif);
+ memcpy(outskb->data+sizeof(__u32)*2+IFNAMSIZ, ip,
+ len-(sizeof(__u32)*2+IFNAMSIZ));
+ netlink_broadcast(ipfwsk, outskb, 0, ~0, GFP_KERNEL);
+ }
+ else {
+#endif
+ if (net_ratelimit())
+ printk(KERN_WARNING "ip_fw: packet drop due to "
+ "netlink failure\n");
+ return 0;
+#if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE)
+ }
+#endif
+ }
+ return 1;
+}
+
+/*
+ * Returns one of the generic firewall policies, like FW_ACCEPT.
+ *
+ * The testing is either false for normal firewall mode or true for
+ * user checking mode (counters are not updated, TOS & mark not done).
+ */
+static int
+ip_fw_check(struct iphdr *ip,
+ const char *rif,
+ __u16 *redirport,
+ struct ip_chain *chain,
+ struct sk_buff *skb,
+ unsigned int slot,
+ int testing)
+{
+ struct tcphdr *tcp=(struct tcphdr *)((__u32 *)ip+ip->ihl);
+ struct udphdr *udp=(struct udphdr *)((__u32 *)ip+ip->ihl);
+ struct icmphdr *icmp=(struct icmphdr *)((__u32 *)ip+ip->ihl);
+ __u32 src, dst;
+ __u16 src_port = 0xFFFF, dst_port = 0xFFFF;
+ char tcpsyn=0;
+ __u16 offset;
+ unsigned char oldtos;
+ struct ip_fwkernel *f;
+ int ret = FW_SKIP+2;
+ unsigned int count;
+
+ /* We handle fragments by dealing with the first fragment as
+ * if it was a normal packet. All other fragments are treated
+ * normally, except that they will NEVER match rules that ask
+ * things we don't know, ie. tcp syn flag or ports). If the
+ * rule is also a fragment-specific rule, non-fragments won't
+ * match it. */
+
+ offset = ntohs(ip->frag_off) & IP_OFFSET;
+
+ /*
+ * Don't allow a fragment of TCP 8 bytes in. Nobody
+ * normal causes this. Its a cracker trying to break
+ * in by doing a flag overwrite to pass the direction
+ * checks.
+ */
+ if (offset == 1 && ip->protocol == IPPROTO_TCP) {
+ if (!testing && net_ratelimit()) {
+ printk("Suspect TCP fragment.\n");
+ dump_packet(ip,rif,NULL,NULL,0,0,0,0);
+ }
+ return FW_BLOCK;
+ }
+
+ /* If we can't investigate ports, treat as fragment. It's
+ * either a trucated whole packet, or a truncated first
+ * fragment, or a TCP first fragment of length 8-15, in which
+ * case the above rule stops reassembly.
+ */
+ if (offset == 0) {
+ unsigned int size_req;
+ switch (ip->protocol) {
+ case IPPROTO_TCP:
+ /* Don't care about things past flags word */
+ size_req = 16;
+ break;
+
+ case IPPROTO_UDP:
+ case IPPROTO_ICMP:
+ size_req = 8;
+ break;
+
+ default:
+ size_req = 0;
+ }
+
+ /* If it is a truncated first fragment then it can be
+ * used to rewrite port information, and thus should
+ * be blocked.
+ */
+ if (ntohs(ip->tot_len) < (ip->ihl<<2)+size_req) {
+ if (!testing && net_ratelimit()) {
+ printk("Suspect short first fragment.\n");
+ dump_packet(ip,rif,NULL,NULL,0,0,0,0);
+ }
+ return FW_BLOCK;
+ }
+ }
+
+ src = ip->saddr;
+ dst = ip->daddr;
+ oldtos = ip->tos;
+
+ /*
+ * If we got interface from which packet came
+ * we can use the address directly. Linux 2.1 now uses address
+ * chains per device too, but unlike BSD we first check if the
+ * incoming packet matches a device address and the routing
+ * table before calling the firewall.
+ */
+
+ dprintf("Packet ");
+ switch(ip->protocol)
+ {
+ case IPPROTO_TCP:
+ dprintf("TCP ");
+ if (!offset) {
+ src_port=ntohs(tcp->source);
+ dst_port=ntohs(tcp->dest);
+
+ /* Connection initilisation can only
+ * be made when the syn bit is set and
+ * neither of the ack or reset is
+ * set. */
+ if(tcp->syn && !(tcp->ack || tcp->rst))
+ tcpsyn=1;
+ }
+ break;
+ case IPPROTO_UDP:
+ dprintf("UDP ");
+ if (!offset) {
+ src_port=ntohs(udp->source);
+ dst_port=ntohs(udp->dest);
+ }
+ break;
+ case IPPROTO_ICMP:
+ if (!offset) {
+ src_port=(__u16)icmp->type;
+ dst_port=(__u16)icmp->code;
+ }
+ dprintf("ICMP ");
+ break;
+ default:
+ dprintf("p=%d ",ip->protocol);
+ break;
+ }
+#ifdef DEBUG_IP_FIREWALL
+ print_ip(ip->saddr);
+
+ if (offset)
+ dprintf(":fragment (%i) ", ((int)offset)<<2);
+ else if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP
+ || ip->protocol==IPPROTO_ICMP)
+ dprintf(":%hu:%hu", src_port, dst_port);
+ dprintf("\n");
+#endif
+
+ if (!testing) FWC_READ_LOCK(&ip_fw_lock);
+ else FWC_HAVE_LOCK(fwc_rlocks);
+
+ f = chain->chain;
+ do {
+ count = 0;
+ for (; f; f = f->next) {
+ count++;
+ if (ip_rule_match(f,rif,ip,
+ tcpsyn,src_port,dst_port,offset)) {
+ if (!testing
+ && !ip_fw_domatch(f, ip, rif, chain->label,
+ skb, slot,
+ src_port, dst_port,
+ count, tcpsyn)) {
+ ret = FW_BLOCK;
+ goto out;
+ }
+ break;
+ }
+ }
+ if (f) {
+ if (f->branch) {
+ /* Do sanity check to see if we have
+ * already set prevchain and if so we
+ * must be in a loop */
+ if (f->branch->reent[slot].prevchain) {
+ if (!testing) {
+ printk(KERN_ERR
+ "IP firewall: "
+ "Loop detected "
+ "at `%s'.\n",
+ f->branch->label);
+ cleanup(chain, 1, slot);
+ ret = FW_BLOCK;
+ } else {
+ cleanup(chain, 0, slot);
+ ret = FW_SKIP+1;
+ }
+ }
+ else {
+ f->branch->reent[slot].prevchain
+ = chain;
+ f->branch->reent[slot].prevrule
+ = f->next;
+ chain = f->branch;
+ f = chain->chain;
+ }
+ }
+ else if (f->simplebranch == FW_SKIP)
+ f = f->next;
+ else if (f->simplebranch == FW_SKIP+1) {
+ /* Just like falling off the chain */
+ goto fall_off_chain;
+ } else {
+ cleanup(chain, 0, slot);
+ ret = f->simplebranch;
+ }
+ } /* f == NULL */
+ else {
+ fall_off_chain:
+ if (chain->reent[slot].prevchain) {
+ struct ip_chain *tmp = chain;
+ f = chain->reent[slot].prevrule;
+ chain = chain->reent[slot].prevchain;
+ tmp->reent[slot].prevchain = NULL;
+ }
+ else {
+ ret = chain->policy;
+ if (!testing) {
+ chain->reent[slot].counters.pcnt++;
+ chain->reent[slot].counters.bcnt
+ += ntohs(ip->tot_len);
+ }
+ }
+ }
+ } while (ret == FW_SKIP+2);
+
+ out:
+ if (!testing) FWC_READ_UNLOCK(&ip_fw_lock);
+
+ /* Recalculate checksum if not going to reject, and TOS changed. */
+ if (ip->tos != oldtos
+ && ret != FW_REJECT && ret != FW_BLOCK
+ && !testing)
+ ip_send_check(ip);
+
+ if (ret == FW_REDIRECT && redirport) {
+ if ((*redirport = htons(f->ipfw.fw_redirpt)) == 0) {
+ /* Wildcard redirection.
+ * Note that redirport will become
+ * 0xFFFF for non-TCP/UDP packets.
+ */
+ *redirport = htons(dst_port);
+ }
+ }
+
+#ifdef DEBUG_ALLOW_ALL
+ return (testing ? ret : FW_ACCEPT);
+#else
+ return ret;
+#endif
+}
+
+/* Must have write lock & interrupts off for any of these */
+
+/* This function sets all the byte counters in a chain to zero. The
+ * input is a pointer to the chain required for zeroing */
+static int zero_fw_chain(struct ip_chain *chainptr)
+{
+ struct ip_fwkernel *i;
+
+ FWC_HAVE_LOCK(fwc_wlocks);
+ for (i = chainptr->chain; i; i = i->next)
+ memset(i->counters, 0, sizeof(struct ip_counters)*NUM_SLOTS);
+ return 0;
+}
+
+static int clear_fw_chain(struct ip_chain *chainptr)
+{
+ struct ip_fwkernel *i= chainptr->chain;
+
+ FWC_HAVE_LOCK(fwc_wlocks);
+ chainptr->chain=NULL;
+
+ while (i) {
+ struct ip_fwkernel *tmp = i->next;
+ if (i->branch)
+ i->branch->refcount--;
+ kfree(i);
+ i = tmp;
+ }
+ return 0;
+}
+
+static int replace_in_chain(struct ip_chain *chainptr,
+ struct ip_fwkernel *frwl,
+ __u32 position)
+{
+ struct ip_fwkernel *f = chainptr->chain;
+
+ FWC_HAVE_LOCK(fwc_wlocks);
+
+ while (--position && f != NULL) f = f->next;
+ if (f == NULL)
+ return EINVAL;
+
+ if (f->branch) f->branch->refcount--;
+ if (frwl->branch) frwl->branch->refcount++;
+
+ frwl->next = f->next;
+ memcpy(f,frwl,sizeof(struct ip_fwkernel));
+ kfree(frwl);
+ return 0;
+}
+
+static int append_to_chain(struct ip_chain *chainptr, struct ip_fwkernel *rule)
+{
+ struct ip_fwkernel *i;
+
+ FWC_HAVE_LOCK(fwc_wlocks);
+ /* Special case if no rules already present */
+ if (chainptr->chain == NULL) {
+
+ /* If pointer writes are atomic then turning off
+ * interupts is not necessary. */
+ chainptr->chain = rule;
+ if (rule->branch) rule->branch->refcount++;
+ return 0;
+ }
+
+ /* Find the rule before the end of the chain */
+ for (i = chainptr->chain; i->next; i = i->next);
+ i->next = rule;
+ if (rule->branch) rule->branch->refcount++;
+ return 0;
+}
+
+/* This function inserts a rule at the position of position in the
+ * chain refenced by chainptr. If position is 1 then this rule will
+ * become the new rule one. */
+static int insert_in_chain(struct ip_chain *chainptr,
+ struct ip_fwkernel *frwl,
+ __u32 position)
+{
+ struct ip_fwkernel *f = chainptr->chain;
+
+ FWC_HAVE_LOCK(fwc_wlocks);
+ /* special case if the position is number 1 */
+ if (position == 1) {
+ frwl->next = chainptr->chain;
+ if (frwl->branch) frwl->branch->refcount++;
+ chainptr->chain = frwl;
+ return 0;
+ }
+ position--;
+ while (--position && f != NULL) f = f->next;
+ if (f == NULL)
+ return EINVAL;
+ if (frwl->branch) frwl->branch->refcount++;
+ frwl->next = f->next;
+
+ f->next = frwl;
+ return 0;
+}
+
+/* This function deletes the a rule from a given rulenum and chain.
+ * With rulenum = 1 is the first rule is deleted. */
+
+static int del_num_from_chain(struct ip_chain *chainptr, __u32 rulenum)
+{
+ struct ip_fwkernel *i=chainptr->chain,*tmp;
+
+ FWC_HAVE_LOCK(fwc_wlocks);
+
+ if (!chainptr->chain)
+ return ENOENT;
+
+ /* Need a special case for the first rule */
+ if (rulenum == 1) {
+ /* store temp to allow for freeing up of memory */
+ tmp = chainptr->chain;
+ if (chainptr->chain->branch) chainptr->chain->branch->refcount--;
+ chainptr->chain = chainptr->chain->next;
+ kfree(tmp); /* free memory that is now unused */
+ } else {
+ rulenum--;
+ while (--rulenum && i->next ) i = i->next;
+ if (!i->next)
+ return ENOENT;
+ tmp = i->next;
+ if (i->next->branch)
+ i->next->branch->refcount--;
+ i->next = i->next->next;
+ kfree(tmp);
+ }
+ return 0;
+}
+
+
+/* This function deletes the a rule from a given rule and chain.
+ * The rule that is deleted is the first occursance of that rule. */
+static int del_rule_from_chain(struct ip_chain *chainptr,
+ struct ip_fwkernel *frwl)
+{
+ struct ip_fwkernel *ltmp,*ftmp = chainptr->chain ;
+ int was_found;
+
+ FWC_HAVE_LOCK(fwc_wlocks);
+
+ /* Sure, we should compare marks, but since the `ipfwadm'
+ * script uses it for an unholy hack... well, life is easier
+ * this way. We also mask it out of the flags word. --PR */
+ for (ltmp=NULL, was_found=0;
+ !was_found && ftmp != NULL;
+ ltmp = ftmp,ftmp = ftmp->next) {
+ if (ftmp->ipfw.fw_src.s_addr!=frwl->ipfw.fw_src.s_addr
+ || ftmp->ipfw.fw_dst.s_addr!=frwl->ipfw.fw_dst.s_addr
+ || ftmp->ipfw.fw_smsk.s_addr!=frwl->ipfw.fw_smsk.s_addr
+ || ftmp->ipfw.fw_dmsk.s_addr!=frwl->ipfw.fw_dmsk.s_addr
+#if 0
+ || ftmp->ipfw.fw_flg!=frwl->ipfw.fw_flg
+#else
+ || ((ftmp->ipfw.fw_flg & ~IP_FW_F_MARKABS)
+ != (frwl->ipfw.fw_flg & ~IP_FW_F_MARKABS))
+#endif
+ || ftmp->ipfw.fw_invflg!=frwl->ipfw.fw_invflg
+ || ftmp->ipfw.fw_proto!=frwl->ipfw.fw_proto
+#if 0
+ || ftmp->ipfw.fw_mark!=frwl->ipfw.fw_mark
+#endif
+ || ftmp->ipfw.fw_redirpt!=frwl->ipfw.fw_redirpt
+ || ftmp->ipfw.fw_spts[0]!=frwl->ipfw.fw_spts[0]
+ || ftmp->ipfw.fw_spts[1]!=frwl->ipfw.fw_spts[1]
+ || ftmp->ipfw.fw_dpts[0]!=frwl->ipfw.fw_dpts[0]
+ || ftmp->ipfw.fw_dpts[1]!=frwl->ipfw.fw_dpts[1]
+ || ftmp->ipfw.fw_outputsize!=frwl->ipfw.fw_outputsize) {
+ duprintf("del_rule_from_chain: mismatch:"
+ "src:%u/%u dst:%u/%u smsk:%u/%u dmsk:%u/%u "
+ "flg:%hX/%hX invflg:%hX/%hX proto:%u/%u "
+ "mark:%u/%u "
+ "ports:%hu-%hu/%hu-%hu %hu-%hu/%hu-%hu "
+ "outputsize:%hu-%hu\n",
+ ftmp->ipfw.fw_src.s_addr,
+ frwl->ipfw.fw_src.s_addr,
+ ftmp->ipfw.fw_dst.s_addr,
+ frwl->ipfw.fw_dst.s_addr,
+ ftmp->ipfw.fw_smsk.s_addr,
+ frwl->ipfw.fw_smsk.s_addr,
+ ftmp->ipfw.fw_dmsk.s_addr,
+ frwl->ipfw.fw_dmsk.s_addr,
+ ftmp->ipfw.fw_flg,
+ frwl->ipfw.fw_flg,
+ ftmp->ipfw.fw_invflg,
+ frwl->ipfw.fw_invflg,
+ ftmp->ipfw.fw_proto,
+ frwl->ipfw.fw_proto,
+ ftmp->ipfw.fw_mark,
+ frwl->ipfw.fw_mark,
+ ftmp->ipfw.fw_spts[0],
+ frwl->ipfw.fw_spts[0],
+ ftmp->ipfw.fw_spts[1],
+ frwl->ipfw.fw_spts[1],
+ ftmp->ipfw.fw_dpts[0],
+ frwl->ipfw.fw_dpts[0],
+ ftmp->ipfw.fw_dpts[1],
+ frwl->ipfw.fw_dpts[1],
+ ftmp->ipfw.fw_outputsize,
+ frwl->ipfw.fw_outputsize);
+ continue;
+ }
+
+ if (strncmp(ftmp->ipfw.fw_vianame,
+ frwl->ipfw.fw_vianame,
+ IFNAMSIZ)) {
+ duprintf("del_rule_from_chain: if mismatch: %s/%s\n",
+ ftmp->ipfw.fw_vianame,
+ frwl->ipfw.fw_vianame);
+ continue;
+ }
+ if (ftmp->branch != frwl->branch) {
+ duprintf("del_rule_from_chain: branch mismatch: "
+ "%s/%s\n",
+ ftmp->branch?ftmp->branch->label:"(null)",
+ frwl->branch?frwl->branch->label:"(null)");
+ continue;
+ }
+ if (ftmp->branch == NULL
+ && ftmp->simplebranch != frwl->simplebranch) {
+ duprintf("del_rule_from_chain: simplebranch mismatch: "
+ "%i/%i\n",
+ ftmp->simplebranch, frwl->simplebranch);
+ continue;
+ }
+ was_found = 1;
+ if (ftmp->branch)
+ ftmp->branch->refcount--;
+ if (ltmp)
+ ltmp->next = ftmp->next;
+ else
+ chainptr->chain = ftmp->next;
+ kfree(ftmp);
+ break;
+ }
+
+ if (was_found)
+ return 0;
+ else {
+ duprintf("del_rule_from_chain: no matching rule found\n");
+ return EINVAL;
+ }
+}
+
+/* This function takes the label of a chain and deletes the first
+ * chain with that name. No special cases required for the built in
+ * chains as they have their refcount initilised to 1 so that they are
+ * never deleted. */
+static int del_chain(ip_chainlabel label)
+{
+ struct ip_chain *tmp,*tmp2;
+
+ FWC_HAVE_LOCK(fwc_wlocks);
+ /* Corner case: return EBUSY not ENOENT for first elem ("input") */
+ if (strcmp(label, ip_fw_chains->label) == 0)
+ return EBUSY;
+
+ for (tmp = ip_fw_chains; tmp->next; tmp = tmp->next)
+ if(strcmp(tmp->next->label,label) == 0)
+ break;
+
+ tmp2 = tmp->next;
+ if (!tmp2)
+ return ENOENT;
+
+ if (tmp2->refcount)
+ return EBUSY;
+
+ if (tmp2->chain)
+ return ENOTEMPTY;
+
+ tmp->next = tmp2->next;
+ kfree(tmp2);
+ return 0;
+}
+
+/* This is a function to initilise a chain. Built in rules start with
+ * refcount = 1 so that they cannot be deleted. User defined rules
+ * start with refcount = 0 so they can be deleted. */
+static struct ip_chain *ip_init_chain(ip_chainlabel name,
+ __u32 ref,
+ int policy)
+{
+ unsigned int i;
+ struct ip_chain *label
+ = kmalloc(SIZEOF_STRUCT_IP_CHAIN, GFP_KERNEL);
+ if (label == NULL)
+ panic("Can't kmalloc for firewall chains.\n");
+ strcpy(label->label,name);
+ label->next = NULL;
+ label->chain = NULL;
+ label->refcount = ref;
+ label->policy = policy;
+ for (i = 0; i < smp_num_cpus*2; i++) {
+ label->reent[i].counters.pcnt = label->reent[i].counters.bcnt
+ = 0;
+ label->reent[i].prevchain = NULL;
+ label->reent[i].prevrule = NULL;
+ }
+
+ return label;
+}
+
+/* This is a function for reating a new chain. The chains is not
+ * created if a chain of the same name already exists */
+static int create_chain(ip_chainlabel label)
+{
+ struct ip_chain *tmp;
+
+ if (!check_label(label))
+ return EINVAL;
+
+ FWC_HAVE_LOCK(fwc_wlocks);
+ for (tmp = ip_fw_chains; tmp->next; tmp = tmp->next)
+ if (strcmp(tmp->label,label) == 0)
+ return EEXIST;
+
+ if (strcmp(tmp->label,label) == 0)
+ return EEXIST;
+
+ tmp->next = ip_init_chain(label, 0, FW_SKIP); /* refcount is
+ * zero since this is a
+ * user defined chain *
+ * and therefore can be
+ * deleted */
+ return 0;
+}
+
+/* This function simply changes the policy on one of the built in
+ * chains. checking must be done before this is call to ensure that
+ * chainptr is pointing to one of the three possible chains */
+static int change_policy(struct ip_chain *chainptr, int policy)
+{
+ FWC_HAVE_LOCK(fwc_wlocks);
+ chainptr->policy = policy;
+ return 0;
+}
+
+/* This function takes an ip_fwuser and converts it to a ip_fwkernel. It also
+ * performs some checks in the structure. */
+static struct ip_fwkernel *convert_ipfw(struct ip_fwuser *fwuser, int *errno)
+{
+ struct ip_fwkernel *fwkern;
+
+ if ( (fwuser->ipfw.fw_flg & ~IP_FW_F_MASK) != 0 ) {
+ duprintf("convert_ipfw: undefined flag bits set (flags=%x)\n",
+ fwuser->ipfw.fw_flg);
+ *errno = EINVAL;
+ return NULL;
+ }
+
+#ifdef DEBUG_IP_FIREWALL_USER
+ /* These are sanity checks that don't really matter.
+ * We can get rid of these once testing is complete.
+ */
+ if ((fwuser->ipfw.fw_flg & IP_FW_F_TCPSYN)
+ && ((fwuser->ipfw.fw_invflg & IP_FW_INV_PROTO)
+ || fwuser->ipfw.fw_proto != IPPROTO_TCP)) {
+ duprintf("convert_ipfw: TCP SYN flag set but proto != TCP!\n");
+ *errno = EINVAL;
+ return NULL;
+ }
+
+ if (strcmp(fwuser->label, IP_FW_LABEL_REDIRECT) != 0
+ && fwuser->ipfw.fw_redirpt != 0) {
+ duprintf("convert_ipfw: Target not REDIR but redirpt != 0!\n");
+ *errno = EINVAL;
+ return NULL;
+ }
+
+ if ((!(fwuser->ipfw.fw_flg & IP_FW_F_FRAG)
+ && (fwuser->ipfw.fw_invflg & IP_FW_INV_FRAG))
+ || (!(fwuser->ipfw.fw_flg & IP_FW_F_TCPSYN)
+ && (fwuser->ipfw.fw_invflg & IP_FW_INV_SYN))) {
+ duprintf("convert_ipfw: Can't have INV flag if flag unset!\n");
+ *errno = EINVAL;
+ return NULL;
+ }
+
+ if (((fwuser->ipfw.fw_invflg & IP_FW_INV_SRCPT)
+ && fwuser->ipfw.fw_spts[0] == 0
+ && fwuser->ipfw.fw_spts[1] == 0xFFFF)
+ || ((fwuser->ipfw.fw_invflg & IP_FW_INV_DSTPT)
+ && fwuser->ipfw.fw_dpts[0] == 0
+ && fwuser->ipfw.fw_dpts[1] == 0xFFFF)
+ || ((fwuser->ipfw.fw_invflg & IP_FW_INV_VIA)
+ && (fwuser->ipfw.fw_vianame)[0] == '\0')
+ || ((fwuser->ipfw.fw_invflg & IP_FW_INV_SRCIP)
+ && fwuser->ipfw.fw_smsk.s_addr == 0)
+ || ((fwuser->ipfw.fw_invflg & IP_FW_INV_DSTIP)
+ && fwuser->ipfw.fw_dmsk.s_addr == 0)) {
+ duprintf("convert_ipfw: INV flag makes rule unmatchable!\n");
+ *errno = EINVAL;
+ return NULL;
+ }
+
+ if ((fwuser->ipfw.fw_flg & IP_FW_F_FRAG)
+ && !(fwuser->ipfw.fw_invflg & IP_FW_INV_FRAG)
+ && (fwuser->ipfw.fw_spts[0] != 0
+ || fwuser->ipfw.fw_spts[1] != 0xFFFF
+ || fwuser->ipfw.fw_dpts[0] != 0
+ || fwuser->ipfw.fw_dpts[1] != 0xFFFF
+ || (fwuser->ipfw.fw_flg & IP_FW_F_TCPSYN))) {
+ duprintf("convert_ipfw: Can't test ports or SYN with frag!\n");
+ *errno = EINVAL;
+ return NULL;
+ }
+#endif
+
+ if ((fwuser->ipfw.fw_spts[0] != 0
+ || fwuser->ipfw.fw_spts[1] != 0xFFFF
+ || fwuser->ipfw.fw_dpts[0] != 0
+ || fwuser->ipfw.fw_dpts[1] != 0xFFFF)
+ && ((fwuser->ipfw.fw_invflg & IP_FW_INV_PROTO)
+ || (fwuser->ipfw.fw_proto != IPPROTO_TCP
+ && fwuser->ipfw.fw_proto != IPPROTO_UDP
+ && fwuser->ipfw.fw_proto != IPPROTO_ICMP))) {
+ duprintf("convert_ipfw: Can only test ports for TCP/UDP/ICMP!\n");
+ *errno = EINVAL;
+ return NULL;
+ }
+
+ fwkern = kmalloc(SIZEOF_STRUCT_IP_FW_KERNEL, GFP_KERNEL);
+ if (!fwkern) {
+ duprintf("convert_ipfw: kmalloc failed!\n");
+ *errno = ENOMEM;
+ return NULL;
+ }
+ memcpy(&fwkern->ipfw,&fwuser->ipfw,sizeof(struct ip_fw));
+
+ if (!find_special(fwuser->label, &fwkern->simplebranch)) {
+ fwkern->branch = find_label(fwuser->label);
+ if (!fwkern->branch) {
+ duprintf("convert_ipfw: chain doesn't exist `%s'.\n",
+ fwuser->label);
+ kfree(fwkern);
+ *errno = ENOENT;
+ return NULL;
+ } else if (fwkern->branch == IP_FW_INPUT_CHAIN
+ || fwkern->branch == IP_FW_FORWARD_CHAIN
+ || fwkern->branch == IP_FW_OUTPUT_CHAIN) {
+ duprintf("convert_ipfw: Can't branch to builtin chain `%s'.\n",
+ fwuser->label);
+ kfree(fwkern);
+ *errno = ENOENT;
+ return NULL;
+ }
+ } else
+ fwkern->branch = NULL;
+ memset(fwkern->counters, 0, sizeof(struct ip_counters)*NUM_SLOTS);
+
+ /* Handle empty vianame by making it a wildcard */
+ if ((fwkern->ipfw.fw_vianame)[0] == '\0')
+ fwkern->ipfw.fw_flg |= IP_FW_F_WILDIF;
+
+ fwkern->next = NULL;
+ return fwkern;
+}
+
+int ip_fw_ctl(int cmd, void *m, int len)
+{
+ int ret;
+ struct ip_chain *chain;
+ unsigned long flags;
+
+ FWC_WRITE_LOCK_IRQ(&ip_fw_lock, flags);
+
+ switch (cmd) {
+ case IP_FW_FLUSH:
+ if (len != sizeof(ip_chainlabel) || !check_label(m))
+ ret = EINVAL;
+ else if ((chain = find_label(m)) == NULL)
+ ret = ENOENT;
+ else ret = clear_fw_chain(chain);
+ break;
+
+ case IP_FW_ZERO:
+ if (len != sizeof(ip_chainlabel) || !check_label(m))
+ ret = EINVAL;
+ else if ((chain = find_label(m)) == NULL)
+ ret = ENOENT;
+ else ret = zero_fw_chain(chain);
+ break;
+
+ case IP_FW_CHECK: {
+ struct ip_fwtest *new = m;
+ struct iphdr *ip;
+
+ /* Don't need write lock. */
+ FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
+
+ if (len != sizeof(struct ip_fwtest) || !check_label(m))
+ return EINVAL;
+
+ /* Need readlock to do find_label */
+ FWC_READ_LOCK(&ip_fw_lock);
+
+ if ((chain = find_label(new->fwt_label)) == NULL)
+ ret = ENOENT;
+ else {
+ ip = &(new->fwt_packet.fwp_iph);
+
+ if (ip->ihl != sizeof(struct iphdr) / sizeof(int)) {
+ duprintf("ip_fw_ctl: ip->ihl=%d, want %d\n",
+ ip->ihl,
+ sizeof(struct iphdr) / sizeof(int));
+ ret = EINVAL;
+ }
+ else {
+ ret = ip_fw_check(ip, new->fwt_packet.fwp_vianame,
+ NULL, chain,
+ NULL, SLOT_NUMBER(), 1);
+ switch (ret) {
+ case FW_ACCEPT:
+ ret = 0; break;
+ case FW_REDIRECT:
+ ret = ECONNABORTED; break;
+ case FW_MASQUERADE:
+ ret = ECONNRESET; break;
+ case FW_REJECT:
+ ret = ECONNREFUSED; break;
+ /* Hack to help diag; these only get
+ returned when testing. */
+ case FW_SKIP+1:
+ ret = ELOOP; break;
+ case FW_SKIP:
+ ret = ENFILE; break;
+ default: /* FW_BLOCK */
+ ret = ETIMEDOUT; break;
+ }
+ }
+ }
+ FWC_READ_UNLOCK(&ip_fw_lock);
+ return ret;
+ }
+
+ case IP_FW_MASQ_TIMEOUTS: {
+ ret = ip_fw_masq_timeouts(m, len);
+ }
+ break;
+
+ case IP_FW_REPLACE: {
+ struct ip_fwkernel *ip_fwkern;
+ struct ip_fwnew *new = m;
+
+ if (len != sizeof(struct ip_fwnew)
+ || !check_label(new->fwn_label))
+ ret = EINVAL;
+ else if ((chain = find_label(new->fwn_label)) == NULL)
+ ret = ENOENT;
+ else if ((ip_fwkern = convert_ipfw(&new->fwn_rule, &ret))
+ != NULL)
+ ret = replace_in_chain(chain, ip_fwkern,
+ new->fwn_rulenum);
+ }
+ break;
+
+ case IP_FW_APPEND: {
+ struct ip_fwchange *new = m;
+ struct ip_fwkernel *ip_fwkern;
+
+ if (len != sizeof(struct ip_fwchange)
+ || !check_label(new->fwc_label))
+ ret = EINVAL;
+ else if ((chain = find_label(new->fwc_label)) == NULL)
+ ret = ENOENT;
+ else if ((ip_fwkern = convert_ipfw(&new->fwc_rule, &ret))
+ != NULL)
+ ret = append_to_chain(chain, ip_fwkern);
+ }
+ break;
+
+ case IP_FW_INSERT: {
+ struct ip_fwkernel *ip_fwkern;
+ struct ip_fwnew *new = m;
+
+ if (len != sizeof(struct ip_fwnew)
+ || !check_label(new->fwn_label))
+ ret = EINVAL;
+ else if ((chain = find_label(new->fwn_label)) == NULL)
+ ret = ENOENT;
+ else if ((ip_fwkern = convert_ipfw(&new->fwn_rule, &ret))
+ != NULL)
+ ret = insert_in_chain(chain, ip_fwkern,
+ new->fwn_rulenum);
+ }
+ break;
+
+ case IP_FW_DELETE: {
+ struct ip_fwchange *new = m;
+ struct ip_fwkernel *ip_fwkern;
+
+ if (len != sizeof(struct ip_fwchange)
+ || !check_label(new->fwc_label))
+ ret = EINVAL;
+ else if ((chain = find_label(new->fwc_label)) == NULL)
+ ret = ENOENT;
+ else if ((ip_fwkern = convert_ipfw(&new->fwc_rule, &ret))
+ != NULL) {
+ ret = del_rule_from_chain(chain, ip_fwkern);
+ kfree(ip_fwkern);
+ }
+ }
+ break;
+
+ case IP_FW_DELETE_NUM: {
+ struct ip_fwdelnum *new = m;
+
+ if (len != sizeof(struct ip_fwdelnum)
+ || !check_label(new->fwd_label))
+ ret = EINVAL;
+ else if ((chain = find_label(new->fwd_label)) == NULL)
+ ret = ENOENT;
+ else ret = del_num_from_chain(chain, new->fwd_rulenum);
+ }
+ break;
+
+ case IP_FW_CREATECHAIN: {
+ if (len != sizeof(ip_chainlabel)) {
+ duprintf("create_chain: bad size %i\n", len);
+ ret = EINVAL;
+ }
+ else ret = create_chain(m);
+ }
+ break;
+
+ case IP_FW_DELETECHAIN: {
+ if (len != sizeof(ip_chainlabel)) {
+ duprintf("delete_chain: bad size %i\n", len);
+ ret = EINVAL;
+ }
+ else ret = del_chain(m);
+ }
+ break;
+
+ case IP_FW_POLICY: {
+ struct ip_fwpolicy *new = m;
+
+ if (len != sizeof(struct ip_fwpolicy)
+ || !check_label(new->fwp_label))
+ ret = EINVAL;
+ else if ((chain = find_label(new->fwp_label)) == NULL)
+ ret = ENOENT;
+ else if (chain != IP_FW_INPUT_CHAIN
+ && chain != IP_FW_FORWARD_CHAIN
+ && chain != IP_FW_OUTPUT_CHAIN) {
+ duprintf("change_policy: can't change policy on user"
+ " defined chain.\n");
+ ret = EINVAL;
+ }
+ else {
+ int pol = FW_SKIP;
+ find_special(new->fwp_policy, &pol);
+
+ switch(pol) {
+ case FW_MASQUERADE:
+ if (chain != IP_FW_FORWARD_CHAIN) {
+ ret = EINVAL;
+ break;
+ }
+ /* Fall thru... */
+ case FW_BLOCK:
+ case FW_ACCEPT:
+ case FW_REJECT:
+ ret = change_policy(chain, pol);
+ break;
+ default:
+ duprintf("change_policy: bad policy `%s'\n",
+ new->fwp_policy);
+ ret = EINVAL;
+ }
+ }
+ break;
+ }
+ default:
+ duprintf("ip_fw_ctl: unknown request %d\n",cmd);
+ ret = ENOPROTOOPT;
+ }
+
+ FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
+ return ret;
+}
+
+/* Returns bytes used - doesn't NUL terminate */
+static int dump_rule(char *buffer,
+ const char *chainlabel,
+ const struct ip_fwkernel *rule)
+{
+ int len;
+ unsigned int i;
+ __u64 packets = 0, bytes = 0;
+
+ FWC_HAVE_LOCK(fwc_wlocks);
+ for (i = 0; i < NUM_SLOTS; i++) {
+ packets += rule->counters[i].pcnt;
+ bytes += rule->counters[i].bcnt;
+ }
+
+ len=sprintf(buffer,
+ "%9s " /* Chain name */
+ "%08X/%08X->%08X/%08X " /* Source & Destination IPs */
+ "%.16s " /* Interface */
+ "%X %X " /* fw_flg and fw_invflg fields */
+ "%u " /* Protocol */
+ "%-9u %-9u %-9u %-9u " /* Packet & byte counters */
+ "%u-%u %u-%u " /* Source & Dest port ranges */
+ "A%02X X%02X " /* TOS and and xor masks */
+ "%08X " /* Redirection port */
+ "%u " /* fw_mark field */
+ "%u " /* output size */
+ "%9s\n", /* Target */
+ chainlabel,
+ ntohl(rule->ipfw.fw_src.s_addr),
+ ntohl(rule->ipfw.fw_smsk.s_addr),
+ ntohl(rule->ipfw.fw_dst.s_addr),
+ ntohl(rule->ipfw.fw_dmsk.s_addr),
+ (rule->ipfw.fw_vianame)[0] ? rule->ipfw.fw_vianame : "-",
+ rule->ipfw.fw_flg,
+ rule->ipfw.fw_invflg,
+ rule->ipfw.fw_proto,
+ (__u32)(packets >> 32), (__u32)packets,
+ (__u32)(bytes >> 32), (__u32)bytes,
+ rule->ipfw.fw_spts[0], rule->ipfw.fw_spts[1],
+ rule->ipfw.fw_dpts[0], rule->ipfw.fw_dpts[1],
+ rule->ipfw.fw_tosand, rule->ipfw.fw_tosxor,
+ rule->ipfw.fw_redirpt,
+ rule->ipfw.fw_mark,
+ rule->ipfw.fw_outputsize,
+ branchname(rule->branch,rule->simplebranch));
+
+ duprintf("dump_rule: %i bytes done.\n", len);
+ return len;
+}
+
+/* File offset is actually in records, not bytes. */
+static int ip_chain_procinfo(char *buffer, char **start,
+ off_t offset, int length
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,29)
+ , int reset
+#endif
+ )
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,29)
+ /* FIXME: No more `atomic' read and reset. Wonderful 8-( --RR */
+ int reset = 0;
+#endif
+ struct ip_chain *i;
+ struct ip_fwkernel *j = ip_fw_chains->chain;
+ unsigned long flags;
+ int len = 0;
+ int last_len = 0;
+ off_t upto = 0;
+
+ duprintf("Offset starts at %lu\n", offset);
+ duprintf("ip_fw_chains is 0x%0lX\n", (unsigned long int)ip_fw_chains);
+
+ /* Need a write lock to lock out ``readers'' which update counters. */
+ FWC_WRITE_LOCK_IRQ(&ip_fw_lock, flags);
+
+ for (i = ip_fw_chains; i; i = i->next) {
+ for (j = i->chain; j; j = j->next) {
+ if (upto == offset) break;
+ duprintf("Skipping rule in chain `%s'\n",
+ i->label);
+ upto++;
+ }
+ if (upto == offset) break;
+ }
+
+ /* Don't init j first time, or once i = NULL */
+ for (; i; (void)((i = i->next) && (j = i->chain))) {
+ duprintf("Dumping chain `%s'\n", i->label);
+ for (; j; j = j->next, upto++, last_len = len)
+ {
+ len += dump_rule(buffer+len, i->label, j);
+ if (len > length) {
+ duprintf("Dumped to %i (past %i). "
+ "Moving back to %i.\n",
+ len, length, last_len);
+ len = last_len;
+ goto outside;
+ }
+ else if (reset)
+ memset(j->counters, 0,
+ sizeof(struct ip_counters)*NUM_SLOTS);
+ }
+ }
+outside:
+ FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
+ buffer[len] = '\0';
+
+ duprintf("ip_chain_procinfo: Length = %i (of %i). Offset = %li.\n",
+ len, length, upto);
+ /* `start' hack - see fs/proc/generic.c line ~165 */
+ *start=(char *)((unsigned int)upto-offset);
+ return len;
+}
+
+static int ip_chain_name_procinfo(char *buffer, char **start,
+ off_t offset, int length)
+{
+ struct ip_chain *i;
+ int len = 0,last_len = 0;
+ off_t pos = 0,begin = 0;
+ unsigned long flags;
+
+ /* Need a write lock to lock out ``readers'' which update counters. */
+ FWC_WRITE_LOCK_IRQ(&ip_fw_lock, flags);
+
+ for (i = ip_fw_chains; i; i = i->next)
+ {
+ unsigned int j;
+ __u32 packetsHi = 0, packetsLo = 0, bytesHi = 0, bytesLo = 0;
+
+ for (j = 0; j < NUM_SLOTS; j++) {
+ packetsLo += i->reent[j].counters.pcnt & 0xFFFFFFFF;
+ packetsHi += ((i->reent[j].counters.pcnt >> 32)
+ & 0xFFFFFFFF);
+ bytesLo += i->reent[j].counters.bcnt & 0xFFFFFFFF;
+ bytesHi += ((i->reent[j].counters.bcnt >> 32)
+ & 0xFFFFFFFF);
+ }
+
+ /* print the label and the policy */
+ len+=sprintf(buffer+len,"%s %s %i %u %u %u %u\n",
+ i->label,branchname(NULL, i->policy),i->refcount,
+ packetsHi, packetsLo, bytesHi, bytesLo);
+ pos=begin+len;
+ if(pos<offset) {
+ len=0;
+ begin=pos;
+ }
+ else if(pos>offset+length) {
+ len = last_len;
+ break;
+ }
+
+ last_len = len;
+ }
+ FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
+
+ *start = buffer+(offset-begin);
+ len-=(offset-begin);
+ if(len>length)
+ len=length;
+ return len;
+}
+
+/*
+ * Interface to the generic firewall chains.
+ */
+int ipfw_input_check(struct firewall_ops *this, int pf,
+ struct net_device *dev, void *phdr, void *arg,
+ struct sk_buff **pskb)
+{
+ return ip_fw_check(phdr, dev->name,
+ arg, IP_FW_INPUT_CHAIN, *pskb, SLOT_NUMBER(), 0);
+}
+
+int ipfw_output_check(struct firewall_ops *this, int pf,
+ struct net_device *dev, void *phdr, void *arg,
+ struct sk_buff **pskb)
+{
+ /* Locally generated bogus packets by root. <SIGH>. */
+ if (((struct iphdr *)phdr)->ihl * 4 < sizeof(struct iphdr)
+ || (*pskb)->len < sizeof(struct iphdr))
+ return FW_ACCEPT;
+ return ip_fw_check(phdr, dev->name,
+ arg, IP_FW_OUTPUT_CHAIN, *pskb, SLOT_NUMBER(), 0);
+}
+
+int ipfw_forward_check(struct firewall_ops *this, int pf,
+ struct net_device *dev, void *phdr, void *arg,
+ struct sk_buff **pskb)
+{
+ return ip_fw_check(phdr, dev->name,
+ arg, IP_FW_FORWARD_CHAIN, *pskb, SLOT_NUMBER(), 0);
+}
+
+struct firewall_ops ipfw_ops=
+{
+ NULL,
+ ipfw_forward_check,
+ ipfw_input_check,
+ ipfw_output_check,
+ NULL,
+ NULL
+};
+
+int ipfw_init_or_cleanup(int init)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ FWC_WRITE_LOCK_IRQ(&ip_fw_lock, flags);
+
+ if (!init) goto cleanup;
+
+#ifdef DEBUG_IP_FIREWALL_LOCKING
+ fwc_wlocks = fwc_rlocks = 0;
+#endif
+
+#if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE)
+ ipfwsk = netlink_kernel_create(NETLINK_FIREWALL, NULL);
+ if (ipfwsk == NULL)
+ goto cleanup_nothing;
+#endif
+
+ ret = register_firewall(PF_INET, &ipfw_ops);
+ if (ret < 0)
+ goto cleanup_netlink;
+
+ proc_net_create(IP_FW_PROC_CHAINS, S_IFREG | S_IRUSR | S_IWUSR, ip_chain_procinfo);
+ proc_net_create(IP_FW_PROC_CHAIN_NAMES, S_IFREG | S_IRUSR | S_IWUSR, ip_chain_name_procinfo);
+
+ IP_FW_INPUT_CHAIN = ip_init_chain(IP_FW_LABEL_INPUT, 1, FW_ACCEPT);
+ IP_FW_FORWARD_CHAIN = ip_init_chain(IP_FW_LABEL_FORWARD, 1, FW_ACCEPT);
+ IP_FW_OUTPUT_CHAIN = ip_init_chain(IP_FW_LABEL_OUTPUT, 1, FW_ACCEPT);
+
+ FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
+ return ret;
+
+ cleanup:
+ while (ip_fw_chains) {
+ struct ip_chain *next = ip_fw_chains->next;
+
+ clear_fw_chain(ip_fw_chains);
+ kfree(ip_fw_chains);
+ ip_fw_chains = next;
+ }
+
+ proc_net_remove(IP_FW_PROC_CHAINS);
+ proc_net_remove(IP_FW_PROC_CHAIN_NAMES);
+
+ unregister_firewall(PF_INET, &ipfw_ops);
+
+ cleanup_netlink:
+#if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE)
+ sock_release(ipfwsk->socket);
+
+ cleanup_nothing:
+#endif
+ FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
+ return ret;
+}
diff --git a/net/ipv4/netfilter/ipfwadm_core.c b/net/ipv4/netfilter/ipfwadm_core.c
new file mode 100644
index 000000000..a1f4e16cf
--- /dev/null
+++ b/net/ipv4/netfilter/ipfwadm_core.c
@@ -0,0 +1,1410 @@
+/* Minor modifications to fit on compatibility framework:
+ Rusty.Russell@rustcorp.com.au
+*/
+
+#define CONFIG_IP_FIREWALL
+#define CONFIG_IP_FIREWALL_VERBOSE
+#define CONFIG_IP_MASQUERADE
+#define CONFIG_IP_ACCT
+#define CONFIG_IP_TRANSPARENT_PROXY
+#define CONFIG_IP_FIREWALL_NETLINK
+
+/*
+ * IP firewalling code. This is taken from 4.4BSD. Please note the
+ * copyright message below. As per the GPL it must be maintained
+ * and the licenses thus do not conflict. While this port is subject
+ * to the GPL I also place my modifications under the original
+ * license in recognition of the original copyright.
+ * -- Alan Cox.
+ *
+ * $Id: ipfwadm_core.c,v 1.1 2000/03/17 14:42:00 davem Exp $
+ *
+ * Ported from BSD to Linux,
+ * Alan Cox 22/Nov/1994.
+ * Zeroing /proc and other additions
+ * Jos Vos 4/Feb/1995.
+ * Merged and included the FreeBSD-Current changes at Ugen's request
+ * (but hey it's a lot cleaner now). Ugen would prefer in some ways
+ * we waited for his final product but since Linux 1.2.0 is about to
+ * appear it's not practical - Read: It works, it's not clean but please
+ * don't consider it to be his standard of finished work.
+ * Alan Cox 12/Feb/1995
+ * Porting bidirectional entries from BSD, fixing accounting issues,
+ * adding struct ip_fwpkt for checking packets with interface address
+ * Jos Vos 5/Mar/1995.
+ * Established connections (ACK check), ACK check on bidirectional rules,
+ * ICMP type check.
+ * Wilfred Mollenvanger 7/7/1995.
+ * TCP attack protection.
+ * Alan Cox 25/8/95, based on information from bugtraq.
+ * ICMP type printk, IP_FW_F_APPEND
+ * Bernd Eckenfels 1996-01-31
+ * Split blocking chain into input and output chains, add new "insert" and
+ * "append" commands to replace semi-intelligent "add" command, let "delete".
+ * only delete the first matching entry, use 0xFFFF (0xFF) as ports (ICMP
+ * types) when counting packets being 2nd and further fragments.
+ * Jos Vos <jos@xos.nl> 8/2/1996.
+ * Add support for matching on device names.
+ * Jos Vos <jos@xos.nl> 15/2/1996.
+ * Transparent proxying support.
+ * Willy Konynenberg <willy@xos.nl> 10/5/96.
+ * Make separate accounting on incoming and outgoing packets possible.
+ * Jos Vos <jos@xos.nl> 18/5/1996.
+ * Added trap out of bad frames.
+ * Alan Cox <alan@cymru.net> 17/11/1996
+ *
+ *
+ * Masquerading functionality
+ *
+ * Copyright (c) 1994 Pauline Middelink
+ *
+ * The pieces which added masquerading functionality are totally
+ * my responsibility and have nothing to with the original authors
+ * copyright or doing.
+ *
+ * Parts distributed under GPL.
+ *
+ * Fixes:
+ * Pauline Middelink : Added masquerading.
+ * Alan Cox : Fixed an error in the merge.
+ * Thomas Quinot : Fixed port spoofing.
+ * Alan Cox : Cleaned up retransmits in spoofing.
+ * Alan Cox : Cleaned up length setting.
+ * Wouter Gadeyne : Fixed masquerading support of ftp PORT commands
+ *
+ * Juan Jose Ciarlante : Masquerading code moved to ip_masq.c
+ * Andi Kleen : Print frag_offsets and the ip flags properly.
+ *
+ * All the real work was done by .....
+ *
+ */
+
+
+/*
+ * Copyright (c) 1993 Daniel Boulet
+ * Copyright (c) 1994 Ugen J.S.Antsilevich
+ *
+ * Redistribution and use in source forms, with and without modification,
+ * are permitted provided that this entire comment appears intact.
+ *
+ * Redistribution in binary form may occur without any restrictions.
+ * Obviously, it would be nice if you gave credit where credit is due
+ * but requiring it would be too onerous.
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind.
+ */
+
+#include <linux/config.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/icmp.h>
+#include <linux/udp.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <net/sock.h>
+#include <net/icmp.h>
+#include <linux/netlink.h>
+#include <linux/init.h>
+#include <linux/netfilter_ipv4/ipfwadm_core.h>
+#include <linux/netfilter_ipv4/compat_firewall.h>
+
+#include <net/checksum.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/version.h>
+
+/*
+ * Implement IP packet firewall
+ */
+
+#ifdef DEBUG_IP_FIREWALL
+#define dprintf1(a) printk(a)
+#define dprintf2(a1,a2) printk(a1,a2)
+#define dprintf3(a1,a2,a3) printk(a1,a2,a3)
+#define dprintf4(a1,a2,a3,a4) printk(a1,a2,a3,a4)
+#else
+#define dprintf1(a)
+#define dprintf2(a1,a2)
+#define dprintf3(a1,a2,a3)
+#define dprintf4(a1,a2,a3,a4)
+#endif
+
+#define print_ip(a) printk("%d.%d.%d.%d",(ntohl(a)>>24)&0xFF,\
+ (ntohl(a)>>16)&0xFF,\
+ (ntohl(a)>>8)&0xFF,\
+ (ntohl(a))&0xFF);
+
+#ifdef DEBUG_IP_FIREWALL
+#define dprint_ip(a) print_ip(a)
+#else
+#define dprint_ip(a)
+#endif
+
+#if defined(CONFIG_IP_ACCT) || defined(CONFIG_IP_FIREWALL)
+
+struct ip_fw *ip_fw_fwd_chain;
+struct ip_fw *ip_fw_in_chain;
+struct ip_fw *ip_fw_out_chain;
+struct ip_fw *ip_acct_chain;
+struct ip_fw *ip_masq_chain;
+
+static struct ip_fw **chains[] =
+ {&ip_fw_fwd_chain, &ip_fw_in_chain, &ip_fw_out_chain, &ip_acct_chain,
+ &ip_masq_chain
+ };
+#endif /* CONFIG_IP_ACCT || CONFIG_IP_FIREWALL */
+
+#ifdef CONFIG_IP_FIREWALL
+int ip_fw_fwd_policy=IP_FW_F_ACCEPT;
+int ip_fw_in_policy=IP_FW_F_ACCEPT;
+int ip_fw_out_policy=IP_FW_F_ACCEPT;
+
+static int *policies[] =
+ {&ip_fw_fwd_policy, &ip_fw_in_policy, &ip_fw_out_policy};
+
+#endif
+
+#ifdef CONFIG_IP_FIREWALL_NETLINK
+struct sock *ipfwsk;
+#endif
+
+/*
+ * Returns 1 if the port is matched by the vector, 0 otherwise
+ */
+
+extern inline int port_match(unsigned short *portptr,int nports,unsigned short port,int range_flag)
+{
+ if (!nports)
+ return 1;
+ if ( range_flag )
+ {
+ if ( portptr[0] <= port && port <= portptr[1] )
+ {
+ return( 1 );
+ }
+ nports -= 2;
+ portptr += 2;
+ }
+ while ( nports-- > 0 )
+ {
+ if ( *portptr++ == port )
+ {
+ return( 1 );
+ }
+ }
+ return(0);
+}
+
+#if defined(CONFIG_IP_ACCT) || defined(CONFIG_IP_FIREWALL)
+
+#ifdef CONFIG_IP_FIREWALL_VERBOSE
+
+/*
+ * VERY ugly piece of code which actually makes kernel printf for
+ * matching packets.
+ */
+
+static char *chain_name(struct ip_fw *chain, int mode)
+{
+ switch (mode) {
+ case IP_FW_MODE_ACCT_IN: return "acct in";
+ case IP_FW_MODE_ACCT_OUT: return "acct out";
+ default:
+ if (chain == ip_fw_fwd_chain)
+ return "fw-fwd";
+ else if (chain == ip_fw_in_chain)
+ return "fw-in";
+ else
+ return "fw-out";
+ }
+}
+
+static char *rule_name(struct ip_fw *f, int mode, char *buf)
+{
+ if (mode == IP_FW_MODE_ACCT_IN || mode == IP_FW_MODE_ACCT_OUT)
+ return "";
+
+ if(f->fw_flg&IP_FW_F_ACCEPT) {
+ if(f->fw_flg&IP_FW_F_REDIR) {
+ sprintf(buf, "acc/r%d ", f->fw_pts[f->fw_nsp+f->fw_ndp]);
+ return buf;
+ } else if(f->fw_flg&IP_FW_F_MASQ)
+ return "acc/masq ";
+ else
+ return "acc ";
+ } else if(f->fw_flg&IP_FW_F_ICMPRPL) {
+ return "rej ";
+ } else {
+ return "deny ";
+ }
+}
+
+static void print_packet(struct iphdr *ip,
+ u16 src_port, u16 dst_port, u16 icmp_type,
+ char *chain, char *rule, char *devname)
+{
+ __u32 *opt = (__u32 *) (ip + 1);
+ int opti;
+ __u16 foff = ntohs(ip->frag_off);
+
+ printk(KERN_INFO "IP %s %s%s", chain, rule, devname);
+
+ switch(ip->protocol)
+ {
+ case IPPROTO_TCP:
+ printk(" TCP ");
+ break;
+ case IPPROTO_UDP:
+ printk(" UDP ");
+ break;
+ case IPPROTO_ICMP:
+ printk(" ICMP/%d ", icmp_type);
+ break;
+ default:
+ printk(" PROTO=%d ", ip->protocol);
+ break;
+ }
+ print_ip(ip->saddr);
+ if(ip->protocol == IPPROTO_TCP || ip->protocol == IPPROTO_UDP)
+ printk(":%hu", src_port);
+ printk(" ");
+ print_ip(ip->daddr);
+ if(ip->protocol == IPPROTO_TCP || ip->protocol == IPPROTO_UDP)
+ printk(":%hu", dst_port);
+ printk(" L=%hu S=0x%2.2hX I=%hu FO=0x%4.4hX T=%hu",
+ ntohs(ip->tot_len), ip->tos, ntohs(ip->id),
+ foff & IP_OFFSET, ip->ttl);
+ if (foff & IP_DF) printk(" DF=1");
+ if (foff & IP_MF) printk(" MF=1");
+ for (opti = 0; opti < (ip->ihl - sizeof(struct iphdr) / 4); opti++)
+ printk(" O=0x%8.8X", *opt++);
+ printk("\n");
+}
+#endif
+
+/*
+ * Returns one of the generic firewall policies, like FW_ACCEPT.
+ * Also does accounting so you can feed it the accounting chain.
+ *
+ * The modes is either IP_FW_MODE_FW (normal firewall mode),
+ * IP_FW_MODE_ACCT_IN or IP_FW_MODE_ACCT_OUT (accounting mode,
+ * steps through the entire chain and handles fragments
+ * differently), or IP_FW_MODE_CHK (handles user-level check,
+ * counters are not updated).
+ */
+
+
+int ip_fw_chk(struct iphdr *ip, struct net_device *rif, __u16 *redirport,
+ struct ip_fw *chain, int policy, int mode)
+{
+ struct ip_fw *f;
+ struct tcphdr *tcp=(struct tcphdr *)((__u32 *)ip+ip->ihl);
+ struct udphdr *udp=(struct udphdr *)((__u32 *)ip+ip->ihl);
+ struct icmphdr *icmp=(struct icmphdr *)((__u32 *)ip+ip->ihl);
+ __u32 src, dst;
+ __u16 src_port=0xFFFF, dst_port=0xFFFF, icmp_type=0xFF;
+ unsigned short f_prt=0, prt;
+ char notcpsyn=0, notcpack=0, match;
+ unsigned short offset;
+ int answer;
+ unsigned char tosand, tosxor;
+
+ /*
+ * If the chain is empty follow policy. The BSD one
+ * accepts anything giving you a time window while
+ * flushing and rebuilding the tables.
+ */
+
+ src = ip->saddr;
+ dst = ip->daddr;
+
+ /*
+ * This way we handle fragmented packets.
+ * we ignore all fragments but the first one
+ * so the whole packet can't be reassembled.
+ * This way we relay on the full info which
+ * stored only in first packet.
+ *
+ * Note that this theoretically allows partial packet
+ * spoofing. Not very dangerous but paranoid people may
+ * wish to play with this. It also allows the so called
+ * "fragment bomb" denial of service attack on some types
+ * of system.
+ */
+
+ offset = ntohs(ip->frag_off) & IP_OFFSET;
+
+ /*
+ * Don't allow a fragment of TCP 8 bytes in. Nobody
+ * normal causes this. Its a cracker trying to break
+ * in by doing a flag overwrite to pass the direction
+ * checks.
+ */
+
+ if (offset == 1 && ip->protocol == IPPROTO_TCP)
+ return FW_BLOCK;
+
+ if (offset!=0 && !(mode & (IP_FW_MODE_ACCT_IN|IP_FW_MODE_ACCT_OUT)) &&
+ (ip->protocol == IPPROTO_TCP || ip->protocol == IPPROTO_UDP ||
+ ip->protocol == IPPROTO_ICMP))
+ return FW_ACCEPT;
+
+ /*
+ * Header fragment for TCP is too small to check the bits.
+ */
+
+ if(ip->protocol==IPPROTO_TCP && (ip->ihl<<2)+16 > ntohs(ip->tot_len))
+ return FW_BLOCK;
+
+ /*
+ * Too short.
+ *
+ * But only too short for a packet with ports...
+ */
+
+ else if((ntohs(ip->tot_len)<8+(ip->ihl<<2))&&(ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP))
+ return FW_BLOCK;
+
+ src = ip->saddr;
+ dst = ip->daddr;
+
+ /*
+ * If we got interface from which packet came
+ * we can use the address directly. This is unlike
+ * 4.4BSD derived systems that have an address chain
+ * per device. We have a device per address with dummy
+ * devices instead.
+ */
+
+ dprintf1("Packet ");
+ switch(ip->protocol)
+ {
+ case IPPROTO_TCP:
+ dprintf1("TCP ");
+ /* ports stay 0xFFFF if it is not the first fragment */
+ if (!offset) {
+ src_port=ntohs(tcp->source);
+ dst_port=ntohs(tcp->dest);
+ if(!tcp->ack && !tcp->rst)
+ /* We do NOT have ACK, value TRUE */
+ notcpack=1;
+ if(!tcp->syn || !notcpack)
+ /* We do NOT have SYN, value TRUE */
+ notcpsyn=1;
+ }
+ prt=IP_FW_F_TCP;
+ break;
+ case IPPROTO_UDP:
+ dprintf1("UDP ");
+ /* ports stay 0xFFFF if it is not the first fragment */
+ if (!offset) {
+ src_port=ntohs(udp->source);
+ dst_port=ntohs(udp->dest);
+ }
+ prt=IP_FW_F_UDP;
+ break;
+ case IPPROTO_ICMP:
+ /* icmp_type stays 255 if it is not the first fragment */
+ if (!offset)
+ icmp_type=(__u16)(icmp->type);
+ dprintf2("ICMP:%d ",icmp_type);
+ prt=IP_FW_F_ICMP;
+ break;
+ default:
+ dprintf2("p=%d ",ip->protocol);
+ prt=IP_FW_F_ALL;
+ break;
+ }
+#ifdef DEBUG_IP_FIREWALL
+ dprint_ip(ip->saddr);
+
+ if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP)
+ /* This will print 65535 when it is not the first fragment! */
+ dprintf2(":%d ", src_port);
+ dprint_ip(ip->daddr);
+ if (ip->protocol==IPPROTO_TCP || ip->protocol==IPPROTO_UDP)
+ /* This will print 65535 when it is not the first fragment! */
+ dprintf2(":%d ",dst_port);
+ dprintf1("\n");
+#endif
+
+ for (f=chain;f;f=f->fw_next)
+ {
+ /*
+ * This is a bit simpler as we don't have to walk
+ * an interface chain as you do in BSD - same logic
+ * however.
+ */
+
+ /*
+ * Match can become 0x01 (a "normal" match was found),
+ * 0x02 (a reverse match was found), and 0x03 (the
+ * IP addresses match in both directions).
+ * Now we know in which direction(s) we should look
+ * for a match for the TCP/UDP ports. Both directions
+ * might match (e.g., when both addresses are on the
+ * same network for which an address/mask is given), but
+ * the ports might only match in one direction.
+ * This was obviously wrong in the original BSD code.
+ */
+ match = 0x00;
+
+ if ((src&f->fw_smsk.s_addr)==f->fw_src.s_addr
+ && (dst&f->fw_dmsk.s_addr)==f->fw_dst.s_addr)
+ /* normal direction */
+ match |= 0x01;
+
+ if ((f->fw_flg & IP_FW_F_BIDIR) &&
+ (dst&f->fw_smsk.s_addr)==f->fw_src.s_addr
+ && (src&f->fw_dmsk.s_addr)==f->fw_dst.s_addr)
+ /* reverse direction */
+ match |= 0x02;
+
+ if (!match)
+ continue;
+
+ /*
+ * Look for a VIA device match
+ */
+ if(f->fw_viadev)
+ {
+ if(rif!=f->fw_viadev)
+ continue; /* Mismatch */
+ }
+
+ /* This looks stupid, because we scan almost static
+ list, searching for static key. However, this way seems
+ to be only reasonable way of handling fw_via rules
+ (btw bsd makes the same thing).
+
+ It will not affect performance if you will follow
+ the following simple rules:
+
+ - if inteface is aliased, ALWAYS specify fw_viadev,
+ so that previous check will guarantee, that we will
+ not waste time when packet arrive on another interface.
+
+ - avoid using fw_via.s_addr if fw_via.s_addr is owned
+ by an aliased interface.
+
+ --ANK
+ */
+ if (f->fw_via.s_addr && rif) {
+ struct in_ifaddr *ifa;
+
+ if (rif->ip_ptr == NULL)
+ continue; /* Mismatch */
+
+ for (ifa = ((struct in_device*)(rif->ip_ptr))->ifa_list;
+ ifa; ifa = ifa->ifa_next) {
+ if (ifa->ifa_local == f->fw_via.s_addr)
+ goto ifa_ok;
+ }
+ continue; /* Mismatch */
+
+ ifa_ok:
+ }
+
+ /*
+ * Ok the chain addresses match.
+ */
+
+#ifdef CONFIG_IP_ACCT
+ /*
+ * See if we're in accounting mode and only want to
+ * count incoming or outgoing packets.
+ */
+
+ if (mode & (IP_FW_MODE_ACCT_IN|IP_FW_MODE_ACCT_OUT) &&
+ ((mode == IP_FW_MODE_ACCT_IN && f->fw_flg&IP_FW_F_ACCTOUT) ||
+ (mode == IP_FW_MODE_ACCT_OUT && f->fw_flg&IP_FW_F_ACCTIN)))
+ continue;
+
+#endif
+ /*
+ * For all non-TCP packets and/or non-first fragments,
+ * notcpsyn and notcpack will always be FALSE,
+ * so the IP_FW_F_TCPSYN and IP_FW_F_TCPACK flags
+ * are actually ignored for these packets.
+ */
+
+ if((f->fw_flg&IP_FW_F_TCPSYN) && notcpsyn)
+ continue;
+
+ if((f->fw_flg&IP_FW_F_TCPACK) && notcpack)
+ continue;
+
+ f_prt=f->fw_flg&IP_FW_F_KIND;
+ if (f_prt!=IP_FW_F_ALL)
+ {
+ /*
+ * Specific firewall - packet's protocol
+ * must match firewall's.
+ */
+
+ if(prt!=f_prt)
+ continue;
+
+ if((prt==IP_FW_F_ICMP &&
+ ! port_match(&f->fw_pts[0], f->fw_nsp,
+ icmp_type,f->fw_flg&IP_FW_F_SRNG)) ||
+ !(prt==IP_FW_F_ICMP || ((match & 0x01) &&
+ port_match(&f->fw_pts[0], f->fw_nsp, src_port,
+ f->fw_flg&IP_FW_F_SRNG) &&
+ port_match(&f->fw_pts[f->fw_nsp], f->fw_ndp, dst_port,
+ f->fw_flg&IP_FW_F_DRNG)) || ((match & 0x02) &&
+ port_match(&f->fw_pts[0], f->fw_nsp, dst_port,
+ f->fw_flg&IP_FW_F_SRNG) &&
+ port_match(&f->fw_pts[f->fw_nsp], f->fw_ndp, src_port,
+ f->fw_flg&IP_FW_F_DRNG))))
+ {
+ continue;
+ }
+ }
+
+#ifdef CONFIG_IP_FIREWALL_VERBOSE
+ if (f->fw_flg & IP_FW_F_PRN)
+ {
+ char buf[16];
+
+ print_packet(ip, src_port, dst_port, icmp_type,
+ chain_name(chain, mode),
+ rule_name(f, mode, buf),
+ rif ? rif->name : "-");
+ }
+#endif
+ if (mode != IP_FW_MODE_CHK) {
+ f->fw_bcnt+=ntohs(ip->tot_len);
+ f->fw_pcnt++;
+ }
+ if (!(mode & (IP_FW_MODE_ACCT_IN|IP_FW_MODE_ACCT_OUT)))
+ break;
+ } /* Loop */
+
+ if (!(mode & (IP_FW_MODE_ACCT_IN|IP_FW_MODE_ACCT_OUT))) {
+
+ /*
+ * We rely on policy defined in the rejecting entry or, if no match
+ * was found, we rely on the general policy variable for this type
+ * of firewall.
+ */
+
+ if (f!=NULL) {
+ policy=f->fw_flg;
+ tosand=f->fw_tosand;
+ tosxor=f->fw_tosxor;
+ } else {
+ tosand=0xFF;
+ tosxor=0x00;
+ }
+
+ if (policy&IP_FW_F_ACCEPT) {
+ /* Adjust priority and recompute checksum */
+ __u8 old_tos = ip->tos;
+ ip->tos = (old_tos & tosand) ^ tosxor;
+ if (ip->tos != old_tos)
+ ip_send_check(ip);
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ if (policy&IP_FW_F_REDIR) {
+ if (redirport)
+ if ((*redirport = htons(f->fw_pts[f->fw_nsp+f->fw_ndp])) == 0) {
+ /* Wildcard redirection.
+ * Note that redirport will become
+ * 0xFFFF for non-TCP/UDP packets.
+ */
+ *redirport = htons(dst_port);
+ }
+ answer = FW_REDIRECT;
+ } else
+#endif
+#ifdef CONFIG_IP_MASQUERADE
+ if (policy&IP_FW_F_MASQ)
+ answer = FW_MASQUERADE;
+ else
+#endif
+ answer = FW_ACCEPT;
+
+ } else if(policy&IP_FW_F_ICMPRPL)
+ answer = FW_REJECT;
+ else
+ answer = FW_BLOCK;
+
+#ifdef CONFIG_IP_FIREWALL_NETLINK
+ if((policy&IP_FW_F_PRN) && (answer == FW_REJECT || answer == FW_BLOCK))
+ {
+ struct sk_buff *skb=alloc_skb(128, GFP_ATOMIC);
+ if(skb)
+ {
+ int len=min(128,ntohs(ip->tot_len));
+ skb_put(skb,len);
+ memcpy(skb->data,ip,len);
+ if(netlink_post(NETLINK_FIREWALL, skb))
+ kfree_skb(skb);
+ }
+ }
+#endif
+ return answer;
+ } else
+ /* we're doing accounting, always ok */
+ return 0;
+}
+
+
+static void zero_fw_chain(struct ip_fw *chainptr)
+{
+ struct ip_fw *ctmp=chainptr;
+ while(ctmp)
+ {
+ ctmp->fw_pcnt=0L;
+ ctmp->fw_bcnt=0L;
+ ctmp=ctmp->fw_next;
+ }
+}
+
+static void free_fw_chain(struct ip_fw *volatile* chainptr)
+{
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ while ( *chainptr != NULL )
+ {
+ struct ip_fw *ftmp;
+ ftmp = *chainptr;
+ *chainptr = ftmp->fw_next;
+ kfree_s(ftmp,sizeof(*ftmp));
+ }
+ restore_flags(flags);
+}
+
+/* Volatiles to keep some of the compiler versions amused */
+
+static int insert_in_chain(struct ip_fw *volatile* chainptr, struct ip_fw *frwl,int len)
+{
+ struct ip_fw *ftmp;
+ unsigned long flags;
+
+ save_flags(flags);
+
+ ftmp = kmalloc( sizeof(struct ip_fw), GFP_ATOMIC );
+ if ( ftmp == NULL )
+ {
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: malloc said no\n");
+#endif
+ return( ENOMEM );
+ }
+
+ memcpy(ftmp, frwl, len);
+ /*
+ * Allow the more recent "minimise cost" flag to be
+ * set. [Rob van Nieuwkerk]
+ */
+ ftmp->fw_tosand |= 0x01;
+ ftmp->fw_tosxor &= 0xFE;
+ ftmp->fw_pcnt=0L;
+ ftmp->fw_bcnt=0L;
+
+ cli();
+
+ if ((ftmp->fw_vianame)[0]) {
+ if (!(ftmp->fw_viadev = dev_get_by_name(ftmp->fw_vianame)))
+ ftmp->fw_viadev = (struct net_device *) -1;
+ } else
+ ftmp->fw_viadev = NULL;
+
+ ftmp->fw_next = *chainptr;
+ *chainptr=ftmp;
+ restore_flags(flags);
+ return(0);
+}
+
+static int append_to_chain(struct ip_fw *volatile* chainptr, struct ip_fw *frwl,int len)
+{
+ struct ip_fw *ftmp;
+ struct ip_fw *chtmp=NULL;
+ struct ip_fw *volatile chtmp_prev=NULL;
+ unsigned long flags;
+
+ save_flags(flags);
+
+ ftmp = kmalloc( sizeof(struct ip_fw), GFP_ATOMIC );
+ if ( ftmp == NULL )
+ {
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: malloc said no\n");
+#endif
+ return( ENOMEM );
+ }
+
+ memcpy(ftmp, frwl, len);
+ /*
+ * Allow the more recent "minimise cost" flag to be
+ * set. [Rob van Nieuwkerk]
+ */
+ ftmp->fw_tosand |= 0x01;
+ ftmp->fw_tosxor &= 0xFE;
+ ftmp->fw_pcnt=0L;
+ ftmp->fw_bcnt=0L;
+
+ ftmp->fw_next = NULL;
+
+ cli();
+
+ if ((ftmp->fw_vianame)[0]) {
+ if (!(ftmp->fw_viadev = dev_get_by_name(ftmp->fw_vianame)))
+ ftmp->fw_viadev = (struct net_device *) -1;
+ } else
+ ftmp->fw_viadev = NULL;
+
+ chtmp_prev=NULL;
+ for (chtmp=*chainptr;chtmp!=NULL;chtmp=chtmp->fw_next)
+ chtmp_prev=chtmp;
+
+ if (chtmp_prev)
+ chtmp_prev->fw_next=ftmp;
+ else
+ *chainptr=ftmp;
+ restore_flags(flags);
+ return(0);
+}
+
+static int del_from_chain(struct ip_fw *volatile*chainptr, struct ip_fw *frwl)
+{
+ struct ip_fw *ftmp,*ltmp;
+ unsigned short tport1,tport2,tmpnum;
+ char matches,was_found;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ ftmp=*chainptr;
+
+ if ( ftmp == NULL )
+ {
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: chain is empty\n");
+#endif
+ restore_flags(flags);
+ return( EINVAL );
+ }
+
+ ltmp=NULL;
+ was_found=0;
+
+ while( !was_found && ftmp != NULL )
+ {
+ matches=1;
+ if (ftmp->fw_src.s_addr!=frwl->fw_src.s_addr
+ || ftmp->fw_dst.s_addr!=frwl->fw_dst.s_addr
+ || ftmp->fw_smsk.s_addr!=frwl->fw_smsk.s_addr
+ || ftmp->fw_dmsk.s_addr!=frwl->fw_dmsk.s_addr
+ || ftmp->fw_via.s_addr!=frwl->fw_via.s_addr
+ || ftmp->fw_flg!=frwl->fw_flg)
+ matches=0;
+
+ tport1=ftmp->fw_nsp+ftmp->fw_ndp;
+ tport2=frwl->fw_nsp+frwl->fw_ndp;
+ if (tport1!=tport2)
+ matches=0;
+ else if (tport1!=0)
+ {
+ for (tmpnum=0;tmpnum < tport1 && tmpnum < IP_FW_MAX_PORTS;tmpnum++)
+ if (ftmp->fw_pts[tmpnum]!=frwl->fw_pts[tmpnum])
+ matches=0;
+ }
+ if (strncmp(ftmp->fw_vianame, frwl->fw_vianame, IFNAMSIZ))
+ matches=0;
+ if(matches)
+ {
+ was_found=1;
+ if (ltmp)
+ {
+ ltmp->fw_next=ftmp->fw_next;
+ kfree_s(ftmp,sizeof(*ftmp));
+ ftmp=ltmp->fw_next;
+ }
+ else
+ {
+ *chainptr=ftmp->fw_next;
+ kfree_s(ftmp,sizeof(*ftmp));
+ ftmp=*chainptr;
+ }
+ }
+ else
+ {
+ ltmp = ftmp;
+ ftmp = ftmp->fw_next;
+ }
+ }
+ restore_flags(flags);
+ if (was_found)
+ return 0;
+ else
+ return(EINVAL);
+}
+
+#endif /* CONFIG_IP_ACCT || CONFIG_IP_FIREWALL */
+
+struct ip_fw *check_ipfw_struct(struct ip_fw *frwl, int len)
+{
+
+ if ( len != sizeof(struct ip_fw) )
+ {
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: len=%d, want %d\n",len, sizeof(struct ip_fw));
+#endif
+ return(NULL);
+ }
+
+ if ( (frwl->fw_flg & ~IP_FW_F_MASK) != 0 )
+ {
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: undefined flag bits set (flags=%x)\n",
+ frwl->fw_flg);
+#endif
+ return(NULL);
+ }
+
+#ifndef CONFIG_IP_TRANSPARENT_PROXY
+ if (frwl->fw_flg & IP_FW_F_REDIR) {
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: unsupported flag IP_FW_F_REDIR\n");
+#endif
+ return(NULL);
+ }
+#endif
+
+#ifndef CONFIG_IP_MASQUERADE
+ if (frwl->fw_flg & IP_FW_F_MASQ) {
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: unsupported flag IP_FW_F_MASQ\n");
+#endif
+ return(NULL);
+ }
+#endif
+
+ if ( (frwl->fw_flg & IP_FW_F_SRNG) && frwl->fw_nsp < 2 )
+ {
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: src range set but fw_nsp=%d\n",
+ frwl->fw_nsp);
+#endif
+ return(NULL);
+ }
+
+ if ( (frwl->fw_flg & IP_FW_F_DRNG) && frwl->fw_ndp < 2 )
+ {
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: dst range set but fw_ndp=%d\n",
+ frwl->fw_ndp);
+#endif
+ return(NULL);
+ }
+
+ if ( frwl->fw_nsp + frwl->fw_ndp > (frwl->fw_flg & IP_FW_F_REDIR ? IP_FW_MAX_PORTS - 1 : IP_FW_MAX_PORTS) )
+ {
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: too many ports (%d+%d)\n",
+ frwl->fw_nsp,frwl->fw_ndp);
+#endif
+ return(NULL);
+ }
+
+ return frwl;
+}
+
+
+
+
+#ifdef CONFIG_IP_ACCT
+
+int ip_acct_ctl(int stage, void *m, int len)
+{
+ if ( stage == IP_ACCT_FLUSH )
+ {
+ free_fw_chain(&ip_acct_chain);
+ return(0);
+ }
+ if ( stage == IP_ACCT_ZERO )
+ {
+ zero_fw_chain(ip_acct_chain);
+ return(0);
+ }
+ if ( stage == IP_ACCT_INSERT || stage == IP_ACCT_APPEND ||
+ stage == IP_ACCT_DELETE )
+ {
+ struct ip_fw *frwl;
+
+ if (!(frwl=check_ipfw_struct(m,len)))
+ return (EINVAL);
+
+ switch (stage)
+ {
+ case IP_ACCT_INSERT:
+ return( insert_in_chain(&ip_acct_chain,frwl,len));
+ case IP_ACCT_APPEND:
+ return( append_to_chain(&ip_acct_chain,frwl,len));
+ case IP_ACCT_DELETE:
+ return( del_from_chain(&ip_acct_chain,frwl));
+ default:
+ /*
+ * Should be panic but... (Why ??? - AC)
+ */
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_acct_ctl: unknown request %d\n",stage);
+#endif
+ return(EINVAL);
+ }
+ }
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_acct_ctl: unknown request %d\n",stage);
+#endif
+ return(EINVAL);
+}
+#endif
+
+#ifdef CONFIG_IP_FIREWALL
+int ip_fw_ctl(int stage, void *m, int len)
+{
+ int cmd, fwtype;
+
+ cmd = stage & IP_FW_COMMAND;
+ fwtype = (stage & IP_FW_TYPE) >> IP_FW_SHIFT;
+
+ if ( cmd == IP_FW_FLUSH )
+ {
+ free_fw_chain(chains[fwtype]);
+ return(0);
+ }
+
+ if ( cmd == IP_FW_ZERO )
+ {
+ zero_fw_chain(*chains[fwtype]);
+ return(0);
+ }
+
+ if ( cmd == IP_FW_POLICY )
+ {
+ int *tmp_policy_ptr;
+ tmp_policy_ptr=(int *)m;
+ *policies[fwtype] = *tmp_policy_ptr;
+ return 0;
+ }
+
+ if ( cmd == IP_FW_CHECK )
+ {
+ struct net_device *viadev;
+ struct ip_fwpkt *ipfwp;
+ struct iphdr *ip;
+
+ if ( len != sizeof(struct ip_fwpkt) )
+ {
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: length=%d, expected %d\n",
+ len, sizeof(struct ip_fwpkt));
+#endif
+ return( EINVAL );
+ }
+
+ ipfwp = (struct ip_fwpkt *)m;
+ ip = &(ipfwp->fwp_iph);
+
+ if ( !(viadev = dev_get_by_name(ipfwp->fwp_vianame)) ) {
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: invalid device \"%s\"\n", ipfwp->fwp_vianame);
+#endif
+ return(EINVAL);
+ } else if ( ip->ihl != sizeof(struct iphdr) / sizeof(int)) {
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: ip->ihl=%d, want %d\n",ip->ihl,
+ sizeof(struct iphdr)/sizeof(int));
+#endif
+ return(EINVAL);
+ }
+
+ switch (ip_fw_chk(ip, viadev, NULL, *chains[fwtype],
+ *policies[fwtype], IP_FW_MODE_CHK))
+ {
+ case FW_ACCEPT:
+ return(0);
+ case FW_REDIRECT:
+ return(ECONNABORTED);
+ case FW_MASQUERADE:
+ return(ECONNRESET);
+ case FW_REJECT:
+ return(ECONNREFUSED);
+ default: /* FW_BLOCK */
+ return(ETIMEDOUT);
+ }
+ }
+
+ if ( cmd == IP_FW_MASQ_TIMEOUTS )
+ return ip_fw_masq_timeouts(m, len);
+
+/*
+ * Here we really working hard-adding new elements
+ * to blocking/forwarding chains or deleting 'em
+ */
+
+ if ( cmd == IP_FW_INSERT || cmd == IP_FW_APPEND || cmd == IP_FW_DELETE )
+ {
+ struct ip_fw *frwl;
+ int fwtype;
+
+ frwl=check_ipfw_struct(m,len);
+ if (frwl==NULL)
+ return (EINVAL);
+ fwtype = (stage & IP_FW_TYPE) >> IP_FW_SHIFT;
+
+ switch (cmd)
+ {
+ case IP_FW_INSERT:
+ return(insert_in_chain(chains[fwtype],frwl,len));
+ case IP_FW_APPEND:
+ return(append_to_chain(chains[fwtype],frwl,len));
+ case IP_FW_DELETE:
+ return(del_from_chain(chains[fwtype],frwl));
+ default:
+ /*
+ * Should be panic but... (Why are BSD people panic obsessed ??)
+ */
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: unknown request %d\n",stage);
+#endif
+ return(EINVAL);
+ }
+ }
+
+#ifdef DEBUG_IP_FIREWALL
+ printk("ip_fw_ctl: unknown request %d\n",stage);
+#endif
+ return(ENOPROTOOPT);
+}
+#endif /* CONFIG_IP_FIREWALL */
+
+#ifdef CONFIG_PROC_FS
+#if defined(CONFIG_IP_FIREWALL) || defined(CONFIG_IP_ACCT)
+
+static int ip_chain_procinfo(int stage, char *buffer, char **start,
+ off_t offset, int length, int reset)
+{
+ off_t pos=0, begin=0;
+ struct ip_fw *i;
+ unsigned long flags;
+ int len, p;
+ int last_len = 0;
+
+
+ switch(stage)
+ {
+#ifdef CONFIG_IP_FIREWALL
+ case IP_FW_IN:
+ i = ip_fw_in_chain;
+ len=sprintf(buffer, "IP firewall input rules, default %d\n",
+ ip_fw_in_policy);
+ break;
+ case IP_FW_OUT:
+ i = ip_fw_out_chain;
+ len=sprintf(buffer, "IP firewall output rules, default %d\n",
+ ip_fw_out_policy);
+ break;
+ case IP_FW_FWD:
+ i = ip_fw_fwd_chain;
+ len=sprintf(buffer, "IP firewall forward rules, default %d\n",
+ ip_fw_fwd_policy);
+ break;
+#endif
+#ifdef CONFIG_IP_ACCT
+ case IP_FW_ACCT:
+ i = ip_acct_chain;
+ len=sprintf(buffer,"IP accounting rules\n");
+ break;
+#endif
+ default:
+ /* this should never be reached, but safety first... */
+ i = NULL;
+ len=0;
+ break;
+ }
+
+ save_flags(flags);
+ cli();
+
+ while(i!=NULL)
+ {
+ len+=sprintf(buffer+len,"%08X/%08X->%08X/%08X %.16s %08X %X ",
+ ntohl(i->fw_src.s_addr),ntohl(i->fw_smsk.s_addr),
+ ntohl(i->fw_dst.s_addr),ntohl(i->fw_dmsk.s_addr),
+ (i->fw_vianame)[0] ? i->fw_vianame : "-",
+ ntohl(i->fw_via.s_addr), i->fw_flg);
+ /* 10 is enough for a 32 bit box but the counters are 64bit on
+ the Alpha and Ultrapenguin */
+ len+=sprintf(buffer+len,"%u %u %-20lu %-20lu",
+ i->fw_nsp,i->fw_ndp, i->fw_pcnt,i->fw_bcnt);
+ for (p = 0; p < IP_FW_MAX_PORTS; p++)
+ len+=sprintf(buffer+len, " %u", i->fw_pts[p]);
+ len+=sprintf(buffer+len, " A%02X X%02X", i->fw_tosand, i->fw_tosxor);
+ buffer[len++]='\n';
+ buffer[len]='\0';
+ pos=begin+len;
+ if(pos<offset)
+ {
+ len=0;
+ begin=pos;
+ }
+ else if(pos>offset+length)
+ {
+ len = last_len;
+ break;
+ }
+ else if(reset)
+ {
+ /* This needs to be done at this specific place! */
+ i->fw_pcnt=0L;
+ i->fw_bcnt=0L;
+ }
+ last_len = len;
+ i=i->fw_next;
+ }
+ restore_flags(flags);
+ *start=buffer+(offset-begin);
+ len-=(offset-begin);
+ if(len>length)
+ len=length;
+ return len;
+}
+#endif
+
+#ifdef CONFIG_IP_ACCT
+
+static int ip_acct_procinfo(char *buffer, char **start, off_t offset,
+ int length
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,29)
+ , int reset
+#endif
+ )
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,29)
+ /* FIXME: No more `atomic' read and reset. Wonderful 8-( --RR */
+ int reset = 0;
+#endif
+ return ip_chain_procinfo(IP_FW_ACCT, buffer,start, offset,length,
+ reset);
+}
+
+#endif
+
+#ifdef CONFIG_IP_FIREWALL
+
+static int ip_fw_in_procinfo(char *buffer, char **start, off_t offset,
+ int length
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,29)
+ , int reset
+#endif
+ )
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,29)
+ /* FIXME: No more `atomic' read and reset. Wonderful 8-( --RR */
+ int reset = 0;
+#endif
+ return ip_chain_procinfo(IP_FW_IN, buffer,start,offset,length,
+ reset);
+}
+
+static int ip_fw_out_procinfo(char *buffer, char **start, off_t offset,
+ int length
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,29)
+ , int reset
+#endif
+ )
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,29)
+ /* FIXME: No more `atomic' read and reset. Wonderful 8-( --RR */
+ int reset = 0;
+#endif
+ return ip_chain_procinfo(IP_FW_OUT, buffer,start,offset,length,
+ reset);
+}
+
+static int ip_fw_fwd_procinfo(char *buffer, char **start, off_t offset,
+ int length
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,29)
+ , int reset
+#endif
+ )
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,29)
+ /* FIXME: No more `atomic' read and reset. Wonderful 8-( --RR */
+ int reset = 0;
+#endif
+ return ip_chain_procinfo(IP_FW_FWD, buffer,start,offset,length,
+ reset);
+}
+#endif
+#endif
+
+
+#ifdef CONFIG_IP_FIREWALL
+/*
+ * Interface to the generic firewall chains.
+ */
+
+int ipfw_input_check(struct firewall_ops *this, int pf,
+ struct net_device *dev, void *phdr, void *arg,
+ struct sk_buff **pskb)
+{
+ return ip_fw_chk(phdr, dev, arg, ip_fw_in_chain, ip_fw_in_policy,
+ IP_FW_MODE_FW);
+}
+
+int ipfw_output_check(struct firewall_ops *this, int pf,
+ struct net_device *dev, void *phdr, void *arg,
+ struct sk_buff **pskb)
+{
+ return ip_fw_chk(phdr, dev, arg, ip_fw_out_chain, ip_fw_out_policy,
+ IP_FW_MODE_FW);
+}
+
+int ipfw_forward_check(struct firewall_ops *this, int pf,
+ struct net_device *dev, void *phdr, void *arg,
+ struct sk_buff **pskb)
+{
+ return ip_fw_chk(phdr, dev, arg, ip_fw_fwd_chain, ip_fw_fwd_policy,
+ IP_FW_MODE_FW);
+}
+
+#ifdef CONFIG_IP_ACCT
+int ipfw_acct_in(struct firewall_ops *this, int pf, struct net_device *dev,
+ void *phdr, void *arg, struct sk_buff **pskb)
+{
+ return ip_fw_chk(phdr,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_IN);
+}
+
+int ipfw_acct_out(struct firewall_ops *this, int pf, struct net_device *dev,
+ void *phdr, void *arg, struct sk_buff **pskb)
+{
+ return ip_fw_chk(phdr,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_OUT);
+}
+#endif
+
+struct firewall_ops ipfw_ops=
+{
+ NULL,
+ ipfw_forward_check,
+ ipfw_input_check,
+ ipfw_output_check,
+#ifdef CONFIG_IP_ACCT
+ ipfw_acct_in,
+ ipfw_acct_out
+#else
+ NULL,
+ NULL
+#endif
+};
+
+#endif
+
+#if defined(CONFIG_IP_ACCT) || defined(CONFIG_IP_FIREWALL)
+
+int ipfw_device_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+ struct net_device *dev=ptr;
+ char *devname = dev->name;
+ unsigned long flags;
+ struct ip_fw *fw;
+ int chn;
+
+ save_flags(flags);
+ cli();
+
+ if (event == NETDEV_UP) {
+ for (chn = 0; chn < IP_FW_CHAINS; chn++)
+ for (fw = *chains[chn]; fw; fw = fw->fw_next)
+ if ((fw->fw_vianame)[0] && !strncmp(devname,
+ fw->fw_vianame, IFNAMSIZ))
+ fw->fw_viadev = dev;
+ } else if (event == NETDEV_DOWN) {
+ for (chn = 0; chn < IP_FW_CHAINS; chn++)
+ for (fw = *chains[chn]; fw; fw = fw->fw_next)
+ /* we could compare just the pointers ... */
+ if ((fw->fw_vianame)[0] && !strncmp(devname,
+ fw->fw_vianame, IFNAMSIZ))
+ fw->fw_viadev = (struct net_device*)-1;
+ }
+
+ restore_flags(flags);
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block ipfw_dev_notifier={
+ ipfw_device_event,
+ NULL,
+ 0
+};
+
+#endif
+
+int ipfw_init_or_cleanup(int init)
+{
+ int ret = 0;
+
+ if (!init)
+ goto cleanup;
+
+ ret = register_firewall(PF_INET, &ipfw_ops);
+ if (ret < 0)
+ goto cleanup_nothing;
+
+#ifdef CONFIG_IP_ACCT
+ proc_net_create("ip_acct", S_IFREG | S_IRUGO | S_IWUSR, ip_acct_procinfo);
+#endif
+ proc_net_create("ip_input", S_IFREG | S_IRUGO | S_IWUSR, ip_fw_in_procinfo);
+ proc_net_create("ip_output", S_IFREG | S_IRUGO | S_IWUSR, ip_fw_out_procinfo);
+ proc_net_create("ip_forward", S_IFREG | S_IRUGO | S_IWUSR, ip_fw_fwd_procinfo);
+
+ /* Register for device up/down reports */
+ register_netdevice_notifier(&ipfw_dev_notifier);
+
+#ifdef CONFIG_IP_FIREWALL_NETLINK
+ ipfwsk = netlink_kernel_create(NETLINK_FIREWALL, NULL);
+#endif
+ return ret;
+
+ cleanup:
+#ifdef CONFIG_IP_FIREWALL_NETLINK
+ sock_release(ipfwsk->socket);
+#endif
+ unregister_netdevice_notifier(&ipfw_dev_notifier);
+
+#ifdef CONFIG_IP_ACCT
+ proc_net_remove("ip_acct");
+#endif
+ proc_net_remove("ip_input");
+ proc_net_remove("ip_output");
+ proc_net_remove("ip_forward");
+
+ free_fw_chain(chains[IP_FW_FWD]);
+ free_fw_chain(chains[IP_FW_IN]);
+ free_fw_chain(chains[IP_FW_OUT]);
+ free_fw_chain(chains[IP_FW_ACCT]);
+
+ unregister_firewall(PF_INET, &ipfw_ops);
+
+ cleanup_nothing:
+ return ret;
+}
diff --git a/net/ipv4/netfilter/ipt_LOG.c b/net/ipv4/netfilter/ipt_LOG.c
new file mode 100644
index 000000000..6e69d6a90
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_LOG.c
@@ -0,0 +1,368 @@
+/*
+ * This is a module which is used for logging packets.
+ */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/spinlock.h>
+#include <net/icmp.h>
+#include <net/udp.h>
+#include <net/tcp.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+struct in_device;
+#include <net/route.h>
+#include <linux/netfilter_ipv4/ipt_LOG.h>
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+struct esphdr {
+ __u32 spi;
+}; /* FIXME evil kludge */
+
+/* Make init and cleanup non-static, so gcc doesn't warn about unused,
+ but don't export the symbols */
+EXPORT_NO_SYMBOLS;
+
+/* Use lock to serialize, so printks don't overlap */
+static spinlock_t log_lock = SPIN_LOCK_UNLOCKED;
+
+/* One level of recursion won't kill us */
+static void dump_packet(const struct ipt_log_info *info,
+ struct iphdr *iph, unsigned int len, int recurse)
+{
+ void *protoh = (u_int32_t *)iph + iph->ihl;
+ unsigned int datalen = len - iph->ihl * 4;
+
+ /* Important fields:
+ * TOS, len, DF/MF, fragment offset, TTL, src, dst, options. */
+ /* Max length: 40 "SRC=255.255.255.255 DST=255.255.255.255 " */
+ printk("SRC=%u.%u.%u.%u DST=%u.%u.%u.%u ",
+ (ntohl(iph->saddr)>>24)&0xFF,
+ (ntohl(iph->saddr)>>16)&0xFF,
+ (ntohl(iph->saddr)>>8)&0xFF,
+ (ntohl(iph->saddr))&0xFF,
+ (ntohl(iph->daddr)>>24)&0xFF,
+ (ntohl(iph->daddr)>>16)&0xFF,
+ (ntohl(iph->daddr)>>8)&0xFF,
+ (ntohl(iph->daddr))&0xFF);
+
+ /* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */
+ printk("LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ",
+ ntohs(iph->tot_len), iph->tos & IPTOS_TOS_MASK,
+ iph->tos & IPTOS_PREC_MASK, iph->ttl, ntohs(iph->id));
+
+ /* Max length: 6 "CE DF MF " */
+ if (ntohs(iph->frag_off) & IP_CE)
+ printk("CE ");
+ if (ntohs(iph->frag_off) & IP_DF)
+ printk("DF ");
+ if (ntohs(iph->frag_off) & IP_MF)
+ printk("MF ");
+
+ /* Max length: 11 "FRAG:65535 " */
+ if (ntohs(iph->frag_off) & IP_OFFSET)
+ printk("FRAG:%u ", ntohs(iph->frag_off) & IP_OFFSET);
+
+ if ((info->logflags & IPT_LOG_IPOPT)
+ && iph->ihl * 4 != sizeof(struct iphdr)) {
+ unsigned int i;
+
+ /* Max length: 127 "OPT (" 15*4*2chars ") " */
+ printk("OPT (");
+ for (i = sizeof(struct iphdr); i < iph->ihl * 4; i++)
+ printk("%02X", ((u_int8_t *)iph)[i]);
+ printk(") ");
+ }
+
+ switch (iph->protocol) {
+ case IPPROTO_TCP: {
+ struct tcphdr *tcph = protoh;
+
+ /* Max length: 10 "PROTO=TCP " */
+ printk("PROTO=TCP ");
+
+ if (ntohs(iph->frag_off) & IP_OFFSET)
+ break;
+
+ /* Max length: 25 "INCOMPLETE [65535 bytes] " */
+ if (datalen < sizeof (*tcph)) {
+ printk("INCOMPLETE [%u bytes] ", datalen);
+ break;
+ }
+
+ /* Max length: 20 "SPT=65535 DPT=65535 " */
+ printk("SPT=%u DPT=%u ",
+ ntohs(tcph->source), ntohs(tcph->dest));
+ /* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */
+ if (info->logflags & IPT_LOG_TCPSEQ)
+ printk("SEQ=%u ACK=%u ",
+ ntohl(tcph->seq), ntohl(tcph->ack_seq));
+ /* Max length: 13 "WINDOW=65535 " */
+ printk("WINDOW=%u ", ntohs(tcph->window));
+ /* Max length: 9 "RES=0x3F " */
+ printk("RES=0x%02x ", (u_int8_t)(ntohl(tcp_flag_word(tcph) & TCP_RESERVED_BITS) >> 22));
+ /* Max length: 36 "URG ACK PSH RST SYN FIN " */
+ if (tcph->urg)
+ printk("URG ");
+ if (tcph->ack)
+ printk("ACK ");
+ if (tcph->psh)
+ printk("PSH ");
+ if (tcph->rst)
+ printk("RST ");
+ if (tcph->syn)
+ printk("SYN ");
+ if (tcph->fin)
+ printk("FIN ");
+ /* Max length: 11 "URGP=65535 " */
+ printk("URGP=%u ", ntohs(tcph->urg_ptr));
+
+ if ((info->logflags & IPT_LOG_TCPOPT)
+ && tcph->doff * 4 != sizeof(struct tcphdr)) {
+ unsigned int i;
+
+ /* Max length: 127 "OPT (" 15*4*2chars ") " */
+ printk("OPT (");
+ for (i =sizeof(struct tcphdr); i < tcph->doff * 4; i++)
+ printk("%02X", ((u_int8_t *)tcph)[i]);
+ printk(") ");
+ }
+ break;
+ }
+ case IPPROTO_UDP: {
+ struct udphdr *udph = protoh;
+
+ /* Max length: 10 "PROTO=UDP " */
+ printk("PROTO=UDP ");
+
+ if (ntohs(iph->frag_off) & IP_OFFSET)
+ break;
+
+ /* Max length: 25 "INCOMPLETE [65535 bytes] " */
+ if (datalen < sizeof (*udph)) {
+ printk("INCOMPLETE [%u bytes] ", datalen);
+ break;
+ }
+
+ /* Max length: 20 "SPT=65535 DPT=65535 " */
+ printk("SPT=%u DPT=%u LEN=%u ",
+ ntohs(udph->source), ntohs(udph->dest),
+ ntohs(udph->len));
+ break;
+ }
+ case IPPROTO_ICMP: {
+ struct icmphdr *icmph = protoh;
+ static size_t required_len[NR_ICMP_TYPES+1]
+ = { [ICMP_ECHOREPLY] = 4,
+ [ICMP_DEST_UNREACH]
+ = 8 + sizeof(struct iphdr) + 8,
+ [ICMP_SOURCE_QUENCH]
+ = 8 + sizeof(struct iphdr) + 8,
+ [ICMP_REDIRECT]
+ = 8 + sizeof(struct iphdr) + 8,
+ [ICMP_ECHO] = 4,
+ [ICMP_TIME_EXCEEDED]
+ = 8 + sizeof(struct iphdr) + 8,
+ [ICMP_PARAMETERPROB]
+ = 8 + sizeof(struct iphdr) + 8,
+ [ICMP_TIMESTAMP] = 20,
+ [ICMP_TIMESTAMPREPLY] = 20,
+ [ICMP_ADDRESS] = 12,
+ [ICMP_ADDRESSREPLY] = 12 };
+
+ /* Max length: 11 "PROTO=ICMP " */
+ printk("PROTO=ICMP ");
+
+ if (ntohs(iph->frag_off) & IP_OFFSET)
+ break;
+
+ /* Max length: 25 "INCOMPLETE [65535 bytes] " */
+ if (datalen < 4) {
+ printk("INCOMPLETE [%u bytes] ", datalen);
+ break;
+ }
+
+ /* Max length: 18 "TYPE=255 CODE=255 " */
+ printk("TYPE=%u CODE=%u ", icmph->type, icmph->code);
+
+ /* Max length: 25 "INCOMPLETE [65535 bytes] " */
+ if (icmph->type <= NR_ICMP_TYPES
+ && required_len[icmph->type]
+ && datalen < required_len[icmph->type]) {
+ printk("INCOMPLETE [%u bytes] ", datalen);
+ break;
+ }
+
+ switch (icmph->type) {
+ case ICMP_ECHOREPLY:
+ case ICMP_ECHO:
+ /* Max length: 19 "ID=65535 SEQ=65535 " */
+ printk("ID=%u SEQ=%u ",
+ ntohs(icmph->un.echo.id),
+ ntohs(icmph->un.echo.sequence));
+ break;
+
+ case ICMP_PARAMETERPROB:
+ /* Max length: 14 "PARAMETER=255 " */
+ printk("PARAMETER=%u ",
+ ntohl(icmph->un.gateway) >> 24);
+ break;
+ case ICMP_REDIRECT:
+ /* Max length: 24 "GATEWAY=255.255.255.255 " */
+ printk("GATEWAY=%u.%u.%u.%u ",
+ (ntohl(icmph->un.gateway)>>24)&0xFF,
+ (ntohl(icmph->un.gateway)>>16)&0xFF,
+ (ntohl(icmph->un.gateway)>>8)&0xFF,
+ (ntohl(icmph->un.gateway))&0xFF);
+ /* Fall through */
+ case ICMP_DEST_UNREACH:
+ case ICMP_SOURCE_QUENCH:
+ case ICMP_TIME_EXCEEDED:
+ /* Max length: 3+maxlen */
+ if (recurse) {
+ printk("[");
+ dump_packet(info,
+ (struct iphdr *)(icmph + 1),
+ datalen-sizeof(struct iphdr),
+ 0);
+ printk("] ");
+ }
+
+ /* Max length: 10 "MTU=65535 " */
+ if (icmph->type == ICMP_DEST_UNREACH
+ && icmph->code == ICMP_FRAG_NEEDED)
+ printk("MTU=%u ", ntohs(icmph->un.frag.mtu));
+ }
+ break;
+ }
+ /* Max Length */
+ case IPPROTO_AH:
+ case IPPROTO_ESP: {
+ struct esphdr *esph = protoh;
+ int esp= (iph->protocol==IPPROTO_ESP);
+
+ /* Max length: 10 "PROTO=ESP " */
+ printk("PROTO=%s ",esp? "ESP" : "AH");
+
+ if (ntohs(iph->frag_off) & IP_OFFSET)
+ break;
+
+ /* Max length: 25 "INCOMPLETE [65535 bytes] " */
+ if (datalen < sizeof (*esph)) {
+ printk("INCOMPLETE [%u bytes] ", datalen);
+ break;
+ }
+
+ /* Length: 15 "SPI=0xF1234567 " */
+ printk("SPI=0x%x ", ntohl(esph->spi) );
+ break;
+ }
+ /* Max length: 10 "PROTO 255 " */
+ default:
+ printk("PROTO=%u ", iph->protocol);
+ }
+
+ /* Proto Max log string length */
+ /* IP: 40+46+6+11+127 = 230 */
+ /* TCP: 10+max(25,20+30+13+9+36+11+127) = 256 */
+ /* UDP: 10+max(25,20) = 35 */
+ /* ICMP: 11+max(25, 18+25+max(19,14,24+3+n+10,3+n+10)) = 91+n */
+ /* ESP: 10+max(25)+15 = 50 */
+ /* AH: 9+max(25)+15 = 49 */
+ /* unknown: 10 */
+
+ /* (ICMP allows recursion one level deep) */
+ /* maxlen = IP + ICMP + IP + max(TCP,UDP,ICMP,unknown) */
+ /* maxlen = 230+ 91 + 230 + 256 = 807 */
+}
+
+static unsigned int
+ipt_log_target(struct sk_buff **pskb,
+ unsigned int hooknum,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *targinfo,
+ void *userinfo)
+{
+ struct iphdr *iph = (*pskb)->nh.iph;
+ const struct ipt_log_info *loginfo = targinfo;
+ char level_string[4] = "< >";
+
+ level_string[1] = '0' + (loginfo->level % 8);
+ spin_lock_bh(&log_lock);
+ printk(level_string);
+ printk("%sIN=%s OUT=%s ",
+ loginfo->prefix,
+ in ? in->name : "",
+ out ? out->name : "");
+ if (in && !out) {
+ /* MAC logging for input chain only. */
+ printk("MAC=");
+ if ((*pskb)->dev && (*pskb)->dev->hard_header_len) {
+ int i;
+ unsigned char *p = (*pskb)->mac.raw;
+ for (i = 0; i < (*pskb)->dev->hard_header_len; i++,p++)
+ printk("%02x%c", *p,
+ i==(*pskb)->dev->hard_header_len - 1
+ ? ' ':':');
+ }
+ }
+
+ dump_packet(loginfo, iph, (*pskb)->len, 1);
+ printk("\n");
+ spin_unlock_bh(&log_lock);
+
+ return IPT_CONTINUE;
+}
+
+static int ipt_log_checkentry(const char *tablename,
+ const struct ipt_entry *e,
+ void *targinfo,
+ unsigned int targinfosize,
+ unsigned int hook_mask)
+{
+ const struct ipt_log_info *loginfo = targinfo;
+
+ if (targinfosize != IPT_ALIGN(sizeof(struct ipt_log_info))) {
+ DEBUGP("LOG: targinfosize %u != %u\n",
+ targinfosize, IPT_ALIGN(sizeof(struct ipt_log_info)));
+ return 0;
+ }
+
+ if (loginfo->level >= 8) {
+ DEBUGP("LOG: level %u >= 8\n", loginfo->level);
+ return 0;
+ }
+
+ if (loginfo->prefix[sizeof(loginfo->prefix)-1] != '\0') {
+ DEBUGP("LOG: prefix term %i\n",
+ loginfo->prefix[sizeof(loginfo->prefix)-1]);
+ return 0;
+ }
+
+ return 1;
+}
+
+static struct ipt_target ipt_log_reg
+= { { NULL, NULL }, "LOG", ipt_log_target, ipt_log_checkentry, THIS_MODULE };
+
+static int __init init(void)
+{
+ if (ipt_register_target(&ipt_log_reg))
+ return -EINVAL;
+
+ return 0;
+}
+
+static void __exit fini(void)
+{
+ ipt_unregister_target(&ipt_log_reg);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/netfilter/ipt_MARK.c b/net/ipv4/netfilter/ipt_MARK.c
new file mode 100644
index 000000000..32906eefe
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_MARK.c
@@ -0,0 +1,68 @@
+/* This is a module which is used for setting the NFMARK field of an skb. */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <net/checksum.h>
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_MARK.h>
+
+EXPORT_NO_SYMBOLS;
+
+static unsigned int
+target(struct sk_buff **pskb,
+ unsigned int hooknum,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *targinfo,
+ void *userinfo)
+{
+ const struct ipt_mark_target_info *markinfo = targinfo;
+
+ if((*pskb)->nfmark != markinfo->mark) {
+ (*pskb)->nfmark = markinfo->mark;
+ (*pskb)->nfcache |= NFC_ALTERED;
+ }
+ return IPT_CONTINUE;
+}
+
+static int
+checkentry(const char *tablename,
+ const struct ipt_entry *e,
+ void *targinfo,
+ unsigned int targinfosize,
+ unsigned int hook_mask)
+{
+ if (targinfosize != IPT_ALIGN(sizeof(struct ipt_mark_target_info))) {
+ printk(KERN_WARNING "MARK: targinfosize %u != %Zu\n",
+ targinfosize,
+ IPT_ALIGN(sizeof(struct ipt_mark_target_info)));
+ return 0;
+ }
+
+ if (strcmp(tablename, "mangle") != 0) {
+ printk(KERN_WARNING "MARK: can only be called from \"mangle\" table, not \"%s\"\n", tablename);
+ return 0;
+ }
+
+ return 1;
+}
+
+static struct ipt_target ipt_mark_reg
+= { { NULL, NULL }, "MARK", target, checkentry, THIS_MODULE };
+
+static int __init init(void)
+{
+ if (ipt_register_target(&ipt_mark_reg))
+ return -EINVAL;
+
+ return 0;
+}
+
+static void __exit fini(void)
+{
+ ipt_unregister_target(&ipt_mark_reg);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c
new file mode 100644
index 000000000..9f94f8f44
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_MASQUERADE.c
@@ -0,0 +1,171 @@
+/* Masquerade. Simple mapping which alters range to a local IP address
+ (depending on route). */
+#include <linux/types.h>
+#include <linux/ip.h>
+#include <linux/timer.h>
+#include <linux/module.h>
+#include <linux/netfilter.h>
+#include <net/protocol.h>
+#include <net/checksum.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv4/ip_nat_rule.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+EXPORT_NO_SYMBOLS;
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+/* Lock protects masq region inside conntrack */
+static DECLARE_RWLOCK(masq_lock);
+
+/* FIXME: Multiple targets. --RR */
+static int
+masquerade_check(const char *tablename,
+ const struct ipt_entry *e,
+ void *targinfo,
+ unsigned int targinfosize,
+ unsigned int hook_mask)
+{
+ const struct ip_nat_multi_range *mr = targinfo;
+
+ if (targinfosize != IPT_ALIGN(sizeof(*mr))) {
+ DEBUGP("masquerade_check: size %u != %u.\n",
+ targinfosize, sizeof(*mr));
+ return 0;
+ }
+ if (hook_mask & ~(1 << NF_IP_POST_ROUTING)) {
+ DEBUGP("masquerade_check: bad hooks %x.\n", hook_mask);
+ return 0;
+ }
+ if (mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) {
+ DEBUGP("masquerade_check: bad MAP_IPS.\n");
+ return 0;
+ }
+ if (mr->rangesize != 1) {
+ DEBUGP("masquerade_check: bad rangesize %u.\n", mr->rangesize);
+ return 0;
+ }
+ return 1;
+}
+
+static unsigned int
+masquerade_target(struct sk_buff **pskb,
+ unsigned int hooknum,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *targinfo,
+ void *userinfo)
+{
+ struct ip_conntrack *ct;
+ enum ip_conntrack_info ctinfo;
+ const struct ip_nat_range *r;
+ struct ip_nat_multi_range newrange;
+ u_int32_t newsrc;
+ struct rtable *rt;
+
+ IP_NF_ASSERT(hooknum == NF_IP_POST_ROUTING);
+
+ /* FIXME: For the moment, don't do local packets, breaks
+ testsuite for 2.3.49 --RR */
+ if ((*pskb)->sk)
+ return NF_ACCEPT;
+
+ ct = ip_conntrack_get(*pskb, &ctinfo);
+ IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW
+ || ctinfo == IP_CT_RELATED));
+
+ r = targinfo;
+
+ if (ip_route_output(&rt, (*pskb)->nh.iph->daddr,
+ 0,
+ RT_TOS((*pskb)->nh.iph->tos)|RTO_CONN,
+ out->ifindex) != 0) {
+ /* Shouldn't happen */
+ printk("MASQUERADE: No route: Rusty's brain broke!\n");
+ return NF_DROP;
+ }
+
+ newsrc = rt->rt_src;
+ DEBUGP("newsrc = %u.%u.%u.%u\n", IP_PARTS(newsrc));
+ ip_rt_put(rt);
+
+ WRITE_LOCK(&masq_lock);
+ ct->nat.masq_index = out->ifindex;
+ WRITE_UNLOCK(&masq_lock);
+
+ /* Transfer from original range. */
+ newrange = ((struct ip_nat_multi_range)
+ { 1, { { r->flags | IP_NAT_RANGE_MAP_IPS,
+ newsrc, newsrc,
+ r->min, r->max } } });
+
+ /* Hand modified range to generic setup. */
+ return ip_nat_setup_info(ct, &newrange, hooknum);
+}
+
+static inline int
+device_cmp(const struct ip_conntrack *i, void *ifindex)
+{
+ int ret;
+
+ READ_LOCK(&masq_lock);
+ ret = (i->nat.masq_index == (int)(long)ifindex);
+ READ_UNLOCK(&masq_lock);
+
+ return ret;
+}
+
+int masq_device_event(struct notifier_block *this,
+ unsigned long event,
+ void *ptr)
+{
+ struct net_device *dev = ptr;
+
+ if (event == NETDEV_DOWN) {
+ /* Device was downed. Search entire table for
+ conntracks which were associated with that device,
+ and forget them. */
+ IP_NF_ASSERT(dev->ifindex != 0);
+
+ ip_ct_selective_cleanup(device_cmp, (void *)(long)dev->ifindex);
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block masq_dev_notifier = {
+ masq_device_event,
+ NULL,
+ 0
+};
+
+static struct ipt_target masquerade
+= { { NULL, NULL }, "MASQUERADE", masquerade_target, masquerade_check,
+ THIS_MODULE };
+
+static int __init init(void)
+{
+ int ret;
+
+ ret = ipt_register_target(&masquerade);
+
+ if (ret == 0) {
+ /* Register for device down reports */
+ register_netdevice_notifier(&masq_dev_notifier);
+ }
+
+ return ret;
+}
+
+static void __exit fini(void)
+{
+ ipt_unregister_target(&masquerade);
+ unregister_netdevice_notifier(&masq_dev_notifier);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/netfilter/ipt_MIRROR.c b/net/ipv4/netfilter/ipt_MIRROR.c
new file mode 100644
index 000000000..9dec181c1
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_MIRROR.c
@@ -0,0 +1,131 @@
+/*
+ This is a module which is used for resending packets with inverted src and dst.
+
+ Based on code from: ip_nat_dumb.c,v 1.9 1999/08/20
+ and various sources.
+
+ Copyright (C) 2000 Emmanuel Roger <winfield@freegates.be>
+
+ 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 of the License, 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <net/ip.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netdevice.h>
+#include <linux/route.h>
+struct in_device;
+#include <net/route.h>
+EXPORT_NO_SYMBOLS;
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+static int route_mirror(struct sk_buff *skb)
+{
+ struct iphdr *iph = skb->nh.iph;
+ struct rtable *rt;
+
+ if (ip_route_output(&rt, iph->daddr, iph->saddr,
+ RT_TOS(iph->tos) | RTO_CONN,
+ 0)) {
+ return -EINVAL;
+ }
+ /* check if the interface we are living by is the same as the one we arrived on */
+
+ if (skb->rx_dev != rt->u.dst.dev) {
+ /* Drop old route. */
+ dst_release(skb->dst);
+ skb->dst = &rt->u.dst;
+ return 0;
+ }
+ else return -EINVAL;
+}
+
+static int
+ip_rewrite(struct sk_buff *skb)
+{
+ struct iphdr *iph = skb->nh.iph;
+ u32 odaddr = iph->saddr;
+ u32 osaddr = iph->daddr;
+
+ skb->nfcache |= NFC_ALTERED;
+
+ /* Rewrite IP header */
+ iph->daddr = odaddr;
+ iph->saddr = osaddr;
+
+ return 0;
+}
+
+
+static unsigned int ipt_mirror_target(struct sk_buff **pskb,
+ unsigned int hooknum,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *targinfo,
+ void *userinfo)
+{
+ if ((*pskb)->dst != NULL) {
+ if (!ip_rewrite(*pskb) && !route_mirror(*pskb)) {
+ ip_send(*pskb);
+ return NF_STOLEN;
+ }
+ }
+ return NF_DROP;
+}
+
+static int ipt_mirror_checkentry(const char *tablename,
+ const struct ipt_entry *e,
+ void *targinfo,
+ unsigned int targinfosize,
+ unsigned int hook_mask)
+{
+ /* Only on INPUT, FORWARD or PRE_ROUTING, otherwise loop danger. */
+ if (hook_mask & ~((1 << NF_IP_PRE_ROUTING)
+ | (1 << NF_IP_FORWARD)
+ | (1 << NF_IP_LOCAL_IN))) {
+ DEBUGP("MIRROR: bad hook\n");
+ return 0;
+ }
+
+ if (targinfosize != IPT_ALIGN(0)) {
+ DEBUGP("MIRROR: targinfosize %u != 0\n", targinfosize);
+ return 0;
+ }
+
+ return 1;
+}
+
+static struct ipt_target ipt_mirror_reg
+= { { NULL, NULL }, "MIRROR", ipt_mirror_target, ipt_mirror_checkentry,
+ THIS_MODULE };
+
+static int __init init(void)
+{
+ return ipt_register_target(&ipt_mirror_reg);
+}
+
+static void __exit fini(void)
+{
+ ipt_unregister_target(&ipt_mirror_reg);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/netfilter/ipt_REDIRECT.c b/net/ipv4/netfilter/ipt_REDIRECT.c
new file mode 100644
index 000000000..690d3a8a1
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_REDIRECT.c
@@ -0,0 +1,104 @@
+/* Redirect. Simple mapping which alters dst to a local IP address. */
+#include <linux/types.h>
+#include <linux/ip.h>
+#include <linux/timer.h>
+#include <linux/module.h>
+#include <linux/netfilter.h>
+#include <linux/netdevice.h>
+#include <linux/if.h>
+#include <linux/inetdevice.h>
+#include <net/protocol.h>
+#include <net/checksum.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv4/ip_nat_rule.h>
+
+EXPORT_NO_SYMBOLS;
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+/* FIXME: Take multiple ranges --RR */
+static int
+redirect_check(const char *tablename,
+ const struct ipt_entry *e,
+ void *targinfo,
+ unsigned int targinfosize,
+ unsigned int hook_mask)
+{
+ const struct ip_nat_multi_range *mr = targinfo;
+
+ if (targinfosize != IPT_ALIGN(sizeof(*mr))) {
+ DEBUGP("redirect_check: size %u.\n", targinfosize);
+ return 0;
+ }
+ if (hook_mask & ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_OUT))) {
+ DEBUGP("redirect_check: bad hooks %x.\n", hook_mask);
+ return 0;
+ }
+ if (mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) {
+ DEBUGP("redirect_check: bad MAP_IPS.\n");
+ return 0;
+ }
+ if (mr->rangesize != 1) {
+ DEBUGP("redirect_check: bad rangesize %u.\n", mr->rangesize);
+ return 0;
+ }
+ return 1;
+}
+
+static unsigned int
+redirect_target(struct sk_buff **pskb,
+ unsigned int hooknum,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *targinfo,
+ void *userinfo)
+{
+ struct ip_conntrack *ct;
+ enum ip_conntrack_info ctinfo;
+ u_int32_t newdst;
+ const struct ip_nat_range *r = targinfo;
+ struct ip_nat_multi_range newrange;
+
+ IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING
+ || hooknum == NF_IP_LOCAL_OUT);
+
+ ct = ip_conntrack_get(*pskb, &ctinfo);
+ IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));
+
+ /* Local packets: make them go to loopback */
+ if (hooknum == NF_IP_LOCAL_OUT)
+ newdst = htonl(0x7F000001);
+ else
+ /* Grab first address on interface. */
+ newdst = (((struct in_device *)(*pskb)->dev->ip_ptr)
+ ->ifa_list->ifa_local);
+
+ /* Transfer from original range. */
+ newrange = ((struct ip_nat_multi_range)
+ { 1, { { r->flags | IP_NAT_RANGE_MAP_IPS,
+ newdst, newdst,
+ r->min, r->max } } });
+
+ /* Hand modified range to generic setup. */
+ return ip_nat_setup_info(ct, &newrange, hooknum);
+}
+
+static struct ipt_target redirect_reg
+= { { NULL, NULL }, "REDIRECT", redirect_target, redirect_check, THIS_MODULE };
+
+static int __init init(void)
+{
+ return ipt_register_target(&redirect_reg);
+}
+
+static void __exit fini(void)
+{
+ ipt_unregister_target(&redirect_reg);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/netfilter/ipt_REJECT.c b/net/ipv4/netfilter/ipt_REJECT.c
new file mode 100644
index 000000000..b183e822c
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_REJECT.c
@@ -0,0 +1,145 @@
+/*
+ * This is a module which is used for rejecting packets.
+ * Added support for customized reject packets (Jozsef Kadlecsik).
+ */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <net/icmp.h>
+#include <net/tcp.h>
+struct in_device;
+#include <net/route.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_REJECT.h>
+EXPORT_NO_SYMBOLS;
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+static unsigned int reject(struct sk_buff **pskb,
+ unsigned int hooknum,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *targinfo,
+ void *userinfo)
+{
+ const struct ipt_reject_info *reject = targinfo;
+
+ switch (reject->with) {
+ case IPT_ICMP_NET_UNREACHABLE:
+ icmp_send(*pskb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH, 0);
+ break;
+ case IPT_ICMP_HOST_UNREACHABLE:
+ icmp_send(*pskb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
+ break;
+ case IPT_ICMP_PROT_UNREACHABLE:
+ icmp_send(*pskb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);
+ break;
+ case IPT_ICMP_PORT_UNREACHABLE:
+ icmp_send(*pskb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+ break;
+ case IPT_ICMP_ECHOREPLY: {
+ struct icmphdr *icmph = (struct icmphdr *)
+ ((u_int32_t *)(*pskb)->nh.iph + (*pskb)->nh.iph->ihl);
+ unsigned int datalen = (*pskb)->len - (*pskb)->nh.iph->ihl * 4;
+
+ /* Not non-head frags, or truncated */
+ if (((ntohs((*pskb)->nh.iph->frag_off) & IP_OFFSET) == 0)
+ && datalen >= 4) {
+ /* Usually I don't like cut & pasting code,
+ but dammit, my party is starting in 45
+ mins! --RR */
+ struct icmp_bxm icmp_param;
+
+ icmp_param.icmph=*icmph;
+ icmp_param.icmph.type=ICMP_ECHOREPLY;
+ icmp_param.data_ptr=(icmph+1);
+ icmp_param.data_len=datalen;
+ icmp_reply(&icmp_param, *pskb);
+ }
+ }
+ break;
+ case IPT_TCP_RESET:
+ tcp_v4_send_reset(*pskb);
+ break;
+ }
+
+ return NF_DROP;
+}
+
+static inline int find_ping_match(const struct ipt_entry_match *m)
+{
+ const struct ipt_icmp *icmpinfo = (const struct ipt_icmp *)m->data;
+
+ if (strcmp(m->u.match->name, "icmp") == 0
+ && icmpinfo->type == ICMP_ECHO
+ && !(icmpinfo->invflags & IPT_ICMP_INV))
+ return 1;
+
+ return 0;
+}
+
+static int check(const char *tablename,
+ const struct ipt_entry *e,
+ void *targinfo,
+ unsigned int targinfosize,
+ unsigned int hook_mask)
+{
+ const struct ipt_reject_info *rejinfo = targinfo;
+
+ if (targinfosize != IPT_ALIGN(sizeof(struct ipt_icmp))) {
+ DEBUGP("REJECT: targinfosize %u != 0\n", targinfosize);
+ return 0;
+ }
+
+ /* Only allow these for packet filtering. */
+ if ((hook_mask & ~((1 << NF_IP_LOCAL_IN)
+ | (1 << NF_IP_FORWARD)
+ | (1 << NF_IP_LOCAL_OUT))) != 0) {
+ DEBUGP("REJECT: bad hook mask %X\n", hook_mask);
+ return 0;
+ }
+
+ if (rejinfo->with == IPT_ICMP_ECHOREPLY) {
+ /* Must specify that it's an ICMP ping packet. */
+ if (e->ip.proto != IPPROTO_ICMP
+ || (e->ip.invflags & IPT_INV_PROTO)) {
+ DEBUGP("REJECT: ECHOREPLY illegal for non-icmp\n");
+ return 0;
+ }
+ /* Must contain ICMP match. */
+ if (IPT_MATCH_ITERATE(e, find_ping_match) == 0) {
+ DEBUGP("REJECT: ECHOREPLY illegal for non-ping\n");
+ return 0;
+ }
+ } else if (rejinfo->with == IPT_TCP_RESET) {
+ if (e->ip.proto != IPPROTO_TCP
+ || (e->ip.invflags & IPT_INV_PROTO)) {
+ DEBUGP("REJECT: TCP_RESET illegal for non-tcp\n");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static struct ipt_target ipt_reject_reg
+= { { NULL, NULL }, "REJECT", reject, check, THIS_MODULE };
+
+static int __init init(void)
+{
+ if (ipt_register_target(&ipt_reject_reg))
+ return -EINVAL;
+ return 0;
+}
+
+static void __exit fini(void)
+{
+ ipt_unregister_target(&ipt_reject_reg);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/netfilter/ipt_TOS.c b/net/ipv4/netfilter/ipt_TOS.c
new file mode 100644
index 000000000..fbfb4974f
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_TOS.c
@@ -0,0 +1,87 @@
+/* This is a module which is used for setting the TOS field of a packet. */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <net/checksum.h>
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_TOS.h>
+
+EXPORT_NO_SYMBOLS;
+
+static unsigned int
+target(struct sk_buff **pskb,
+ unsigned int hooknum,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *targinfo,
+ void *userinfo)
+{
+ struct iphdr *iph = (*pskb)->nh.iph;
+ const struct ipt_tos_target_info *tosinfo = targinfo;
+
+ if ((iph->tos & IPTOS_TOS_MASK) != tosinfo->tos) {
+ u_int8_t diffs[2];
+
+ diffs[0] = iph->tos;
+ iph->tos = (iph->tos & IPTOS_PREC_MASK) | tosinfo->tos;
+ diffs[1] = iph->tos;
+ iph->check = csum_fold(csum_partial((char *)diffs,
+ sizeof(diffs),
+ iph->check^0xFFFF));
+ (*pskb)->nfcache |= NFC_ALTERED;
+ }
+ return IPT_CONTINUE;
+}
+
+static int
+checkentry(const char *tablename,
+ const struct ipt_entry *e,
+ void *targinfo,
+ unsigned int targinfosize,
+ unsigned int hook_mask)
+{
+ const u_int8_t tos = ((struct ipt_tos_target_info *)targinfo)->tos;
+
+ if (targinfosize != IPT_ALIGN(sizeof(struct ipt_tos_target_info))) {
+ printk(KERN_WARNING "TOS: targinfosize %u != %Zu\n",
+ targinfosize,
+ IPT_ALIGN(sizeof(struct ipt_tos_target_info)));
+ return 0;
+ }
+
+ if (strcmp(tablename, "mangle") != 0) {
+ printk(KERN_WARNING "TOS: can only be called from \"mangle\" table, not \"%s\"\n", tablename);
+ return 0;
+ }
+
+ if (tos != IPTOS_LOWDELAY
+ && tos != IPTOS_THROUGHPUT
+ && tos != IPTOS_RELIABILITY
+ && tos != IPTOS_MINCOST
+ && tos != IPTOS_NORMALSVC) {
+ printk(KERN_WARNING "TOS: bad tos value %#x\n", tos);
+ return 0;
+ }
+
+ return 1;
+}
+
+static struct ipt_target ipt_tos_reg
+= { { NULL, NULL }, "TOS", target, checkentry, THIS_MODULE };
+
+static int __init init(void)
+{
+ if (ipt_register_target(&ipt_tos_reg))
+ return -EINVAL;
+
+ return 0;
+}
+
+static void __exit fini(void)
+{
+ ipt_unregister_target(&ipt_tos_reg);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/netfilter/ipt_limit.c b/net/ipv4/netfilter/ipt_limit.c
new file mode 100644
index 000000000..3785ba371
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_limit.c
@@ -0,0 +1,144 @@
+/* Kernel module to control the rate
+ *
+ * Jérôme de Vivie <devivie@info.enserb.u-bordeaux.fr>
+ * Hervé Eychenne <eychenne@info.enserb.u-bordeaux.fr>
+ *
+ * 2 September 1999: Changed from the target RATE to the match
+ * `limit', removed logging. Did I mention that
+ * Alexey is a fucking genius?
+ * Rusty Russell (rusty@rustcorp.com.au). */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_limit.h>
+EXPORT_NO_SYMBOLS;
+
+#define IP_PARTS_NATIVE(n) \
+(unsigned int)((n)>>24)&0xFF, \
+(unsigned int)((n)>>16)&0xFF, \
+(unsigned int)((n)>>8)&0xFF, \
+(unsigned int)((n)&0xFF)
+
+#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
+
+/* The algorithm used is the Simple Token Bucket Filter (TBF)
+ * see net/sched/sch_tbf.c in the linux source tree
+ */
+
+static spinlock_t limit_lock = SPIN_LOCK_UNLOCKED;
+
+/* Rusty: This is my (non-mathematically-inclined) understanding of
+ this algorithm. The `average rate' in jiffies becomes your initial
+ amount of credit `credit' and the most credit you can ever have
+ `credit_cap'. The `peak rate' becomes the cost of passing the
+ test, `cost'.
+
+ `prev' tracks the last packet hit: you gain one credit per jiffy.
+ If you get credit balance more than this, the extra credit is
+ discarded. Every time the match passes, you lose `cost' credits;
+ if you don't have that many, the test fails.
+
+ See Alexey's formal explanation in net/sched/sch_tbf.c.
+
+ To avoid underflow, we multiply by 128 (ie. you get 128 credits per
+ jiffy). Hence a cost of 2^32-1, means one pass per 32768 seconds
+ at 1024HZ (or one every 9 hours). A cost of 1 means 12800 passes
+ per second at 100HZ. */
+
+#define CREDITS_PER_JIFFY 128
+
+static int
+ipt_limit_match(const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *matchinfo,
+ int offset,
+ const void *hdr,
+ u_int16_t datalen,
+ int *hotdrop)
+{
+ struct ipt_rateinfo *r = ((struct ipt_rateinfo *)matchinfo)->master;
+ unsigned long now = jiffies;
+
+ spin_lock_bh(&limit_lock);
+ r->credit += (now - xchg(&r->prev, now)) * CREDITS_PER_JIFFY;
+ if (r->credit > r->credit_cap)
+ r->credit = r->credit_cap;
+
+ if (r->credit >= r->cost) {
+ /* We're not limited. */
+ r->credit -= r->cost;
+ spin_unlock_bh(&limit_lock);
+ return 1;
+ }
+
+ spin_unlock_bh(&limit_lock);
+ return 0;
+}
+
+/* Precision saver. */
+static u_int32_t
+user2credits(u_int32_t user)
+{
+ /* If multiplying would overflow... */
+ if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY))
+ /* Divide first. */
+ return (user / IPT_LIMIT_SCALE) * HZ * CREDITS_PER_JIFFY;
+
+ return (user * HZ * CREDITS_PER_JIFFY) / IPT_LIMIT_SCALE;
+}
+
+static int
+ipt_limit_checkentry(const char *tablename,
+ const struct ipt_ip *ip,
+ void *matchinfo,
+ unsigned int matchsize,
+ unsigned int hook_mask)
+{
+ struct ipt_rateinfo *r = matchinfo;
+
+ if (matchsize != IPT_ALIGN(sizeof(struct ipt_rateinfo)))
+ return 0;
+
+ /* Check for overflow. */
+ if (r->burst == 0
+ || user2credits(r->avg * r->burst) < user2credits(r->avg)) {
+ printk("Call rusty: overflow in ipt_limit: %u/%u\n",
+ r->avg, r->burst);
+ return 0;
+ }
+
+ /* User avg in seconds * IPT_LIMIT_SCALE: convert to jiffies *
+ 128. */
+ r->prev = jiffies;
+ r->credit = user2credits(r->avg * r->burst); /* Credits full. */
+ r->credit_cap = user2credits(r->avg * r->burst); /* Credits full. */
+ r->cost = user2credits(r->avg);
+
+ /* For SMP, we only want to use one set of counters. */
+ r->master = r;
+
+ return 1;
+}
+
+static struct ipt_match ipt_limit_reg
+= { { NULL, NULL }, "limit", ipt_limit_match, ipt_limit_checkentry,
+ THIS_MODULE };
+
+static int __init init(void)
+{
+ if (ipt_register_match(&ipt_limit_reg))
+ return -EINVAL;
+ return 0;
+}
+
+static void __exit fini(void)
+{
+ ipt_unregister_match(&ipt_limit_reg);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/netfilter/ipt_mac.c b/net/ipv4/netfilter/ipt_mac.c
new file mode 100644
index 000000000..90dbec59d
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_mac.c
@@ -0,0 +1,63 @@
+/* Kernel module to match MAC address parameters. */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/if_ether.h>
+
+#include <linux/netfilter_ipv4/ipt_mac.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+EXPORT_NO_SYMBOLS;
+
+static int
+match(const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *matchinfo,
+ int offset,
+ const void *hdr,
+ u_int16_t datalen,
+ int *hotdrop)
+{
+ const struct ipt_mac_info *info = matchinfo;
+
+ /* Is mac pointer valid? */
+ return (skb->mac.raw >= skb->head
+ && skb->mac.raw < skb->head + skb->len - ETH_HLEN
+ /* If so, compare... */
+ && ((memcmp(skb->mac.ethernet->h_source, info->srcaddr, ETH_ALEN)
+ == 0) ^ info->invert));
+}
+
+static int
+ipt_mac_checkentry(const char *tablename,
+ const struct ipt_ip *ip,
+ void *matchinfo,
+ unsigned int matchsize,
+ unsigned int hook_mask)
+{
+ if (hook_mask
+ & ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_IN))) {
+ printk("ipt_mac: only valid for PRE_ROUTING or LOCAL_IN.\n");
+ return 0;
+ }
+
+ if (matchsize != IPT_ALIGN(sizeof(struct ipt_mac_info)))
+ return 0;
+
+ return 1;
+}
+
+static struct ipt_match mac_match
+= { { NULL, NULL }, "mac", &match, &ipt_mac_checkentry, THIS_MODULE };
+
+static int __init init(void)
+{
+ return ipt_register_match(&mac_match);
+}
+
+static void __exit fini(void)
+{
+ ipt_unregister_match(&mac_match);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/netfilter/ipt_mark.c b/net/ipv4/netfilter/ipt_mark.c
new file mode 100644
index 000000000..0d828fd20
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_mark.c
@@ -0,0 +1,52 @@
+/* Kernel module to match NFMARK values. */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+
+#include <linux/netfilter_ipv4/ipt_mark.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+EXPORT_NO_SYMBOLS;
+
+static int
+match(const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *matchinfo,
+ int offset,
+ const void *hdr,
+ u_int16_t datalen,
+ int *hotdrop)
+{
+ const struct ipt_mark_info *info = matchinfo;
+
+ return ((skb->nfmark & info->mask) == info->mark) ^ info->invert;
+}
+
+static int
+checkentry(const char *tablename,
+ const struct ipt_ip *ip,
+ void *matchinfo,
+ unsigned int matchsize,
+ unsigned int hook_mask)
+{
+ if (matchsize != IPT_ALIGN(sizeof(struct ipt_mark_info)))
+ return 0;
+
+ return 1;
+}
+
+static struct ipt_match mark_match
+= { { NULL, NULL }, "mark", &match, &checkentry, THIS_MODULE };
+
+static int __init init(void)
+{
+ return ipt_register_match(&mark_match);
+}
+
+static void __exit fini(void)
+{
+ ipt_unregister_match(&mark_match);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/netfilter/ipt_multiport.c b/net/ipv4/netfilter/ipt_multiport.c
new file mode 100644
index 000000000..08cc4a968
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_multiport.c
@@ -0,0 +1,102 @@
+/* Kernel module to match one of a list of TCP/UDP ports: ports are in
+ the same place so we can treat them as equal. */
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/udp.h>
+#include <linux/skbuff.h>
+
+#include <linux/netfilter_ipv4/ipt_multiport.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+#if 0
+#define duprintf(format, args...) printk(format , ## args)
+#else
+#define duprintf(format, args...)
+#endif
+
+EXPORT_NO_SYMBOLS;
+
+/* Returns 1 if the port is matched by the test, 0 otherwise. */
+static inline int
+ports_match(const u_int16_t *portlist, enum ipt_multiport_flags flags,
+ u_int8_t count, u_int16_t src, u_int16_t dst)
+{
+ unsigned int i;
+ for (i=0; i<count; i++) {
+ if (flags != IPT_MULTIPORT_DESTINATION
+ && portlist[i] == src)
+ return 1;
+
+ if (flags != IPT_MULTIPORT_SOURCE
+ && portlist[i] == dst)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+match(const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *matchinfo,
+ int offset,
+ const void *hdr,
+ u_int16_t datalen,
+ int *hotdrop)
+{
+ const struct udphdr *udp = hdr;
+ const struct ipt_multiport *multiinfo = matchinfo;
+
+ /* Must be big enough to read ports. */
+ if (offset == 0 && datalen < sizeof(struct udphdr)) {
+ /* We've been asked to examine this packet, and we
+ can't. Hence, no choice but to drop. */
+ duprintf("ipt_multiport:"
+ " Dropping evil offset=0 tinygram.\n");
+ *hotdrop = 1;
+ return 0;
+ }
+
+ /* Must not be a fragment. */
+ return !offset
+ && ports_match(multiinfo->ports,
+ multiinfo->flags, multiinfo->count,
+ ntohs(udp->source), ntohs(udp->dest));
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+checkentry(const char *tablename,
+ const struct ipt_ip *ip,
+ void *matchinfo,
+ unsigned int matchsize,
+ unsigned int hook_mask)
+{
+ const struct ipt_multiport *multiinfo = matchinfo;
+
+ /* Must specify proto == TCP/UDP, no unknown flags or bad count */
+ return (ip->proto == IPPROTO_TCP || ip->proto == IPPROTO_UDP)
+ && !(ip->flags & IPT_INV_PROTO)
+ && matchsize == IPT_ALIGN(sizeof(struct ipt_multiport))
+ && (multiinfo->flags == IPT_MULTIPORT_SOURCE
+ || multiinfo->flags == IPT_MULTIPORT_DESTINATION
+ || multiinfo->flags == IPT_MULTIPORT_EITHER)
+ && multiinfo->count <= IPT_MULTI_PORTS;
+}
+
+static struct ipt_match multiport_match
+= { { NULL, NULL }, "multiport", &match, &checkentry, THIS_MODULE };
+
+static int __init init(void)
+{
+ return ipt_register_match(&multiport_match);
+}
+
+static void __exit fini(void)
+{
+ ipt_unregister_match(&multiport_match);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/netfilter/ipt_owner.c b/net/ipv4/netfilter/ipt_owner.c
new file mode 100644
index 000000000..5438571d3
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_owner.c
@@ -0,0 +1,136 @@
+/* Kernel module to match various things tied to sockets associated with
+ locally generated outgoing packets.
+
+ (C)2000 Marc Boucher
+ */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/file.h>
+#include <net/sock.h>
+
+#include <linux/netfilter_ipv4/ipt_owner.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+EXPORT_NO_SYMBOLS;
+
+static int
+match_pid(const struct sk_buff *skb, pid_t pid)
+{
+ struct task_struct *p;
+ int i;
+
+ read_lock(&tasklist_lock);
+ p = find_task_by_pid(pid);
+ if(p && p->files) {
+ for (i=0; i < p->files->max_fds; i++) {
+ if (fcheck_task(p, i) == skb->sk->socket->file) {
+ read_unlock(&tasklist_lock);
+ return 1;
+ }
+ }
+ }
+ read_unlock(&tasklist_lock);
+ return 0;
+}
+
+static int
+match_sid(const struct sk_buff *skb, pid_t sid)
+{
+ struct task_struct *p;
+ int i, found=0;
+
+ read_lock(&tasklist_lock);
+ for_each_task(p) {
+ if ((p->session != sid) || !p->files)
+ continue;
+
+ for (i=0; i < p->files->max_fds; i++) {
+ if (fcheck_task(p, i) == skb->sk->socket->file) {
+ found = 1;
+ break;
+ }
+ }
+ if(found)
+ break;
+ }
+ read_unlock(&tasklist_lock);
+
+ return found;
+}
+
+static int
+match(const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *matchinfo,
+ int offset,
+ const void *hdr,
+ u_int16_t datalen,
+ int *hotdrop)
+{
+ const struct ipt_owner_info *info = matchinfo;
+
+ if (!skb->sk || !skb->sk->socket || !skb->sk->socket->file)
+ return 0;
+
+ if(info->match & IPT_OWNER_UID) {
+ if((skb->sk->socket->file->f_uid != info->uid) ^
+ !!(info->invert & IPT_OWNER_UID))
+ return 0;
+ }
+
+ if(info->match & IPT_OWNER_GID) {
+ if((skb->sk->socket->file->f_gid != info->gid) ^
+ !!(info->invert & IPT_OWNER_GID))
+ return 0;
+ }
+
+ if(info->match & IPT_OWNER_PID) {
+ if (!match_pid(skb, info->pid) ^
+ !!(info->invert & IPT_OWNER_PID))
+ return 0;
+ }
+
+ if(info->match & IPT_OWNER_SID) {
+ if (!match_sid(skb, info->sid) ^
+ !!(info->invert & IPT_OWNER_SID))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+checkentry(const char *tablename,
+ const struct ipt_ip *ip,
+ void *matchinfo,
+ unsigned int matchsize,
+ unsigned int hook_mask)
+{
+ if (hook_mask
+ & ~((1 << NF_IP_LOCAL_OUT) | (1 << NF_IP_POST_ROUTING))) {
+ printk("ipt_owner: only valid for LOCAL_OUT or POST_ROUTING.\n");
+ return 0;
+ }
+
+ if (matchsize != IPT_ALIGN(sizeof(struct ipt_owner_info)))
+ return 0;
+
+ return 1;
+}
+
+static struct ipt_match owner_match
+= { { NULL, NULL }, "owner", &match, &checkentry, THIS_MODULE };
+
+static int __init init(void)
+{
+ return ipt_register_match(&owner_match);
+}
+
+static void __exit fini(void)
+{
+ ipt_unregister_match(&owner_match);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/netfilter/ipt_state.c b/net/ipv4/netfilter/ipt_state.c
new file mode 100644
index 000000000..1baa54d62
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_state.c
@@ -0,0 +1,61 @@
+/* Kernel module to match connection tracking information.
+ * GPL (C) 1999 Rusty Russell (rusty@rustcorp.com.au).
+ */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_state.h>
+EXPORT_NO_SYMBOLS;
+
+static int
+match(const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *matchinfo,
+ int offset,
+ const void *hdr,
+ u_int16_t datalen,
+ int *hotdrop)
+{
+ const struct ipt_state_info *sinfo = matchinfo;
+ enum ip_conntrack_info ctinfo;
+ unsigned int statebit;
+
+ if (!ip_conntrack_get((struct sk_buff *)skb, &ctinfo))
+ statebit = IPT_STATE_INVALID;
+ else
+ statebit = IPT_STATE_BIT(ctinfo);
+
+ return (sinfo->statemask & statebit);
+}
+
+static int check(const char *tablename,
+ const struct ipt_ip *ip,
+ void *matchinfo,
+ unsigned int matchsize,
+ unsigned int hook_mask)
+{
+ if (matchsize != IPT_ALIGN(sizeof(struct ipt_state_info)))
+ return 0;
+
+ return 1;
+}
+
+static struct ipt_match state_match
+= { { NULL, NULL }, "state", &match, &check, THIS_MODULE };
+
+static int __init init(void)
+{
+ __MOD_INC_USE_COUNT(ip_conntrack_module);
+ return ipt_register_match(&state_match);
+}
+
+static void __exit fini(void)
+{
+ ipt_unregister_match(&state_match);
+ __MOD_DEC_USE_COUNT(ip_conntrack_module);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/netfilter/ipt_tos.c b/net/ipv4/netfilter/ipt_tos.c
new file mode 100644
index 000000000..6da72b2d8
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_tos.c
@@ -0,0 +1,53 @@
+/* Kernel module to match TOS values. */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+
+#include <linux/netfilter_ipv4/ipt_tos.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+EXPORT_NO_SYMBOLS;
+
+static int
+match(const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *matchinfo,
+ int offset,
+ const void *hdr,
+ u_int16_t datalen,
+ int *hotdrop)
+{
+ const struct ipt_tos_info *info = matchinfo;
+ const struct iphdr *iph = skb->nh.iph;
+
+ return (iph->tos == info->tos) ^ info->invert;
+}
+
+static int
+checkentry(const char *tablename,
+ const struct ipt_ip *ip,
+ void *matchinfo,
+ unsigned int matchsize,
+ unsigned int hook_mask)
+{
+ if (matchsize != IPT_ALIGN(sizeof(struct ipt_tos_info)))
+ return 0;
+
+ return 1;
+}
+
+static struct ipt_match tos_match
+= { { NULL, NULL }, "tos", &match, &checkentry, THIS_MODULE };
+
+static int __init init(void)
+{
+ return ipt_register_match(&tos_match);
+}
+
+static void __exit fini(void)
+{
+ ipt_unregister_match(&tos_match);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/netfilter/ipt_unclean.c b/net/ipv4/netfilter/ipt_unclean.c
new file mode 100644
index 000000000..056224a87
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_unclean.c
@@ -0,0 +1,576 @@
+/* Kernel module to match suspect packets. */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <linux/tcp.h>
+#include <linux/icmp.h>
+#include <net/checksum.h>
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+EXPORT_NO_SYMBOLS;
+
+#define limpk(format, args...) \
+do { \
+ if (net_ratelimit()) \
+ printk("ipt_unclean: %s" format, \
+ embedded ? "(embedded packet) " : "" , ## args); \
+} while(0)
+
+enum icmp_error_status
+{
+ ICMP_MAY_BE_ERROR,
+ ICMP_IS_ERROR,
+ ICMP_NOT_ERROR
+};
+
+struct icmp_info
+{
+ size_t min_len, max_len;
+ enum icmp_error_status err;
+ u_int8_t min_code, max_code;
+};
+
+static int
+check_ip(struct iphdr *iph, size_t length, int embedded);
+
+/* ICMP-specific checks. */
+static int
+check_icmp(const struct icmphdr *icmph,
+ u_int16_t datalen,
+ unsigned int offset,
+ int more_frags,
+ int embedded)
+{
+ static struct icmp_info info[]
+ = { [ICMP_ECHOREPLY]
+ = { 8, 65536, ICMP_NOT_ERROR, 0, 0 },
+ [ICMP_DEST_UNREACH]
+ = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 15 },
+ [ICMP_SOURCE_QUENCH]
+ = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 0 },
+ [ICMP_REDIRECT]
+ = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 3 },
+ [ICMP_ECHO]
+ = { 8, 65536, ICMP_NOT_ERROR, 0, 0 },
+ /* Router advertisement. */
+ [9]
+ = { 8, 8 + 255 * 8, ICMP_NOT_ERROR, 0, 0 },
+ /* Router solicitation. */
+ [10]
+ = { 8, 8, ICMP_NOT_ERROR, 0, 0 },
+ [ICMP_TIME_EXCEEDED]
+ = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 1 },
+ [ICMP_PARAMETERPROB]
+ = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 1 },
+ [ICMP_TIMESTAMP]
+ = { 20, 20, ICMP_NOT_ERROR, 0, 0 },
+ [ICMP_TIMESTAMPREPLY]
+ = { 20, 20, ICMP_NOT_ERROR, 0, 0 },
+ [ICMP_INFO_REQUEST]
+ = { 8, 65536, ICMP_NOT_ERROR, 0, 0 },
+ [ICMP_INFO_REPLY]
+ = { 8, 65536, ICMP_NOT_ERROR, 0, 0 },
+ [ICMP_ADDRESS]
+ = { 12, 12, ICMP_NOT_ERROR, 0, 0 },
+ [ICMP_ADDRESSREPLY]
+ = { 12, 12, ICMP_NOT_ERROR, 0, 0 } };
+
+ /* Can't do anything if it's a fragment. */
+ if (!offset)
+ return 1;
+
+ /* Must cover type and code. */
+ if (datalen < 2) {
+ limpk("ICMP len=%u too short\n", datalen);
+ return 0;
+ }
+
+ /* If not embedded. */
+ if (!embedded) {
+ /* Bad checksum? Don't print, just drop. */
+ if (!more_frags
+ && ip_compute_csum((unsigned char *) icmph, datalen) != 0)
+ return 0;
+
+ /* CHECK: Truncated ICMP (even if first fragment). */
+ if (icmph->type < sizeof(info)/sizeof(struct icmp_info)
+ && info[icmph->type].min_len != 0
+ && datalen < info[icmph->type].min_len) {
+ limpk("ICMP type %u len %u too short\n",
+ icmph->type, datalen);
+ return 0;
+ }
+
+ /* CHECK: Check within known error ICMPs. */
+ if (icmph->type < sizeof(info)/sizeof(struct icmp_info)
+ && info[icmph->type].err == ICMP_IS_ERROR) {
+ /* CHECK: Embedded packet must be at least
+ length of iph + 8 bytes. */
+ struct iphdr *inner = (void *)icmph + 8;
+
+ if (datalen - 8 < sizeof(struct iphdr)) {
+ limpk("ICMP error internal way too short\n");
+ return 0;
+ }
+ if (datalen - 8 < inner->ihl*4 + 8) {
+ limpk("ICMP error internal too short\n");
+ return 0;
+ }
+ if (!check_ip(inner, datalen - 8, 1))
+ return 0;
+ }
+ } else {
+ /* CHECK: Can't embed ICMP unless known non-error. */
+ if (icmph->type >= sizeof(info)/sizeof(struct icmp_info)
+ || info[icmph->type].err != ICMP_NOT_ERROR) {
+ limpk("ICMP type %u not embeddable\n",
+ icmph->type);
+ return 0;
+ }
+ }
+
+ /* CHECK: Invalid ICMP codes. */
+ if (icmph->type < sizeof(info)/sizeof(struct icmp_info)
+ && (icmph->code < info[icmph->type].min_code
+ || icmph->code > info[icmph->type].max_code)) {
+ limpk("ICMP type=%u code=%u\n",
+ icmph->type, icmph->code);
+ return 0;
+ }
+
+ /* CHECK: Above maximum length. */
+ if (icmph->type < sizeof(info)/sizeof(struct icmp_info)
+ && info[icmph->type].max_len != 0
+ && datalen > info[icmph->type].max_len) {
+ limpk("ICMP type=%u too long: %u bytes\n",
+ icmph->type, datalen);
+ return 0;
+ }
+
+ switch (icmph->type) {
+ case ICMP_PARAMETERPROB: {
+ /* CHECK: Problem param must be within error packet's
+ * IP header. */
+ struct iphdr *iph = (void *)icmph + 8;
+ u_int32_t arg = ntohl(icmph->un.gateway);
+
+ if (icmph->code == 0) {
+ if ((arg >> 24) >= iph->ihl*4) {
+ limpk("ICMP PARAMETERPROB ptr = %u\n",
+ ntohl(icmph->un.gateway) >> 24);
+ return 0;
+ }
+ arg &= 0x00FFFFFF;
+ }
+
+ /* CHECK: Rest must be zero. */
+ if (arg) {
+ limpk("ICMP PARAMETERPROB nonzero arg = %u\n",
+ arg);
+ return 0;
+ }
+ break;
+ }
+
+ case ICMP_TIME_EXCEEDED:
+ case ICMP_SOURCE_QUENCH:
+ /* CHECK: Unused must be zero. */
+ if (icmph->un.gateway != 0) {
+ limpk("ICMP type=%u unused = %u\n",
+ icmph->type, ntohl(icmph->un.gateway));
+ return 0;
+ }
+ break;
+ }
+
+ return 1;
+}
+
+/* UDP-specific checks. */
+static int
+check_udp(const struct iphdr *iph,
+ const struct udphdr *udph,
+ u_int16_t datalen,
+ unsigned int offset,
+ int more_frags,
+ int embedded)
+{
+ /* Can't do anything if it's a fragment. */
+ if (!offset)
+ return 1;
+
+ /* CHECK: Must cover UDP header. */
+ if (datalen < sizeof(struct udphdr)) {
+ limpk("UDP len=%u too short\n", datalen);
+ return 0;
+ }
+
+ /* Bad checksum? Don't print, just drop. */
+ /* FIXME: SRC ROUTE packets won't match checksum --RR */
+ if (!more_frags && !embedded
+ && csum_tcpudp_magic(iph->saddr, iph->daddr, datalen, IPPROTO_UDP,
+ csum_partial((char *)udph, datalen, 0)) != 0)
+ return 0;
+
+ /* CHECK: Ports can't be zero. */
+ if (!udph->source || !udph->dest) {
+ limpk("UDP zero ports %u/%u\n",
+ ntohs(udph->source), ntohs(udph->dest));
+ return 0;
+ }
+
+ if (!more_frags) {
+ if (!embedded) {
+ /* CHECK: UDP length must match. */
+ if (ntohs(udph->len) != datalen) {
+ limpk("UDP len too short %u vs %u\n",
+ ntohs(udph->len), datalen);
+ return 0;
+ }
+ } else {
+ /* CHECK: UDP length be >= this truncated pkt. */
+ if (ntohs(udph->len) < datalen) {
+ limpk("UDP len too long %u vs %u\n",
+ ntohs(udph->len), datalen);
+ return 0;
+ }
+ }
+ } else {
+ /* CHECK: UDP length must be > this frag's length. */
+ if (ntohs(udph->len) <= datalen) {
+ limpk("UDP fragment len too short %u vs %u\n",
+ ntohs(udph->len), datalen);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+#define TH_FIN 0x01
+#define TH_SYN 0x02
+#define TH_RST 0x04
+#define TH_PUSH 0x08
+#define TH_ACK 0x10
+#define TH_URG 0x20
+
+/* TCP-specific checks. */
+static int
+check_tcp(const struct iphdr *iph,
+ const struct tcphdr *tcph,
+ u_int16_t datalen,
+ unsigned int offset,
+ int more_frags,
+ int embedded)
+{
+ u_int8_t *opt = (u_int8_t *)(tcph + 1);
+ u_int8_t tcpflags;
+ int end_of_options = 0;
+ size_t i;
+
+ /* CHECK: Can't have offset=1: used to override TCP syn-checks. */
+ /* In fact, this is caught below (offset < 516). */
+
+ /* Can't do anything if it's a fragment. */
+ if (!offset)
+ return 1;
+
+ /* CHECK: Smaller than minimal TCP hdr. */
+ if (datalen < sizeof(struct tcphdr)) {
+ if (!embedded) {
+ limpk("Packet length %u < TCP header.\n", datalen);
+ return 0;
+ }
+ /* Must have ports available (datalen >= 8). */
+ /* CHECK: TCP ports inside ICMP error */
+ if (!tcph->source || !tcph->dest) {
+ limpk("Zero TCP ports %u/%u.\n",
+ htons(tcph->source), htons(tcph->dest));
+ return 0;
+ }
+ return 1;
+ }
+
+ /* CHECK: Smaller than actual TCP hdr. */
+ if (datalen < tcph->doff * 4) {
+ if (!embedded) {
+ limpk("Packet length %u < actual TCP header.\n",
+ datalen);
+ return 0;
+ } else
+ return 1;
+ }
+
+ /* Bad checksum? Don't print, just drop. */
+ /* FIXME: SRC ROUTE packets won't match checksum --RR */
+ if (!more_frags && !embedded
+ && csum_tcpudp_magic(iph->saddr, iph->daddr, datalen, IPPROTO_TCP,
+ csum_partial((char *)tcph, datalen, 0)) != 0)
+ return 0;
+
+ /* CHECK: TCP ports non-zero */
+ if (!tcph->source || !tcph->dest) {
+ limpk("Zero TCP ports %u/%u.\n",
+ htons(tcph->source), htons(tcph->dest));
+ return 0;
+ }
+
+ /* CHECK: TCP reserved bits zero. */
+ if(tcp_flag_word(tcph) & TCP_RESERVED_BITS) {
+ limpk("TCP reserved bits not zero\n");
+ return 0;
+ }
+
+ /* CHECK: TCP flags. */
+ tcpflags = ((u_int8_t *)tcph)[13];
+ if (tcpflags != TH_SYN
+ && tcpflags != (TH_SYN|TH_ACK)
+ && tcpflags != (TH_RST|TH_ACK)
+ && tcpflags != (TH_RST|TH_ACK|TH_PUSH)
+ && tcpflags != (TH_FIN|TH_ACK)
+ && tcpflags != TH_ACK
+ && tcpflags != (TH_ACK|TH_PUSH)
+ && tcpflags != (TH_ACK|TH_URG)
+ && tcpflags != (TH_ACK|TH_URG|TH_PUSH)
+ && tcpflags != (TH_FIN|TH_ACK|TH_PUSH)
+ && tcpflags != (TH_FIN|TH_ACK|TH_URG)
+ && tcpflags != (TH_FIN|TH_ACK|TH_URG|TH_PUSH)) {
+ limpk("TCP flags bad: %u\n", tcpflags);
+ return 0;
+ }
+
+ for (i = sizeof(struct tcphdr); i < tcph->doff * 4; ) {
+ switch (opt[i]) {
+ case 0:
+ end_of_options = 1;
+ i++;
+ break;
+ case 1:
+ i++;
+ break;
+ default:
+ /* CHECK: options after EOO. */
+ if (end_of_options) {
+ limpk("TCP option %u after end\n",
+ opt[i]);
+ return 0;
+ }
+ /* CHECK: options at tail. */
+ else if (i+1 >= tcph->doff * 4) {
+ limpk("TCP option %u at tail\n",
+ opt[i]);
+ return 0;
+ }
+ /* CHECK: zero-length options. */
+ else if (opt[i+1] == 0) {
+ limpk("TCP option %u 0 len\n",
+ opt[i]);
+ return 0;
+ }
+ /* CHECK: oversize options. */
+ else if (opt[i+1] + i >= tcph->doff * 4) {
+ limpk("TCP option %u at %Zu too long\n",
+ (unsigned int) opt[i], i);
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+/* Returns 1 if ok */
+/* Standard IP checks. */
+static int
+check_ip(struct iphdr *iph, size_t length, int embedded)
+{
+ u_int8_t *opt = (u_int8_t *)(iph + 1);
+ int end_of_options = 0;
+ void *protoh;
+ size_t datalen;
+ unsigned int i;
+ unsigned int offset;
+
+ /* Should only happen for local outgoing raw-socket packets. */
+ /* CHECK: length >= ip header. */
+ if (length < sizeof(struct iphdr) || length < iph->ihl * 4) {
+ limpk("Packet length %Zu < IP header.\n", length);
+ return 0;
+ }
+
+ offset = ntohs(iph->frag_off) & IP_OFFSET;
+ protoh = (void *)iph + iph->ihl * 4;
+ datalen = length - iph->ihl * 4;
+
+ /* CHECK: Embedded fragment. */
+ if (embedded && offset) {
+ limpk("Embedded fragment.\n");
+ return 0;
+ }
+
+ for (i = sizeof(struct iphdr); i < iph->ihl * 4; ) {
+ switch (opt[i]) {
+ case 0:
+ end_of_options = 1;
+ i++;
+ break;
+ case 1:
+ i++;
+ break;
+ default:
+ /* CHECK: options after EOO. */
+ if (end_of_options) {
+ limpk("IP option %u after end\n",
+ opt[i]);
+ return 0;
+ }
+ /* CHECK: options at tail. */
+ else if (i+1 >= iph->ihl * 4) {
+ limpk("IP option %u at tail\n",
+ opt[i]);
+ return 0;
+ }
+ /* CHECK: zero-length options. */
+ else if (opt[i+1] == 0) {
+ limpk("IP option %u 0 len\n",
+ opt[i]);
+ return 0;
+ }
+ /* CHECK: oversize options. */
+ else if (opt[i+1] + i >= iph->ihl * 4) {
+ limpk("IP option %u at %u too long\n",
+ opt[i], i);
+ return 0;
+ }
+ }
+ }
+
+ /* Fragment checks. */
+
+ /* CHECK: More fragments, but doesn't fill 8-byte boundary. */
+ if ((ntohs(iph->frag_off) & IP_MF)
+ && (ntohs(iph->tot_len) % 8) != 0) {
+ limpk("Truncated fragment %u long.\n", ntohs(iph->tot_len));
+ return 0;
+ }
+
+ /* CHECK: Oversize fragment a-la Ping of Death. */
+ if (offset * 8 + datalen > 65535) {
+ limpk("Oversize fragment to %u.\n", offset * 8);
+ return 0;
+ }
+
+ /* CHECK: DF set and offset or MF set. */
+ if ((ntohs(iph->frag_off) & IP_DF)
+ && (offset || (ntohs(iph->frag_off) & IP_MF))) {
+ limpk("DF set and offset=%u, MF=%u.\n",
+ offset, ntohs(iph->frag_off) & IP_MF);
+ return 0;
+ }
+
+ /* CHECK: Zero-sized fragments. */
+ if ((offset || (ntohs(iph->frag_off) & IP_MF))
+ && datalen == 0) {
+ limpk("Zero size fragment offset=%u\n", offset);
+ return 0;
+ }
+
+ /* Note: we can have even middle fragments smaller than this:
+ consider a large packet passing through a 600MTU then
+ 576MTU link: this gives a fragment of 24 data bytes. But
+ everyone packs fragments largest first, hence a fragment
+ can't START before 576 - MAX_IP_HEADER_LEN. */
+
+ /* Used to be min-size 576: I recall Alan Cox saying ax25 goes
+ down to 128 (576 taken from RFC 791: All hosts must be
+ prepared to accept datagrams of up to 576 octets). Use 128
+ here. */
+#define MIN_LIKELY_MTU 128
+ /* CHECK: Min size of first frag = 128. */
+ if ((ntohs(iph->frag_off) & IP_MF)
+ && offset == 0
+ && ntohs(iph->tot_len) < MIN_LIKELY_MTU) {
+ limpk("First fragment size %u < %u\n", ntohs(iph->tot_len),
+ MIN_LIKELY_MTU);
+ return 0;
+ }
+
+ /* CHECK: Min offset of frag = 128 - 60 (max IP hdr len). */
+ if (offset && offset * 8 < MIN_LIKELY_MTU - 60) {
+ limpk("Fragment starts at %u < %u\n", offset * 8,
+ MIN_LIKELY_MTU-60);
+ return 0;
+ }
+
+ /* CHECK: Protocol specification non-zero. */
+ if (iph->protocol == 0) {
+ limpk("Zero protocol\n");
+ return 0;
+ }
+
+ /* Per-protocol checks. */
+ switch (iph->protocol) {
+ case IPPROTO_ICMP:
+ return check_icmp(protoh, datalen, offset,
+ (ntohs(iph->frag_off) & IP_MF),
+ embedded);
+
+ case IPPROTO_UDP:
+ return check_udp(iph, protoh, datalen, offset,
+ (ntohs(iph->frag_off) & IP_MF),
+ embedded);
+
+ case IPPROTO_TCP:
+ return check_tcp(iph, protoh, datalen, offset,
+ (ntohs(iph->frag_off) & IP_MF),
+ embedded);
+ default:
+ /* Ignorance is bliss. */
+ return 1;
+ }
+}
+
+static int
+match(const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const void *matchinfo,
+ int offset,
+ const void *hdr,
+ u_int16_t datalen,
+ int *hotdrop)
+{
+ return !check_ip(skb->nh.iph, skb->len, 0);
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+checkentry(const char *tablename,
+ const struct ipt_ip *ip,
+ void *matchinfo,
+ unsigned int matchsize,
+ unsigned int hook_mask)
+{
+ if (matchsize != IPT_ALIGN(0))
+ return 0;
+
+ return 1;
+}
+
+static struct ipt_match unclean_match
+= { { NULL, NULL }, "unclean", &match, &checkentry, THIS_MODULE };
+
+static int __init init(void)
+{
+ return ipt_register_match(&unclean_match);
+}
+
+static void __exit fini(void)
+{
+ ipt_unregister_match(&unclean_match);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c
new file mode 100644
index 000000000..a10bb3682
--- /dev/null
+++ b/net/ipv4/netfilter/iptable_filter.c
@@ -0,0 +1,181 @@
+/*
+ * This is the 1999 rewrite of IP Firewalling, aiming for kernel 2.3.x.
+ *
+ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ */
+#include <linux/module.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+#define FILTER_VALID_HOOKS ((1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD) | (1 << NF_IP_LOCAL_OUT))
+
+/* Standard entry. */
+struct ipt_standard
+{
+ struct ipt_entry entry;
+ struct ipt_standard_target target;
+};
+
+struct ipt_error_target
+{
+ struct ipt_entry_target target;
+ char errorname[IPT_FUNCTION_MAXNAMELEN];
+};
+
+struct ipt_error
+{
+ struct ipt_entry entry;
+ struct ipt_error_target target;
+};
+
+static struct
+{
+ struct ipt_replace repl;
+ struct ipt_standard entries[3];
+ struct ipt_error term;
+} initial_table __initdata
+= { { "filter", FILTER_VALID_HOOKS, 4,
+ sizeof(struct ipt_standard) * 3 + sizeof(struct ipt_error),
+ { [NF_IP_LOCAL_IN] 0,
+ [NF_IP_FORWARD] sizeof(struct ipt_standard),
+ [NF_IP_LOCAL_OUT] sizeof(struct ipt_standard) * 2 },
+ { [NF_IP_LOCAL_IN] 0,
+ [NF_IP_FORWARD] sizeof(struct ipt_standard),
+ [NF_IP_LOCAL_OUT] sizeof(struct ipt_standard) * 2 },
+ 0, NULL, { } },
+ {
+ /* LOCAL_IN */
+ { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
+ 0,
+ sizeof(struct ipt_entry),
+ sizeof(struct ipt_standard),
+ 0, { 0, 0 }, { } },
+ { { sizeof(struct ipt_standard_target), { "" }, { } },
+ -NF_ACCEPT - 1 } },
+ /* FORWARD */
+ { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
+ 0,
+ sizeof(struct ipt_entry),
+ sizeof(struct ipt_standard),
+ 0, { 0, 0 }, { } },
+ { { sizeof(struct ipt_standard_target), { "" }, { } },
+ -NF_ACCEPT - 1 } },
+ /* LOCAL_OUT */
+ { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
+ 0,
+ sizeof(struct ipt_entry),
+ sizeof(struct ipt_standard),
+ 0, { 0, 0 }, { } },
+ { { sizeof(struct ipt_standard_target), { "" }, { } },
+ -NF_ACCEPT - 1 } }
+ },
+ /* ERROR */
+ { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
+ 0,
+ sizeof(struct ipt_entry),
+ sizeof(struct ipt_error),
+ 0, { 0, 0 }, { } },
+ { { sizeof(struct ipt_error_target), { IPT_ERROR_TARGET },
+ { } },
+ "ERROR"
+ }
+ }
+};
+
+static struct ipt_table packet_filter
+= { { NULL, NULL }, "filter", &initial_table.repl,
+ FILTER_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL };
+
+/* The work comes in here from netfilter.c. */
+static unsigned int
+ipt_hook(unsigned int hook,
+ struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ return ipt_do_table(pskb, hook, in, out, &packet_filter, NULL);
+}
+
+static unsigned int
+ipt_local_out_hook(unsigned int hook,
+ struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ /* root is playing with raw sockets. */
+ if ((*pskb)->len < sizeof(struct iphdr)
+ || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) {
+ if (net_ratelimit())
+ printk("ipt_hook: happy cracking.\n");
+ return NF_ACCEPT;
+ }
+
+ return ipt_do_table(pskb, hook, in, out, &packet_filter, NULL);
+}
+
+static struct nf_hook_ops ipt_ops[]
+= { { { NULL, NULL }, ipt_hook, PF_INET, NF_IP_LOCAL_IN, NF_IP_PRI_FILTER },
+ { { NULL, NULL }, ipt_hook, PF_INET, NF_IP_FORWARD, NF_IP_PRI_FILTER },
+ { { NULL, NULL }, ipt_local_out_hook, PF_INET, NF_IP_LOCAL_OUT,
+ NF_IP_PRI_FILTER }
+};
+
+/* Default to no forward for security reasons. */
+static int forward = NF_DROP;
+MODULE_PARM(forward, "i");
+
+static int __init init(void)
+{
+ int ret;
+
+ if (forward < 0 || forward > NF_MAX_VERDICT) {
+ printk("iptables forward must be 0 or 1\n");
+ return -EINVAL;
+ }
+
+ /* Entry 1 is the FORWARD hook */
+ initial_table.entries[1].target.verdict = -forward - 1;
+
+ /* Register table */
+ ret = ipt_register_table(&packet_filter);
+ if (ret < 0)
+ return ret;
+
+ /* Register hooks */
+ ret = nf_register_hook(&ipt_ops[0]);
+ if (ret < 0)
+ goto cleanup_table;
+
+ ret = nf_register_hook(&ipt_ops[1]);
+ if (ret < 0)
+ goto cleanup_hook0;
+
+ ret = nf_register_hook(&ipt_ops[2]);
+ if (ret < 0)
+ goto cleanup_hook1;
+
+ return ret;
+
+ cleanup_hook1:
+ nf_unregister_hook(&ipt_ops[1]);
+ cleanup_hook0:
+ nf_unregister_hook(&ipt_ops[0]);
+ cleanup_table:
+ ipt_unregister_table(&packet_filter);
+
+ return ret;
+}
+
+static void __exit fini(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof(ipt_ops)/sizeof(struct nf_hook_ops); i++)
+ nf_unregister_hook(&ipt_ops[i]);
+
+ ipt_unregister_table(&packet_filter);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c
new file mode 100644
index 000000000..ef506ca7a
--- /dev/null
+++ b/net/ipv4/netfilter/iptable_mangle.c
@@ -0,0 +1,152 @@
+/*
+ * This is the 1999 rewrite of IP Firewalling, aiming for kernel 2.3.x.
+ *
+ * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ */
+#include <linux/module.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+#define MANGLE_VALID_HOOKS ((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_OUT))
+
+/* Standard entry. */
+struct ipt_standard
+{
+ struct ipt_entry entry;
+ struct ipt_standard_target target;
+};
+
+struct ipt_error_target
+{
+ struct ipt_entry_target target;
+ char errorname[IPT_FUNCTION_MAXNAMELEN];
+};
+
+struct ipt_error
+{
+ struct ipt_entry entry;
+ struct ipt_error_target target;
+};
+
+static struct
+{
+ struct ipt_replace repl;
+ struct ipt_standard entries[2];
+ struct ipt_error term;
+} initial_table __initdata
+= { { "mangle", MANGLE_VALID_HOOKS, 3,
+ sizeof(struct ipt_standard) * 2 + sizeof(struct ipt_error),
+ { [NF_IP_PRE_ROUTING] 0,
+ [NF_IP_LOCAL_OUT] sizeof(struct ipt_standard) },
+ { [NF_IP_PRE_ROUTING] 0,
+ [NF_IP_LOCAL_OUT] sizeof(struct ipt_standard) },
+ 0, NULL, { } },
+ {
+ /* PRE_ROUTING */
+ { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
+ 0,
+ sizeof(struct ipt_entry),
+ sizeof(struct ipt_standard),
+ 0, { 0, 0 }, { } },
+ { { sizeof(struct ipt_standard_target), { "" }, { } },
+ -NF_ACCEPT - 1 } },
+ /* LOCAL_OUT */
+ { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
+ 0,
+ sizeof(struct ipt_entry),
+ sizeof(struct ipt_standard),
+ 0, { 0, 0 }, { } },
+ { { sizeof(struct ipt_standard_target), { "" }, { } },
+ -NF_ACCEPT - 1 } }
+ },
+ /* ERROR */
+ { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
+ 0,
+ sizeof(struct ipt_entry),
+ sizeof(struct ipt_error),
+ 0, { 0, 0 }, { } },
+ { { sizeof(struct ipt_error_target), { IPT_ERROR_TARGET },
+ { } },
+ "ERROR"
+ }
+ }
+};
+
+static struct ipt_table packet_mangler
+= { { NULL, NULL }, "mangle", &initial_table.repl,
+ MANGLE_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL };
+
+/* The work comes in here from netfilter.c. */
+static unsigned int
+ipt_hook(unsigned int hook,
+ struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ return ipt_do_table(pskb, hook, in, out, &packet_mangler, NULL);
+}
+
+static unsigned int
+ipt_local_out_hook(unsigned int hook,
+ struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ /* root is playing with raw sockets. */
+ if ((*pskb)->len < sizeof(struct iphdr)
+ || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) {
+ if (net_ratelimit())
+ printk("ipt_hook: happy cracking.\n");
+ return NF_ACCEPT;
+ }
+
+ return ipt_do_table(pskb, hook, in, out, &packet_mangler, NULL);
+}
+
+static struct nf_hook_ops ipt_ops[]
+= { { { NULL, NULL }, ipt_hook, PF_INET, NF_IP_PRE_ROUTING, NF_IP_PRI_MANGLE },
+ { { NULL, NULL }, ipt_local_out_hook, PF_INET, NF_IP_LOCAL_OUT,
+ NF_IP_PRI_MANGLE }
+};
+
+static int __init init(void)
+{
+ int ret;
+
+ /* Register table */
+ ret = ipt_register_table(&packet_mangler);
+ if (ret < 0)
+ return ret;
+
+ /* Register hooks */
+ ret = nf_register_hook(&ipt_ops[0]);
+ if (ret < 0)
+ goto cleanup_table;
+
+ ret = nf_register_hook(&ipt_ops[1]);
+ if (ret < 0)
+ goto cleanup_hook0;
+
+ return ret;
+
+ cleanup_hook0:
+ nf_unregister_hook(&ipt_ops[0]);
+ cleanup_table:
+ ipt_unregister_table(&packet_mangler);
+
+ return ret;
+}
+
+static void __exit fini(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof(ipt_ops)/sizeof(struct nf_hook_ops); i++)
+ nf_unregister_hook(&ipt_ops[i]);
+
+ ipt_unregister_table(&packet_mangler);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 4e649eded..c683f2f23 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -5,7 +5,7 @@
*
* ROUTE - implementation of the IP router.
*
- * Version: $Id: route.c,v 1.81 2000/02/09 11:16:42 davem Exp $
+ * Version: $Id: route.c,v 1.82 2000/03/17 14:41:52 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -1187,10 +1187,7 @@ ip_route_input_mc(struct sk_buff *skb, u32 daddr, u32 saddr,
rth->rt_dst = daddr;
rth->key.tos = tos;
#ifdef CONFIG_IP_ROUTE_FWMARK
- if (skb->nfreason == NF_REASON_FOR_ROUTING)
- rth->key.fwmark = skb->nfmark;
- else
- rth->key.fwmark = 0;
+ rth->key.fwmark = skb->nfmark;
#endif
rth->key.src = saddr;
rth->rt_src = saddr;
@@ -1269,10 +1266,7 @@ int ip_route_input_slow(struct sk_buff *skb, u32 daddr, u32 saddr,
key.src = saddr;
key.tos = tos;
#ifdef CONFIG_IP_ROUTE_FWMARK
- if (skb->nfreason == NF_REASON_FOR_ROUTING)
- key.fwmark = skb->nfmark;
- else
- key.fwmark = 0;
+ key.fwmark = skb->nfmark;
#endif
key.iif = dev->ifindex;
key.oif = 0;
@@ -1395,10 +1389,7 @@ int ip_route_input_slow(struct sk_buff *skb, u32 daddr, u32 saddr,
rth->rt_dst = daddr;
rth->key.tos = tos;
#ifdef CONFIG_IP_ROUTE_FWMARK
- if (skb->nfreason == NF_REASON_FOR_ROUTING)
- rth->key.fwmark = skb->nfmark;
- else
- rth->key.fwmark = 0;
+ rth->key.fwmark = skb->nfmark;
#endif
rth->key.src = saddr;
rth->rt_src = saddr;
@@ -1473,10 +1464,7 @@ local_input:
rth->rt_dst = daddr;
rth->key.tos = tos;
#ifdef CONFIG_IP_ROUTE_FWMARK
- if (skb->nfreason == NF_REASON_FOR_ROUTING)
- rth->key.fwmark = skb->nfmark;
- else
- rth->key.fwmark = 0;
+ rth->key.fwmark = skb->nfmark;
#endif
rth->key.src = saddr;
rth->rt_src = saddr;
@@ -1563,9 +1551,7 @@ int ip_route_input(struct sk_buff *skb, u32 daddr, u32 saddr,
rth->key.iif == iif &&
rth->key.oif == 0 &&
#ifdef CONFIG_IP_ROUTE_FWMARK
- rth->key.fwmark
- == (skb->nfreason == NF_REASON_FOR_ROUTING
- ? skb->nfmark : 0) &&
+ rth->key.fwmark == skb->nfmark &&
#endif
rth->key.tos == tos) {
rth->u.dst.lastuse = jiffies;
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 204f25574..1edee9f51 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -5,7 +5,7 @@
*
* Implementation of the Transmission Control Protocol(TCP).
*
- * Version: $Id: tcp_ipv4.c,v 1.201 2000/03/08 19:36:42 davem Exp $
+ * Version: $Id: tcp_ipv4.c,v 1.202 2000/03/17 14:41:53 davem Exp $
*
* IPv4 specific functions
*
@@ -72,8 +72,6 @@ extern int sysctl_ip_dynaddr;
struct inode tcp_inode;
struct socket *tcp_socket=&tcp_inode.u.socket_i;
-static void tcp_v4_send_reset(struct sk_buff *skb);
-
void tcp_v4_send_check(struct sock *sk, struct tcphdr *th, int len,
struct sk_buff *skb);
@@ -1059,7 +1057,7 @@ void tcp_v4_send_check(struct sock *sk, struct tcphdr *th, int len,
* Exception: precedence violation. We do not implement it in any case.
*/
-static void tcp_v4_send_reset(struct sk_buff *skb)
+void tcp_v4_send_reset(struct sk_buff *skb)
{
struct tcphdr *th = skb->h.th;
struct tcphdr rth;
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index 818ad66ca..c52797d70 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -6,7 +6,7 @@
* Pedro Roque <roque@di.fc.ul.pt>
* Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
*
- * $Id: sit.c,v 1.35 2000/01/06 00:42:08 davem Exp $
+ * $Id: sit.c,v 1.36 2000/03/17 14:42:08 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -15,6 +15,7 @@
*/
#define __NO_VERSION__
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/types.h>
@@ -388,6 +389,10 @@ int ipip6_rcv(struct sk_buff *skb, unsigned short len)
skb->dev = tunnel->dev;
dst_release(skb->dst);
skb->dst = NULL;
+#ifdef CONFIG_NETFILTER
+ nf_conntrack_put(skb->nfct);
+ skb->nfct = NULL;
+#endif
netif_rx(skb);
read_unlock(&ipip6_lock);
return 0;
@@ -547,6 +552,11 @@ static int ipip6_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
ip_select_ident(iph, &rt->u.dst);
ip_send_check(iph);
+#ifdef CONFIG_NETFILTER
+ nf_conntrack_put(skb->nfct);
+ skb->nfct = NULL;
+#endif
+
stats->tx_bytes += skb->len;
stats->tx_packets++;
ip_send(skb);
diff --git a/net/khttpd/security.c b/net/khttpd/security.c
index df346aadb..bd578941d 100644
--- a/net/khttpd/security.c
+++ b/net/khttpd/security.c
@@ -115,7 +115,7 @@ struct file *OpenFileForSecurity(char *Filename)
- filp = filp_open(Filename,00,O_RDONLY);
+ filp = filp_open(Filename, 0, O_RDONLY, NULL);
if ((IS_ERR(filp))||(filp==NULL)||(filp->f_dentry==NULL))
diff --git a/net/netsyms.c b/net/netsyms.c
index 48cd5b503..c6745cafe 100644
--- a/net/netsyms.c
+++ b/net/netsyms.c
@@ -229,6 +229,7 @@ EXPORT_SYMBOL(inet_del_protocol);
EXPORT_SYMBOL(ip_route_output);
EXPORT_SYMBOL(ip_route_input);
EXPORT_SYMBOL(icmp_send);
+EXPORT_SYMBOL(icmp_reply);
EXPORT_SYMBOL(ip_options_compile);
EXPORT_SYMBOL(ip_options_undo);
EXPORT_SYMBOL(arp_send);
@@ -339,6 +340,7 @@ EXPORT_SYMBOL(tcp_sendmsg);
EXPORT_SYMBOL(tcp_v4_rebuild_header);
EXPORT_SYMBOL(tcp_v4_send_check);
EXPORT_SYMBOL(tcp_v4_conn_request);
+EXPORT_SYMBOL(tcp_v4_send_reset);
EXPORT_SYMBOL(tcp_create_openreq_child);
EXPORT_SYMBOL(tcp_bucket_create);
EXPORT_SYMBOL(__tcp_put_port);
diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c
index 143d6e361..31dedf1ea 100644
--- a/net/sched/cls_fw.c
+++ b/net/sched/cls_fw.c
@@ -66,7 +66,7 @@ static int fw_classify(struct sk_buff *skb, struct tcf_proto *tp,
struct fw_head *head = (struct fw_head*)tp->root;
struct fw_filter *f;
#ifdef CONFIG_NETFILTER
- u32 id = (skb->nfreason == NF_REASON_FOR_CLS_FW ? skb->nfmark : 0);
+ u32 id = skb->nfmark;
#else
u32 id = 0;
#endif
diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c
index ddc738fcc..947aede01 100644
--- a/net/sched/sch_ingress.c
+++ b/net/sched/sch_ingress.c
@@ -224,14 +224,14 @@ used on the egress (might slow things for an iota)
return fwres;
}
-/* after iptables */
+/* after ipt_filter */
static struct nf_hook_ops ing_ops =
{
{ NULL, NULL},
ing_hook,
PF_INET,
NF_IP_PRE_ROUTING,
- 1
+ NF_IP_PRI_FILTER + 1
};
int ingress_init(struct Qdisc *sch,struct rtattr *opt)
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index d63024983..97e323d0c 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -636,7 +636,7 @@ svc_tcp_recvfrom(struct svc_rqst *rqstp)
dprintk("svc: incomplete TCP record (%d of %d)\n",
len, svsk->sk_reclen);
svc_sock_received(svsk, ready);
- len = -EAGAIN; /* record not complete */
+ return -EAGAIN; /* record not complete */
}
/* Frob argbuf */
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index a57c2a06d..cbe730b5d 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -8,7 +8,7 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
- * Version: $Id: af_unix.c,v 1.89 2000/02/27 19:52:50 davem Exp $
+ * Version: $Id: af_unix.c,v 1.90 2000/03/16 20:38:45 davem Exp $
*
* Fixes:
* Linus Torvalds : Assorted bug cures.
@@ -569,7 +569,7 @@ static unix_socket *unix_find_other(struct sockaddr_un *sunname, int len,
/* Do not believe to VFS, grab kernel lock */
lock_kernel();
- dentry = open_namei(sunname->sun_path, 2|O_NOFOLLOW, S_IFSOCK);
+ dentry = __open_namei(sunname->sun_path, 2|O_NOFOLLOW, S_IFSOCK, NULL);
if (IS_ERR(dentry)) {
*error = PTR_ERR(dentry);
unlock_kernel();
diff --git a/scripts/Makefile b/scripts/Makefile
index 66311cbd0..d6d231d73 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -40,6 +40,6 @@ docproc: docproc.o
${HOSTCC} -o docproc docproc.o
clean:
- rm -f *~ kconfig.tk *.o tkparse mkdep split-include
+ rm -f *~ kconfig.tk *.o tkparse mkdep split-include docproc
include $(TOPDIR)/Rules.make