Index: releng/8.4/UPDATING =================================================================== --- releng/8.4/UPDATING (revision 252333) +++ releng/8.4/UPDATING (revision 252334) @@ -1,1861 +1,1870 @@ Updating Information for FreeBSD current users This file is maintained and copyrighted by M. Warner Losh . See end of file for further details. For commonly done items, please see the COMMON ITEMS: section later in the file. Items affecting the ports and packages system can be found in /usr/ports/UPDATING. Please read that file before running portupgrade. NOTE TO PEOPLE WHO THINK THAT FreeBSD 8.x IS SLOW ON IA64 OR SUN4V: For ia64 the INVARIANTS and INVARIANT_SUPPORT kernel options were left in the GENERIC kernel because the kernel does not work properly without them. For sun4v all of the normal kernel debugging tools present in HEAD were left in place because sun4v support still needs work to become production ready. +20130628: p1 FreeBSD-EN-13:01.fxp + FreeBSD-EN-13:02.vtnet + Fix a problem where dhclient(8) utility tries to initilaize an + fxp(4) forever because the driver resets the controller chip + twice upon initialization. [EN-13:01] + + Fix a problem where frames sent to additional MAC addresses are + not forwarded to the vtnet(4) interface. [EN-13:02] + 20130429: Fix a bug that allows NFS clients to issue READDIR on files. 20130225: A new compression method (lz4) has been merged. Please refer to zpool-features(7) for more information. Please refer to the "ZFS notes" section of this file for information on upgrading boot ZFS pools. 20121130: A new version of ZFS (pool version 5000) has been merged to 8-STABLE. Starting with this version the old system of ZFS pool versioning is superseded by "feature flags". This concept enables forward compatibility against certain future changes in functionality of ZFS pools. The first two read-only compatible "feature flags" for ZFS pools are "com.delphix:async_destroy" and "com.delphix:empty_bpobj". For more information read the new zpool-features(7) manual page. Please refer to the "ZFS notes" section of this file for information on upgrading boot ZFS pools. 20121018: WITH_CTF can now be specified in src.conf (not recommended, there are some problems with static executables), make.conf (would also affect ports which do not use GNU make and do not override the compile targets) or in the kernel config (via "makeoptions WITH_CTF=yes"). When WITH_CTF was specified there before this was silently ignored, so make sure that WITH_CTF is not used in places which could lead to unwanted behavior. 20120913: The random(4) support for the VIA hardware random number generator (`PADLOCK') is no longer enabled unconditionally. Add the PADLOCK_RNG option in the custom kernel config if needed. The GENERIC kernels on i386 and amd64 do include the option, so the change only affects the custom kernel configurations. 20120727: The sparc64 ZFS loader has been changed to no longer try to auto- detect ZFS providers based on diskN aliases but now requires these to be explicitly listed in the OFW boot-device environment variable. 20120411: 8.3-RELEASE. 20120106: A new VOP_ADVISE() was added to support posix_fadvise(2). All filesystem modules must be recompiled. 20111116: A new VOP_ALLOCATE() was added to support posix_fallocate(2). All filesystem modules must be recompiled. 20111101: The broken amd(4) driver has been replaced with esp(4) in the amd64, i386 and pc98 GENERIC kernel configuration files. 20110606: A new version of ZFS (version 28) has been merged. This version does not depend on a python library and the sysutils/py-zfs port is not used anymore. For upgrading your boot pool, please read "ZFS notes" in the COMMON ITEMS: section later in this file. 20110428: IPsec's HMAC_SHA256-512 support has been fixed to be RFC4868 compliant, and will now use half of hash for authentication. This will break interoperability with all stacks (including all older FreeBSD versions) who implement draft-ietf-ipsec-ciph-sha-256-00 (they use 96 bits of hash for authentication). The only workaround with such peers is to use another HMAC algorithm for IPsec ("phase 2") authentication. 20110221: 8.2-RELEASE. 20101126: New version of minidump format for amd64 architecture was introduced in r215872. To analyze vmcore files produced by kernels at or after this version you will need updated userland, libkvm actually, that is able to handle the new version. 20101022: A workaround for a fixed ld bug has been removed in kernel code, so make sure that your system ld is built from sources after revision 211583 from 2010-08-21 (r210245 from 2010-07-19 if building stable/8 kernel on head, r211584 from 2010-08-21 for stable/7). A symptom of incorrect ld version is different addresses for set_pcpu section and __start_set_pcpu symbol in kernel and/or modules. 20100915: A new version of ZFS (version 15) has been merged. This version uses a python library for the following subcommands: zfs allow, zfs unallow, zfs groupspace, zfs userspace. For full functionality of these commands the following port must be installed: sysutils/py-zfs 20100720: 8.1-RELEASE. 20100502: The config(8) command has been updated to maintain compatibility with config files from 8.0-RELEASE. You will need a new version of config to build kernels (this version can be used from 8.0-RELEASE forward). The buildworld target will generate it, so following the instructions in this file for updating will work glitch-free. Merely doing a make buildkernel without first doing a make buildworld (or kernel-toolchain), or attempting to build a kernel using traidtional methods will generate a config version warning, indicating you should update. 20100408: The rc.firewall and rc.firewall6 were unified, and rc.firewall6 and rc.d/ip6fw were removed. According to the removal of rc.d/ip6fw, ipv6_firewall_* rc variables are obsoleted. Instead, the following new rc variables are added to rc.d/ipfw: firewall_client_net_ipv6, firewall_simple_iif_ipv6, firewall_simple_inet_ipv6, firewall_simple_oif_ipv6, firewall_simple_onet_ipv6 The meanings correspond to the relevant IPv4 variables. 20100417: COMPAT_IA32 has been added as an alias for COMPAT_FREEBSD32. A new version of config(8) is required. The error message when you hit this condition is confusing (COMPAT_FREEBSD32 duplicate option), when it should really say "your config is too old to compile this new kernel." 20100406: The kernel option COMPAT_IA32 has been replaced with COMPAT_FREEBSD32 to allow 32-bit compatibility on non-x86 platforms. All kernel configurations on amd64 and ia64 platforms using these options must be modified accordingly. 20100125: Introduce the kernel thread "deadlock resolver" (which can be enabled via the DEADLKRES option, see NOTES for more details) and the sleepq_type() function for sleepqueues. 20091125: 8.0-RELEASE. 20090929: 802.11s D3.03 support was committed. This is incompatible with the previous code, which was based on D3.0. 20090915: ZFS snapshots are now mounted with MNT_IGNORE flag. Use -v option for mount(8) and -a option for df(1) to see them. 20090813: Remove the option STOP_NMI. The default action is now to use NMI only for KDB via the newly introduced function stop_cpus_hard() and maintain stop_cpus() to just use a normal IPI_STOP on ia32 and amd64. 20090803: RELENG_8 branched. 20090719: Bump the shared library version numbers for all libraries that do not use symbol versioning as part of the 8.0-RELEASE cycle. Bump __FreeBSD_version to 800105. 20090714: Due to changes in the implementation of virtual network stack support, all network-related kernel modules must be recompiled. As this change breaks the ABI, bump __FreeBSD_version to 800104. 20090713: The TOE interface to the TCP syncache has been modified to remove struct tcpopt () from the ABI of the network stack. The cxgb driver is the only TOE consumer affected by this change, and needs to be recompiled along with the kernel. As this change breaks the ABI, bump __FreeBSD_version to 800103. 20090712: Padding has been added to struct tcpcb, sackhint and tcpstat in to facilitate future MFCs and bug fixes whilst maintainig the ABI. However, this change breaks the ABI, so bump __FreeBSD_version to 800102. User space tools that rely on the size of any of these structs (e.g. sockstat) need to be recompiled. 20090630: The NFS_LEGACYRPC option has been removed along with the old kernel RPC implementation that this option selected. Kernel configurations may need to be adjusted. 20090629: The network interface device nodes at /dev/net/ have been removed. All ioctl operations can be performed the normal way using routing sockets. The kqueue functionality can generally be replaced with routing sockets. 20090628: The documentation from the FreeBSD Documentation Project (Handbook, FAQ, etc.) is now installed via packages by sysinstall(8) and under the /usr/local/share/doc/freebsd directory instead of /usr/share/doc. 20090624: The ABI of various structures related to the SYSV IPC API have been changed. As a result, the COMPAT_FREEBSD[456] and COMPAT_43 kernel options now all require COMPAT_FREEBSD7. Bump __FreeBSD_version to 800100. 20090622: Layout of struct vnet has changed as routing related variables were moved to their own Vimage module. Modules need to be recompiled. Bump __FreeBSD_version to 800099. 20090619: NGROUPS_MAX and NGROUPS have been increased from 16 to 1023 and 1024 respectively. As long as no more than 16 groups per process are used, no changes should be visible. When more than 16 groups are used, old binaries may fail if they call getgroups() or getgrouplist() with statically sized storage. Recompiling will work around this, but applications should be modified to use dynamically allocated storage for group arrays as POSIX.1-2008 does not cap an implementation's number of supported groups at NGROUPS_MAX+1 as previous versions did. NFS and portalfs mounts may also be affected as the list of groups is truncated to 16. Users of NFS who use more than 16 groups, should take care that negative group permissions are not used on the exported file systems as they will not be reliable unless a GSSAPI based authentication method is used. 20090616: The compiling option ADAPTIVE_LOCKMGRS has been introduced. This option compiles in the support for adaptive spinning for lockmgrs which want to enable it. The lockinit() function now accepts the flag LK_ADAPTIVE in order to make the lock object subject to adaptive spinning when both held in write and read mode. 20090613: The layout of the structure returned by IEEE80211_IOC_STA_INFO has changed. User applications that use this ioctl need to be rebuilt. 20090611: The layout of struct thread has changed. Kernel and modules need to be rebuilt. 20090608: The layout of structs ifnet, domain, protosw and vnet_net has changed. Kernel modules need to be rebuilt. Bump __FreeBSD_version to 800097. 20090602: window(1) has been removed from the base system. It can now be installed from ports. The port is called misc/window. 20090601: The way we are storing and accessing `routing table' entries has changed. Programs reading the FIB, like netstat, need to be re-compiled. 20090601: A new netisr implementation has been added for FreeBSD 8. Network file system modules, such as igmp, ipdivert, and others, should be rebuilt. Bump __FreeBSD_version to 800096. 20090530: Remove the tunable/sysctl debug.mpsafevfs as its initial purpose is no more valid. 20090530: Add VOP_ACCESSX(9). File system modules need to be rebuilt. Bump __FreeBSD_version to 800094. 20090529: Add mnt_xflag field to 'struct mount'. File system modules need to be rebuilt. Bump __FreeBSD_version to 800093. 20090528: The compiling option ADAPTIVE_SX has been retired while it has been introduced the option NO_ADAPTIVE_SX which handles the reversed logic. The KPI for sx_init_flags() changes as accepting flags: SX_ADAPTIVESPIN flag has been retired while the SX_NOADAPTIVE flag has been introduced in order to handle the reversed logic. Bump __FreeBSD_version to 800092. 20090527: Add support for hierarchical jails. Remove global securelevel. Bump __FreeBSD_version to 800091. 20090523: The layout of struct vnet_net has changed, therefore modules need to be rebuilt. Bump __FreeBSD_version to 800090. 20090523: The newly imported zic(8) produces a new format in the output. Please run tzsetup(8) to install the newly created data to /etc/localtime. 20090520: The sysctl tree for the usb stack has renamed from hw.usb2.* to hw.usb.* and is now consistent again with previous releases. 20090520: 802.11 monitor mode support was revised and driver api's were changed. Drivers dependent on net80211 now support DLT_IEEE802_11_RADIO instead of DLT_IEEE802_11. No user-visible data structures were changed but applications that use DLT_IEEE802_11 may require changes. Bump __FreeBSD_version to 800088. 20090430: The layout of the following structs has changed: sysctl_oid, socket, ifnet, inpcbinfo, tcpcb, syncache_head, vnet_inet, vnet_inet6 and vnet_ipfw. Most modules need to be rebuild or panics may be experienced. World rebuild is required for correctly checking networking state from userland. Bump __FreeBSD_version to 800085. 20090429: MLDv2 and Source-Specific Multicast (SSM) have been merged to the IPv6 stack. VIMAGE hooks are in but not yet used. The implementation of SSM within FreeBSD's IPv6 stack closely follows the IPv4 implementation. For kernel developers: * The most important changes are that the ip6_output() and ip6_input() paths no longer take the IN6_MULTI_LOCK, and this lock has been downgraded to a non-recursive mutex. * As with the changes to the IPv4 stack to support SSM, filtering of inbound multicast traffic must now be performed by transport protocols within the IPv6 stack. This does not apply to TCP and SCTP, however, it does apply to UDP in IPv6 and raw IPv6. * The KPIs used by IPv6 multicast are similar to those used by the IPv4 stack, with the following differences: * im6o_mc_filter() is analogous to imo_multicast_filter(). * The legacy KAME entry points in6_joingroup and in6_leavegroup() are shimmed to in6_mc_join() and in6_mc_leave() respectively. * IN6_LOOKUP_MULTI() has been deprecated and removed. * IPv6 relies on MLD for the DAD mechanism. KAME's internal KPIs for MLDv1 have an additional 'timer' argument which is used to jitter the initial membership report for the solicited-node multicast membership on-link. * This is not strictly needed for MLDv2, which already jitters its report transmissions. However, the 'timer' argument is preserved in case MLDv1 is active on the interface. * The KAME linked-list based IPv6 membership implementation has been refactored to use a vector similar to that used by the IPv4 stack. Code which maintains a list of its own multicast memberships internally, e.g. carp, has been updated to reflect the new semantics. * There is a known Lock Order Reversal (LOR) due to in6_setscope() acquiring the IF_AFDATA_LOCK and being called within ip6_output(). Whilst MLDv2 tries to avoid this otherwise benign LOR, it is an implementation constraint which needs to be addressed in HEAD. For application developers: * The changes are broadly similar to those made for the IPv4 stack. * The use of IPv4 and IPv6 multicast socket options on the same socket, using mapped addresses, HAS NOT been tested or supported. * There are a number of issues with the implementation of various IPv6 multicast APIs which need to be resolved in the API surface before the implementation is fully compatible with KAME userland use, and these are mostly to do with interface index treatment. * The literature available discusses the use of either the delta / ASM API with setsockopt(2)/getsockopt(2), or the full-state / ASM API using setsourcefilter(3)/getsourcefilter(3). For more information please refer to RFC 3768, 'Socket Interface Extensions for Multicast Source Filters'. * Applications which use the published RFC 3678 APIs should be fine. For systems administrators: * The mtest(8) utility has been refactored to support IPv6, in addition to IPv4. Interface addresses are no longer accepted as arguments, their names must be used instead. The utility will map the interface name to its first IPv4 address as returned by getifaddrs(3). * The ifmcstat(8) utility has also been updated to print the MLDv2 endpoint state and source filter lists via sysctl(3). * The net.inet6.ip6.mcast.loop sysctl may be tuned to 0 to disable loopback of IPv6 multicast datagrams by default; it defaults to 1 to preserve the existing behaviour. Disabling multicast loopback is recommended for optimal system performance. * The IPv6 MROUTING code has been changed to examine this sysctl instead of attempting to perform a group lookup before looping back forwarded datagrams. Bump __FreeBSD_version to 800084. 20090422: Implement low-level Bluetooth HCI API. Bump __FreeBSD_version to 800083. 20090419: The layout of struct malloc_type, used by modules to register new memory allocation types, has changed. Most modules will need to be rebuilt or panics may be experienced. Bump __FreeBSD_version to 800081. 20090415: Anticipate overflowing inp_flags - add inp_flags2. This changes most offsets in inpcb, so checking v4 connection state will require a world rebuild. Bump __FreeBSD_version to 800080. 20090415: Add an llentry to struct route and struct route_in6. Modules embedding a struct route will need to be recompiled. Bump __FreeBSD_version to 800079. 20090414: The size of rt_metrics_lite and by extension rtentry has changed. Networking administration apps will need to be recompiled. The route command now supports show as an alias for get, weighting of routes, sticky and nostick flags to alter the behavior of stateful load balancing. Bump __FreeBSD_version to 800078. 20090408: Do not use Giant for kbdmux(4) locking. This is wrong and apparently causing more problems than it solves. This will re-open the issue where interrupt handlers may race with kbdmux(4) in polling mode. Typical symptoms include (but not limited to) duplicated and/or missing characters when low level console functions (such as gets) are used while interrupts are enabled (for example geli password prompt, mountroot prompt etc.). Disabling kbdmux(4) may help. 20090407: The size of structs vnet_net, vnet_inet and vnet_ipfw has changed; kernel modules referencing any of the above need to be recompiled. Bump __FreeBSD_version to 800075. 20090320: GEOM_PART has become the default partition slicer for storage devices, replacing GEOM_MBR, GEOM_BSD, GEOM_PC98 and GEOM_GPT slicers. It introduces some changes: MSDOS/EBR: the devices created from MSDOS extended partition entries (EBR) can be named differently than with GEOM_MBR and are now symlinks to devices with offset-based names. fstabs may need to be modified. BSD: the "geometry does not match label" warning is harmless in most cases but it points to problems in file system misalignment with disk geometry. The "c" partition is now implicit, covers the whole top-level drive and cannot be (mis)used by users. General: Kernel dumps are now not allowed to be written to devices whose partition types indicate they are meant to be used for file systems (or, in case of MSDOS partitions, as something else than the "386BSD" type). Most of these changes date approximately from 200812. 20090319: The uscanner(4) driver has been removed from the kernel. This follows Linux removing theirs in 2.6 and making libusb the default interface (supported by sane). 20090319: The multicast forwarding code has been cleaned up. netstat(1) only relies on KVM now for printing bandwidth upcall meters. The IPv4 and IPv6 modules are split into ip_mroute_mod and ip6_mroute_mod respectively. The config(5) options for statically compiling this code remain the same, i.e. 'options MROUTING'. 20090315: Support for the IFF_NEEDSGIANT network interface flag has been removed, which means that non-MPSAFE network device drivers are no longer supported. In particular, if_ar, if_sr, and network device drivers from the old (legacy) USB stack can no longer be built or used. 20090313: POSIX.1 Native Language Support (NLS) has been enabled in libc and a bunch of new language catalog files have also been added. This means that some common libc messages are now localized and they depend on the LC_MESSAGES environmental variable. 20090313: The k8temp(4) driver has been renamed to amdtemp(4) since support for K10 and K11 CPU families was added. 20090309: IGMPv3 and Source-Specific Multicast (SSM) have been merged to the IPv4 stack. VIMAGE hooks are in but not yet used. For kernel developers, the most important changes are that the ip_output() and ip_input() paths no longer take the IN_MULTI_LOCK(), and this lock has been downgraded to a non-recursive mutex. Transport protocols (UDP, Raw IP) are now responsible for filtering inbound multicast traffic according to group membership and source filters. The imo_multicast_filter() KPI exists for this purpose. Transports which do not use multicast (SCTP, TCP) already reject multicast by default. Forwarding and receive performance may improve as a mutex acquisition is no longer needed in the ip_input() low-level input path. in_addmulti() and in_delmulti() are shimmed to new KPIs which exist to support SSM in-kernel. For application developers, it is recommended that loopback of multicast datagrams be disabled for best performance, as this will still cause the lock to be taken for each looped-back datagram transmission. The net.inet.ip.mcast.loop sysctl may be tuned to 0 to disable loopback by default; it defaults to 1 to preserve the existing behaviour. For systems administrators, to obtain best performance with multicast reception and multiple groups, it is always recommended that a card with a suitably precise hash filter is used. Hash collisions will still result in the lock being taken within the transport protocol input path to check group membership. If deploying FreeBSD in an environment with IGMP snooping switches, it is recommended that the net.inet.igmp.sendlocal sysctl remain enabled; this forces 224.0.0.0/24 group membership to be announced via IGMP. The size of 'struct igmpstat' has changed; netstat needs to be recompiled to reflect this. Bump __FreeBSD_version to 800070. 20090309: libusb20.so.1 is now installed as libusb.so.1 and the ports system updated to use it. This requires a buildworld/installworld in order to update the library and dependencies (usbconfig, etc). Its advisable to rebuild all ports which uses libusb. More specific directions are given in the ports collection UPDATING file. Any /etc/libmap.conf entries for libusb are no longer required and can be removed. 20090302: A workaround is committed to allow the creation of System V shared memory segment of size > 2 GB on the 64-bit architectures. Due to a limitation of the existing ABI, the shm_segsz member of the struct shmid_ds, returned by shmctl(IPC_STAT) call is wrong for large segments. Note that limits must be explicitly raised to allow such segments to be created. 20090301: The layout of struct ifnet has changed, requiring a rebuild of all network device driver modules. 20090227: The /dev handling for the new USB stack has changed, a buildworld/installworld is required for libusb20. 20090223: The new USB2 stack has now been permanently moved in and all kernel and module names reverted to their previous values (eg, usb, ehci, ohci, ums, ...). The old usb stack can be compiled in by prefixing the name with the letter 'o', the old usb modules have been removed. Updating entry 20090216 for xorg and 20090215 for libmap may still apply. 20090217: The rc.conf(5) option if_up_delay has been renamed to defaultroute_delay to better reflect its purpose. If you have customized this setting in /etc/rc.conf you need to update it to use the new name. 20090216: xorg 7.4 wants to configure its input devices via hald which does not yet work with USB2. If the keyboard/mouse does not work in xorg then add Option "AllowEmptyInput" "off" to your ServerLayout section. This will cause X to use the configured kbd and mouse sections from your xorg.conf. 20090215: The GENERIC kernels for all architectures now default to the new USB2 stack. No kernel config options or code have been removed so if a problem arises please report it and optionally revert to the old USB stack. If you are loading USB kernel modules or have a custom kernel that includes GENERIC then ensure that usb names are also changed over, eg uftdi -> usb2_serial_ftdi. Older programs linked against the ports libusb 0.1 need to be redirected to the new stack's libusb20. /etc/libmap.conf can be used for this: # Map old usb library to new one for usb2 stack libusb-0.1.so.8 libusb20.so.1 20090209: All USB ethernet devices now attach as interfaces under the name ueN (eg. ue0). This is to provide a predictable name as vendors often change usb chipsets in a product without notice. 20090203: The ichsmb(4) driver has been changed to require SMBus slave addresses be left-justified (xxxxxxx0b) rather than right-justified. All of the other SMBus controller drivers require left-justified slave addresses, so this change makes all the drivers provide the same interface. 20090201: INET6 statistics (struct ip6stat) was updated. netstat(1) needs to be recompiled. 20090119: NTFS has been removed from GENERIC kernel on amd64 to match GENERIC on i386. Should not cause any issues since mount_ntfs(8) will load ntfs.ko module automatically when NTFS support is actually needed, unless ntfs.ko is not installed or security level prohibits loading kernel modules. If either is the case, "options NTFS" has to be added into kernel config. 20090115: TCP Appropriate Byte Counting (RFC 3465) support added to kernel. New field in struct tcpcb breaks ABI, so bump __FreeBSD_version to 800061. User space tools that rely on the size of struct tcpcb in tcp_var.h (e.g. sockstat) need to be recompiled. 20081225: ng_tty(4) module updated to match the new TTY subsystem. Due to API change, user-level applications must be updated. New API support added to mpd5 CVS and expected to be present in next mpd5.3 release. 20081219: With __FreeBSD_version 800060 the makefs tool is part of the base system (it was a port). 20081216: The afdata and ifnet locks have been changed from mutexes to rwlocks, network modules will need to be re-compiled. 20081214: __FreeBSD_version 800059 incorporates the new arp-v2 rewrite. RTF_CLONING, RTF_LLINFO and RTF_WASCLONED flags are eliminated. The new code reduced struct rtentry{} by 16 bytes on 32-bit architecture and 40 bytes on 64-bit architecture. The userland applications "arp" and "ndp" have been updated accordingly. The output from "netstat -r" shows only routing entries and none of the L2 information. 20081130: __FreeBSD_version 800057 marks the switchover from the binary ath hal to source code. Users must add the line: options AH_SUPPORT_AR5416 to their kernel config files when specifying: device ath_hal The ath_hal module no longer exists; the code is now compiled together with the driver in the ath module. It is now possible to tailor chip support (i.e. reduce the set of chips and thereby the code size); consult ath_hal(4) for details. 20081121: __FreeBSD_version 800054 adds memory barriers to , new interfaces to ifnet to facilitate multiple hardware transmit queues for cards that support them, and a lock-less ring-buffer implementation to enable drivers to more efficiently manage queueing of packets. 20081117: A new version of ZFS (version 13) has been merged to -HEAD. This version has zpool attribute "listsnapshots" off by default, which means "zfs list" does not show snapshots, and is the same as Solaris behavior. 20081028: dummynet(4) ABI has changed. ipfw(8) needs to be recompiled. 20081009: The uhci, ohci, ehci and slhci USB Host controller drivers have been put into separate modules. If you load the usb module separately through loader.conf you will need to load the appropriate *hci module as well. E.g. for a UHCI-based USB 2.0 controller add the following to loader.conf: uhci_load="YES" ehci_load="YES" 20081009: The ABI used by the PMC toolset has changed. Please keep userland (libpmc(3)) and the kernel module (hwpmc(4)) in sync. 20081009: atapci kernel module now includes only generic PCI ATA driver. AHCI driver moved to ataahci kernel module. All vendor-specific code moved into separate kernel modules: ataacard, ataacerlabs, ataadaptec, ataamd, ataati, atacenatek, atacypress, atacyrix, atahighpoint, ataintel, ataite, atajmicron, atamarvell, atamicron, atanational, atanetcell, atanvidia, atapromise, ataserverworks, atasiliconimage, atasis, atavia 20080820: The TTY subsystem of the kernel has been replaced by a new implementation, which provides better scalability and an improved driver model. Most common drivers have been migrated to the new TTY subsystem, while others have not. The following drivers have not yet been ported to the new TTY layer: PCI/ISA: cy, digi, rc, rp, sio USB: ubser, ucycom Line disciplines: ng_h4, ng_tty, ppp, sl, snp Adding these drivers to your kernel configuration file shall cause compilation to fail. 20080818: ntpd has been upgraded to 4.2.4p5. 20080801: OpenSSH has been upgraded to 5.1p1. For many years, FreeBSD's version of OpenSSH preferred DSA over RSA for host and user authentication keys. With this upgrade, we've switched to the vendor's default of RSA over DSA. This may cause upgraded clients to warn about unknown host keys even for previously known hosts. Users should follow the usual procedure for verifying host keys before accepting the RSA key. This can be circumvented by setting the "HostKeyAlgorithms" option to "ssh-dss,ssh-rsa" in ~/.ssh/config or on the ssh command line. Please note that the sequence of keys offered for authentication has been changed as well. You may want to specify IdentityFile in a different order to revert this behavior. 20080713: The sio(4) driver has been removed from the i386 and amd64 kernel configuration files. This means uart(4) is now the default serial port driver on those platforms as well. To prevent collisions with the sio(4) driver, the uart(4) driver uses different names for its device nodes. This means the onboard serial port will now most likely be called "ttyu0" instead of "ttyd0". You may need to reconfigure applications to use the new device names. When using the serial port as a boot console, be sure to update /boot/device.hints and /etc/ttys before booting the new kernel. If you forget to do so, you can still manually specify the hints at the loader prompt: set hint.uart.0.at="isa" set hint.uart.0.port="0x3F8" set hint.uart.0.flags="0x10" set hint.uart.0.irq="4" boot -s 20080609: The gpt(8) utility has been removed. Use gpart(8) to partition disks instead. 20080603: The version that Linuxulator emulates was changed from 2.4.2 to 2.6.16. If you experience any problems with Linux binaries please try to set sysctl compat.linux.osrelease to 2.4.2 and if it fixes the problem contact emulation mailing list. 20080525: ISDN4BSD (I4B) was removed from the src tree. You may need to update a your kernel configuration and remove relevant entries. 20080509: I have checked in code to support multiple routing tables. See the man pages setfib(1) and setfib(2). This is a hopefully backwards compatible version, but to make use of it you need to compile your kernel with options ROUTETABLES=2 (or more up to 16). 20080420: The 802.11 wireless support was redone to enable multi-bss operation on devices that are capable. The underlying device is no longer used directly but instead wlanX devices are cloned with ifconfig. This requires changes to rc.conf files. For example, change: ifconfig_ath0="WPA DHCP" to wlans_ath0=wlan0 ifconfig_wlan0="WPA DHCP" see rc.conf(5) for more details. In addition, mergemaster of /etc/rc.d is highly recommended. Simultaneous update of userland and kernel wouldn't hurt either. As part of the multi-bss changes the wlan_scan_ap and wlan_scan_sta modules were merged into the base wlan module. All references to these modules (e.g. in kernel config files) must be removed. 20080408: psm(4) has gained write(2) support in native operation level. Arbitrary commands can be written to /dev/psm%d and status can be read back from it. Therefore, an application is responsible for status validation and error recovery. It is a no-op in other operation levels. 20080312: Support for KSE threading has been removed from the kernel. To run legacy applications linked against KSE libmap.conf may be used. The following libmap.conf may be used to ensure compatibility with any prior release: libpthread.so.1 libthr.so.1 libpthread.so.2 libthr.so.2 libkse.so.3 libthr.so.3 20080301: The layout of struct vmspace has changed. This affects libkvm and any executables that link against libkvm and use the kvm_getprocs() function. In particular, but not exclusively, it affects ps(1), fstat(1), pkill(1), systat(1), top(1) and w(1). The effects are minimal, but it's advisable to upgrade world nonetheless. 20080229: The latest em driver no longer has support in it for the 82575 adapter, this is now moved to the igb driver. The split was done to make new features that are incompatible with older hardware easier to do. 20080220: The new geom_lvm(4) geom class has been renamed to geom_linux_lvm(4), likewise the kernel option is now GEOM_LINUX_LVM. 20080211: The default NFS mount mode has changed from UDP to TCP for increased reliability. If you rely on (insecurely) NFS mounting across a firewall you may need to update your firewall rules. 20080208: Belatedly note the addition of m_collapse for compacting mbuf chains. 20080126: The fts(3) structures have been changed to use adequate integer types for their members and so to be able to cope with huge file trees. The old fts(3) ABI is preserved through symbol versioning in libc, so third-party binaries using fts(3) should still work, although they will not take advantage of the extended types. At the same time, some third-party software might fail to build after this change due to unportable assumptions made in its source code about fts(3) structure members. Such software should be fixed by its vendor or, in the worst case, in the ports tree. FreeBSD_version 800015 marks this change for the unlikely case that a portable fix is impossible. 20080123: To upgrade to -current after this date, you must be running FreeBSD not older than 6.0-RELEASE. Upgrading to -current from 5.x now requires a stop over at RELENG_6 or RELENG_7 systems. 20071128: The ADAPTIVE_GIANT kernel option has been retired because its functionality is the default now. 20071118: The AT keyboard emulation of sunkbd(4) has been turned on by default. In order to make the special symbols of the Sun keyboards driven by sunkbd(4) work under X these now have to be configured the same way as Sun USB keyboards driven by ukbd(4) (which also does AT keyboard emulation), f.e.: Option "XkbLayout" "us" Option "XkbRules" "xorg" Option "XkbSymbols" "pc(pc105)+sun_vndr/usb(sun_usb)+us" 20071024: It has been decided that it is desirable to provide ABI backwards compatibility to the FreeBSD 4/5/6 versions of the PCIOCGETCONF, PCIOCREAD and PCIOCWRITE IOCTLs, which was broken with the introduction of PCI domain support (see the 20070930 entry). Unfortunately, this required the ABI of PCIOCGETCONF to be broken again in order to be able to provide backwards compatibility to the old version of that IOCTL. Thus consumers of PCIOCGETCONF have to be recompiled again. As for prominent ports this affects neither pciutils nor xorg-server this time, the hal port needs to be rebuilt however. 20071020: The misnamed kthread_create() and friends have been renamed to kproc_create() etc. Many of the callers already used kproc_start().. I will return kthread_create() and friends in a while with implementations that actually create threads, not procs. Renaming corresponds with version 800002. 20071010: RELENG_7 branched. 20071009: Setting WITHOUT_LIBPTHREAD now means WITHOUT_LIBKSE and WITHOUT_LIBTHR are set. 20070930: The PCI code has been made aware of PCI domains. This means that the location strings as used by pciconf(8) etc are now in the following format: pci::[:]. It also means that consumers of potentially need to be recompiled; this includes the hal and xorg-server ports. 20070928: The caching daemon (cached) was renamed to nscd. nscd.conf configuration file should be used instead of cached.conf and nscd_enable, nscd_pidfile and nscd_flags options should be used instead of cached_enable, cached_pidfile and cached_flags in rc.conf. 20070921: The getfacl(1) utility now prints owning user and group name instead of owning uid and gid in the three line comment header. This is the same behavior as getfacl(1) on Solaris and Linux. 20070704: The new IPsec code is now compiled in using the IPSEC option. The IPSEC option now requires "device crypto" be defined in your kernel configuration. The FAST_IPSEC kernel option is now deprecated. 20070702: The packet filter (pf) code has been updated to OpenBSD 4.1 Please note the changed syntax - keep state is now on by default. Also note the fact that ftp-proxy(8) has been changed from bottom up and has been moved from libexec to usr/sbin. Changes in the ALTQ handling also affect users of IPFW's ALTQ capabilities. 20070701: Remove KAME IPsec in favor of FAST_IPSEC, which is now the only IPsec supported by FreeBSD. The new IPsec stack supports both IPv4 and IPv6. The kernel option will change after the code changes have settled in. For now the kernel option IPSEC is deprecated and FAST_IPSEC is the only option, that will change after some settling time. 20070701: The wicontrol(8) utility has been removed from the base system. wi(4) cards should be configured using ifconfig(8), see the man page for more information. 20070612: The i386/amd64 GENERIC kernel now defaults to the nfe(4) driver instead of the nve(4) driver. Please update your configuration accordingly. 20070612: By default, /etc/rc.d/sendmail no longer rebuilds the aliases database if it is missing or older than the aliases file. If desired, set the new rc.conf option sendmail_rebuild_aliases to "YES" to restore that functionality. 20070612: The IPv4 multicast socket code has been considerably modified, and moved to the file sys/netinet/in_mcast.c. Initial support for the RFC 3678 Source-Specific Multicast Socket API has been added to the IPv4 network stack. Strict multicast and broadcast reception is now the default for UDP/IPv4 sockets; the net.inet.udp.strict_mcast_mship sysctl variable has now been removed. The RFC 1724 hack for interface selection has been removed; the use of the Linux-derived ip_mreqn structure with IP_MULTICAST_IF has been added to replace it. Consumers such as routed will soon be updated to reflect this. These changes affect users who are running routed(8) or rdisc(8) from the FreeBSD base system on point-to-point or unnumbered interfaces. 20070610: The net80211 layer has changed significantly and all wireless drivers that depend on it need to be recompiled. Further these changes require that any program that interacts with the wireless support in the kernel be recompiled; this includes: ifconfig, wpa_supplicant, hostapd, and wlanstats. Users must also, for the moment, kldload the wlan_scan_sta and/or wlan_scan_ap modules if they use modules for wireless support. These modules implement scanning support for station and ap modes, respectively. Failure to load the appropriate module before marking a wireless interface up will result in a message to the console and the device not operating properly. 20070610: The pam_nologin(8) module ceases to provide an authentication function and starts providing an account management function. Consequent changes to /etc/pam.d should be brought in using mergemaster(8). Third-party files in /usr/local/etc/pam.d may need manual editing as follows. Locate this line (or similar): auth required pam_nologin.so no_warn and change it according to this example: account required pam_nologin.so no_warn That is, the first word needs to be changed from "auth" to "account". The new line can be moved to the account section within the file for clarity. Not updating pam.conf(5) files will result in nologin(5) ignored by the respective services. 20070529: The ether_ioctl() function has been synchronized with ioctl(2) and ifnet.if_ioctl. Due to that, the size of one of its arguments has changed on 64-bit architectures. All kernel modules using ether_ioctl() need to be rebuilt on such architectures. 20070516: Improved INCLUDE_CONFIG_FILE support has been introduced to the config(8) utility. In order to take advantage of this new functionality, you are expected to recompile and install src/usr.sbin/config. If you don't rebuild config(8), and your kernel configuration depends on INCLUDE_CONFIG_FILE, the kernel build will be broken because of a missing "kernconfstring" symbol. 20070513: Symbol versioning is enabled by default. To disable it, use option WITHOUT_SYMVER. It is not advisable to attempt to disable symbol versioning once it is enabled; your installworld will break because a symbol version-less libc will get installed before the install tools. As a result, the old install tools, which previously had symbol dependencies to FBSD_1.0, will fail because the freshly installed libc will not have them. The default threading library (providing "libpthread") has been changed to libthr. If you wish to have libkse as your default, use option DEFAULT_THREAD_LIB=libkse for the buildworld. 20070423: The ABI breakage in sendmail(8)'s libmilter has been repaired so it is no longer necessary to recompile mail filters (aka, milters). If you recompiled mail filters after the 20070408 note, it is not necessary to recompile them again. 20070417: The new trunk(4) driver has been renamed to lagg(4) as it better reflects its purpose. ifconfig will need to be recompiled. 20070408: sendmail(8) has been updated to version 8.14.1. Mail filters (aka, milters) compiled against the libmilter included in the base operating system should be recompiled. 20070302: Firmwares for ipw(4) and iwi(4) are now included in the base tree. In order to use them one must agree to the respective LICENSE in share/doc/legal and define legal.intel_.license_ack=1 via loader.conf(5) or kenv(1). Make sure to deinstall the now deprecated modules from the respective firmware ports. 20070228: The name resolution/mapping functions addr2ascii(3) and ascii2addr(3) were removed from FreeBSD's libc. These originally came from INRIA IPv6. Nothing in FreeBSD ever used them. They may be regarded as deprecated in previous releases. The AF_LINK support for getnameinfo(3) was merged from NetBSD to replace it as a more portable (and re-entrant) API. 20070224: To support interrupt filtering a modification to the newbus API has occurred, ABI was broken and __FreeBSD_version was bumped to 700031. Please make sure that your kernel and modules are in sync. For more info: http://docs.freebsd.org/cgi/mid.cgi?20070221233124.GA13941 20070224: The IPv6 multicast forwarding code may now be loaded into GENERIC kernels by loading the ip_mroute.ko module. This is built into the module unless WITHOUT_INET6 or WITHOUT_INET6_SUPPORT options are set; see src.conf(5) for more information. 20070214: The output of netstat -r has changed. Without -n, we now only print a "network name" without the prefix length if the network address and mask exactly match a Class A/B/C network, and an entry exists in the nsswitch "networks" map. With -n, we print the full unabbreviated CIDR network prefix in the form "a.b.c.d/p". 0.0.0.0/0 is always printed as "default". This change is in preparation for changes such as equal-cost multipath, and to more generally assist operational deployment of FreeBSD as a modern IPv4 router. 20070210: PIM has been turned on by default in the IPv4 multicast routing code. The kernel option 'PIM' has now been removed. PIM is now built by default if option 'MROUTING' is specified. It may now be loaded into GENERIC kernels by loading the ip_mroute.ko module. 20070207: Support for IPIP tunnels (VIFF_TUNNEL) in IPv4 multicast routing has been removed. Its functionality may be achieved by explicitly configuring gif(4) interfaces and using the 'phyint' keyword in mrouted.conf. XORP does not support source-routed IPv4 multicast tunnels nor the integrated IPIP tunneling, therefore it is not affected by this change. The __FreeBSD_version macro has been bumped to 700030. 20061221: Support for PCI Message Signalled Interrupts has been re-enabled in the bge driver, only for those chips which are believed to support it properly. If there are any problems, MSI can be disabled completely by setting the 'hw.pci.enable_msi' and 'hw.pci.enable_msix' tunables to 0 in the loader. 20061214: Support for PCI Message Signalled Interrupts has been disabled again in the bge driver. Many revisions of the hardware fail to support it properly. Support can be re-enabled by removing the #define of BGE_DISABLE_MSI in "src/sys/dev/bge/if_bge.c". 20061214: Support for PCI Message Signalled Interrupts has been added to the bge driver. If there are any problems, MSI can be disabled completely by setting the 'hw.pci.enable_msi' and 'hw.pci.enable_msix' tunables to 0 in the loader. 20061205: The removal of several facets of the experimental Threading system from the kernel means that the proc and thread structures have changed quite a bit. I suggest all kernel modules that might reference these structures be recompiled.. Especially the linux module. 20061126: Sound infrastructure has been updated with various fixes and improvements. Most of the changes are pretty much transparent, with exceptions of followings: 1) All sound driver specific sysctls (hw.snd.pcm%d.*) have been moved to their own dev sysctl nodes, for example: hw.snd.pcm0.vchans -> dev.pcm.0.vchans 2) /dev/dspr%d.%d has been deprecated. Each channel now has its own chardev in the form of "dsp%d.%d", where is p = playback, r = record and v = virtual, respectively. Users are encouraged to use these devs instead of (old) "/dev/dsp%d.%d". This does not affect those who are using "/dev/dsp". 20061122: geom(4)'s gmirror(8) class metadata structure has been rev'd from v3 to v4. If you update across this point and your metadata is converted for you, you will not be easily able to downgrade since the /boot/kernel.old/geom_mirror.ko kernel module will be unable to read the v4 metadata. You can resolve this by doing from the loader(8) prompt: set vfs.root.mountfrom="ufs:/dev/XXX" where XXX is the root slice of one of the disks that composed the mirror (i.e.: /dev/ad0s1a). You can then rebuild the array the same way you built it originally. 20061122: The following binaries have been disconnected from the build: mount_devfs, mount_ext2fs, mount_fdescfs, mount_procfs, mount_linprocfs, and mount_std. The functionality of these programs has been moved into the mount program. For example, to mount a devfs filesystem, instead of using mount_devfs, use: "mount -t devfs". This does not affect entries in /etc/fstab, since entries in /etc/fstab are always processed with "mount -t fstype". 20061113: Support for PCI Message Signalled Interrupts on i386 and amd64 has been added to the kernel and various drivers will soon be updated to use MSI when it is available. If there are any problems, MSI can be disabled completely by setting the 'hw.pci.enable_msi' and 'hw.pci.enable_msix' tunables to 0 in the loader. 20061110: The MUTEX_PROFILING option has been renamed to LOCK_PROFILING. The lockmgr object layout has been changed as a result of having a lock_object embedded in it. As a consequence all file system kernel modules must be re-compiled. The mutex profiling man page has not yet been updated to reflect this change. 20061026: KSE in the kernel has now been made optional and turned on by default. Use 'nooption KSE' in your kernel config to turn it off. All kernel modules *must* be recompiled after this change. There-after, modules from a KSE kernel should be compatible with modules from a NOKSE kernel due to the temporary padding fields added to 'struct proc'. 20060929: mrouted and its utilities have been removed from the base system. 20060927: Some ioctl(2) command codes have changed. Full backward ABI compatibility is provided if the "options COMPAT_FREEBSD6" is present in the kernel configuration file. Make sure to add this option to your kernel config file, or recompile X.Org and the rest of ports; otherwise they may refuse to work. 20060924: tcpslice has been removed from the base system. 20060913: The sizes of struct tcpcb (and struct xtcpcb) have changed due to the rewrite of TCP syncookies. Tools like netstat, sockstat, and systat needs to be rebuilt. 20060903: libpcap updated to v0.9.4 and tcpdump to v3.9.4 20060816: The IPFIREWALL_FORWARD_EXTENDED option is gone and the behaviour for IPFIREWALL_FORWARD is now as it was before when it was first committed and for years after. The behaviour is now ON. 20060725: enigma(1)/crypt(1) utility has been changed on 64 bit architectures. Now it can decrypt files created from different architectures. Unfortunately, it is no longer able to decrypt a cipher text generated with an older version on 64 bit architectures. If you have such a file, you need old utility to decrypt it. 20060709: The interface version of the i4b kernel part has changed. So after updating the kernel sources and compiling a new kernel, the i4b user space tools in "/usr/src/usr.sbin/i4b" must also be rebuilt, and vice versa. 20060627: The XBOX kernel now defaults to the nfe(4) driver instead of the nve(4) driver. Please update your configuration accordingly. 20060514: The i386-only lnc(4) driver for the AMD Am7900 LANCE and Am79C9xx PCnet family of NICs has been removed. The new le(4) driver serves as an equivalent but cross-platform replacement with the pcn(4) driver still providing performance-optimized support for the subset of AMD Am79C971 PCnet-FAST and greater chips as before. 20060511: The machdep.* sysctls and the adjkerntz utility have been modified a bit. The new adjkerntz utility uses the new sysctl names and sysctlbyname() calls, so it may be impossible to run an old /sbin/adjkerntz utility in single-user mode with a new kernel. Replace the `adjkerntz -i' step before `make installworld' with: /usr/obj/usr/src/sbin/adjkerntz/adjkerntz -i and proceed as usual with the rest of the installworld-stage steps. Otherwise, you risk installing binaries with their timestamp set several hours in the future, especially if you are running with local time set to GMT+X hours. 20060412: The ip6fw utility has been removed. The behavior provided by ip6fw has been in ipfw2 for a good while and the rc.d scripts have been updated to deal with it. There are some rules that might not migrate cleanly. Use rc.firewall6 as a template to rewrite rules. 20060428: The puc(4) driver has been overhauled. The ebus(4) and sbus(4) attachments have been removed. Make sure to configure scc(4) on sparc64. Note also that by default puc(4) will use uart(4) and not sio(4) for serial ports because interrupt handling has been optimized for multi-port serial cards and only uart(4) implements the interface to support it. 20060330: The scc(4) driver replaces puc(4) for Serial Communications Controllers (SCCs) like the Siemens SAB82532 and the Zilog Z8530. On sparc64, it is advised to add scc(4) to the kernel configuration to make sure that the serial ports remain functional. 20060317: Most world/kernel related NO_* build options changed names. New knobs have common prefixes WITHOUT_*/WITH_* (modelled after FreeBSD ports) and should be set in /etc/src.conf (the src.conf(5) manpage is provided). Full backwards compatibility is maintained for the time being though it's highly recommended to start moving old options out of the system-wide /etc/make.conf file into the new /etc/src.conf while also properly renaming them. More conversions will likely follow. Posting to current@: http://lists.freebsd.org/pipermail/freebsd-current/2006-March/061725.html 20060305: The NETSMBCRYPTO kernel option has been retired because its functionality is always included in NETSMB and smbfs.ko now. 20060303: The TDFX_LINUX kernel option was retired and replaced by the tdfx_linux device. The latter can be loaded as the 3dfx_linux.ko kernel module. Loading it alone should suffice to get 3dfx support for Linux apps because it will pull in 3dfx.ko and linux.ko through its dependencies. 20060204: The 'audit' group was added to support the new auditing functionality in the base system. Be sure to follow the directions for updating, including the requirement to run mergemaster -p. 20060201: The kernel ABI to file system modules was changed on i386. Please make sure that your kernel and modules are in sync. 20060118: This actually occured some time ago, but installing the kernel now also installs a bunch of symbol files for the kernel modules. This increases the size of /boot/kernel to about 67Mbytes. You will need twice this if you will eventually back this up to kernel.old on your next install. If you have a shortage of room in your root partition, you should add -DINSTALL_NODEBUG to your make arguments or add INSTALL_NODEBUG="yes" to your /etc/make.conf. 20060113: libc's malloc implementation has been replaced. This change has the potential to uncover application bugs that previously went unnoticed. See the malloc(3) manual page for more details. 20060112: The generic netgraph(4) cookie has been changed. If you upgrade kernel passing this point, you also need to upgrade userland and netgraph(4) utilities like ports/net/mpd or ports/net/mpd4. 20060106: si(4)'s device files now contain the unit number. Uses of {cua,tty}A[0-9a-f] should be replaced by {cua,tty}A0[0-9a-f]. 20060106: The kernel ABI was mostly destroyed due to a change in the size of struct lock_object which is nested in other structures such as mutexes which are nested in all sorts of other structures. Make sure your kernel and modules are in sync. 20051231: The page coloring algorithm in the VM subsystem was converted from tuning with kernel options to autotuning. Please remove any PQ_* option except PQ_NOOPT from your kernel config. 20051211: The net80211-related tools in the tools/tools/ath directory have been moved to tools/tools/net80211 and renamed with a "wlan" prefix. Scripts that use them should be adjusted accordingly. 20051202: Scripts in the local_startup directories (as defined in /etc/defaults/rc.conf) that have the new rc.d semantics will now be run as part of the base system rcorder. If there are errors or problems with one of these local scripts, it could cause boot problems. If you encounter such problems, boot in single user mode, remove that script from the */rc.d directory. Please report the problem to the port's maintainer, and the freebsd-ports@freebsd.org mailing list. 20051129: The nodev mount option was deprecated in RELENG_6 (where it was a no-op), and is now unsupported. If you have nodev or dev listed in /etc/fstab, remove it, otherwise it will result in a mount error. 20051129: ABI between ipfw(4) and ipfw(8) has been changed. You need to rebuild ipfw(8) when rebuilding kernel. 20051108: rp(4)'s device files now contain the unit number. Uses of {cua,tty}R[0-9a-f] should be replaced by {cua,tty}R0[0-9a-f]. 20051029: /etc/rc.d/ppp-user has been renamed to /etc/rc.d/ppp. Its /etc/rc.conf.d configuration file has been `ppp' from the beginning, and hence there is no need to touch it. 20051014: Now most modules get their build-time options from the kernel configuration file. A few modules still have fixed options due to their non-conformant implementation, but they will be corrected eventually. You may need to review the options of the modules in use, explicitly specify the non-default options in the kernel configuration file, and rebuild the kernel and modules afterwards. 20051001: kern.polling.enable sysctl MIB is now deprecated. Use ifconfig(8) to turn polling(4) on your interfaces. 20050927: The old bridge(4) implementation was retired. The new if_bridge(4) serves as a full functional replacement. 20050722: The ai_addrlen of a struct addrinfo was changed to a socklen_t to conform to POSIX-2001. This change broke an ABI compatibility on 64 bit architecture. You have to recompile userland programs that use getaddrinfo(3) on 64 bit architecture. 20050711: RELENG_6 branched here. 20050629: The pccard_ifconfig rc.conf variable has been removed and a new variable, ifconfig_DEFAULT has been introduced. Unlike pccard_ifconfig, ifconfig_DEFAULT applies to ALL interfaces that do not have ifconfig_ifn entries rather than just those in removable_interfaces. 20050616: Some previous versions of PAM have permitted the use of non-absolute paths in /etc/pam.conf or /etc/pam.d/* when referring to third party PAM modules in /usr/local/lib. A change has been made to require the use of absolute paths in order to avoid ambiguity and dependence on library path configuration, which may affect existing configurations. 20050610: Major changes to network interface API. All drivers must be recompiled. Drivers not in the base system will need to be updated to the new APIs. 20050609: Changes were made to kinfo_proc in sys/user.h. Please recompile userland, or commands like `fstat', `pkill', `ps', `top' and `w' will not behave correctly. The API and ABI for hwpmc(4) have changed with the addition of sampling support. Please recompile lib/libpmc(3) and usr.sbin/{pmcstat,pmccontrol}. 20050606: The OpenBSD dhclient was imported in place of the ISC dhclient and the network interface configuration scripts were updated accordingly. If you use DHCP to configure your interfaces, you must now run devd. Also, DNS updating was lost so you will need to find a workaround if you use this feature. The '_dhcp' user was added to support the OpenBSD dhclient. Be sure to run mergemaster -p (like you are supposed to do every time anyway). 20050605: if_bridge was added to the tree. This has changed struct ifnet. Please recompile userland and all network related modules. 20050603: The n_net of a struct netent was changed to an uint32_t, and 1st argument of getnetbyaddr() was changed to an uint32_t, to conform to POSIX-2001. These changes broke an ABI compatibility on 64 bit architecture. With these changes, shlib major of libpcap was bumped. You have to recompile userland programs that use getnetbyaddr(3), getnetbyname(3), getnetent(3) and/or libpcap on 64 bit architecture. 20050528: Kernel parsing of extra options on '#!' first lines of shell scripts has changed. Lines with multiple options likely will fail after this date. For full details, please see http://people.freebsd.org/~gad/Updating-20050528.txt 20050503: The packet filter (pf) code has been updated to OpenBSD 3.7 Please note the changed anchor syntax and the fact that authpf(8) now needs a mounted fdescfs(5) to function. 20050415: The NO_MIXED_MODE kernel option has been removed from the i386 amd64 platforms as its use has been superceded by the new local APIC timer code. Any kernel config files containing this option should be updated. 20050227: The on-disk format of LC_CTYPE files was changed to be machine independent. Please make sure NOT to use NO_CLEAN buildworld when crossing this point. Crossing this point also requires recompile or reinstall of all locale depended packages. 20050225: The ifi_epoch member of struct if_data has been changed to contain the uptime at which the interface was created or the statistics zeroed rather then the wall clock time because wallclock time may go backwards. This should have no impact unless an snmp implementation is using this value (I know of none at this point.) 20050224: The acpi_perf and acpi_throttle drivers are now part of the acpi(4) main module. They are no longer built separately. 20050223: The layout of struct image_params has changed. You have to recompile all compatibility modules (linux, svr4, etc) for use with the new kernel. 20050223: The p4tcc driver has been merged into cpufreq(4). This makes "options CPU_ENABLE_TCC" obsolete. Please load cpufreq.ko or compile in "device cpufreq" to restore this functionality. 20050220: The responsibility of recomputing the file system summary of a SoftUpdates-enabled dirty volume has been transferred to the background fsck. A rebuild of fsck(8) utility is recommended if you have updated the kernel. To get the old behavior (recompute file system summary at mount time), you can set vfs.ffs.compute_summary_at_mount=1 before mounting the new volume. 20050206: The cpufreq import is complete. As part of this, the sysctls for acpi(4) throttling have been removed. The power_profile script has been updated, so you can use performance/economy_cpu_freq in rc.conf(5) to set AC on/offline cpu frequencies. 20050206: NG_VERSION has been increased. Recompiling kernel (or ng_socket.ko) requires recompiling libnetgraph and userland netgraph utilities. 20050114: Support for abbreviated forms of a number of ipfw options is now deprecated. Warnings are printed to stderr indicating the correct full form when a match occurs. Some abbreviations may be supported at a later date based on user feedback. To be considered for support, abbreviations must be in use prior to this commit and unlikely to be confused with current key words. 20041221: By a popular demand, a lot of NOFOO options were renamed to NO_FOO (see bsd.compat.mk for a full list). The old spellings are still supported, but will cause annoying warnings on stderr. Make sure you upgrade properly (see the COMMON ITEMS: section later in this file). 20041219: Auto-loading of ancillary wlan modules such as wlan_wep has been temporarily disabled; you need to statically configure the modules you need into your kernel or explicitly load them prior to use. Specifically, if you intend to use WEP encryption with an 802.11 device load/configure wlan_wep; if you want to use WPA with the ath driver load/configure wlan_tkip, wlan_ccmp, and wlan_xauth as required. 20041213: The behaviour of ppp(8) has changed slightly. If lqr is enabled (``enable lqr''), older versions would revert to LCP ECHO mode on negotiation failure. Now, ``enable echo'' is required for this behaviour. The ppp version number has been bumped to 3.4.2 to reflect the change. 20041201: The wlan support has been updated to split the crypto support into separate modules. For static WEP you must configure the wlan_wep module in your system or build and install the module in place where it can be loaded (the kernel will auto-load the module when a wep key is configured). 20041201: The ath driver has been updated to split the tx rate control algorithm into a separate module. You need to include either ath_rate_onoe or ath_rate_amrr when configuring the kernel. 20041116: Support for systems with an 80386 CPU has been removed. Please use FreeBSD 5.x or earlier on systems with an 80386. 20041110: We have had a hack which would mount the root filesystem R/W if the device were named 'md*'. As part of the vnode work I'm doing I have had to remove this hack. People building systems which use preloaded MD root filesystems may need to insert a "/sbin/mount -u -o rw /dev/md0 /" in their /etc/rc scripts. 20041104: FreeBSD 5.3 shipped here. 20041102: The size of struct tcpcb has changed again due to the removal of RFC1644 T/TCP. You have to recompile userland programs that read kmem for tcp sockets directly (netstat, sockstat, etc.) 20041022: The size of struct tcpcb has changed. You have to recompile userland programs that read kmem for tcp sockets directly (netstat, sockstat, etc.) 20041016: RELENG_5 branched here. For older entries, please see updating in the RELENG_5 branch. COMMON ITEMS: General Notes ------------- Avoid using make -j when upgrading. From time to time in the past there have been problems using -j with buildworld and/or installworld. This is especially true when upgrading between "distant" versions (eg one that cross a major release boundary or several minor releases, or when several months have passed on the -current branch). Sometimes, obscure build problems are the result of environment poisoning. This can happen because the make utility reads its environment when searching for values for global variables. To run your build attempts in an "environmental clean room", prefix all make commands with 'env -i '. See the env(1) manual page for more details. When upgrading from one major version to another it is generally best to upgrade to the latest code in the currently installed branch first, then do an upgrade to the new branch. This is the best-tested upgrade path, and has the highest probability of being successful. Please try this approach before reporting problems with a major version upgrade. ZFS notes --------- When upgrading the boot ZFS pool to a new version, always follow these two steps: 1.) recompile and reinstall the ZFS boot loader and boot block (this is part of "make buildworld" and "make installworld") 2.) update the ZFS boot block on your boot drive The following example updates the ZFS boot block on the first partition (freebsd-boot) of a GPT partitioned drive ad0: "gpart bootcode -p /boot/gptzfsboot -i 1 ad0" Non-boot pools do not need these updates. To build a kernel ----------------- If you are updating from a prior version of FreeBSD (even one just a few days old), you should follow this procedure. It is the most failsafe as it uses a /usr/obj tree with a fresh mini-buildworld, make kernel-toolchain make -DALWAYS_CHECK_MAKE buildkernel KERNCONF=YOUR_KERNEL_HERE make -DALWAYS_CHECK_MAKE installkernel KERNCONF=YOUR_KERNEL_HERE To test a kernel once --------------------- If you just want to boot a kernel once (because you are not sure if it works, or if you want to boot a known bad kernel to provide debugging information) run make installkernel KERNCONF=YOUR_KERNEL_HERE KODIR=/boot/testkernel nextboot -k testkernel To just build a kernel when you know that it won't mess you up -------------------------------------------------------------- This assumes you are already running a 5.X system. Replace ${arch} with the architecture of your machine (e.g. "i386", "alpha", "amd64", "ia64", "pc98", "sparc64", etc). cd src/sys/${arch}/conf config KERNEL_NAME_HERE cd ../compile/KERNEL_NAME_HERE make depend make make install If this fails, go to the "To build a kernel" section. To rebuild everything and install it on the current system. ----------------------------------------------------------- # Note: sometimes if you are running current you gotta do more than # is listed here if you are upgrading from a really old current. make buildworld make kernel KERNCONF=YOUR_KERNEL_HERE [1] [3] mergemaster -p [5] make installworld mergemaster [4] make delete-old [6] To cross-install current onto a separate partition -------------------------------------------------- # In this approach we use a separate partition to hold # current's root, 'usr', and 'var' directories. A partition # holding "/", "/usr" and "/var" should be about 2GB in # size. make buildworld make buildkernel KERNCONF=YOUR_KERNEL_HERE make installworld DESTDIR=${CURRENT_ROOT} make distribution DESTDIR=${CURRENT_ROOT} # if newfs'd make installkernel KERNCONF=YOUR_KERNEL_HERE DESTDIR=${CURRENT_ROOT} cp /etc/fstab ${CURRENT_ROOT}/etc/fstab # if newfs'd To upgrade in-place from 5.x-stable to current ---------------------------------------------- make buildworld [9] make kernel KERNCONF=YOUR_KERNEL_HERE [8] [1] [3] mergemaster -p [5] make installworld mergemaster -i [4] make delete-old [6] Make sure that you've read the UPDATING file to understand the tweaks to various things you need. At this point in the life cycle of current, things change often and you are on your own to cope. The defaults can also change, so please read ALL of the UPDATING entries. Also, if you are tracking -current, you must be subscribed to freebsd-current@freebsd.org. Make sure that before you update your sources that you have read and understood all the recent messages there. If in doubt, please track -stable which has much fewer pitfalls. [1] If you have third party modules, such as vmware, you should disable them at this point so they don't crash your system on reboot. [3] From the bootblocks, boot -s, and then do fsck -p mount -u / mount -a cd src adjkerntz -i # if CMOS is wall time Also, when doing a major release upgrade, it is required that you boot into single user mode to do the installworld. [4] Note: This step is non-optional. Failure to do this step can result in a significant reduction in the functionality of the system. Attempting to do it by hand is not recommended and those that pursue this avenue should read this file carefully, as well as the archives of freebsd-current and freebsd-hackers mailing lists for potential gotchas. [5] Usually this step is a noop. However, from time to time you may need to do this if you get unknown user in the following step. It never hurts to do it all the time. You may need to install a new mergemaster (cd src/usr.sbin/mergemaster && make install) after the buildworld before this step if you last updated from current before 20020224 or from -stable before 20020408. [6] This only deletes old files and directories. Old libraries can be deleted by "make delete-old-libs", but you have to make sure that no program is using those libraries anymore. [8] In order to have a kernel that can run the 4.x binaries needed to do an installworld, you must include the COMPAT_FREEBSD4 option in your kernel. Failure to do so may leave you with a system that is hard to boot to recover. A similar kernel option COMPAT_FREEBSD5 is required to run the 5.x binaries on more recent kernels. Make sure that you merge any new devices from GENERIC since the last time you updated your kernel config file. [9] When checking out sources, you must include the -P flag to have cvs prune empty directories. If CPUTYPE is defined in your /etc/make.conf, make sure to use the "?=" instead of the "=" assignment operator, so that buildworld can override the CPUTYPE if it needs to. MAKEOBJDIRPREFIX must be defined in an environment variable, and not on the command line, or in /etc/make.conf. buildworld will warn if it is improperly defined. FORMAT: This file contains a list, in reverse chronological order, of major breakages in tracking -current. It is not guaranteed to be a complete list of such breakages, and only contains entries since October 16, 2004. If you need to see UPDATING entries from before that date, you will need to fetch an UPDATING file from an older FreeBSD release. Copyright information: Copyright 1998-2005 M. Warner Losh. All Rights Reserved. Redistribution, publication, translation and use, with or without modification, in full or in part, in any form or format of this document are permitted without further permission from the author. THIS DOCUMENT IS PROVIDED BY WARNER LOSH ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WARNER LOSH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. If you find this document useful, and you want to, you may buy the author a beer. Contact Warner Losh if you have any questions about your use of this document. $FreeBSD$ Index: releng/8.4/sys/conf/newvers.sh =================================================================== --- releng/8.4/sys/conf/newvers.sh (revision 252333) +++ releng/8.4/sys/conf/newvers.sh (revision 252334) @@ -1,152 +1,152 @@ #!/bin/sh - # # Copyright (c) 1984, 1986, 1990, 1993 # The Regents of the University of California. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 4. Neither the name of the University nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # @(#)newvers.sh 8.1 (Berkeley) 4/20/94 # $FreeBSD$ TYPE="FreeBSD" REVISION="8.4" -BRANCH="RELEASE" +BRANCH="RELEASE-p1" if [ "X${BRANCH_OVERRIDE}" != "X" ]; then BRANCH=${BRANCH_OVERRIDE} fi RELEASE="${REVISION}-${BRANCH}" VERSION="${TYPE} ${RELEASE}" SYSDIR=$(dirname $0)/.. if [ "X${PARAMFILE}" != "X" ]; then RELDATE=$(awk '/__FreeBSD_version.*propagated to newvers/ {print $3}' \ ${PARAMFILE}) else RELDATE=$(awk '/__FreeBSD_version.*propagated to newvers/ {print $3}' \ ${SYSDIR}/sys/param.h) fi b=share/examples/etc/bsd-style-copyright year=`date '+%Y'` # look for copyright template for bsd_copyright in ../$b ../../$b ../../../$b /usr/src/$b /usr/$b do if [ -r "$bsd_copyright" ]; then COPYRIGHT=`sed \ -e "s/\[year\]/1992-$year/" \ -e 's/\[your name here\]\.* /The FreeBSD Project./' \ -e 's/\[your name\]\.*/The FreeBSD Project./' \ -e '/\[id for your version control system, if any\]/d' \ $bsd_copyright` break fi done # no copyright found, use a dummy if [ X"$COPYRIGHT" = X ]; then COPYRIGHT="/*- * Copyright (c) 1992-$year The FreeBSD Project. * All rights reserved. * */" fi # add newline COPYRIGHT="$COPYRIGHT " LC_ALL=C; export LC_ALL if [ ! -r version ] then echo 0 > version fi touch version v=`cat version` u=${USER:-root} d=`pwd` h=${HOSTNAME:-`hostname`} t=`date` i=`${MAKE:-make} -V KERN_IDENT` compiler_v=$($(${MAKE:-make} -V CC) -v 2>&1 | grep 'version') for dir in /bin /usr/bin /usr/local/bin; do if [ -x "${dir}/svnversion" ] ; then svnversion=${dir}/svnversion break fi done if [ -d "${SYSDIR}/../.git" ] ; then for dir in /bin /usr/bin /usr/local/bin; do if [ -x "${dir}/git" ] ; then git_cmd="${dir}/git --git-dir=${SYSDIR}/../.git" break fi done fi if [ -n "$svnversion" ] ; then echo "$svnversion" svn=`cd ${SYSDIR} && $svnversion` case "$svn" in [0-9]*) svn=" r${svn}" ;; *) unset svn ;; esac fi if [ -n "$git_cmd" ] ; then git=`$git_cmd rev-parse --verify --short HEAD 2>/dev/null` svn=`$git_cmd svn find-rev $git 2>/dev/null` if [ -n "$svn" ] ; then svn=" r${svn}" git="=${git}" else svn=`$git_cmd log | fgrep 'git-svn-id:' | head -1 | \ sed -n 's/^.*@\([0-9][0-9]*\).*$/\1/p'` if [ -n $svn ] ; then svn=" r${svn}" git="+${git}" else git=" ${git}" fi fi if $git_cmd --work-tree=${SYSDIR}/.. diff-index \ --name-only HEAD | read dummy; then git="${git}-dirty" fi fi cat << EOF > vers.c $COPYRIGHT #define SCCSSTR "@(#)${VERSION} #${v}${svn}${git}: ${t}" #define VERSTR "${VERSION} #${v}${svn}${git}: ${t}\\n ${u}@${h}:${d}\\n" #define RELSTR "${RELEASE}" char sccs[sizeof(SCCSSTR) > 128 ? sizeof(SCCSSTR) : 128] = SCCSSTR; char version[sizeof(VERSTR) > 256 ? sizeof(VERSTR) : 256] = VERSTR; char compiler_version[] = "${compiler_v}"; char ostype[] = "${TYPE}"; char osrelease[sizeof(RELSTR) > 32 ? sizeof(RELSTR) : 32] = RELSTR; int osreldate = ${RELDATE}; char kern_ident[] = "${i}"; EOF echo $((v + 1)) > version Index: releng/8.4/sys/dev/fxp/if_fxp.c =================================================================== --- releng/8.4/sys/dev/fxp/if_fxp.c (revision 252333) +++ releng/8.4/sys/dev/fxp/if_fxp.c (revision 252334) @@ -1,3250 +1,3264 @@ /*- * Copyright (c) 1995, David Greenman * Copyright (c) 2001 Jonathan Lemon * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); /* * Intel EtherExpress Pro/100B PCI Fast Ethernet driver */ #ifdef HAVE_KERNEL_OPTION_HEADERS #include "opt_device_polling.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for PCIM_CMD_xxx */ #include #include #include #include #include MODULE_DEPEND(fxp, pci, 1, 1, 1); MODULE_DEPEND(fxp, ether, 1, 1, 1); MODULE_DEPEND(fxp, miibus, 1, 1, 1); #include "miibus_if.h" /* * NOTE! On !x86 we typically have an alignment constraint. The * card DMAs the packet immediately following the RFA. However, * the first thing in the packet is a 14-byte Ethernet header. * This means that the packet is misaligned. To compensate, * we actually offset the RFA 2 bytes into the cluster. This * alignes the packet after the Ethernet header at a 32-bit * boundary. HOWEVER! This means that the RFA is misaligned! */ #define RFA_ALIGNMENT_FUDGE 2 /* * Set initial transmit threshold at 64 (512 bytes). This is * increased by 64 (512 bytes) at a time, to maximum of 192 * (1536 bytes), if an underrun occurs. */ static int tx_threshold = 64; /* * The configuration byte map has several undefined fields which * must be one or must be zero. Set up a template for these bits. * The actual configuration is performed in fxp_init_body. * * See struct fxp_cb_config for the bit definitions. */ static const u_char fxp_cb_config_template[] = { 0x0, 0x0, /* cb_status */ 0x0, 0x0, /* cb_command */ 0x0, 0x0, 0x0, 0x0, /* link_addr */ 0x0, /* 0 */ 0x0, /* 1 */ 0x0, /* 2 */ 0x0, /* 3 */ 0x0, /* 4 */ 0x0, /* 5 */ 0x32, /* 6 */ 0x0, /* 7 */ 0x0, /* 8 */ 0x0, /* 9 */ 0x6, /* 10 */ 0x0, /* 11 */ 0x0, /* 12 */ 0x0, /* 13 */ 0xf2, /* 14 */ 0x48, /* 15 */ 0x0, /* 16 */ 0x40, /* 17 */ 0xf0, /* 18 */ 0x0, /* 19 */ 0x3f, /* 20 */ 0x5, /* 21 */ 0x0, /* 22 */ 0x0, /* 23 */ 0x0, /* 24 */ 0x0, /* 25 */ 0x0, /* 26 */ 0x0, /* 27 */ 0x0, /* 28 */ 0x0, /* 29 */ 0x0, /* 30 */ 0x0 /* 31 */ }; /* * Claim various Intel PCI device identifiers for this driver. The * sub-vendor and sub-device field are extensively used to identify * particular variants, but we don't currently differentiate between * them. */ static const struct fxp_ident fxp_ident_table[] = { { 0x1029, -1, 0, "Intel 82559 PCI/CardBus Pro/100" }, { 0x1030, -1, 0, "Intel 82559 Pro/100 Ethernet" }, { 0x1031, -1, 3, "Intel 82801CAM (ICH3) Pro/100 VE Ethernet" }, { 0x1032, -1, 3, "Intel 82801CAM (ICH3) Pro/100 VE Ethernet" }, { 0x1033, -1, 3, "Intel 82801CAM (ICH3) Pro/100 VM Ethernet" }, { 0x1034, -1, 3, "Intel 82801CAM (ICH3) Pro/100 VM Ethernet" }, { 0x1035, -1, 3, "Intel 82801CAM (ICH3) Pro/100 Ethernet" }, { 0x1036, -1, 3, "Intel 82801CAM (ICH3) Pro/100 Ethernet" }, { 0x1037, -1, 3, "Intel 82801CAM (ICH3) Pro/100 Ethernet" }, { 0x1038, -1, 3, "Intel 82801CAM (ICH3) Pro/100 VM Ethernet" }, { 0x1039, -1, 4, "Intel 82801DB (ICH4) Pro/100 VE Ethernet" }, { 0x103A, -1, 4, "Intel 82801DB (ICH4) Pro/100 Ethernet" }, { 0x103B, -1, 4, "Intel 82801DB (ICH4) Pro/100 VM Ethernet" }, { 0x103C, -1, 4, "Intel 82801DB (ICH4) Pro/100 Ethernet" }, { 0x103D, -1, 4, "Intel 82801DB (ICH4) Pro/100 VE Ethernet" }, { 0x103E, -1, 4, "Intel 82801DB (ICH4) Pro/100 VM Ethernet" }, { 0x1050, -1, 5, "Intel 82801BA (D865) Pro/100 VE Ethernet" }, { 0x1051, -1, 5, "Intel 82562ET (ICH5/ICH5R) Pro/100 VE Ethernet" }, { 0x1059, -1, 0, "Intel 82551QM Pro/100 M Mobile Connection" }, { 0x1064, -1, 6, "Intel 82562EZ (ICH6)" }, { 0x1065, -1, 6, "Intel 82562ET/EZ/GT/GZ PRO/100 VE Ethernet" }, { 0x1068, -1, 6, "Intel 82801FBM (ICH6-M) Pro/100 VE Ethernet" }, { 0x1069, -1, 6, "Intel 82562EM/EX/GX Pro/100 Ethernet" }, { 0x1091, -1, 7, "Intel 82562GX Pro/100 Ethernet" }, { 0x1092, -1, 7, "Intel Pro/100 VE Network Connection" }, { 0x1093, -1, 7, "Intel Pro/100 VM Network Connection" }, { 0x1094, -1, 7, "Intel Pro/100 946GZ (ICH7) Network Connection" }, { 0x1209, -1, 0, "Intel 82559ER Embedded 10/100 Ethernet" }, { 0x1229, 0x01, 0, "Intel 82557 Pro/100 Ethernet" }, { 0x1229, 0x02, 0, "Intel 82557 Pro/100 Ethernet" }, { 0x1229, 0x03, 0, "Intel 82557 Pro/100 Ethernet" }, { 0x1229, 0x04, 0, "Intel 82558 Pro/100 Ethernet" }, { 0x1229, 0x05, 0, "Intel 82558 Pro/100 Ethernet" }, { 0x1229, 0x06, 0, "Intel 82559 Pro/100 Ethernet" }, { 0x1229, 0x07, 0, "Intel 82559 Pro/100 Ethernet" }, { 0x1229, 0x08, 0, "Intel 82559 Pro/100 Ethernet" }, { 0x1229, 0x09, 0, "Intel 82559ER Pro/100 Ethernet" }, { 0x1229, 0x0c, 0, "Intel 82550 Pro/100 Ethernet" }, { 0x1229, 0x0d, 0, "Intel 82550C Pro/100 Ethernet" }, { 0x1229, 0x0e, 0, "Intel 82550 Pro/100 Ethernet" }, { 0x1229, 0x0f, 0, "Intel 82551 Pro/100 Ethernet" }, { 0x1229, 0x10, 0, "Intel 82551 Pro/100 Ethernet" }, { 0x1229, -1, 0, "Intel 82557/8/9 Pro/100 Ethernet" }, { 0x2449, -1, 2, "Intel 82801BA/CAM (ICH2/3) Pro/100 Ethernet" }, { 0x27dc, -1, 7, "Intel 82801GB (ICH7) 10/100 Ethernet" }, { 0, -1, 0, NULL }, }; #ifdef FXP_IP_CSUM_WAR #define FXP_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP) #else #define FXP_CSUM_FEATURES (CSUM_TCP | CSUM_UDP) #endif static int fxp_probe(device_t dev); static int fxp_attach(device_t dev); static int fxp_detach(device_t dev); static int fxp_shutdown(device_t dev); static int fxp_suspend(device_t dev); static int fxp_resume(device_t dev); static const struct fxp_ident *fxp_find_ident(device_t dev); static void fxp_intr(void *xsc); static void fxp_rxcsum(struct fxp_softc *sc, struct ifnet *ifp, struct mbuf *m, uint16_t status, int pos); static int fxp_intr_body(struct fxp_softc *sc, struct ifnet *ifp, uint8_t statack, int count); static void fxp_init(void *xsc); static void fxp_init_body(struct fxp_softc *sc, int); static void fxp_tick(void *xsc); static void fxp_start(struct ifnet *ifp); static void fxp_start_body(struct ifnet *ifp); static int fxp_encap(struct fxp_softc *sc, struct mbuf **m_head); static void fxp_txeof(struct fxp_softc *sc); static void fxp_stop(struct fxp_softc *sc); static void fxp_release(struct fxp_softc *sc); static int fxp_ioctl(struct ifnet *ifp, u_long command, caddr_t data); static void fxp_watchdog(struct fxp_softc *sc); static void fxp_add_rfabuf(struct fxp_softc *sc, struct fxp_rx *rxp); static void fxp_discard_rfabuf(struct fxp_softc *sc, struct fxp_rx *rxp); static int fxp_new_rfabuf(struct fxp_softc *sc, struct fxp_rx *rxp); static int fxp_mc_addrs(struct fxp_softc *sc); static void fxp_mc_setup(struct fxp_softc *sc); static uint16_t fxp_eeprom_getword(struct fxp_softc *sc, int offset, int autosize); static void fxp_eeprom_putword(struct fxp_softc *sc, int offset, uint16_t data); static void fxp_autosize_eeprom(struct fxp_softc *sc); static void fxp_load_eeprom(struct fxp_softc *sc); static void fxp_read_eeprom(struct fxp_softc *sc, u_short *data, int offset, int words); static void fxp_write_eeprom(struct fxp_softc *sc, u_short *data, int offset, int words); static int fxp_ifmedia_upd(struct ifnet *ifp); static void fxp_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr); static int fxp_serial_ifmedia_upd(struct ifnet *ifp); static void fxp_serial_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr); static int fxp_miibus_readreg(device_t dev, int phy, int reg); static int fxp_miibus_writereg(device_t dev, int phy, int reg, int value); static void fxp_miibus_statchg(device_t dev); static void fxp_load_ucode(struct fxp_softc *sc); static void fxp_update_stats(struct fxp_softc *sc); static void fxp_sysctl_node(struct fxp_softc *sc); static int sysctl_int_range(SYSCTL_HANDLER_ARGS, int low, int high); static int sysctl_hw_fxp_bundle_max(SYSCTL_HANDLER_ARGS); static int sysctl_hw_fxp_int_delay(SYSCTL_HANDLER_ARGS); static void fxp_scb_wait(struct fxp_softc *sc); static void fxp_scb_cmd(struct fxp_softc *sc, int cmd); static void fxp_dma_wait(struct fxp_softc *sc, volatile uint16_t *status, bus_dma_tag_t dmat, bus_dmamap_t map); static device_method_t fxp_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fxp_probe), DEVMETHOD(device_attach, fxp_attach), DEVMETHOD(device_detach, fxp_detach), DEVMETHOD(device_shutdown, fxp_shutdown), DEVMETHOD(device_suspend, fxp_suspend), DEVMETHOD(device_resume, fxp_resume), /* MII interface */ DEVMETHOD(miibus_readreg, fxp_miibus_readreg), DEVMETHOD(miibus_writereg, fxp_miibus_writereg), DEVMETHOD(miibus_statchg, fxp_miibus_statchg), { 0, 0 } }; static driver_t fxp_driver = { "fxp", fxp_methods, sizeof(struct fxp_softc), }; static devclass_t fxp_devclass; DRIVER_MODULE(fxp, pci, fxp_driver, fxp_devclass, 0, 0); DRIVER_MODULE(miibus, fxp, miibus_driver, miibus_devclass, 0, 0); static struct resource_spec fxp_res_spec_mem[] = { { SYS_RES_MEMORY, FXP_PCI_MMBA, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, { -1, 0 } }; static struct resource_spec fxp_res_spec_io[] = { { SYS_RES_IOPORT, FXP_PCI_IOBA, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, { -1, 0 } }; /* * Wait for the previous command to be accepted (but not necessarily * completed). */ static void fxp_scb_wait(struct fxp_softc *sc) { union { uint16_t w; uint8_t b[2]; } flowctl; int i = 10000; while (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) && --i) DELAY(2); if (i == 0) { flowctl.b[0] = CSR_READ_1(sc, FXP_CSR_FC_THRESH); flowctl.b[1] = CSR_READ_1(sc, FXP_CSR_FC_STATUS); device_printf(sc->dev, "SCB timeout: 0x%x 0x%x 0x%x 0x%x\n", CSR_READ_1(sc, FXP_CSR_SCB_COMMAND), CSR_READ_1(sc, FXP_CSR_SCB_STATACK), CSR_READ_1(sc, FXP_CSR_SCB_RUSCUS), flowctl.w); } } static void fxp_scb_cmd(struct fxp_softc *sc, int cmd) { if (cmd == FXP_SCB_COMMAND_CU_RESUME && sc->cu_resume_bug) { CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_CB_COMMAND_NOP); fxp_scb_wait(sc); } CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, cmd); } static void fxp_dma_wait(struct fxp_softc *sc, volatile uint16_t *status, bus_dma_tag_t dmat, bus_dmamap_t map) { int i; for (i = 10000; i > 0; i--) { DELAY(2); bus_dmamap_sync(dmat, map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); if ((le16toh(*status) & FXP_CB_STATUS_C) != 0) break; } if (i == 0) device_printf(sc->dev, "DMA timeout\n"); } static const struct fxp_ident * fxp_find_ident(device_t dev) { uint16_t devid; uint8_t revid; const struct fxp_ident *ident; if (pci_get_vendor(dev) == FXP_VENDORID_INTEL) { devid = pci_get_device(dev); revid = pci_get_revid(dev); for (ident = fxp_ident_table; ident->name != NULL; ident++) { if (ident->devid == devid && (ident->revid == revid || ident->revid == -1)) { return (ident); } } } return (NULL); } /* * Return identification string if this device is ours. */ static int fxp_probe(device_t dev) { const struct fxp_ident *ident; ident = fxp_find_ident(dev); if (ident != NULL) { device_set_desc(dev, ident->name); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static void fxp_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error) { uint32_t *addr; if (error) return; KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg)); addr = arg; *addr = segs->ds_addr; } static int fxp_attach(device_t dev) { struct fxp_softc *sc; struct fxp_cb_tx *tcbp; struct fxp_tx *txp; struct fxp_rx *rxp; struct ifnet *ifp; uint32_t val; uint16_t data; u_char eaddr[ETHER_ADDR_LEN]; int error, flags, i, pmc, prefer_iomap; error = 0; sc = device_get_softc(dev); sc->dev = dev; mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); callout_init_mtx(&sc->stat_ch, &sc->sc_mtx, 0); ifmedia_init(&sc->sc_media, 0, fxp_serial_ifmedia_upd, fxp_serial_ifmedia_sts); ifp = sc->ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { device_printf(dev, "can not if_alloc()\n"); error = ENOSPC; goto fail; } /* * Enable bus mastering. */ pci_enable_busmaster(dev); val = pci_read_config(dev, PCIR_COMMAND, 2); /* * Figure out which we should try first - memory mapping or i/o mapping? * We default to memory mapping. Then we accept an override from the * command line. Then we check to see which one is enabled. */ prefer_iomap = 0; resource_int_value(device_get_name(dev), device_get_unit(dev), "prefer_iomap", &prefer_iomap); if (prefer_iomap) sc->fxp_spec = fxp_res_spec_io; else sc->fxp_spec = fxp_res_spec_mem; error = bus_alloc_resources(dev, sc->fxp_spec, sc->fxp_res); if (error) { if (sc->fxp_spec == fxp_res_spec_mem) sc->fxp_spec = fxp_res_spec_io; else sc->fxp_spec = fxp_res_spec_mem; error = bus_alloc_resources(dev, sc->fxp_spec, sc->fxp_res); } if (error) { device_printf(dev, "could not allocate resources\n"); error = ENXIO; goto fail; } if (bootverbose) { device_printf(dev, "using %s space register mapping\n", sc->fxp_spec == fxp_res_spec_mem ? "memory" : "I/O"); } /* * Put CU/RU idle state and prepare full reset. */ CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET); DELAY(10); /* Full reset and disable interrupts. */ CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SOFTWARE_RESET); DELAY(10); CSR_WRITE_1(sc, FXP_CSR_SCB_INTRCNTL, FXP_SCB_INTR_DISABLE); /* * Find out how large of an SEEPROM we have. */ fxp_autosize_eeprom(sc); fxp_load_eeprom(sc); /* * Find out the chip revision; lump all 82557 revs together. */ sc->ident = fxp_find_ident(dev); if (sc->ident->ich > 0) { /* Assume ICH controllers are 82559. */ sc->revision = FXP_REV_82559_A0; } else { data = sc->eeprom[FXP_EEPROM_MAP_CNTR]; if ((data >> 8) == 1) sc->revision = FXP_REV_82557; else sc->revision = pci_get_revid(dev); } /* * Check availability of WOL. 82559ER does not support WOL. */ if (sc->revision >= FXP_REV_82558_A4 && sc->revision != FXP_REV_82559S_A) { data = sc->eeprom[FXP_EEPROM_MAP_ID]; if ((data & 0x20) != 0 && pci_find_extcap(sc->dev, PCIY_PMG, &pmc) == 0) sc->flags |= FXP_FLAG_WOLCAP; } if (sc->revision == FXP_REV_82550_C) { /* * 82550C with server extension requires microcode to * receive fragmented UDP datagrams. However if the * microcode is used for client-only featured 82550C * it locks up controller. */ data = sc->eeprom[FXP_EEPROM_MAP_COMPAT]; if ((data & 0x0400) == 0) sc->flags |= FXP_FLAG_NO_UCODE; } /* Receiver lock-up workaround detection. */ if (sc->revision < FXP_REV_82558_A4) { data = sc->eeprom[FXP_EEPROM_MAP_COMPAT]; if ((data & 0x03) != 0x03) { sc->flags |= FXP_FLAG_RXBUG; device_printf(dev, "Enabling Rx lock-up workaround\n"); } } /* * Determine whether we must use the 503 serial interface. */ data = sc->eeprom[FXP_EEPROM_MAP_PRI_PHY]; if (sc->revision == FXP_REV_82557 && (data & FXP_PHY_DEVICE_MASK) != 0 && (data & FXP_PHY_SERIAL_ONLY)) sc->flags |= FXP_FLAG_SERIAL_MEDIA; fxp_sysctl_node(sc); /* * Enable workarounds for certain chip revision deficiencies. * * Systems based on the ICH2/ICH2-M chip from Intel, and possibly * some systems based a normal 82559 design, have a defect where * the chip can cause a PCI protocol violation if it receives * a CU_RESUME command when it is entering the IDLE state. The * workaround is to disable Dynamic Standby Mode, so the chip never * deasserts CLKRUN#, and always remains in an active state. * * See Intel 82801BA/82801BAM Specification Update, Errata #30. */ if ((sc->ident->ich >= 2 && sc->ident->ich <= 3) || (sc->ident->ich == 0 && sc->revision >= FXP_REV_82559_A0)) { data = sc->eeprom[FXP_EEPROM_MAP_ID]; if (data & 0x02) { /* STB enable */ uint16_t cksum; int i; device_printf(dev, "Disabling dynamic standby mode in EEPROM\n"); data &= ~0x02; sc->eeprom[FXP_EEPROM_MAP_ID] = data; fxp_write_eeprom(sc, &data, FXP_EEPROM_MAP_ID, 1); device_printf(dev, "New EEPROM ID: 0x%x\n", data); cksum = 0; for (i = 0; i < (1 << sc->eeprom_size) - 1; i++) cksum += sc->eeprom[i]; i = (1 << sc->eeprom_size) - 1; cksum = 0xBABA - cksum; fxp_write_eeprom(sc, &cksum, i, 1); device_printf(dev, "EEPROM checksum @ 0x%x: 0x%x -> 0x%x\n", i, sc->eeprom[i], cksum); sc->eeprom[i] = cksum; /* * If the user elects to continue, try the software * workaround, as it is better than nothing. */ sc->flags |= FXP_FLAG_CU_RESUME_BUG; } } /* * If we are not a 82557 chip, we can enable extended features. */ if (sc->revision != FXP_REV_82557) { /* * If MWI is enabled in the PCI configuration, and there * is a valid cacheline size (8 or 16 dwords), then tell * the board to turn on MWI. */ if (val & PCIM_CMD_MWRICEN && pci_read_config(dev, PCIR_CACHELNSZ, 1) != 0) sc->flags |= FXP_FLAG_MWI_ENABLE; /* turn on the extended TxCB feature */ sc->flags |= FXP_FLAG_EXT_TXCB; /* enable reception of long frames for VLAN */ sc->flags |= FXP_FLAG_LONG_PKT_EN; } else { /* a hack to get long VLAN frames on a 82557 */ sc->flags |= FXP_FLAG_SAVE_BAD; } /* For 82559 or later chips, Rx checksum offload is supported. */ if (sc->revision >= FXP_REV_82559_A0) { /* 82559ER does not support Rx checksum offloading. */ if (sc->ident->devid != 0x1209) sc->flags |= FXP_FLAG_82559_RXCSUM; } /* * Enable use of extended RFDs and TCBs for 82550 * and later chips. Note: we need extended TXCB support * too, but that's already enabled by the code above. * Be careful to do this only on the right devices. */ if (sc->revision == FXP_REV_82550 || sc->revision == FXP_REV_82550_C || sc->revision == FXP_REV_82551_E || sc->revision == FXP_REV_82551_F || sc->revision == FXP_REV_82551_10) { sc->rfa_size = sizeof (struct fxp_rfa); sc->tx_cmd = FXP_CB_COMMAND_IPCBXMIT; sc->flags |= FXP_FLAG_EXT_RFA; /* Use extended RFA instead of 82559 checksum mode. */ sc->flags &= ~FXP_FLAG_82559_RXCSUM; } else { sc->rfa_size = sizeof (struct fxp_rfa) - FXP_RFAX_LEN; sc->tx_cmd = FXP_CB_COMMAND_XMIT; } /* * Allocate DMA tags and DMA safe memory. */ sc->maxtxseg = FXP_NTXSEG; sc->maxsegsize = MCLBYTES; if (sc->flags & FXP_FLAG_EXT_RFA) { sc->maxtxseg--; sc->maxsegsize = FXP_TSO_SEGSIZE; } error = bus_dma_tag_create(bus_get_dma_tag(dev), 2, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, sc->maxsegsize * sc->maxtxseg + sizeof(struct ether_vlan_header), sc->maxtxseg, sc->maxsegsize, 0, busdma_lock_mutex, &Giant, &sc->fxp_txmtag); if (error) { device_printf(dev, "could not create TX DMA tag\n"); goto fail; } error = bus_dma_tag_create(bus_get_dma_tag(dev), 2, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, 0, busdma_lock_mutex, &Giant, &sc->fxp_rxmtag); if (error) { device_printf(dev, "could not create RX DMA tag\n"); goto fail; } error = bus_dma_tag_create(bus_get_dma_tag(dev), 4, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, sizeof(struct fxp_stats), 1, sizeof(struct fxp_stats), 0, busdma_lock_mutex, &Giant, &sc->fxp_stag); if (error) { device_printf(dev, "could not create stats DMA tag\n"); goto fail; } error = bus_dmamem_alloc(sc->fxp_stag, (void **)&sc->fxp_stats, BUS_DMA_NOWAIT | BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->fxp_smap); if (error) { device_printf(dev, "could not allocate stats DMA memory\n"); goto fail; } error = bus_dmamap_load(sc->fxp_stag, sc->fxp_smap, sc->fxp_stats, sizeof(struct fxp_stats), fxp_dma_map_addr, &sc->stats_addr, BUS_DMA_NOWAIT); if (error) { device_printf(dev, "could not load the stats DMA buffer\n"); goto fail; } error = bus_dma_tag_create(bus_get_dma_tag(dev), 4, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, FXP_TXCB_SZ, 1, FXP_TXCB_SZ, 0, busdma_lock_mutex, &Giant, &sc->cbl_tag); if (error) { device_printf(dev, "could not create TxCB DMA tag\n"); goto fail; } error = bus_dmamem_alloc(sc->cbl_tag, (void **)&sc->fxp_desc.cbl_list, BUS_DMA_NOWAIT | BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->cbl_map); if (error) { device_printf(dev, "could not allocate TxCB DMA memory\n"); goto fail; } error = bus_dmamap_load(sc->cbl_tag, sc->cbl_map, sc->fxp_desc.cbl_list, FXP_TXCB_SZ, fxp_dma_map_addr, &sc->fxp_desc.cbl_addr, BUS_DMA_NOWAIT); if (error) { device_printf(dev, "could not load TxCB DMA buffer\n"); goto fail; } error = bus_dma_tag_create(bus_get_dma_tag(dev), 4, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, sizeof(struct fxp_cb_mcs), 1, sizeof(struct fxp_cb_mcs), 0, busdma_lock_mutex, &Giant, &sc->mcs_tag); if (error) { device_printf(dev, "could not create multicast setup DMA tag\n"); goto fail; } error = bus_dmamem_alloc(sc->mcs_tag, (void **)&sc->mcsp, BUS_DMA_NOWAIT | BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->mcs_map); if (error) { device_printf(dev, "could not allocate multicast setup DMA memory\n"); goto fail; } error = bus_dmamap_load(sc->mcs_tag, sc->mcs_map, sc->mcsp, sizeof(struct fxp_cb_mcs), fxp_dma_map_addr, &sc->mcs_addr, BUS_DMA_NOWAIT); if (error) { device_printf(dev, "can't load the multicast setup DMA buffer\n"); goto fail; } /* * Pre-allocate the TX DMA maps and setup the pointers to * the TX command blocks. */ txp = sc->fxp_desc.tx_list; tcbp = sc->fxp_desc.cbl_list; for (i = 0; i < FXP_NTXCB; i++) { txp[i].tx_cb = tcbp + i; error = bus_dmamap_create(sc->fxp_txmtag, 0, &txp[i].tx_map); if (error) { device_printf(dev, "can't create DMA map for TX\n"); goto fail; } } error = bus_dmamap_create(sc->fxp_rxmtag, 0, &sc->spare_map); if (error) { device_printf(dev, "can't create spare DMA map\n"); goto fail; } /* * Pre-allocate our receive buffers. */ sc->fxp_desc.rx_head = sc->fxp_desc.rx_tail = NULL; for (i = 0; i < FXP_NRFABUFS; i++) { rxp = &sc->fxp_desc.rx_list[i]; error = bus_dmamap_create(sc->fxp_rxmtag, 0, &rxp->rx_map); if (error) { device_printf(dev, "can't create DMA map for RX\n"); goto fail; } if (fxp_new_rfabuf(sc, rxp) != 0) { error = ENOMEM; goto fail; } fxp_add_rfabuf(sc, rxp); } /* * Read MAC address. */ eaddr[0] = sc->eeprom[FXP_EEPROM_MAP_IA0] & 0xff; eaddr[1] = sc->eeprom[FXP_EEPROM_MAP_IA0] >> 8; eaddr[2] = sc->eeprom[FXP_EEPROM_MAP_IA1] & 0xff; eaddr[3] = sc->eeprom[FXP_EEPROM_MAP_IA1] >> 8; eaddr[4] = sc->eeprom[FXP_EEPROM_MAP_IA2] & 0xff; eaddr[5] = sc->eeprom[FXP_EEPROM_MAP_IA2] >> 8; if (bootverbose) { device_printf(dev, "PCI IDs: %04x %04x %04x %04x %04x\n", pci_get_vendor(dev), pci_get_device(dev), pci_get_subvendor(dev), pci_get_subdevice(dev), pci_get_revid(dev)); device_printf(dev, "Dynamic Standby mode is %s\n", sc->eeprom[FXP_EEPROM_MAP_ID] & 0x02 ? "enabled" : "disabled"); } /* * If this is only a 10Mbps device, then there is no MII, and * the PHY will use a serial interface instead. * * The Seeq 80c24 AutoDUPLEX(tm) Ethernet Interface Adapter * doesn't have a programming interface of any sort. The * media is sensed automatically based on how the link partner * is configured. This is, in essence, manual configuration. */ if (sc->flags & FXP_FLAG_SERIAL_MEDIA) { ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_MANUAL, 0, NULL); ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_MANUAL); } else { /* * i82557 wedge when isolating all of their PHYs. */ flags = MIIF_NOISOLATE; if (sc->revision >= FXP_REV_82558_A4) flags |= MIIF_DOPAUSE; error = mii_attach(dev, &sc->miibus, ifp, fxp_ifmedia_upd, fxp_ifmedia_sts, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, flags); if (error != 0) { device_printf(dev, "attaching PHYs failed\n"); goto fail; } } if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_init = fxp_init; ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = fxp_ioctl; ifp->if_start = fxp_start; ifp->if_capabilities = ifp->if_capenable = 0; /* Enable checksum offload/TSO for 82550 or better chips */ if (sc->flags & FXP_FLAG_EXT_RFA) { ifp->if_hwassist = FXP_CSUM_FEATURES | CSUM_TSO; ifp->if_capabilities |= IFCAP_HWCSUM | IFCAP_TSO4; ifp->if_capenable |= IFCAP_HWCSUM | IFCAP_TSO4; } if (sc->flags & FXP_FLAG_82559_RXCSUM) { ifp->if_capabilities |= IFCAP_RXCSUM; ifp->if_capenable |= IFCAP_RXCSUM; } if (sc->flags & FXP_FLAG_WOLCAP) { ifp->if_capabilities |= IFCAP_WOL_MAGIC; ifp->if_capenable |= IFCAP_WOL_MAGIC; } #ifdef DEVICE_POLLING /* Inform the world we support polling. */ ifp->if_capabilities |= IFCAP_POLLING; #endif /* * Attach the interface. */ ether_ifattach(ifp, eaddr); /* * Tell the upper layer(s) we support long frames. * Must appear after the call to ether_ifattach() because * ether_ifattach() sets ifi_hdrlen to the default value. */ ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); ifp->if_capabilities |= IFCAP_VLAN_MTU; ifp->if_capenable |= IFCAP_VLAN_MTU; /* the hw bits already set */ if ((sc->flags & FXP_FLAG_EXT_RFA) != 0) { ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM | IFCAP_VLAN_HWTSO; ifp->if_capenable |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM | IFCAP_VLAN_HWTSO; } /* * Let the system queue as many packets as we have available * TX descriptors. */ IFQ_SET_MAXLEN(&ifp->if_snd, FXP_NTXCB - 1); ifp->if_snd.ifq_drv_maxlen = FXP_NTXCB - 1; IFQ_SET_READY(&ifp->if_snd); /* * Hook our interrupt after all initialization is complete. */ error = bus_setup_intr(dev, sc->fxp_res[1], INTR_TYPE_NET | INTR_MPSAFE, NULL, fxp_intr, sc, &sc->ih); if (error) { device_printf(dev, "could not setup irq\n"); ether_ifdetach(sc->ifp); goto fail; } /* * Configure hardware to reject magic frames otherwise * system will hang on recipt of magic frames. */ if ((sc->flags & FXP_FLAG_WOLCAP) != 0) { FXP_LOCK(sc); /* Clear wakeup events. */ CSR_WRITE_1(sc, FXP_CSR_PMDR, CSR_READ_1(sc, FXP_CSR_PMDR)); fxp_init_body(sc, 0); fxp_stop(sc); FXP_UNLOCK(sc); } fail: if (error) fxp_release(sc); return (error); } /* * Release all resources. The softc lock should not be held and the * interrupt should already be torn down. */ static void fxp_release(struct fxp_softc *sc) { struct fxp_rx *rxp; struct fxp_tx *txp; int i; FXP_LOCK_ASSERT(sc, MA_NOTOWNED); KASSERT(sc->ih == NULL, ("fxp_release() called with intr handle still active")); if (sc->miibus) device_delete_child(sc->dev, sc->miibus); bus_generic_detach(sc->dev); ifmedia_removeall(&sc->sc_media); if (sc->fxp_desc.cbl_list) { bus_dmamap_unload(sc->cbl_tag, sc->cbl_map); bus_dmamem_free(sc->cbl_tag, sc->fxp_desc.cbl_list, sc->cbl_map); } if (sc->fxp_stats) { bus_dmamap_unload(sc->fxp_stag, sc->fxp_smap); bus_dmamem_free(sc->fxp_stag, sc->fxp_stats, sc->fxp_smap); } if (sc->mcsp) { bus_dmamap_unload(sc->mcs_tag, sc->mcs_map); bus_dmamem_free(sc->mcs_tag, sc->mcsp, sc->mcs_map); } bus_release_resources(sc->dev, sc->fxp_spec, sc->fxp_res); if (sc->fxp_rxmtag) { for (i = 0; i < FXP_NRFABUFS; i++) { rxp = &sc->fxp_desc.rx_list[i]; if (rxp->rx_mbuf != NULL) { bus_dmamap_sync(sc->fxp_rxmtag, rxp->rx_map, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->fxp_rxmtag, rxp->rx_map); m_freem(rxp->rx_mbuf); } bus_dmamap_destroy(sc->fxp_rxmtag, rxp->rx_map); } bus_dmamap_destroy(sc->fxp_rxmtag, sc->spare_map); bus_dma_tag_destroy(sc->fxp_rxmtag); } if (sc->fxp_txmtag) { for (i = 0; i < FXP_NTXCB; i++) { txp = &sc->fxp_desc.tx_list[i]; if (txp->tx_mbuf != NULL) { bus_dmamap_sync(sc->fxp_txmtag, txp->tx_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->fxp_txmtag, txp->tx_map); m_freem(txp->tx_mbuf); } bus_dmamap_destroy(sc->fxp_txmtag, txp->tx_map); } bus_dma_tag_destroy(sc->fxp_txmtag); } if (sc->fxp_stag) bus_dma_tag_destroy(sc->fxp_stag); if (sc->cbl_tag) bus_dma_tag_destroy(sc->cbl_tag); if (sc->mcs_tag) bus_dma_tag_destroy(sc->mcs_tag); if (sc->ifp) if_free(sc->ifp); mtx_destroy(&sc->sc_mtx); } /* * Detach interface. */ static int fxp_detach(device_t dev) { struct fxp_softc *sc = device_get_softc(dev); #ifdef DEVICE_POLLING if (sc->ifp->if_capenable & IFCAP_POLLING) ether_poll_deregister(sc->ifp); #endif FXP_LOCK(sc); /* * Stop DMA and drop transmit queue, but disable interrupts first. */ CSR_WRITE_1(sc, FXP_CSR_SCB_INTRCNTL, FXP_SCB_INTR_DISABLE); fxp_stop(sc); FXP_UNLOCK(sc); callout_drain(&sc->stat_ch); /* * Close down routes etc. */ ether_ifdetach(sc->ifp); /* * Unhook interrupt before dropping lock. This is to prevent * races with fxp_intr(). */ bus_teardown_intr(sc->dev, sc->fxp_res[1], sc->ih); sc->ih = NULL; /* Release our allocated resources. */ fxp_release(sc); return (0); } /* * Device shutdown routine. Called at system shutdown after sync. The * main purpose of this routine is to shut off receiver DMA so that * kernel memory doesn't get clobbered during warmboot. */ static int fxp_shutdown(device_t dev) { /* * Make sure that DMA is disabled prior to reboot. Not doing * do could allow DMA to corrupt kernel memory during the * reboot before the driver initializes. */ return (fxp_suspend(dev)); } /* * Device suspend routine. Stop the interface and save some PCI * settings in case the BIOS doesn't restore them properly on * resume. */ static int fxp_suspend(device_t dev) { struct fxp_softc *sc = device_get_softc(dev); struct ifnet *ifp; int pmc; uint16_t pmstat; FXP_LOCK(sc); ifp = sc->ifp; if (pci_find_extcap(sc->dev, PCIY_PMG, &pmc) == 0) { pmstat = pci_read_config(sc->dev, pmc + PCIR_POWER_STATUS, 2); pmstat &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE); if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0) { /* Request PME. */ pmstat |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE; sc->flags |= FXP_FLAG_WOL; /* Reconfigure hardware to accept magic frames. */ - fxp_init_body(sc, 1); + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + fxp_init_body(sc, 0); } pci_write_config(sc->dev, pmc + PCIR_POWER_STATUS, pmstat, 2); } fxp_stop(sc); sc->suspended = 1; FXP_UNLOCK(sc); return (0); } /* * Device resume routine. re-enable busmastering, and restart the interface if * appropriate. */ static int fxp_resume(device_t dev) { struct fxp_softc *sc = device_get_softc(dev); struct ifnet *ifp = sc->ifp; int pmc; uint16_t pmstat; FXP_LOCK(sc); if (pci_find_extcap(sc->dev, PCIY_PMG, &pmc) == 0) { sc->flags &= ~FXP_FLAG_WOL; pmstat = pci_read_config(sc->dev, pmc + PCIR_POWER_STATUS, 2); /* Disable PME and clear PME status. */ pmstat &= ~PCIM_PSTAT_PMEENABLE; pci_write_config(sc->dev, pmc + PCIR_POWER_STATUS, pmstat, 2); if ((sc->flags & FXP_FLAG_WOLCAP) != 0) CSR_WRITE_1(sc, FXP_CSR_PMDR, CSR_READ_1(sc, FXP_CSR_PMDR)); } CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET); DELAY(10); /* reinitialize interface if necessary */ if (ifp->if_flags & IFF_UP) fxp_init_body(sc, 1); sc->suspended = 0; FXP_UNLOCK(sc); return (0); } static void fxp_eeprom_shiftin(struct fxp_softc *sc, int data, int length) { uint16_t reg; int x; /* * Shift in data. */ for (x = 1 << (length - 1); x; x >>= 1) { if (data & x) reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI; else reg = FXP_EEPROM_EECS; CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); DELAY(1); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg | FXP_EEPROM_EESK); DELAY(1); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); DELAY(1); } } /* * Read from the serial EEPROM. Basically, you manually shift in * the read opcode (one bit at a time) and then shift in the address, * and then you shift out the data (all of this one bit at a time). * The word size is 16 bits, so you have to provide the address for * every 16 bits of data. */ static uint16_t fxp_eeprom_getword(struct fxp_softc *sc, int offset, int autosize) { uint16_t reg, data; int x; CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS); /* * Shift in read opcode. */ fxp_eeprom_shiftin(sc, FXP_EEPROM_OPC_READ, 3); /* * Shift in address. */ data = 0; for (x = 1 << (sc->eeprom_size - 1); x; x >>= 1) { if (offset & x) reg = FXP_EEPROM_EECS | FXP_EEPROM_EEDI; else reg = FXP_EEPROM_EECS; CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); DELAY(1); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg | FXP_EEPROM_EESK); DELAY(1); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); DELAY(1); reg = CSR_READ_2(sc, FXP_CSR_EEPROMCONTROL) & FXP_EEPROM_EEDO; data++; if (autosize && reg == 0) { sc->eeprom_size = data; break; } } /* * Shift out data. */ data = 0; reg = FXP_EEPROM_EECS; for (x = 1 << 15; x; x >>= 1) { CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg | FXP_EEPROM_EESK); DELAY(1); if (CSR_READ_2(sc, FXP_CSR_EEPROMCONTROL) & FXP_EEPROM_EEDO) data |= x; CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, reg); DELAY(1); } CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0); DELAY(1); return (data); } static void fxp_eeprom_putword(struct fxp_softc *sc, int offset, uint16_t data) { int i; /* * Erase/write enable. */ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS); fxp_eeprom_shiftin(sc, 0x4, 3); fxp_eeprom_shiftin(sc, 0x03 << (sc->eeprom_size - 2), sc->eeprom_size); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0); DELAY(1); /* * Shift in write opcode, address, data. */ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS); fxp_eeprom_shiftin(sc, FXP_EEPROM_OPC_WRITE, 3); fxp_eeprom_shiftin(sc, offset, sc->eeprom_size); fxp_eeprom_shiftin(sc, data, 16); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0); DELAY(1); /* * Wait for EEPROM to finish up. */ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS); DELAY(1); for (i = 0; i < 1000; i++) { if (CSR_READ_2(sc, FXP_CSR_EEPROMCONTROL) & FXP_EEPROM_EEDO) break; DELAY(50); } CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0); DELAY(1); /* * Erase/write disable. */ CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, FXP_EEPROM_EECS); fxp_eeprom_shiftin(sc, 0x4, 3); fxp_eeprom_shiftin(sc, 0, sc->eeprom_size); CSR_WRITE_2(sc, FXP_CSR_EEPROMCONTROL, 0); DELAY(1); } /* * From NetBSD: * * Figure out EEPROM size. * * 559's can have either 64-word or 256-word EEPROMs, the 558 * datasheet only talks about 64-word EEPROMs, and the 557 datasheet * talks about the existance of 16 to 256 word EEPROMs. * * The only known sizes are 64 and 256, where the 256 version is used * by CardBus cards to store CIS information. * * The address is shifted in msb-to-lsb, and after the last * address-bit the EEPROM is supposed to output a `dummy zero' bit, * after which follows the actual data. We try to detect this zero, by * probing the data-out bit in the EEPROM control register just after * having shifted in a bit. If the bit is zero, we assume we've * shifted enough address bits. The data-out should be tri-state, * before this, which should translate to a logical one. */ static void fxp_autosize_eeprom(struct fxp_softc *sc) { /* guess maximum size of 256 words */ sc->eeprom_size = 8; /* autosize */ (void) fxp_eeprom_getword(sc, 0, 1); } static void fxp_read_eeprom(struct fxp_softc *sc, u_short *data, int offset, int words) { int i; for (i = 0; i < words; i++) data[i] = fxp_eeprom_getword(sc, offset + i, 0); } static void fxp_write_eeprom(struct fxp_softc *sc, u_short *data, int offset, int words) { int i; for (i = 0; i < words; i++) fxp_eeprom_putword(sc, offset + i, data[i]); } static void fxp_load_eeprom(struct fxp_softc *sc) { int i; uint16_t cksum; fxp_read_eeprom(sc, sc->eeprom, 0, 1 << sc->eeprom_size); cksum = 0; for (i = 0; i < (1 << sc->eeprom_size) - 1; i++) cksum += sc->eeprom[i]; cksum = 0xBABA - cksum; if (cksum != sc->eeprom[(1 << sc->eeprom_size) - 1]) device_printf(sc->dev, "EEPROM checksum mismatch! (0x%04x -> 0x%04x)\n", cksum, sc->eeprom[(1 << sc->eeprom_size) - 1]); } /* * Grab the softc lock and call the real fxp_start_body() routine */ static void fxp_start(struct ifnet *ifp) { struct fxp_softc *sc = ifp->if_softc; FXP_LOCK(sc); fxp_start_body(ifp); FXP_UNLOCK(sc); } /* * Start packet transmission on the interface. * This routine must be called with the softc lock held, and is an * internal entry point only. */ static void fxp_start_body(struct ifnet *ifp) { struct fxp_softc *sc = ifp->if_softc; struct mbuf *mb_head; int txqueued; FXP_LOCK_ASSERT(sc, MA_OWNED); if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING) return; if (sc->tx_queued > FXP_NTXCB_HIWAT) fxp_txeof(sc); /* * We're finished if there is nothing more to add to the list or if * we're all filled up with buffers to transmit. * NOTE: One TxCB is reserved to guarantee that fxp_mc_setup() can add * a NOP command when needed. */ txqueued = 0; while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd) && sc->tx_queued < FXP_NTXCB - 1) { /* * Grab a packet to transmit. */ IFQ_DRV_DEQUEUE(&ifp->if_snd, mb_head); if (mb_head == NULL) break; if (fxp_encap(sc, &mb_head)) { if (mb_head == NULL) break; IFQ_DRV_PREPEND(&ifp->if_snd, mb_head); ifp->if_drv_flags |= IFF_DRV_OACTIVE; } txqueued++; /* * Pass packet to bpf if there is a listener. */ BPF_MTAP(ifp, mb_head); } /* * We're finished. If we added to the list, issue a RESUME to get DMA * going again if suspended. */ if (txqueued > 0) { bus_dmamap_sync(sc->cbl_tag, sc->cbl_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); fxp_scb_wait(sc); fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_RESUME); /* * Set a 5 second timer just in case we don't hear * from the card again. */ sc->watchdog_timer = 5; } } static int fxp_encap(struct fxp_softc *sc, struct mbuf **m_head) { struct ifnet *ifp; struct mbuf *m; struct fxp_tx *txp; struct fxp_cb_tx *cbp; struct tcphdr *tcp; bus_dma_segment_t segs[FXP_NTXSEG]; int error, i, nseg, tcp_payload; FXP_LOCK_ASSERT(sc, MA_OWNED); ifp = sc->ifp; tcp_payload = 0; tcp = NULL; /* * Get pointer to next available tx desc. */ txp = sc->fxp_desc.tx_last->tx_next; /* * A note in Appendix B of the Intel 8255x 10/100 Mbps * Ethernet Controller Family Open Source Software * Developer Manual says: * Using software parsing is only allowed with legal * TCP/IP or UDP/IP packets. * ... * For all other datagrams, hardware parsing must * be used. * Software parsing appears to truncate ICMP and * fragmented UDP packets that contain one to three * bytes in the second (and final) mbuf of the packet. */ if (sc->flags & FXP_FLAG_EXT_RFA) txp->tx_cb->ipcb_ip_activation_high = FXP_IPCB_HARDWAREPARSING_ENABLE; m = *m_head; if (m->m_pkthdr.csum_flags & CSUM_TSO) { /* * 82550/82551 requires ethernet/IP/TCP headers must be * contained in the first active transmit buffer. */ struct ether_header *eh; struct ip *ip; uint32_t ip_off, poff; if (M_WRITABLE(*m_head) == 0) { /* Get a writable copy. */ m = m_dup(*m_head, M_DONTWAIT); m_freem(*m_head); if (m == NULL) { *m_head = NULL; return (ENOBUFS); } *m_head = m; } ip_off = sizeof(struct ether_header); m = m_pullup(*m_head, ip_off); if (m == NULL) { *m_head = NULL; return (ENOBUFS); } eh = mtod(m, struct ether_header *); /* Check the existence of VLAN tag. */ if (eh->ether_type == htons(ETHERTYPE_VLAN)) { ip_off = sizeof(struct ether_vlan_header); m = m_pullup(m, ip_off); if (m == NULL) { *m_head = NULL; return (ENOBUFS); } } m = m_pullup(m, ip_off + sizeof(struct ip)); if (m == NULL) { *m_head = NULL; return (ENOBUFS); } ip = (struct ip *)(mtod(m, char *) + ip_off); poff = ip_off + (ip->ip_hl << 2); m = m_pullup(m, poff + sizeof(struct tcphdr)); if (m == NULL) { *m_head = NULL; return (ENOBUFS); } tcp = (struct tcphdr *)(mtod(m, char *) + poff); m = m_pullup(m, poff + (tcp->th_off << 2)); if (m == NULL) { *m_head = NULL; return (ENOBUFS); } /* * Since 82550/82551 doesn't modify IP length and pseudo * checksum in the first frame driver should compute it. */ ip = (struct ip *)(mtod(m, char *) + ip_off); tcp = (struct tcphdr *)(mtod(m, char *) + poff); ip->ip_sum = 0; ip->ip_len = htons(m->m_pkthdr.tso_segsz + (ip->ip_hl << 2) + (tcp->th_off << 2)); tcp->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, htons(IPPROTO_TCP + (tcp->th_off << 2) + m->m_pkthdr.tso_segsz)); /* Compute total TCP payload. */ tcp_payload = m->m_pkthdr.len - ip_off - (ip->ip_hl << 2); tcp_payload -= tcp->th_off << 2; *m_head = m; } else if (m->m_pkthdr.csum_flags & FXP_CSUM_FEATURES) { /* * Deal with TCP/IP checksum offload. Note that * in order for TCP checksum offload to work, * the pseudo header checksum must have already * been computed and stored in the checksum field * in the TCP header. The stack should have * already done this for us. */ txp->tx_cb->ipcb_ip_schedule = FXP_IPCB_TCPUDP_CHECKSUM_ENABLE; if (m->m_pkthdr.csum_flags & CSUM_TCP) txp->tx_cb->ipcb_ip_schedule |= FXP_IPCB_TCP_PACKET; #ifdef FXP_IP_CSUM_WAR /* * XXX The 82550 chip appears to have trouble * dealing with IP header checksums in very small * datagrams, namely fragments from 1 to 3 bytes * in size. For example, say you want to transmit * a UDP packet of 1473 bytes. The packet will be * fragmented over two IP datagrams, the latter * containing only one byte of data. The 82550 will * botch the header checksum on the 1-byte fragment. * As long as the datagram contains 4 or more bytes * of data, you're ok. * * The following code attempts to work around this * problem: if the datagram is less than 38 bytes * in size (14 bytes ether header, 20 bytes IP header, * plus 4 bytes of data), we punt and compute the IP * header checksum by hand. This workaround doesn't * work very well, however, since it can be fooled * by things like VLAN tags and IP options that make * the header sizes/offsets vary. */ if (m->m_pkthdr.csum_flags & CSUM_IP) { if (m->m_pkthdr.len < 38) { struct ip *ip; m->m_data += ETHER_HDR_LEN; ip = mtod(m, struct ip *); ip->ip_sum = in_cksum(m, ip->ip_hl << 2); m->m_data -= ETHER_HDR_LEN; m->m_pkthdr.csum_flags &= ~CSUM_IP; } else { txp->tx_cb->ipcb_ip_activation_high = FXP_IPCB_HARDWAREPARSING_ENABLE; txp->tx_cb->ipcb_ip_schedule |= FXP_IPCB_IP_CHECKSUM_ENABLE; } } #endif } error = bus_dmamap_load_mbuf_sg(sc->fxp_txmtag, txp->tx_map, *m_head, segs, &nseg, 0); if (error == EFBIG) { m = m_collapse(*m_head, M_DONTWAIT, sc->maxtxseg); if (m == NULL) { m_freem(*m_head); *m_head = NULL; return (ENOMEM); } *m_head = m; error = bus_dmamap_load_mbuf_sg(sc->fxp_txmtag, txp->tx_map, *m_head, segs, &nseg, 0); if (error != 0) { m_freem(*m_head); *m_head = NULL; return (ENOMEM); } } else if (error != 0) return (error); if (nseg == 0) { m_freem(*m_head); *m_head = NULL; return (EIO); } KASSERT(nseg <= sc->maxtxseg, ("too many DMA segments")); bus_dmamap_sync(sc->fxp_txmtag, txp->tx_map, BUS_DMASYNC_PREWRITE); cbp = txp->tx_cb; for (i = 0; i < nseg; i++) { /* * If this is an 82550/82551, then we're using extended * TxCBs _and_ we're using checksum offload. This means * that the TxCB is really an IPCB. One major difference * between the two is that with plain extended TxCBs, * the bottom half of the TxCB contains two entries from * the TBD array, whereas IPCBs contain just one entry: * one entry (8 bytes) has been sacrificed for the TCP/IP * checksum offload control bits. So to make things work * right, we have to start filling in the TBD array * starting from a different place depending on whether * the chip is an 82550/82551 or not. */ if (sc->flags & FXP_FLAG_EXT_RFA) { cbp->tbd[i + 1].tb_addr = htole32(segs[i].ds_addr); cbp->tbd[i + 1].tb_size = htole32(segs[i].ds_len); } else { cbp->tbd[i].tb_addr = htole32(segs[i].ds_addr); cbp->tbd[i].tb_size = htole32(segs[i].ds_len); } } if (sc->flags & FXP_FLAG_EXT_RFA) { /* Configure dynamic TBD for 82550/82551. */ cbp->tbd_number = 0xFF; cbp->tbd[nseg].tb_size |= htole32(0x8000); } else cbp->tbd_number = nseg; /* Configure TSO. */ if (m->m_pkthdr.csum_flags & CSUM_TSO) { cbp->tbd[-1].tb_size = htole32(m->m_pkthdr.tso_segsz << 16); cbp->tbd[1].tb_size |= htole32(tcp_payload << 16); cbp->ipcb_ip_schedule |= FXP_IPCB_LARGESEND_ENABLE | FXP_IPCB_IP_CHECKSUM_ENABLE | FXP_IPCB_TCP_PACKET | FXP_IPCB_TCPUDP_CHECKSUM_ENABLE; } /* Configure VLAN hardware tag insertion. */ if ((m->m_flags & M_VLANTAG) != 0) { cbp->ipcb_vlan_id = htons(m->m_pkthdr.ether_vtag); txp->tx_cb->ipcb_ip_activation_high |= FXP_IPCB_INSERTVLAN_ENABLE; } txp->tx_mbuf = m; txp->tx_cb->cb_status = 0; txp->tx_cb->byte_count = 0; if (sc->tx_queued != FXP_CXINT_THRESH - 1) txp->tx_cb->cb_command = htole16(sc->tx_cmd | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S); else txp->tx_cb->cb_command = htole16(sc->tx_cmd | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S | FXP_CB_COMMAND_I); if ((m->m_pkthdr.csum_flags & CSUM_TSO) == 0) txp->tx_cb->tx_threshold = tx_threshold; /* * Advance the end of list forward. */ sc->fxp_desc.tx_last->tx_cb->cb_command &= htole16(~FXP_CB_COMMAND_S); sc->fxp_desc.tx_last = txp; /* * Advance the beginning of the list forward if there are * no other packets queued (when nothing is queued, tx_first * sits on the last TxCB that was sent out). */ if (sc->tx_queued == 0) sc->fxp_desc.tx_first = txp; sc->tx_queued++; return (0); } #ifdef DEVICE_POLLING static poll_handler_t fxp_poll; static int fxp_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) { struct fxp_softc *sc = ifp->if_softc; uint8_t statack; int rx_npkts = 0; FXP_LOCK(sc); if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { FXP_UNLOCK(sc); return (rx_npkts); } statack = FXP_SCB_STATACK_CXTNO | FXP_SCB_STATACK_CNA | FXP_SCB_STATACK_FR; if (cmd == POLL_AND_CHECK_STATUS) { uint8_t tmp; tmp = CSR_READ_1(sc, FXP_CSR_SCB_STATACK); if (tmp == 0xff || tmp == 0) { FXP_UNLOCK(sc); return (rx_npkts); /* nothing to do */ } tmp &= ~statack; /* ack what we can */ if (tmp != 0) CSR_WRITE_1(sc, FXP_CSR_SCB_STATACK, tmp); statack |= tmp; } rx_npkts = fxp_intr_body(sc, ifp, statack, count); FXP_UNLOCK(sc); return (rx_npkts); } #endif /* DEVICE_POLLING */ /* * Process interface interrupts. */ static void fxp_intr(void *xsc) { struct fxp_softc *sc = xsc; struct ifnet *ifp = sc->ifp; uint8_t statack; FXP_LOCK(sc); if (sc->suspended) { FXP_UNLOCK(sc); return; } #ifdef DEVICE_POLLING if (ifp->if_capenable & IFCAP_POLLING) { FXP_UNLOCK(sc); return; } #endif while ((statack = CSR_READ_1(sc, FXP_CSR_SCB_STATACK)) != 0) { /* * It should not be possible to have all bits set; the * FXP_SCB_INTR_SWI bit always returns 0 on a read. If * all bits are set, this may indicate that the card has * been physically ejected, so ignore it. */ if (statack == 0xff) { FXP_UNLOCK(sc); return; } /* * First ACK all the interrupts in this pass. */ CSR_WRITE_1(sc, FXP_CSR_SCB_STATACK, statack); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) fxp_intr_body(sc, ifp, statack, -1); } FXP_UNLOCK(sc); } static void fxp_txeof(struct fxp_softc *sc) { struct ifnet *ifp; struct fxp_tx *txp; ifp = sc->ifp; bus_dmamap_sync(sc->cbl_tag, sc->cbl_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); for (txp = sc->fxp_desc.tx_first; sc->tx_queued && (le16toh(txp->tx_cb->cb_status) & FXP_CB_STATUS_C) != 0; txp = txp->tx_next) { if (txp->tx_mbuf != NULL) { bus_dmamap_sync(sc->fxp_txmtag, txp->tx_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->fxp_txmtag, txp->tx_map); m_freem(txp->tx_mbuf); txp->tx_mbuf = NULL; /* clear this to reset csum offload bits */ txp->tx_cb->tbd[0].tb_addr = 0; } sc->tx_queued--; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } sc->fxp_desc.tx_first = txp; bus_dmamap_sync(sc->cbl_tag, sc->cbl_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); if (sc->tx_queued == 0) sc->watchdog_timer = 0; } static void fxp_rxcsum(struct fxp_softc *sc, struct ifnet *ifp, struct mbuf *m, uint16_t status, int pos) { struct ether_header *eh; struct ip *ip; struct udphdr *uh; int32_t hlen, len, pktlen, temp32; uint16_t csum, *opts; if ((sc->flags & FXP_FLAG_82559_RXCSUM) == 0) { if ((status & FXP_RFA_STATUS_PARSE) != 0) { if (status & FXP_RFDX_CS_IP_CSUM_BIT_VALID) m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; if (status & FXP_RFDX_CS_IP_CSUM_VALID) m->m_pkthdr.csum_flags |= CSUM_IP_VALID; if ((status & FXP_RFDX_CS_TCPUDP_CSUM_BIT_VALID) && (status & FXP_RFDX_CS_TCPUDP_CSUM_VALID)) { m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; } } return; } pktlen = m->m_pkthdr.len; if (pktlen < sizeof(struct ether_header) + sizeof(struct ip)) return; eh = mtod(m, struct ether_header *); if (eh->ether_type != htons(ETHERTYPE_IP)) return; ip = (struct ip *)(eh + 1); if (ip->ip_v != IPVERSION) return; hlen = ip->ip_hl << 2; pktlen -= sizeof(struct ether_header); if (hlen < sizeof(struct ip)) return; if (ntohs(ip->ip_len) < hlen) return; if (ntohs(ip->ip_len) != pktlen) return; if (ip->ip_off & htons(IP_MF | IP_OFFMASK)) return; /* can't handle fragmented packet */ switch (ip->ip_p) { case IPPROTO_TCP: if (pktlen < (hlen + sizeof(struct tcphdr))) return; break; case IPPROTO_UDP: if (pktlen < (hlen + sizeof(struct udphdr))) return; uh = (struct udphdr *)((caddr_t)ip + hlen); if (uh->uh_sum == 0) return; /* no checksum */ break; default: return; } /* Extract computed checksum. */ csum = be16dec(mtod(m, char *) + pos); /* checksum fixup for IP options */ len = hlen - sizeof(struct ip); if (len > 0) { opts = (uint16_t *)(ip + 1); for (; len > 0; len -= sizeof(uint16_t), opts++) { temp32 = csum - *opts; temp32 = (temp32 >> 16) + (temp32 & 65535); csum = temp32 & 65535; } } m->m_pkthdr.csum_flags |= CSUM_DATA_VALID; m->m_pkthdr.csum_data = csum; } static int fxp_intr_body(struct fxp_softc *sc, struct ifnet *ifp, uint8_t statack, int count) { struct mbuf *m; struct fxp_rx *rxp; struct fxp_rfa *rfa; int rnr = (statack & FXP_SCB_STATACK_RNR) ? 1 : 0; int rx_npkts; uint16_t status; rx_npkts = 0; FXP_LOCK_ASSERT(sc, MA_OWNED); if (rnr) sc->rnr++; #ifdef DEVICE_POLLING /* Pick up a deferred RNR condition if `count' ran out last time. */ if (sc->flags & FXP_FLAG_DEFERRED_RNR) { sc->flags &= ~FXP_FLAG_DEFERRED_RNR; rnr = 1; } #endif /* * Free any finished transmit mbuf chains. * * Handle the CNA event likt a CXTNO event. It used to * be that this event (control unit not ready) was not * encountered, but it is now with the SMPng modifications. * The exact sequence of events that occur when the interface * is brought up are different now, and if this event * goes unhandled, the configuration/rxfilter setup sequence * can stall for several seconds. The result is that no * packets go out onto the wire for about 5 to 10 seconds * after the interface is ifconfig'ed for the first time. */ if (statack & (FXP_SCB_STATACK_CXTNO | FXP_SCB_STATACK_CNA)) fxp_txeof(sc); /* * Try to start more packets transmitting. */ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) fxp_start_body(ifp); /* * Just return if nothing happened on the receive side. */ if (!rnr && (statack & FXP_SCB_STATACK_FR) == 0) return (rx_npkts); /* * Process receiver interrupts. If a no-resource (RNR) * condition exists, get whatever packets we can and * re-start the receiver. * * When using polling, we do not process the list to completion, * so when we get an RNR interrupt we must defer the restart * until we hit the last buffer with the C bit set. * If we run out of cycles and rfa_headm has the C bit set, * record the pending RNR in the FXP_FLAG_DEFERRED_RNR flag so * that the info will be used in the subsequent polling cycle. */ for (;;) { rxp = sc->fxp_desc.rx_head; m = rxp->rx_mbuf; rfa = (struct fxp_rfa *)(m->m_ext.ext_buf + RFA_ALIGNMENT_FUDGE); bus_dmamap_sync(sc->fxp_rxmtag, rxp->rx_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); #ifdef DEVICE_POLLING /* loop at most count times if count >=0 */ if (count >= 0 && count-- == 0) { if (rnr) { /* Defer RNR processing until the next time. */ sc->flags |= FXP_FLAG_DEFERRED_RNR; rnr = 0; } break; } #endif /* DEVICE_POLLING */ status = le16toh(rfa->rfa_status); if ((status & FXP_RFA_STATUS_C) == 0) break; if ((status & FXP_RFA_STATUS_RNR) != 0) rnr++; /* * Advance head forward. */ sc->fxp_desc.rx_head = rxp->rx_next; /* * Add a new buffer to the receive chain. * If this fails, the old buffer is recycled * instead. */ if (fxp_new_rfabuf(sc, rxp) == 0) { int total_len; /* * Fetch packet length (the top 2 bits of * actual_size are flags set by the controller * upon completion), and drop the packet in case * of bogus length or CRC errors. */ total_len = le16toh(rfa->actual_size) & 0x3fff; if ((sc->flags & FXP_FLAG_82559_RXCSUM) != 0 && (ifp->if_capenable & IFCAP_RXCSUM) != 0) { /* Adjust for appended checksum bytes. */ total_len -= 2; } if (total_len < (int)sizeof(struct ether_header) || total_len > (MCLBYTES - RFA_ALIGNMENT_FUDGE - sc->rfa_size) || status & (FXP_RFA_STATUS_CRC | FXP_RFA_STATUS_ALIGN | FXP_RFA_STATUS_OVERRUN)) { m_freem(m); fxp_add_rfabuf(sc, rxp); continue; } m->m_pkthdr.len = m->m_len = total_len; m->m_pkthdr.rcvif = ifp; /* Do IP checksum checking. */ if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) fxp_rxcsum(sc, ifp, m, status, total_len); if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0 && (status & FXP_RFA_STATUS_VLAN) != 0) { m->m_pkthdr.ether_vtag = ntohs(rfa->rfax_vlan_id); m->m_flags |= M_VLANTAG; } /* * Drop locks before calling if_input() since it * may re-enter fxp_start() in the netisr case. * This would result in a lock reversal. Better * performance might be obtained by chaining all * packets received, dropping the lock, and then * calling if_input() on each one. */ FXP_UNLOCK(sc); (*ifp->if_input)(ifp, m); FXP_LOCK(sc); rx_npkts++; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return (rx_npkts); } else { /* Reuse RFA and loaded DMA map. */ ifp->if_iqdrops++; fxp_discard_rfabuf(sc, rxp); } fxp_add_rfabuf(sc, rxp); } if (rnr) { fxp_scb_wait(sc); CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, sc->fxp_desc.rx_head->rx_addr); fxp_scb_cmd(sc, FXP_SCB_COMMAND_RU_START); } return (rx_npkts); } static void fxp_update_stats(struct fxp_softc *sc) { struct ifnet *ifp = sc->ifp; struct fxp_stats *sp = sc->fxp_stats; struct fxp_hwstats *hsp; uint32_t *status; FXP_LOCK_ASSERT(sc, MA_OWNED); bus_dmamap_sync(sc->fxp_stag, sc->fxp_smap, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); /* Update statistical counters. */ if (sc->revision >= FXP_REV_82559_A0) status = &sp->completion_status; else if (sc->revision >= FXP_REV_82558_A4) status = (uint32_t *)&sp->tx_tco; else status = &sp->tx_pause; if (*status == htole32(FXP_STATS_DR_COMPLETE)) { hsp = &sc->fxp_hwstats; hsp->tx_good += le32toh(sp->tx_good); hsp->tx_maxcols += le32toh(sp->tx_maxcols); hsp->tx_latecols += le32toh(sp->tx_latecols); hsp->tx_underruns += le32toh(sp->tx_underruns); hsp->tx_lostcrs += le32toh(sp->tx_lostcrs); hsp->tx_deffered += le32toh(sp->tx_deffered); hsp->tx_single_collisions += le32toh(sp->tx_single_collisions); hsp->tx_multiple_collisions += le32toh(sp->tx_multiple_collisions); hsp->tx_total_collisions += le32toh(sp->tx_total_collisions); hsp->rx_good += le32toh(sp->rx_good); hsp->rx_crc_errors += le32toh(sp->rx_crc_errors); hsp->rx_alignment_errors += le32toh(sp->rx_alignment_errors); hsp->rx_rnr_errors += le32toh(sp->rx_rnr_errors); hsp->rx_overrun_errors += le32toh(sp->rx_overrun_errors); hsp->rx_cdt_errors += le32toh(sp->rx_cdt_errors); hsp->rx_shortframes += le32toh(sp->rx_shortframes); hsp->tx_pause += le32toh(sp->tx_pause); hsp->rx_pause += le32toh(sp->rx_pause); hsp->rx_controls += le32toh(sp->rx_controls); hsp->tx_tco += le16toh(sp->tx_tco); hsp->rx_tco += le16toh(sp->rx_tco); ifp->if_opackets += le32toh(sp->tx_good); ifp->if_collisions += le32toh(sp->tx_total_collisions); if (sp->rx_good) { ifp->if_ipackets += le32toh(sp->rx_good); sc->rx_idle_secs = 0; } else if (sc->flags & FXP_FLAG_RXBUG) { /* * Receiver's been idle for another second. */ sc->rx_idle_secs++; } ifp->if_ierrors += le32toh(sp->rx_crc_errors) + le32toh(sp->rx_alignment_errors) + le32toh(sp->rx_rnr_errors) + le32toh(sp->rx_overrun_errors); /* * If any transmit underruns occured, bump up the transmit * threshold by another 512 bytes (64 * 8). */ if (sp->tx_underruns) { ifp->if_oerrors += le32toh(sp->tx_underruns); if (tx_threshold < 192) tx_threshold += 64; } *status = 0; bus_dmamap_sync(sc->fxp_stag, sc->fxp_smap, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); } } /* * Update packet in/out/collision statistics. The i82557 doesn't * allow you to access these counters without doing a fairly * expensive DMA to get _all_ of the statistics it maintains, so * we do this operation here only once per second. The statistics * counters in the kernel are updated from the previous dump-stats * DMA and then a new dump-stats DMA is started. The on-chip * counters are zeroed when the DMA completes. If we can't start * the DMA immediately, we don't wait - we just prepare to read * them again next time. */ static void fxp_tick(void *xsc) { struct fxp_softc *sc = xsc; struct ifnet *ifp = sc->ifp; FXP_LOCK_ASSERT(sc, MA_OWNED); /* Update statistical counters. */ fxp_update_stats(sc); /* * Release any xmit buffers that have completed DMA. This isn't * strictly necessary to do here, but it's advantagous for mbufs * with external storage to be released in a timely manner rather * than being defered for a potentially long time. This limits * the delay to a maximum of one second. */ fxp_txeof(sc); /* * If we haven't received any packets in FXP_MAC_RX_IDLE seconds, * then assume the receiver has locked up and attempt to clear * the condition by reprogramming the multicast filter. This is * a work-around for a bug in the 82557 where the receiver locks * up if it gets certain types of garbage in the syncronization * bits prior to the packet header. This bug is supposed to only * occur in 10Mbps mode, but has been seen to occur in 100Mbps * mode as well (perhaps due to a 10/100 speed transition). */ if (sc->rx_idle_secs > FXP_MAX_RX_IDLE) { sc->rx_idle_secs = 0; - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; fxp_init_body(sc, 1); + } return; } /* * If there is no pending command, start another stats * dump. Otherwise punt for now. */ if (CSR_READ_1(sc, FXP_CSR_SCB_COMMAND) == 0) { /* * Start another stats dump. */ fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_DUMPRESET); } if (sc->miibus != NULL) mii_tick(device_get_softc(sc->miibus)); /* * Check that chip hasn't hung. */ fxp_watchdog(sc); /* * Schedule another timeout one second from now. */ callout_reset(&sc->stat_ch, hz, fxp_tick, sc); } /* * Stop the interface. Cancels the statistics updater and resets * the interface. */ static void fxp_stop(struct fxp_softc *sc) { struct ifnet *ifp = sc->ifp; struct fxp_tx *txp; int i; ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); sc->watchdog_timer = 0; /* * Cancel stats updater. */ callout_stop(&sc->stat_ch); /* * Preserve PCI configuration, configure, IA/multicast * setup and put RU and CU into idle state. */ CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SELECTIVE_RESET); DELAY(50); /* Disable interrupts. */ CSR_WRITE_1(sc, FXP_CSR_SCB_INTRCNTL, FXP_SCB_INTR_DISABLE); fxp_update_stats(sc); /* * Release any xmit buffers. */ txp = sc->fxp_desc.tx_list; if (txp != NULL) { for (i = 0; i < FXP_NTXCB; i++) { if (txp[i].tx_mbuf != NULL) { bus_dmamap_sync(sc->fxp_txmtag, txp[i].tx_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->fxp_txmtag, txp[i].tx_map); m_freem(txp[i].tx_mbuf); txp[i].tx_mbuf = NULL; /* clear this to reset csum offload bits */ txp[i].tx_cb->tbd[0].tb_addr = 0; } } } bus_dmamap_sync(sc->cbl_tag, sc->cbl_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); sc->tx_queued = 0; } /* * Watchdog/transmission transmit timeout handler. Called when a * transmission is started on the interface, but no interrupt is * received before the timeout. This usually indicates that the * card has wedged for some reason. */ static void fxp_watchdog(struct fxp_softc *sc) { FXP_LOCK_ASSERT(sc, MA_OWNED); if (sc->watchdog_timer == 0 || --sc->watchdog_timer) return; device_printf(sc->dev, "device timeout\n"); sc->ifp->if_oerrors++; + sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING; fxp_init_body(sc, 1); } /* * Acquire locks and then call the real initialization function. This * is necessary because ether_ioctl() calls if_init() and this would * result in mutex recursion if the mutex was held. */ static void fxp_init(void *xsc) { struct fxp_softc *sc = xsc; FXP_LOCK(sc); fxp_init_body(sc, 1); FXP_UNLOCK(sc); } /* * Perform device initialization. This routine must be called with the * softc lock held. */ static void fxp_init_body(struct fxp_softc *sc, int setmedia) { struct ifnet *ifp = sc->ifp; struct mii_data *mii; struct fxp_cb_config *cbp; struct fxp_cb_ias *cb_ias; struct fxp_cb_tx *tcbp; struct fxp_tx *txp; int i, prm; FXP_LOCK_ASSERT(sc, MA_OWNED); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + return; + /* * Cancel any pending I/O */ fxp_stop(sc); /* * Issue software reset, which also unloads the microcode. */ sc->flags &= ~FXP_FLAG_UCODE; CSR_WRITE_4(sc, FXP_CSR_PORT, FXP_PORT_SOFTWARE_RESET); DELAY(50); prm = (ifp->if_flags & IFF_PROMISC) ? 1 : 0; /* * Initialize base of CBL and RFA memory. Loading with zero * sets it up for regular linear addressing. */ CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, 0); fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_BASE); fxp_scb_wait(sc); fxp_scb_cmd(sc, FXP_SCB_COMMAND_RU_BASE); /* * Initialize base of dump-stats buffer. */ fxp_scb_wait(sc); bzero(sc->fxp_stats, sizeof(struct fxp_stats)); bus_dmamap_sync(sc->fxp_stag, sc->fxp_smap, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, sc->stats_addr); fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_DUMP_ADR); /* * Attempt to load microcode if requested. * For ICH based controllers do not load microcode. */ if (sc->ident->ich == 0) { if (ifp->if_flags & IFF_LINK0 && (sc->flags & FXP_FLAG_UCODE) == 0) fxp_load_ucode(sc); } /* * Set IFF_ALLMULTI status. It's needed in configure action * command. */ fxp_mc_addrs(sc); /* * We temporarily use memory that contains the TxCB list to * construct the config CB. The TxCB list memory is rebuilt * later. */ cbp = (struct fxp_cb_config *)sc->fxp_desc.cbl_list; /* * This bcopy is kind of disgusting, but there are a bunch of must be * zero and must be one bits in this structure and this is the easiest * way to initialize them all to proper values. */ bcopy(fxp_cb_config_template, cbp, sizeof(fxp_cb_config_template)); cbp->cb_status = 0; cbp->cb_command = htole16(FXP_CB_COMMAND_CONFIG | FXP_CB_COMMAND_EL); cbp->link_addr = 0xffffffff; /* (no) next command */ cbp->byte_count = sc->flags & FXP_FLAG_EXT_RFA ? 32 : 22; cbp->rx_fifo_limit = 8; /* rx fifo threshold (32 bytes) */ cbp->tx_fifo_limit = 0; /* tx fifo threshold (0 bytes) */ cbp->adaptive_ifs = 0; /* (no) adaptive interframe spacing */ cbp->mwi_enable = sc->flags & FXP_FLAG_MWI_ENABLE ? 1 : 0; cbp->type_enable = 0; /* actually reserved */ cbp->read_align_en = sc->flags & FXP_FLAG_READ_ALIGN ? 1 : 0; cbp->end_wr_on_cl = sc->flags & FXP_FLAG_WRITE_ALIGN ? 1 : 0; cbp->rx_dma_bytecount = 0; /* (no) rx DMA max */ cbp->tx_dma_bytecount = 0; /* (no) tx DMA max */ cbp->dma_mbce = 0; /* (disable) dma max counters */ cbp->late_scb = 0; /* (don't) defer SCB update */ cbp->direct_dma_dis = 1; /* disable direct rcv dma mode */ cbp->tno_int_or_tco_en =0; /* (disable) tx not okay interrupt */ cbp->ci_int = 1; /* interrupt on CU idle */ cbp->ext_txcb_dis = sc->flags & FXP_FLAG_EXT_TXCB ? 0 : 1; cbp->ext_stats_dis = 1; /* disable extended counters */ cbp->keep_overrun_rx = 0; /* don't pass overrun frames to host */ cbp->save_bf = sc->flags & FXP_FLAG_SAVE_BAD ? 1 : prm; cbp->disc_short_rx = !prm; /* discard short packets */ cbp->underrun_retry = 1; /* retry mode (once) on DMA underrun */ cbp->two_frames = 0; /* do not limit FIFO to 2 frames */ cbp->dyn_tbd = sc->flags & FXP_FLAG_EXT_RFA ? 1 : 0; cbp->ext_rfa = sc->flags & FXP_FLAG_EXT_RFA ? 1 : 0; cbp->mediatype = sc->flags & FXP_FLAG_SERIAL_MEDIA ? 0 : 1; cbp->csma_dis = 0; /* (don't) disable link */ cbp->tcp_udp_cksum = ((sc->flags & FXP_FLAG_82559_RXCSUM) != 0 && (ifp->if_capenable & IFCAP_RXCSUM) != 0) ? 1 : 0; cbp->vlan_tco = 0; /* (don't) enable vlan wakeup */ cbp->link_wake_en = 0; /* (don't) assert PME# on link change */ cbp->arp_wake_en = 0; /* (don't) assert PME# on arp */ cbp->mc_wake_en = 0; /* (don't) enable PME# on mcmatch */ cbp->nsai = 1; /* (don't) disable source addr insert */ cbp->preamble_length = 2; /* (7 byte) preamble */ cbp->loopback = 0; /* (don't) loopback */ cbp->linear_priority = 0; /* (normal CSMA/CD operation) */ cbp->linear_pri_mode = 0; /* (wait after xmit only) */ cbp->interfrm_spacing = 6; /* (96 bits of) interframe spacing */ cbp->promiscuous = prm; /* promiscuous mode */ cbp->bcast_disable = 0; /* (don't) disable broadcasts */ cbp->wait_after_win = 0; /* (don't) enable modified backoff alg*/ cbp->ignore_ul = 0; /* consider U/L bit in IA matching */ cbp->crc16_en = 0; /* (don't) enable crc-16 algorithm */ cbp->crscdt = sc->flags & FXP_FLAG_SERIAL_MEDIA ? 1 : 0; cbp->stripping = !prm; /* truncate rx packet to byte count */ cbp->padding = 1; /* (do) pad short tx packets */ cbp->rcv_crc_xfer = 0; /* (don't) xfer CRC to host */ cbp->long_rx_en = sc->flags & FXP_FLAG_LONG_PKT_EN ? 1 : 0; cbp->ia_wake_en = 0; /* (don't) wake up on address match */ cbp->magic_pkt_dis = sc->flags & FXP_FLAG_WOL ? 0 : 1; cbp->force_fdx = 0; /* (don't) force full duplex */ cbp->fdx_pin_en = 1; /* (enable) FDX# pin */ cbp->multi_ia = 0; /* (don't) accept multiple IAs */ cbp->mc_all = ifp->if_flags & IFF_ALLMULTI ? 1 : prm; cbp->gamla_rx = sc->flags & FXP_FLAG_EXT_RFA ? 1 : 0; cbp->vlan_strip_en = ((sc->flags & FXP_FLAG_EXT_RFA) != 0 && (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0) ? 1 : 0; if (sc->revision == FXP_REV_82557) { /* * The 82557 has no hardware flow control, the values * below are the defaults for the chip. */ cbp->fc_delay_lsb = 0; cbp->fc_delay_msb = 0x40; cbp->pri_fc_thresh = 3; cbp->tx_fc_dis = 0; cbp->rx_fc_restop = 0; cbp->rx_fc_restart = 0; cbp->fc_filter = 0; cbp->pri_fc_loc = 1; } else { /* Set pause RX FIFO threshold to 1KB. */ CSR_WRITE_1(sc, FXP_CSR_FC_THRESH, 1); /* Set pause time. */ cbp->fc_delay_lsb = 0xff; cbp->fc_delay_msb = 0xff; cbp->pri_fc_thresh = 3; mii = device_get_softc(sc->miibus); if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) /* enable transmit FC */ cbp->tx_fc_dis = 0; else /* disable transmit FC */ cbp->tx_fc_dis = 1; if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) { /* enable FC restart/restop frames */ cbp->rx_fc_restart = 1; cbp->rx_fc_restop = 1; } else { /* disable FC restart/restop frames */ cbp->rx_fc_restart = 0; cbp->rx_fc_restop = 0; } cbp->fc_filter = !prm; /* drop FC frames to host */ cbp->pri_fc_loc = 1; /* FC pri location (byte31) */ } /* Enable 82558 and 82559 extended statistics functionality. */ if (sc->revision >= FXP_REV_82558_A4) { if (sc->revision >= FXP_REV_82559_A0) { /* * Extend configuration table size to 32 * to include TCO configuration. */ cbp->byte_count = 32; cbp->ext_stats_dis = 1; /* Enable TCO stats. */ cbp->tno_int_or_tco_en = 1; cbp->gamla_rx = 1; } else cbp->ext_stats_dis = 0; } /* * Start the config command/DMA. */ fxp_scb_wait(sc); bus_dmamap_sync(sc->cbl_tag, sc->cbl_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, sc->fxp_desc.cbl_addr); fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_START); /* ...and wait for it to complete. */ fxp_dma_wait(sc, &cbp->cb_status, sc->cbl_tag, sc->cbl_map); /* * Now initialize the station address. Temporarily use the TxCB * memory area like we did above for the config CB. */ cb_ias = (struct fxp_cb_ias *)sc->fxp_desc.cbl_list; cb_ias->cb_status = 0; cb_ias->cb_command = htole16(FXP_CB_COMMAND_IAS | FXP_CB_COMMAND_EL); cb_ias->link_addr = 0xffffffff; bcopy(IF_LLADDR(sc->ifp), cb_ias->macaddr, ETHER_ADDR_LEN); /* * Start the IAS (Individual Address Setup) command/DMA. */ fxp_scb_wait(sc); bus_dmamap_sync(sc->cbl_tag, sc->cbl_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, sc->fxp_desc.cbl_addr); fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_START); /* ...and wait for it to complete. */ fxp_dma_wait(sc, &cb_ias->cb_status, sc->cbl_tag, sc->cbl_map); /* * Initialize the multicast address list. */ fxp_mc_setup(sc); /* * Initialize transmit control block (TxCB) list. */ txp = sc->fxp_desc.tx_list; tcbp = sc->fxp_desc.cbl_list; bzero(tcbp, FXP_TXCB_SZ); for (i = 0; i < FXP_NTXCB; i++) { txp[i].tx_mbuf = NULL; tcbp[i].cb_status = htole16(FXP_CB_STATUS_C | FXP_CB_STATUS_OK); tcbp[i].cb_command = htole16(FXP_CB_COMMAND_NOP); tcbp[i].link_addr = htole32(sc->fxp_desc.cbl_addr + (((i + 1) & FXP_TXCB_MASK) * sizeof(struct fxp_cb_tx))); if (sc->flags & FXP_FLAG_EXT_TXCB) tcbp[i].tbd_array_addr = htole32(FXP_TXCB_DMA_ADDR(sc, &tcbp[i].tbd[2])); else tcbp[i].tbd_array_addr = htole32(FXP_TXCB_DMA_ADDR(sc, &tcbp[i].tbd[0])); txp[i].tx_next = &txp[(i + 1) & FXP_TXCB_MASK]; } /* * Set the suspend flag on the first TxCB and start the control * unit. It will execute the NOP and then suspend. */ tcbp->cb_command = htole16(FXP_CB_COMMAND_NOP | FXP_CB_COMMAND_S); bus_dmamap_sync(sc->cbl_tag, sc->cbl_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); sc->fxp_desc.tx_first = sc->fxp_desc.tx_last = txp; sc->tx_queued = 1; fxp_scb_wait(sc); CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, sc->fxp_desc.cbl_addr); fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_START); /* * Initialize receiver buffer area - RFA. */ fxp_scb_wait(sc); CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, sc->fxp_desc.rx_head->rx_addr); fxp_scb_cmd(sc, FXP_SCB_COMMAND_RU_START); if (sc->miibus != NULL && setmedia != 0) mii_mediachg(device_get_softc(sc->miibus)); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; /* * Enable interrupts. */ #ifdef DEVICE_POLLING /* * ... but only do that if we are not polling. And because (presumably) * the default is interrupts on, we need to disable them explicitly! */ if (ifp->if_capenable & IFCAP_POLLING ) CSR_WRITE_1(sc, FXP_CSR_SCB_INTRCNTL, FXP_SCB_INTR_DISABLE); else #endif /* DEVICE_POLLING */ CSR_WRITE_1(sc, FXP_CSR_SCB_INTRCNTL, 0); /* * Start stats updater. */ callout_reset(&sc->stat_ch, hz, fxp_tick, sc); } static int fxp_serial_ifmedia_upd(struct ifnet *ifp) { return (0); } static void fxp_serial_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { ifmr->ifm_active = IFM_ETHER|IFM_MANUAL; } /* * Change media according to request. */ static int fxp_ifmedia_upd(struct ifnet *ifp) { struct fxp_softc *sc = ifp->if_softc; struct mii_data *mii; struct mii_softc *miisc; mii = device_get_softc(sc->miibus); FXP_LOCK(sc); LIST_FOREACH(miisc, &mii->mii_phys, mii_list) mii_phy_reset(miisc); mii_mediachg(mii); FXP_UNLOCK(sc); return (0); } /* * Notify the world which media we're using. */ static void fxp_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { struct fxp_softc *sc = ifp->if_softc; struct mii_data *mii; mii = device_get_softc(sc->miibus); FXP_LOCK(sc); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; FXP_UNLOCK(sc); } /* * Add a buffer to the end of the RFA buffer list. * Return 0 if successful, 1 for failure. A failure results in * reusing the RFA buffer. * The RFA struct is stuck at the beginning of mbuf cluster and the * data pointer is fixed up to point just past it. */ static int fxp_new_rfabuf(struct fxp_softc *sc, struct fxp_rx *rxp) { struct mbuf *m; struct fxp_rfa *rfa; bus_dmamap_t tmp_map; int error; m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m == NULL) return (ENOBUFS); /* * Move the data pointer up so that the incoming data packet * will be 32-bit aligned. */ m->m_data += RFA_ALIGNMENT_FUDGE; /* * Get a pointer to the base of the mbuf cluster and move * data start past it. */ rfa = mtod(m, struct fxp_rfa *); m->m_data += sc->rfa_size; rfa->size = htole16(MCLBYTES - sc->rfa_size - RFA_ALIGNMENT_FUDGE); rfa->rfa_status = 0; rfa->rfa_control = htole16(FXP_RFA_CONTROL_EL); rfa->actual_size = 0; m->m_len = m->m_pkthdr.len = MCLBYTES - RFA_ALIGNMENT_FUDGE - sc->rfa_size; /* * Initialize the rest of the RFA. Note that since the RFA * is misaligned, we cannot store values directly. We're thus * using the le32enc() function which handles endianness and * is also alignment-safe. */ le32enc(&rfa->link_addr, 0xffffffff); le32enc(&rfa->rbd_addr, 0xffffffff); /* Map the RFA into DMA memory. */ error = bus_dmamap_load(sc->fxp_rxmtag, sc->spare_map, rfa, MCLBYTES - RFA_ALIGNMENT_FUDGE, fxp_dma_map_addr, &rxp->rx_addr, BUS_DMA_NOWAIT); if (error) { m_freem(m); return (error); } if (rxp->rx_mbuf != NULL) bus_dmamap_unload(sc->fxp_rxmtag, rxp->rx_map); tmp_map = sc->spare_map; sc->spare_map = rxp->rx_map; rxp->rx_map = tmp_map; rxp->rx_mbuf = m; bus_dmamap_sync(sc->fxp_rxmtag, rxp->rx_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); return (0); } static void fxp_add_rfabuf(struct fxp_softc *sc, struct fxp_rx *rxp) { struct fxp_rfa *p_rfa; struct fxp_rx *p_rx; /* * If there are other buffers already on the list, attach this * one to the end by fixing up the tail to point to this one. */ if (sc->fxp_desc.rx_head != NULL) { p_rx = sc->fxp_desc.rx_tail; p_rfa = (struct fxp_rfa *) (p_rx->rx_mbuf->m_ext.ext_buf + RFA_ALIGNMENT_FUDGE); p_rx->rx_next = rxp; le32enc(&p_rfa->link_addr, rxp->rx_addr); p_rfa->rfa_control = 0; bus_dmamap_sync(sc->fxp_rxmtag, p_rx->rx_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); } else { rxp->rx_next = NULL; sc->fxp_desc.rx_head = rxp; } sc->fxp_desc.rx_tail = rxp; } static void fxp_discard_rfabuf(struct fxp_softc *sc, struct fxp_rx *rxp) { struct mbuf *m; struct fxp_rfa *rfa; m = rxp->rx_mbuf; m->m_data = m->m_ext.ext_buf; /* * Move the data pointer up so that the incoming data packet * will be 32-bit aligned. */ m->m_data += RFA_ALIGNMENT_FUDGE; /* * Get a pointer to the base of the mbuf cluster and move * data start past it. */ rfa = mtod(m, struct fxp_rfa *); m->m_data += sc->rfa_size; rfa->size = htole16(MCLBYTES - sc->rfa_size - RFA_ALIGNMENT_FUDGE); rfa->rfa_status = 0; rfa->rfa_control = htole16(FXP_RFA_CONTROL_EL); rfa->actual_size = 0; /* * Initialize the rest of the RFA. Note that since the RFA * is misaligned, we cannot store values directly. We're thus * using the le32enc() function which handles endianness and * is also alignment-safe. */ le32enc(&rfa->link_addr, 0xffffffff); le32enc(&rfa->rbd_addr, 0xffffffff); bus_dmamap_sync(sc->fxp_rxmtag, rxp->rx_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); } static int fxp_miibus_readreg(device_t dev, int phy, int reg) { struct fxp_softc *sc = device_get_softc(dev); int count = 10000; int value; CSR_WRITE_4(sc, FXP_CSR_MDICONTROL, (FXP_MDI_READ << 26) | (reg << 16) | (phy << 21)); while (((value = CSR_READ_4(sc, FXP_CSR_MDICONTROL)) & 0x10000000) == 0 && count--) DELAY(10); if (count <= 0) device_printf(dev, "fxp_miibus_readreg: timed out\n"); return (value & 0xffff); } static int fxp_miibus_writereg(device_t dev, int phy, int reg, int value) { struct fxp_softc *sc = device_get_softc(dev); int count = 10000; CSR_WRITE_4(sc, FXP_CSR_MDICONTROL, (FXP_MDI_WRITE << 26) | (reg << 16) | (phy << 21) | (value & 0xffff)); while ((CSR_READ_4(sc, FXP_CSR_MDICONTROL) & 0x10000000) == 0 && count--) DELAY(10); if (count <= 0) device_printf(dev, "fxp_miibus_writereg: timed out\n"); return (0); } static void fxp_miibus_statchg(device_t dev) { struct fxp_softc *sc; struct mii_data *mii; struct ifnet *ifp; sc = device_get_softc(dev); mii = device_get_softc(sc->miibus); ifp = sc->ifp; if (mii == NULL || ifp == NULL || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || (mii->mii_media_status & (IFM_AVALID | IFM_ACTIVE)) != (IFM_AVALID | IFM_ACTIVE)) return; if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T && sc->flags & FXP_FLAG_CU_RESUME_BUG) sc->cu_resume_bug = 1; else sc->cu_resume_bug = 0; /* * Call fxp_init_body in order to adjust the flow control settings. * Note that the 82557 doesn't support hardware flow control. */ if (sc->revision == FXP_REV_82557) return; + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; fxp_init_body(sc, 0); } static int fxp_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct fxp_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; struct mii_data *mii; int flag, mask, error = 0, reinit; switch (command) { case SIOCSIFFLAGS: FXP_LOCK(sc); /* * If interface is marked up and not running, then start it. * If it is marked down and running, stop it. * XXX If it's up then re-initialize it. This is so flags * such as IFF_PROMISC are handled. */ if (ifp->if_flags & IFF_UP) { if (((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) && ((ifp->if_flags ^ sc->if_flags) & - (IFF_PROMISC | IFF_ALLMULTI | IFF_LINK0)) != 0) + (IFF_PROMISC | IFF_ALLMULTI | IFF_LINK0)) != 0) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; fxp_init_body(sc, 0); - else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + } else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) fxp_init_body(sc, 1); } else { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) fxp_stop(sc); } sc->if_flags = ifp->if_flags; FXP_UNLOCK(sc); break; case SIOCADDMULTI: case SIOCDELMULTI: FXP_LOCK(sc); - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; fxp_init_body(sc, 0); + } FXP_UNLOCK(sc); break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: if (sc->miibus != NULL) { mii = device_get_softc(sc->miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); } else { error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, command); } break; case SIOCSIFCAP: reinit = 0; mask = ifp->if_capenable ^ ifr->ifr_reqcap; #ifdef DEVICE_POLLING if (mask & IFCAP_POLLING) { if (ifr->ifr_reqcap & IFCAP_POLLING) { error = ether_poll_register(fxp_poll, ifp); if (error) return(error); FXP_LOCK(sc); CSR_WRITE_1(sc, FXP_CSR_SCB_INTRCNTL, FXP_SCB_INTR_DISABLE); ifp->if_capenable |= IFCAP_POLLING; FXP_UNLOCK(sc); } else { error = ether_poll_deregister(ifp); /* Enable interrupts in any case */ FXP_LOCK(sc); CSR_WRITE_1(sc, FXP_CSR_SCB_INTRCNTL, 0); ifp->if_capenable &= ~IFCAP_POLLING; FXP_UNLOCK(sc); } } #endif FXP_LOCK(sc); if ((mask & IFCAP_TXCSUM) != 0 && (ifp->if_capabilities & IFCAP_TXCSUM) != 0) { ifp->if_capenable ^= IFCAP_TXCSUM; if ((ifp->if_capenable & IFCAP_TXCSUM) != 0) ifp->if_hwassist |= FXP_CSUM_FEATURES; else ifp->if_hwassist &= ~FXP_CSUM_FEATURES; } if ((mask & IFCAP_RXCSUM) != 0 && (ifp->if_capabilities & IFCAP_RXCSUM) != 0) { ifp->if_capenable ^= IFCAP_RXCSUM; if ((sc->flags & FXP_FLAG_82559_RXCSUM) != 0) reinit++; } if ((mask & IFCAP_TSO4) != 0 && (ifp->if_capabilities & IFCAP_TSO4) != 0) { ifp->if_capenable ^= IFCAP_TSO4; if ((ifp->if_capenable & IFCAP_TSO4) != 0) ifp->if_hwassist |= CSUM_TSO; else ifp->if_hwassist &= ~CSUM_TSO; } if ((mask & IFCAP_WOL_MAGIC) != 0 && (ifp->if_capabilities & IFCAP_WOL_MAGIC) != 0) ifp->if_capenable ^= IFCAP_WOL_MAGIC; if ((mask & IFCAP_VLAN_MTU) != 0 && (ifp->if_capabilities & IFCAP_VLAN_MTU) != 0) { ifp->if_capenable ^= IFCAP_VLAN_MTU; if (sc->revision != FXP_REV_82557) flag = FXP_FLAG_LONG_PKT_EN; else /* a hack to get long frames on the old chip */ flag = FXP_FLAG_SAVE_BAD; sc->flags ^= flag; if (ifp->if_flags & IFF_UP) reinit++; } if ((mask & IFCAP_VLAN_HWCSUM) != 0 && (ifp->if_capabilities & IFCAP_VLAN_HWCSUM) != 0) ifp->if_capenable ^= IFCAP_VLAN_HWCSUM; if ((mask & IFCAP_VLAN_HWTSO) != 0 && (ifp->if_capabilities & IFCAP_VLAN_HWTSO) != 0) ifp->if_capenable ^= IFCAP_VLAN_HWTSO; if ((mask & IFCAP_VLAN_HWTAGGING) != 0 && (ifp->if_capabilities & IFCAP_VLAN_HWTAGGING) != 0) { ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) == 0) ifp->if_capenable &= ~(IFCAP_VLAN_HWTSO | IFCAP_VLAN_HWCSUM); reinit++; } - if (reinit > 0 && ifp->if_flags & IFF_UP) + if (reinit > 0 && (ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; fxp_init_body(sc, 0); + } FXP_UNLOCK(sc); VLAN_CAPABILITIES(ifp); break; default: error = ether_ioctl(ifp, command, data); } return (error); } /* * Fill in the multicast address list and return number of entries. */ static int fxp_mc_addrs(struct fxp_softc *sc) { struct fxp_cb_mcs *mcsp = sc->mcsp; struct ifnet *ifp = sc->ifp; struct ifmultiaddr *ifma; int nmcasts; nmcasts = 0; if ((ifp->if_flags & IFF_ALLMULTI) == 0) { if_maddr_rlock(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; if (nmcasts >= MAXMCADDR) { ifp->if_flags |= IFF_ALLMULTI; nmcasts = 0; break; } bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), &sc->mcsp->mc_addr[nmcasts][0], ETHER_ADDR_LEN); nmcasts++; } if_maddr_runlock(ifp); } mcsp->mc_cnt = htole16(nmcasts * ETHER_ADDR_LEN); return (nmcasts); } /* * Program the multicast filter. * * We have an artificial restriction that the multicast setup command * must be the first command in the chain, so we take steps to ensure * this. By requiring this, it allows us to keep up the performance of * the pre-initialized command ring (esp. link pointers) by not actually * inserting the mcsetup command in the ring - i.e. its link pointer * points to the TxCB ring, but the mcsetup descriptor itself is not part * of it. We then can do 'CU_START' on the mcsetup descriptor and have it * lead into the regular TxCB ring when it completes. */ static void fxp_mc_setup(struct fxp_softc *sc) { struct fxp_cb_mcs *mcsp; int count; FXP_LOCK_ASSERT(sc, MA_OWNED); mcsp = sc->mcsp; mcsp->cb_status = 0; mcsp->cb_command = htole16(FXP_CB_COMMAND_MCAS | FXP_CB_COMMAND_EL); mcsp->link_addr = 0xffffffff; fxp_mc_addrs(sc); /* * Wait until command unit is idle. This should never be the * case when nothing is queued, but make sure anyway. */ count = 100; while ((CSR_READ_1(sc, FXP_CSR_SCB_RUSCUS) >> 6) != FXP_SCB_CUS_IDLE && --count) DELAY(10); if (count == 0) { device_printf(sc->dev, "command queue timeout\n"); return; } /* * Start the multicast setup command. */ fxp_scb_wait(sc); bus_dmamap_sync(sc->mcs_tag, sc->mcs_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, sc->mcs_addr); fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_START); /* ...and wait for it to complete. */ fxp_dma_wait(sc, &mcsp->cb_status, sc->mcs_tag, sc->mcs_map); } static uint32_t fxp_ucode_d101a[] = D101_A_RCVBUNDLE_UCODE; static uint32_t fxp_ucode_d101b0[] = D101_B0_RCVBUNDLE_UCODE; static uint32_t fxp_ucode_d101ma[] = D101M_B_RCVBUNDLE_UCODE; static uint32_t fxp_ucode_d101s[] = D101S_RCVBUNDLE_UCODE; static uint32_t fxp_ucode_d102[] = D102_B_RCVBUNDLE_UCODE; static uint32_t fxp_ucode_d102c[] = D102_C_RCVBUNDLE_UCODE; static uint32_t fxp_ucode_d102e[] = D102_E_RCVBUNDLE_UCODE; #define UCODE(x) x, sizeof(x)/sizeof(uint32_t) static const struct ucode { uint32_t revision; uint32_t *ucode; int length; u_short int_delay_offset; u_short bundle_max_offset; } ucode_table[] = { { FXP_REV_82558_A4, UCODE(fxp_ucode_d101a), D101_CPUSAVER_DWORD, 0 }, { FXP_REV_82558_B0, UCODE(fxp_ucode_d101b0), D101_CPUSAVER_DWORD, 0 }, { FXP_REV_82559_A0, UCODE(fxp_ucode_d101ma), D101M_CPUSAVER_DWORD, D101M_CPUSAVER_BUNDLE_MAX_DWORD }, { FXP_REV_82559S_A, UCODE(fxp_ucode_d101s), D101S_CPUSAVER_DWORD, D101S_CPUSAVER_BUNDLE_MAX_DWORD }, { FXP_REV_82550, UCODE(fxp_ucode_d102), D102_B_CPUSAVER_DWORD, D102_B_CPUSAVER_BUNDLE_MAX_DWORD }, { FXP_REV_82550_C, UCODE(fxp_ucode_d102c), D102_C_CPUSAVER_DWORD, D102_C_CPUSAVER_BUNDLE_MAX_DWORD }, { FXP_REV_82551_F, UCODE(fxp_ucode_d102e), D102_E_CPUSAVER_DWORD, D102_E_CPUSAVER_BUNDLE_MAX_DWORD }, { FXP_REV_82551_10, UCODE(fxp_ucode_d102e), D102_E_CPUSAVER_DWORD, D102_E_CPUSAVER_BUNDLE_MAX_DWORD }, { 0, NULL, 0, 0, 0 } }; static void fxp_load_ucode(struct fxp_softc *sc) { const struct ucode *uc; struct fxp_cb_ucode *cbp; int i; if (sc->flags & FXP_FLAG_NO_UCODE) return; for (uc = ucode_table; uc->ucode != NULL; uc++) if (sc->revision == uc->revision) break; if (uc->ucode == NULL) return; cbp = (struct fxp_cb_ucode *)sc->fxp_desc.cbl_list; cbp->cb_status = 0; cbp->cb_command = htole16(FXP_CB_COMMAND_UCODE | FXP_CB_COMMAND_EL); cbp->link_addr = 0xffffffff; /* (no) next command */ for (i = 0; i < uc->length; i++) cbp->ucode[i] = htole32(uc->ucode[i]); if (uc->int_delay_offset) *(uint16_t *)&cbp->ucode[uc->int_delay_offset] = htole16(sc->tunable_int_delay + sc->tunable_int_delay / 2); if (uc->bundle_max_offset) *(uint16_t *)&cbp->ucode[uc->bundle_max_offset] = htole16(sc->tunable_bundle_max); /* * Download the ucode to the chip. */ fxp_scb_wait(sc); bus_dmamap_sync(sc->cbl_tag, sc->cbl_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, sc->fxp_desc.cbl_addr); fxp_scb_cmd(sc, FXP_SCB_COMMAND_CU_START); /* ...and wait for it to complete. */ fxp_dma_wait(sc, &cbp->cb_status, sc->cbl_tag, sc->cbl_map); device_printf(sc->dev, "Microcode loaded, int_delay: %d usec bundle_max: %d\n", sc->tunable_int_delay, uc->bundle_max_offset == 0 ? 0 : sc->tunable_bundle_max); sc->flags |= FXP_FLAG_UCODE; bzero(cbp, FXP_TXCB_SZ); } #define FXP_SYSCTL_STAT_ADD(c, h, n, p, d) \ SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d) static void fxp_sysctl_node(struct fxp_softc *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid_list *child, *parent; struct sysctl_oid *tree; struct fxp_hwstats *hsp; ctx = device_get_sysctl_ctx(sc->dev); child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)); SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "int_delay", CTLTYPE_INT | CTLFLAG_RW, &sc->tunable_int_delay, 0, sysctl_hw_fxp_int_delay, "I", "FXP driver receive interrupt microcode bundling delay"); SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "bundle_max", CTLTYPE_INT | CTLFLAG_RW, &sc->tunable_bundle_max, 0, sysctl_hw_fxp_bundle_max, "I", "FXP driver receive interrupt microcode bundle size limit"); SYSCTL_ADD_INT(ctx, child,OID_AUTO, "rnr", CTLFLAG_RD, &sc->rnr, 0, "FXP RNR events"); /* * Pull in device tunables. */ sc->tunable_int_delay = TUNABLE_INT_DELAY; sc->tunable_bundle_max = TUNABLE_BUNDLE_MAX; (void) resource_int_value(device_get_name(sc->dev), device_get_unit(sc->dev), "int_delay", &sc->tunable_int_delay); (void) resource_int_value(device_get_name(sc->dev), device_get_unit(sc->dev), "bundle_max", &sc->tunable_bundle_max); sc->rnr = 0; hsp = &sc->fxp_hwstats; tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD, NULL, "FXP statistics"); parent = SYSCTL_CHILDREN(tree); /* Rx MAC statistics. */ tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "rx", CTLFLAG_RD, NULL, "Rx MAC statistics"); child = SYSCTL_CHILDREN(tree); FXP_SYSCTL_STAT_ADD(ctx, child, "good_frames", &hsp->rx_good, "Good frames"); FXP_SYSCTL_STAT_ADD(ctx, child, "crc_errors", &hsp->rx_crc_errors, "CRC errors"); FXP_SYSCTL_STAT_ADD(ctx, child, "alignment_errors", &hsp->rx_alignment_errors, "Alignment errors"); FXP_SYSCTL_STAT_ADD(ctx, child, "rnr_errors", &hsp->rx_rnr_errors, "RNR errors"); FXP_SYSCTL_STAT_ADD(ctx, child, "overrun_errors", &hsp->rx_overrun_errors, "Overrun errors"); FXP_SYSCTL_STAT_ADD(ctx, child, "cdt_errors", &hsp->rx_cdt_errors, "Collision detect errors"); FXP_SYSCTL_STAT_ADD(ctx, child, "shortframes", &hsp->rx_shortframes, "Short frame errors"); if (sc->revision >= FXP_REV_82558_A4) { FXP_SYSCTL_STAT_ADD(ctx, child, "pause", &hsp->rx_pause, "Pause frames"); FXP_SYSCTL_STAT_ADD(ctx, child, "controls", &hsp->rx_controls, "Unsupported control frames"); } if (sc->revision >= FXP_REV_82559_A0) FXP_SYSCTL_STAT_ADD(ctx, child, "tco", &hsp->rx_tco, "TCO frames"); /* Tx MAC statistics. */ tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "tx", CTLFLAG_RD, NULL, "Tx MAC statistics"); child = SYSCTL_CHILDREN(tree); FXP_SYSCTL_STAT_ADD(ctx, child, "good_frames", &hsp->tx_good, "Good frames"); FXP_SYSCTL_STAT_ADD(ctx, child, "maxcols", &hsp->tx_maxcols, "Maximum collisions errors"); FXP_SYSCTL_STAT_ADD(ctx, child, "latecols", &hsp->tx_latecols, "Late collisions errors"); FXP_SYSCTL_STAT_ADD(ctx, child, "underruns", &hsp->tx_underruns, "Underrun errors"); FXP_SYSCTL_STAT_ADD(ctx, child, "lostcrs", &hsp->tx_lostcrs, "Lost carrier sense"); FXP_SYSCTL_STAT_ADD(ctx, child, "deffered", &hsp->tx_deffered, "Deferred"); FXP_SYSCTL_STAT_ADD(ctx, child, "single_collisions", &hsp->tx_single_collisions, "Single collisions"); FXP_SYSCTL_STAT_ADD(ctx, child, "multiple_collisions", &hsp->tx_multiple_collisions, "Multiple collisions"); FXP_SYSCTL_STAT_ADD(ctx, child, "total_collisions", &hsp->tx_total_collisions, "Total collisions"); if (sc->revision >= FXP_REV_82558_A4) FXP_SYSCTL_STAT_ADD(ctx, child, "pause", &hsp->tx_pause, "Pause frames"); if (sc->revision >= FXP_REV_82559_A0) FXP_SYSCTL_STAT_ADD(ctx, child, "tco", &hsp->tx_tco, "TCO frames"); } #undef FXP_SYSCTL_STAT_ADD static int sysctl_int_range(SYSCTL_HANDLER_ARGS, int low, int high) { int error, value; value = *(int *)arg1; error = sysctl_handle_int(oidp, &value, 0, req); if (error || !req->newptr) return (error); if (value < low || value > high) return (EINVAL); *(int *)arg1 = value; return (0); } /* * Interrupt delay is expressed in microseconds, a multiplier is used * to convert this to the appropriate clock ticks before using. */ static int sysctl_hw_fxp_int_delay(SYSCTL_HANDLER_ARGS) { return (sysctl_int_range(oidp, arg1, arg2, req, 300, 3000)); } static int sysctl_hw_fxp_bundle_max(SYSCTL_HANDLER_ARGS) { return (sysctl_int_range(oidp, arg1, arg2, req, 1, 0xffff)); } Index: releng/8.4/sys/dev/virtio/network/if_vtnet.c =================================================================== --- releng/8.4/sys/dev/virtio/network/if_vtnet.c (revision 252333) +++ releng/8.4/sys/dev/virtio/network/if_vtnet.c (revision 252334) @@ -1,2746 +1,2746 @@ /*- * Copyright (c) 2011, Bryan Venteicher * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Driver for VirtIO network devices. */ #include __FBSDID("$FreeBSD$"); #ifdef HAVE_KERNEL_OPTION_HEADERS #include "opt_device_polling.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "virtio_if.h" static int vtnet_modevent(module_t, int, void *); static int vtnet_probe(device_t); static int vtnet_attach(device_t); static int vtnet_detach(device_t); static int vtnet_suspend(device_t); static int vtnet_resume(device_t); static int vtnet_shutdown(device_t); static int vtnet_config_change(device_t); static void vtnet_negotiate_features(struct vtnet_softc *); static int vtnet_alloc_virtqueues(struct vtnet_softc *); static void vtnet_get_hwaddr(struct vtnet_softc *); static void vtnet_set_hwaddr(struct vtnet_softc *); static int vtnet_is_link_up(struct vtnet_softc *); static void vtnet_update_link_status(struct vtnet_softc *); static void vtnet_watchdog(struct vtnet_softc *); static void vtnet_config_change_task(void *, int); static int vtnet_change_mtu(struct vtnet_softc *, int); static int vtnet_ioctl(struct ifnet *, u_long, caddr_t); static int vtnet_init_rx_vq(struct vtnet_softc *); static void vtnet_free_rx_mbufs(struct vtnet_softc *); static void vtnet_free_tx_mbufs(struct vtnet_softc *); static void vtnet_free_ctrl_vq(struct vtnet_softc *); #ifdef DEVICE_POLLING static poll_handler_t vtnet_poll; #endif static struct mbuf * vtnet_alloc_rxbuf(struct vtnet_softc *, int, struct mbuf **); static int vtnet_replace_rxbuf(struct vtnet_softc *, struct mbuf *, int); static int vtnet_newbuf(struct vtnet_softc *); static void vtnet_discard_merged_rxbuf(struct vtnet_softc *, int); static void vtnet_discard_rxbuf(struct vtnet_softc *, struct mbuf *); static int vtnet_enqueue_rxbuf(struct vtnet_softc *, struct mbuf *); static void vtnet_vlan_tag_remove(struct mbuf *); static int vtnet_rx_csum(struct vtnet_softc *, struct mbuf *, struct virtio_net_hdr *); static int vtnet_rxeof_merged(struct vtnet_softc *, struct mbuf *, int); static int vtnet_rxeof(struct vtnet_softc *, int, int *); static void vtnet_rx_intr_task(void *, int); static int vtnet_rx_vq_intr(void *); static void vtnet_txeof(struct vtnet_softc *); static struct mbuf * vtnet_tx_offload(struct vtnet_softc *, struct mbuf *, struct virtio_net_hdr *); static int vtnet_enqueue_txbuf(struct vtnet_softc *, struct mbuf **, struct vtnet_tx_header *); static int vtnet_encap(struct vtnet_softc *, struct mbuf **); static void vtnet_start_locked(struct ifnet *); static void vtnet_start(struct ifnet *); static void vtnet_tick(void *); static void vtnet_tx_intr_task(void *, int); static int vtnet_tx_vq_intr(void *); static void vtnet_stop(struct vtnet_softc *); static int vtnet_reinit(struct vtnet_softc *); static void vtnet_init_locked(struct vtnet_softc *); static void vtnet_init(void *); static void vtnet_exec_ctrl_cmd(struct vtnet_softc *, void *, struct sglist *, int, int); static void vtnet_rx_filter(struct vtnet_softc *sc); static int vtnet_ctrl_rx_cmd(struct vtnet_softc *, int, int); static int vtnet_set_promisc(struct vtnet_softc *, int); static int vtnet_set_allmulti(struct vtnet_softc *, int); static void vtnet_rx_filter_mac(struct vtnet_softc *); static int vtnet_exec_vlan_filter(struct vtnet_softc *, int, uint16_t); static void vtnet_rx_filter_vlan(struct vtnet_softc *); static void vtnet_set_vlan_filter(struct vtnet_softc *, int, uint16_t); static void vtnet_register_vlan(void *, struct ifnet *, uint16_t); static void vtnet_unregister_vlan(void *, struct ifnet *, uint16_t); static int vtnet_ifmedia_upd(struct ifnet *); static void vtnet_ifmedia_sts(struct ifnet *, struct ifmediareq *); static void vtnet_add_statistics(struct vtnet_softc *); static int vtnet_enable_rx_intr(struct vtnet_softc *); static int vtnet_enable_tx_intr(struct vtnet_softc *); static void vtnet_disable_rx_intr(struct vtnet_softc *); static void vtnet_disable_tx_intr(struct vtnet_softc *); /* Tunables. */ static int vtnet_csum_disable = 0; TUNABLE_INT("hw.vtnet.csum_disable", &vtnet_csum_disable); static int vtnet_tso_disable = 0; TUNABLE_INT("hw.vtnet.tso_disable", &vtnet_tso_disable); static int vtnet_lro_disable = 0; TUNABLE_INT("hw.vtnet.lro_disable", &vtnet_lro_disable); /* * Reducing the number of transmit completed interrupts can * improve performance. To do so, the define below keeps the * Tx vq interrupt disabled and adds calls to vtnet_txeof() * in the start and watchdog paths. The price to pay for this * is the m_free'ing of transmitted mbufs may be delayed until * the watchdog fires. */ #define VTNET_TX_INTR_MODERATION static uma_zone_t vtnet_tx_header_zone; static struct virtio_feature_desc vtnet_feature_desc[] = { { VIRTIO_NET_F_CSUM, "TxChecksum" }, { VIRTIO_NET_F_GUEST_CSUM, "RxChecksum" }, { VIRTIO_NET_F_MAC, "MacAddress" }, { VIRTIO_NET_F_GSO, "TxAllGSO" }, { VIRTIO_NET_F_GUEST_TSO4, "RxTSOv4" }, { VIRTIO_NET_F_GUEST_TSO6, "RxTSOv6" }, { VIRTIO_NET_F_GUEST_ECN, "RxECN" }, { VIRTIO_NET_F_GUEST_UFO, "RxUFO" }, { VIRTIO_NET_F_HOST_TSO4, "TxTSOv4" }, { VIRTIO_NET_F_HOST_TSO6, "TxTSOv6" }, { VIRTIO_NET_F_HOST_ECN, "TxTSOECN" }, { VIRTIO_NET_F_HOST_UFO, "TxUFO" }, { VIRTIO_NET_F_MRG_RXBUF, "MrgRxBuf" }, { VIRTIO_NET_F_STATUS, "Status" }, { VIRTIO_NET_F_CTRL_VQ, "ControlVq" }, { VIRTIO_NET_F_CTRL_RX, "RxMode" }, { VIRTIO_NET_F_CTRL_VLAN, "VLanFilter" }, { VIRTIO_NET_F_CTRL_RX_EXTRA, "RxModeExtra" }, { 0, NULL } }; static device_method_t vtnet_methods[] = { /* Device methods. */ DEVMETHOD(device_probe, vtnet_probe), DEVMETHOD(device_attach, vtnet_attach), DEVMETHOD(device_detach, vtnet_detach), DEVMETHOD(device_suspend, vtnet_suspend), DEVMETHOD(device_resume, vtnet_resume), DEVMETHOD(device_shutdown, vtnet_shutdown), /* VirtIO methods. */ DEVMETHOD(virtio_config_change, vtnet_config_change), DEVMETHOD_END }; static driver_t vtnet_driver = { "vtnet", vtnet_methods, sizeof(struct vtnet_softc) }; static devclass_t vtnet_devclass; DRIVER_MODULE(vtnet, virtio_pci, vtnet_driver, vtnet_devclass, vtnet_modevent, 0); MODULE_VERSION(vtnet, 1); MODULE_DEPEND(vtnet, virtio, 1, 1, 1); static int vtnet_modevent(module_t mod, int type, void *unused) { int error; error = 0; switch (type) { case MOD_LOAD: vtnet_tx_header_zone = uma_zcreate("vtnet_tx_hdr", sizeof(struct vtnet_tx_header), NULL, NULL, NULL, NULL, 0, 0); break; case MOD_QUIESCE: case MOD_UNLOAD: if (uma_zone_get_cur(vtnet_tx_header_zone) > 0) error = EBUSY; else if (type == MOD_UNLOAD) { uma_zdestroy(vtnet_tx_header_zone); vtnet_tx_header_zone = NULL; } break; case MOD_SHUTDOWN: break; default: error = EOPNOTSUPP; break; } return (error); } static int vtnet_probe(device_t dev) { if (virtio_get_device_type(dev) != VIRTIO_ID_NETWORK) return (ENXIO); device_set_desc(dev, "VirtIO Networking Adapter"); return (BUS_PROBE_DEFAULT); } static int vtnet_attach(device_t dev) { struct vtnet_softc *sc; struct ifnet *ifp; int tx_size, error; sc = device_get_softc(dev); sc->vtnet_dev = dev; VTNET_LOCK_INIT(sc); callout_init_mtx(&sc->vtnet_tick_ch, VTNET_MTX(sc), 0); ifmedia_init(&sc->vtnet_media, IFM_IMASK, vtnet_ifmedia_upd, vtnet_ifmedia_sts); ifmedia_add(&sc->vtnet_media, VTNET_MEDIATYPE, 0, NULL); ifmedia_set(&sc->vtnet_media, VTNET_MEDIATYPE); vtnet_add_statistics(sc); virtio_set_feature_desc(dev, vtnet_feature_desc); vtnet_negotiate_features(sc); if (virtio_with_feature(dev, VIRTIO_NET_F_MRG_RXBUF)) { sc->vtnet_flags |= VTNET_FLAG_MRG_RXBUFS; sc->vtnet_hdr_size = sizeof(struct virtio_net_hdr_mrg_rxbuf); } else sc->vtnet_hdr_size = sizeof(struct virtio_net_hdr); sc->vtnet_rx_mbuf_size = MCLBYTES; sc->vtnet_rx_mbuf_count = VTNET_NEEDED_RX_MBUFS(sc); if (virtio_with_feature(dev, VIRTIO_NET_F_CTRL_VQ)) { sc->vtnet_flags |= VTNET_FLAG_CTRL_VQ; if (virtio_with_feature(dev, VIRTIO_NET_F_CTRL_RX)) { sc->vtnet_mac_filter = malloc( sizeof(struct vtnet_mac_filter), M_DEVBUF, M_NOWAIT | M_ZERO); if (sc->vtnet_mac_filter == NULL) { device_printf(dev, "cannot allocate mac filter table\n"); error = ENOMEM; goto fail; } sc->vtnet_flags |= VTNET_FLAG_CTRL_RX; } if (virtio_with_feature(dev, VIRTIO_NET_F_CTRL_VLAN)) sc->vtnet_flags |= VTNET_FLAG_VLAN_FILTER; } vtnet_get_hwaddr(sc); error = vtnet_alloc_virtqueues(sc); if (error) { device_printf(dev, "cannot allocate virtqueues\n"); goto fail; } ifp = sc->vtnet_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { device_printf(dev, "cannot allocate ifnet structure\n"); error = ENOSPC; goto fail; } ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = vtnet_init; ifp->if_start = vtnet_start; ifp->if_ioctl = vtnet_ioctl; sc->vtnet_rx_size = virtqueue_size(sc->vtnet_rx_vq); sc->vtnet_rx_process_limit = sc->vtnet_rx_size; tx_size = virtqueue_size(sc->vtnet_tx_vq); sc->vtnet_tx_size = tx_size; IFQ_SET_MAXLEN(&ifp->if_snd, tx_size - 1); ifp->if_snd.ifq_drv_maxlen = tx_size - 1; IFQ_SET_READY(&ifp->if_snd); ether_ifattach(ifp, sc->vtnet_hwaddr); if (virtio_with_feature(dev, VIRTIO_NET_F_STATUS)) ifp->if_capabilities |= IFCAP_LINKSTATE; /* Tell the upper layer(s) we support long frames. */ ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); ifp->if_capabilities |= IFCAP_JUMBO_MTU | IFCAP_VLAN_MTU; if (virtio_with_feature(dev, VIRTIO_NET_F_CSUM)) { ifp->if_capabilities |= IFCAP_TXCSUM; if (virtio_with_feature(dev, VIRTIO_NET_F_HOST_TSO4)) ifp->if_capabilities |= IFCAP_TSO4; if (virtio_with_feature(dev, VIRTIO_NET_F_HOST_TSO6)) ifp->if_capabilities |= IFCAP_TSO6; if (ifp->if_capabilities & IFCAP_TSO) ifp->if_capabilities |= IFCAP_VLAN_HWTSO; if (virtio_with_feature(dev, VIRTIO_NET_F_HOST_ECN)) sc->vtnet_flags |= VTNET_FLAG_TSO_ECN; } if (virtio_with_feature(dev, VIRTIO_NET_F_GUEST_CSUM)) { ifp->if_capabilities |= IFCAP_RXCSUM; if (virtio_with_feature(dev, VIRTIO_NET_F_GUEST_TSO4) || virtio_with_feature(dev, VIRTIO_NET_F_GUEST_TSO6)) ifp->if_capabilities |= IFCAP_LRO; } if (ifp->if_capabilities & IFCAP_HWCSUM) { /* * VirtIO does not support VLAN tagging, but we can fake * it by inserting and removing the 802.1Q header during * transmit and receive. We are then able to do checksum * offloading of VLAN frames. */ ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM; } ifp->if_capenable = ifp->if_capabilities; /* * Capabilities after here are not enabled by default. */ if (sc->vtnet_flags & VTNET_FLAG_VLAN_FILTER) { ifp->if_capabilities |= IFCAP_VLAN_HWFILTER; sc->vtnet_vlan_attach = EVENTHANDLER_REGISTER(vlan_config, vtnet_register_vlan, sc, EVENTHANDLER_PRI_FIRST); sc->vtnet_vlan_detach = EVENTHANDLER_REGISTER(vlan_unconfig, vtnet_unregister_vlan, sc, EVENTHANDLER_PRI_FIRST); } #ifdef DEVICE_POLLING ifp->if_capabilities |= IFCAP_POLLING; #endif TASK_INIT(&sc->vtnet_rx_intr_task, 0, vtnet_rx_intr_task, sc); TASK_INIT(&sc->vtnet_tx_intr_task, 0, vtnet_tx_intr_task, sc); TASK_INIT(&sc->vtnet_cfgchg_task, 0, vtnet_config_change_task, sc); sc->vtnet_tq = taskqueue_create_fast("vtnet_taskq", M_NOWAIT, taskqueue_thread_enqueue, &sc->vtnet_tq); if (sc->vtnet_tq == NULL) { error = ENOMEM; device_printf(dev, "cannot allocate taskqueue\n"); ether_ifdetach(ifp); goto fail; } error = virtio_setup_intr(dev, INTR_TYPE_NET); if (error) { device_printf(dev, "cannot setup virtqueue interrupts\n"); ether_ifdetach(ifp); goto fail; } taskqueue_start_threads(&sc->vtnet_tq, 1, PI_NET, "%s taskq", device_get_nameunit(dev)); /* * Device defaults to promiscuous mode for backwards * compatibility. Turn it off if possible. */ if (sc->vtnet_flags & VTNET_FLAG_CTRL_RX) { VTNET_LOCK(sc); if (vtnet_set_promisc(sc, 0) != 0) { ifp->if_flags |= IFF_PROMISC; device_printf(dev, "cannot disable promiscuous mode\n"); } VTNET_UNLOCK(sc); } else ifp->if_flags |= IFF_PROMISC; fail: if (error) vtnet_detach(dev); return (error); } static int vtnet_detach(device_t dev) { struct vtnet_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); ifp = sc->vtnet_ifp; KASSERT(mtx_initialized(VTNET_MTX(sc)), ("vtnet mutex not initialized")); #ifdef DEVICE_POLLING if (ifp != NULL && ifp->if_capenable & IFCAP_POLLING) ether_poll_deregister(ifp); #endif if (device_is_attached(dev)) { VTNET_LOCK(sc); vtnet_stop(sc); VTNET_UNLOCK(sc); callout_drain(&sc->vtnet_tick_ch); taskqueue_drain(taskqueue_fast, &sc->vtnet_cfgchg_task); ether_ifdetach(ifp); } if (sc->vtnet_tq != NULL) { taskqueue_drain(sc->vtnet_tq, &sc->vtnet_rx_intr_task); taskqueue_drain(sc->vtnet_tq, &sc->vtnet_tx_intr_task); taskqueue_free(sc->vtnet_tq); sc->vtnet_tq = NULL; } if (sc->vtnet_vlan_attach != NULL) { EVENTHANDLER_DEREGISTER(vlan_config, sc->vtnet_vlan_attach); sc->vtnet_vlan_attach = NULL; } if (sc->vtnet_vlan_detach != NULL) { EVENTHANDLER_DEREGISTER(vlan_unconfg, sc->vtnet_vlan_detach); sc->vtnet_vlan_detach = NULL; } if (sc->vtnet_mac_filter != NULL) { free(sc->vtnet_mac_filter, M_DEVBUF); sc->vtnet_mac_filter = NULL; } if (ifp != NULL) { if_free(ifp); sc->vtnet_ifp = NULL; } if (sc->vtnet_rx_vq != NULL) vtnet_free_rx_mbufs(sc); if (sc->vtnet_tx_vq != NULL) vtnet_free_tx_mbufs(sc); if (sc->vtnet_ctrl_vq != NULL) vtnet_free_ctrl_vq(sc); ifmedia_removeall(&sc->vtnet_media); VTNET_LOCK_DESTROY(sc); return (0); } static int vtnet_suspend(device_t dev) { struct vtnet_softc *sc; sc = device_get_softc(dev); VTNET_LOCK(sc); vtnet_stop(sc); sc->vtnet_flags |= VTNET_FLAG_SUSPENDED; VTNET_UNLOCK(sc); return (0); } static int vtnet_resume(device_t dev) { struct vtnet_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); ifp = sc->vtnet_ifp; VTNET_LOCK(sc); if (ifp->if_flags & IFF_UP) vtnet_init_locked(sc); sc->vtnet_flags &= ~VTNET_FLAG_SUSPENDED; VTNET_UNLOCK(sc); return (0); } static int vtnet_shutdown(device_t dev) { /* * Suspend already does all of what we need to * do here; we just never expect to be resumed. */ return (vtnet_suspend(dev)); } static int vtnet_config_change(device_t dev) { struct vtnet_softc *sc; sc = device_get_softc(dev); taskqueue_enqueue_fast(taskqueue_fast, &sc->vtnet_cfgchg_task); return (1); } static void vtnet_negotiate_features(struct vtnet_softc *sc) { device_t dev; uint64_t mask, features; dev = sc->vtnet_dev; mask = 0; if (vtnet_csum_disable) mask |= VIRTIO_NET_F_CSUM | VIRTIO_NET_F_GUEST_CSUM; /* * TSO and LRO are only available when their corresponding * checksum offload feature is also negotiated. */ if (vtnet_csum_disable || vtnet_tso_disable) mask |= VIRTIO_NET_F_HOST_TSO4 | VIRTIO_NET_F_HOST_TSO6 | VIRTIO_NET_F_HOST_ECN; if (vtnet_csum_disable || vtnet_lro_disable) mask |= VTNET_LRO_FEATURES; features = VTNET_FEATURES & ~mask; #ifdef VTNET_TX_INTR_MODERATION features |= VIRTIO_F_NOTIFY_ON_EMPTY; #endif sc->vtnet_features = virtio_negotiate_features(dev, features); if (virtio_with_feature(dev, VIRTIO_NET_F_MRG_RXBUF) == 0 && virtio_with_feature(dev, VTNET_LRO_FEATURES)) { /* * LRO without mergeable buffers requires special care. This * is not ideal because every receive buffer must be large * enough to hold the maximum TCP packet, the Ethernet header, * and the vtnet_rx_header. This requires up to 34 descriptors * when using MCLBYTES clusters. If we do not have indirect * descriptors, LRO is disabled since the virtqueue will not * be able to contain very many receive buffers. */ if (virtio_with_feature(dev, VIRTIO_RING_F_INDIRECT_DESC) == 0) { device_printf(dev, "LRO disabled due to lack of both mergeable " "buffers and indirect descriptors\n"); sc->vtnet_features = virtio_negotiate_features(dev, features & ~VTNET_LRO_FEATURES); } else sc->vtnet_flags |= VTNET_FLAG_LRO_NOMRG; } } static int vtnet_alloc_virtqueues(struct vtnet_softc *sc) { device_t dev; struct vq_alloc_info vq_info[3]; int nvqs, rxsegs; dev = sc->vtnet_dev; nvqs = 2; /* * Indirect descriptors are not needed for the Rx * virtqueue when mergeable buffers are negotiated. * The header is placed inline with the data, not * in a separate descriptor, and mbuf clusters are * always physically contiguous. */ if ((sc->vtnet_flags & VTNET_FLAG_MRG_RXBUFS) == 0) { rxsegs = sc->vtnet_flags & VTNET_FLAG_LRO_NOMRG ? VTNET_MAX_RX_SEGS : VTNET_MIN_RX_SEGS; } else rxsegs = 0; VQ_ALLOC_INFO_INIT(&vq_info[0], rxsegs, vtnet_rx_vq_intr, sc, &sc->vtnet_rx_vq, "%s receive", device_get_nameunit(dev)); VQ_ALLOC_INFO_INIT(&vq_info[1], VTNET_MAX_TX_SEGS, vtnet_tx_vq_intr, sc, &sc->vtnet_tx_vq, "%s transmit", device_get_nameunit(dev)); if (sc->vtnet_flags & VTNET_FLAG_CTRL_VQ) { nvqs++; VQ_ALLOC_INFO_INIT(&vq_info[2], 0, NULL, NULL, &sc->vtnet_ctrl_vq, "%s control", device_get_nameunit(dev)); } return (virtio_alloc_virtqueues(dev, 0, nvqs, vq_info)); } static void vtnet_get_hwaddr(struct vtnet_softc *sc) { device_t dev; dev = sc->vtnet_dev; if (virtio_with_feature(dev, VIRTIO_NET_F_MAC)) { virtio_read_device_config(dev, offsetof(struct virtio_net_config, mac), sc->vtnet_hwaddr, ETHER_ADDR_LEN); } else { /* Generate random locally administered unicast address. */ sc->vtnet_hwaddr[0] = 0xB2; arc4rand(&sc->vtnet_hwaddr[1], ETHER_ADDR_LEN - 1, 0); vtnet_set_hwaddr(sc); } } static void vtnet_set_hwaddr(struct vtnet_softc *sc) { device_t dev; dev = sc->vtnet_dev; virtio_write_device_config(dev, offsetof(struct virtio_net_config, mac), sc->vtnet_hwaddr, ETHER_ADDR_LEN); } static int vtnet_is_link_up(struct vtnet_softc *sc) { device_t dev; struct ifnet *ifp; uint16_t status; dev = sc->vtnet_dev; ifp = sc->vtnet_ifp; VTNET_LOCK_ASSERT(sc); if ((ifp->if_capenable & IFCAP_LINKSTATE) == 0) return (1); status = virtio_read_dev_config_2(dev, offsetof(struct virtio_net_config, status)); return ((status & VIRTIO_NET_S_LINK_UP) != 0); } static void vtnet_update_link_status(struct vtnet_softc *sc) { struct ifnet *ifp; int link; ifp = sc->vtnet_ifp; link = vtnet_is_link_up(sc); if (link && ((sc->vtnet_flags & VTNET_FLAG_LINK) == 0)) { sc->vtnet_flags |= VTNET_FLAG_LINK; if_link_state_change(ifp, LINK_STATE_UP); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) vtnet_start_locked(ifp); } else if (!link && (sc->vtnet_flags & VTNET_FLAG_LINK)) { sc->vtnet_flags &= ~VTNET_FLAG_LINK; if_link_state_change(ifp, LINK_STATE_DOWN); } } static void vtnet_watchdog(struct vtnet_softc *sc) { struct ifnet *ifp; ifp = sc->vtnet_ifp; #ifdef VTNET_TX_INTR_MODERATION vtnet_txeof(sc); #endif if (sc->vtnet_watchdog_timer == 0 || --sc->vtnet_watchdog_timer) return; if_printf(ifp, "watchdog timeout -- resetting\n"); #ifdef VTNET_DEBUG virtqueue_dump(sc->vtnet_tx_vq); #endif ifp->if_oerrors++; ifp->if_drv_flags &= ~IFF_DRV_RUNNING; vtnet_init_locked(sc); } static void vtnet_config_change_task(void *arg, int pending) { struct vtnet_softc *sc; sc = arg; VTNET_LOCK(sc); vtnet_update_link_status(sc); VTNET_UNLOCK(sc); } static int vtnet_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct vtnet_softc *sc; struct ifreq *ifr; int reinit, mask, error; sc = ifp->if_softc; ifr = (struct ifreq *) data; reinit = 0; error = 0; switch (cmd) { case SIOCSIFMTU: if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > VTNET_MAX_MTU) error = EINVAL; else if (ifp->if_mtu != ifr->ifr_mtu) { VTNET_LOCK(sc); error = vtnet_change_mtu(sc, ifr->ifr_mtu); VTNET_UNLOCK(sc); } break; case SIOCSIFFLAGS: VTNET_LOCK(sc); if ((ifp->if_flags & IFF_UP) == 0) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) vtnet_stop(sc); } else if (ifp->if_drv_flags & IFF_DRV_RUNNING) { if ((ifp->if_flags ^ sc->vtnet_if_flags) & (IFF_PROMISC | IFF_ALLMULTI)) { if (sc->vtnet_flags & VTNET_FLAG_CTRL_RX) vtnet_rx_filter(sc); else error = ENOTSUP; } } else vtnet_init_locked(sc); if (error == 0) sc->vtnet_if_flags = ifp->if_flags; VTNET_UNLOCK(sc); break; case SIOCADDMULTI: case SIOCDELMULTI: VTNET_LOCK(sc); if ((sc->vtnet_flags & VTNET_FLAG_CTRL_RX) && (ifp->if_drv_flags & IFF_DRV_RUNNING)) vtnet_rx_filter_mac(sc); VTNET_UNLOCK(sc); break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &sc->vtnet_media, cmd); break; case SIOCSIFCAP: mask = ifr->ifr_reqcap ^ ifp->if_capenable; #ifdef DEVICE_POLLING if (mask & IFCAP_POLLING) { if (ifr->ifr_reqcap & IFCAP_POLLING) { error = ether_poll_register(vtnet_poll, ifp); if (error) break; VTNET_LOCK(sc); vtnet_disable_rx_intr(sc); vtnet_disable_tx_intr(sc); ifp->if_capenable |= IFCAP_POLLING; VTNET_UNLOCK(sc); } else { error = ether_poll_deregister(ifp); /* Enable interrupts even in error case. */ VTNET_LOCK(sc); vtnet_enable_tx_intr(sc); vtnet_enable_rx_intr(sc); ifp->if_capenable &= ~IFCAP_POLLING; VTNET_UNLOCK(sc); } } #endif VTNET_LOCK(sc); if (mask & IFCAP_TXCSUM) { ifp->if_capenable ^= IFCAP_TXCSUM; if (ifp->if_capenable & IFCAP_TXCSUM) ifp->if_hwassist |= VTNET_CSUM_OFFLOAD; else ifp->if_hwassist &= ~VTNET_CSUM_OFFLOAD; } if (mask & IFCAP_TSO4) { ifp->if_capenable ^= IFCAP_TSO4; if (ifp->if_capenable & IFCAP_TSO4) ifp->if_hwassist |= CSUM_TSO; else ifp->if_hwassist &= ~CSUM_TSO; } if (mask & IFCAP_RXCSUM) { ifp->if_capenable ^= IFCAP_RXCSUM; reinit = 1; } if (mask & IFCAP_LRO) { ifp->if_capenable ^= IFCAP_LRO; reinit = 1; } if (mask & IFCAP_VLAN_HWFILTER) { ifp->if_capenable ^= IFCAP_VLAN_HWFILTER; reinit = 1; } if (mask & IFCAP_VLAN_HWTSO) ifp->if_capenable ^= IFCAP_VLAN_HWTSO; if (mask & IFCAP_VLAN_HWTAGGING) ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; if (reinit && (ifp->if_drv_flags & IFF_DRV_RUNNING)) { ifp->if_drv_flags &= ~IFF_DRV_RUNNING; vtnet_init_locked(sc); } VLAN_CAPABILITIES(ifp); VTNET_UNLOCK(sc); break; default: error = ether_ioctl(ifp, cmd, data); break; } VTNET_LOCK_ASSERT_NOTOWNED(sc); return (error); } static int vtnet_change_mtu(struct vtnet_softc *sc, int new_mtu) { struct ifnet *ifp; int new_frame_size, clsize; ifp = sc->vtnet_ifp; if ((sc->vtnet_flags & VTNET_FLAG_MRG_RXBUFS) == 0) { new_frame_size = sizeof(struct vtnet_rx_header) + sizeof(struct ether_vlan_header) + new_mtu; if (new_frame_size > MJUM9BYTES) return (EINVAL); if (new_frame_size <= MCLBYTES) clsize = MCLBYTES; else clsize = MJUM9BYTES; } else { new_frame_size = sizeof(struct virtio_net_hdr_mrg_rxbuf) + sizeof(struct ether_vlan_header) + new_mtu; if (new_frame_size <= MCLBYTES) clsize = MCLBYTES; else clsize = MJUMPAGESIZE; } sc->vtnet_rx_mbuf_size = clsize; sc->vtnet_rx_mbuf_count = VTNET_NEEDED_RX_MBUFS(sc); KASSERT(sc->vtnet_rx_mbuf_count < VTNET_MAX_RX_SEGS, ("too many rx mbufs: %d", sc->vtnet_rx_mbuf_count)); ifp->if_mtu = new_mtu; if (ifp->if_drv_flags & IFF_DRV_RUNNING) { ifp->if_drv_flags &= ~IFF_DRV_RUNNING; vtnet_init_locked(sc); } return (0); } static int vtnet_init_rx_vq(struct vtnet_softc *sc) { struct virtqueue *vq; int nbufs, error; vq = sc->vtnet_rx_vq; nbufs = 0; error = ENOSPC; while (!virtqueue_full(vq)) { if ((error = vtnet_newbuf(sc)) != 0) break; nbufs++; } if (nbufs > 0) { virtqueue_notify(vq); /* * EMSGSIZE signifies the virtqueue did not have enough * entries available to hold the last mbuf. This is not * an error. We should not get ENOSPC since we check if * the virtqueue is full before attempting to add a * buffer. */ if (error == EMSGSIZE) error = 0; } return (error); } static void vtnet_free_rx_mbufs(struct vtnet_softc *sc) { struct virtqueue *vq; struct mbuf *m; int last; vq = sc->vtnet_rx_vq; last = 0; while ((m = virtqueue_drain(vq, &last)) != NULL) m_freem(m); KASSERT(virtqueue_empty(vq), ("mbufs remaining in Rx Vq")); } static void vtnet_free_tx_mbufs(struct vtnet_softc *sc) { struct virtqueue *vq; struct vtnet_tx_header *txhdr; int last; vq = sc->vtnet_tx_vq; last = 0; while ((txhdr = virtqueue_drain(vq, &last)) != NULL) { m_freem(txhdr->vth_mbuf); uma_zfree(vtnet_tx_header_zone, txhdr); } KASSERT(virtqueue_empty(vq), ("mbufs remaining in Tx Vq")); } static void vtnet_free_ctrl_vq(struct vtnet_softc *sc) { /* * The control virtqueue is only polled, therefore * it should already be empty. */ KASSERT(virtqueue_empty(sc->vtnet_ctrl_vq), ("Ctrl Vq not empty")); } #ifdef DEVICE_POLLING static int vtnet_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) { struct vtnet_softc *sc; int rx_done; sc = ifp->if_softc; rx_done = 0; VTNET_LOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) { if (cmd == POLL_AND_CHECK_STATUS) vtnet_update_link_status(sc); if (virtqueue_nused(sc->vtnet_rx_vq) > 0) vtnet_rxeof(sc, count, &rx_done); vtnet_txeof(sc); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) vtnet_start_locked(ifp); } VTNET_UNLOCK(sc); return (rx_done); } #endif /* DEVICE_POLLING */ static struct mbuf * vtnet_alloc_rxbuf(struct vtnet_softc *sc, int nbufs, struct mbuf **m_tailp) { struct mbuf *m_head, *m_tail, *m; int i, clsize; clsize = sc->vtnet_rx_mbuf_size; m_head = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, clsize); if (m_head == NULL) goto fail; m_head->m_len = clsize; m_tail = m_head; if (nbufs > 1) { KASSERT(sc->vtnet_flags & VTNET_FLAG_LRO_NOMRG, ("chained Rx mbuf requested without LRO_NOMRG")); for (i = 1; i < nbufs; i++) { m = m_getjcl(M_DONTWAIT, MT_DATA, 0, clsize); if (m == NULL) goto fail; m->m_len = clsize; m_tail->m_next = m; m_tail = m; } } if (m_tailp != NULL) *m_tailp = m_tail; return (m_head); fail: sc->vtnet_stats.mbuf_alloc_failed++; m_freem(m_head); return (NULL); } static int vtnet_replace_rxbuf(struct vtnet_softc *sc, struct mbuf *m0, int len0) { struct mbuf *m, *m_prev; struct mbuf *m_new, *m_tail; int len, clsize, nreplace, error; m = m0; m_prev = NULL; len = len0; m_tail = NULL; clsize = sc->vtnet_rx_mbuf_size; nreplace = 0; KASSERT(sc->vtnet_flags & VTNET_FLAG_LRO_NOMRG || m->m_next == NULL, ("chained Rx mbuf without LRO_NOMRG")); /* * Since LRO_NOMRG mbuf chains are so large, we want to avoid * allocating an entire chain for each received frame. When * the received frame's length is less than that of the chain, * the unused mbufs are reassigned to the new chain. */ while (len > 0) { /* * Something is seriously wrong if we received * a frame larger than the mbuf chain. Drop it. */ if (m == NULL) { sc->vtnet_stats.rx_frame_too_large++; return (EMSGSIZE); } KASSERT(m->m_len == clsize, ("mbuf length not expected cluster size: %d", m->m_len)); m->m_len = MIN(m->m_len, len); len -= m->m_len; m_prev = m; m = m->m_next; nreplace++; } KASSERT(m_prev != NULL, ("m_prev == NULL")); KASSERT(nreplace <= sc->vtnet_rx_mbuf_count, ("too many replacement mbufs: %d/%d", nreplace, sc->vtnet_rx_mbuf_count)); m_new = vtnet_alloc_rxbuf(sc, nreplace, &m_tail); if (m_new == NULL) { m_prev->m_len = clsize; return (ENOBUFS); } /* * Move unused mbufs, if any, from the original chain * onto the end of the new chain. */ if (m_prev->m_next != NULL) { m_tail->m_next = m_prev->m_next; m_prev->m_next = NULL; } error = vtnet_enqueue_rxbuf(sc, m_new); if (error) { /* * BAD! We could not enqueue the replacement mbuf chain. We * must restore the m0 chain to the original state if it was * modified so we can subsequently discard it. * * NOTE: The replacement is suppose to be an identical copy * to the one just dequeued so this is an unexpected error. */ sc->vtnet_stats.rx_enq_replacement_failed++; if (m_tail->m_next != NULL) { m_prev->m_next = m_tail->m_next; m_tail->m_next = NULL; } m_prev->m_len = clsize; m_freem(m_new); } return (error); } static int vtnet_newbuf(struct vtnet_softc *sc) { struct mbuf *m; int error; m = vtnet_alloc_rxbuf(sc, sc->vtnet_rx_mbuf_count, NULL); if (m == NULL) return (ENOBUFS); error = vtnet_enqueue_rxbuf(sc, m); if (error) m_freem(m); return (error); } static void vtnet_discard_merged_rxbuf(struct vtnet_softc *sc, int nbufs) { struct virtqueue *vq; struct mbuf *m; vq = sc->vtnet_rx_vq; while (--nbufs > 0) { if ((m = virtqueue_dequeue(vq, NULL)) == NULL) break; vtnet_discard_rxbuf(sc, m); } } static void vtnet_discard_rxbuf(struct vtnet_softc *sc, struct mbuf *m) { int error; /* * Requeue the discarded mbuf. This should always be * successful since it was just dequeued. */ error = vtnet_enqueue_rxbuf(sc, m); KASSERT(error == 0, ("cannot requeue discarded mbuf")); } static int vtnet_enqueue_rxbuf(struct vtnet_softc *sc, struct mbuf *m) { struct sglist sg; struct sglist_seg segs[VTNET_MAX_RX_SEGS]; struct vtnet_rx_header *rxhdr; struct virtio_net_hdr *hdr; uint8_t *mdata; int offset, error; VTNET_LOCK_ASSERT(sc); KASSERT(sc->vtnet_flags & VTNET_FLAG_LRO_NOMRG || m->m_next == NULL, ("chained Rx mbuf without LRO_NOMRG")); sglist_init(&sg, VTNET_MAX_RX_SEGS, segs); mdata = mtod(m, uint8_t *); offset = 0; if ((sc->vtnet_flags & VTNET_FLAG_MRG_RXBUFS) == 0) { rxhdr = (struct vtnet_rx_header *) mdata; hdr = &rxhdr->vrh_hdr; offset += sizeof(struct vtnet_rx_header); error = sglist_append(&sg, hdr, sc->vtnet_hdr_size); KASSERT(error == 0, ("cannot add header to sglist")); } error = sglist_append(&sg, mdata + offset, m->m_len - offset); if (error) return (error); if (m->m_next != NULL) { error = sglist_append_mbuf(&sg, m->m_next); if (error) return (error); } return (virtqueue_enqueue(sc->vtnet_rx_vq, m, &sg, 0, sg.sg_nseg)); } static void vtnet_vlan_tag_remove(struct mbuf *m) { struct ether_vlan_header *evl; evl = mtod(m, struct ether_vlan_header *); m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag); m->m_flags |= M_VLANTAG; /* Strip the 802.1Q header. */ bcopy((char *) evl, (char *) evl + ETHER_VLAN_ENCAP_LEN, ETHER_HDR_LEN - ETHER_TYPE_LEN); m_adj(m, ETHER_VLAN_ENCAP_LEN); } #ifdef notyet static int vtnet_rx_csum(struct vtnet_softc *sc, struct mbuf *m, struct virtio_net_hdr *hdr) { struct ether_header *eh; struct ether_vlan_header *evh; struct ip *ip; struct ip6_hdr *ip6; struct udphdr *udp; int ip_offset, csum_start, csum_offset, hlen; uint16_t eth_type; uint8_t ip_proto; /* * Convert the VirtIO checksum interface to FreeBSD's interface. * The host only provides us with the offset at which to start * checksumming, and the offset from that to place the completed * checksum. While this maps well with how Linux does checksums, * for FreeBSD, we must parse the received packet in order to set * the appropriate CSUM_* flags. */ /* * Every mbuf added to the receive virtqueue is always at least * MCLBYTES big, so assume something is amiss if the first mbuf * does not contain both the Ethernet and protocol headers. */ ip_offset = sizeof(struct ether_header); if (m->m_len < ip_offset) return (1); eh = mtod(m, struct ether_header *); eth_type = ntohs(eh->ether_type); if (eth_type == ETHERTYPE_VLAN) { ip_offset = sizeof(struct ether_vlan_header); if (m->m_len < ip_offset) return (1); evh = mtod(m, struct ether_vlan_header *); eth_type = ntohs(evh->evl_proto); } switch (eth_type) { case ETHERTYPE_IP: if (m->m_len < ip_offset + sizeof(struct ip)) return (1); ip = (struct ip *)(mtod(m, uint8_t *) + ip_offset); /* Sanity check the IP header. */ if (ip->ip_v != IPVERSION) return (1); hlen = ip->ip_hl << 2; if (hlen < sizeof(struct ip)) return (1); if (ntohs(ip->ip_len) < hlen) return (1); if (ntohs(ip->ip_len) != (m->m_pkthdr.len - ip_offset)) return (1); ip_proto = ip->ip_p; csum_start = ip_offset + hlen; break; case ETHERTYPE_IPV6: if (m->m_len < ip_offset + sizeof(struct ip6_hdr)) return (1); /* * XXX FreeBSD does not handle any IPv6 checksum offloading * at the moment. */ ip6 = (struct ip6_hdr *)(mtod(m, uint8_t *) + ip_offset); /* XXX Assume no extension headers are present. */ ip_proto = ip6->ip6_nxt; csum_start = ip_offset + sizeof(struct ip6_hdr); break; default: sc->vtnet_stats.rx_csum_bad_ethtype++; return (1); } /* Assume checksum begins right after the IP header. */ if (hdr->csum_start != csum_start) { sc->vtnet_stats.rx_csum_bad_start++; return (1); } switch (ip_proto) { case IPPROTO_TCP: csum_offset = offsetof(struct tcphdr, th_sum); break; case IPPROTO_UDP: csum_offset = offsetof(struct udphdr, uh_sum); break; case IPPROTO_SCTP: csum_offset = offsetof(struct sctphdr, checksum); break; default: sc->vtnet_stats.rx_csum_bad_ipproto++; return (1); } if (hdr->csum_offset != csum_offset) { sc->vtnet_stats.rx_csum_bad_offset++; return (1); } /* * The IP header checksum is almost certainly valid but I'm * uncertain if that is guaranteed. * * m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED | CSUM_IP_VALID; */ switch (ip_proto) { case IPPROTO_UDP: if (m->m_len < csum_start + sizeof(struct udphdr)) return (1); udp = (struct udphdr *)(mtod(m, uint8_t *) + csum_start); if (udp->uh_sum == 0) return (0); /* FALLTHROUGH */ case IPPROTO_TCP: m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xFFFF; break; case IPPROTO_SCTP: m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID; break; } sc->vtnet_stats.rx_csum_offloaded++; return (0); } #endif /* * Alternative method of doing receive checksum offloading. Rather * than parsing the received frame down to the IP header, use the * csum_offset to determine which CSUM_* flags are appropriate. We * can get by with doing this only because the checksum offsets are * unique for the things we care about. */ static int vtnet_rx_csum(struct vtnet_softc *sc, struct mbuf *m, struct virtio_net_hdr *hdr) { struct ether_header *eh; struct ether_vlan_header *evh; struct udphdr *udp; int csum_len; uint16_t eth_type; csum_len = hdr->csum_start + hdr->csum_offset; if (csum_len < sizeof(struct ether_header) + sizeof(struct ip)) return (1); if (m->m_len < csum_len) return (1); eh = mtod(m, struct ether_header *); eth_type = ntohs(eh->ether_type); if (eth_type == ETHERTYPE_VLAN) { evh = mtod(m, struct ether_vlan_header *); eth_type = ntohs(evh->evl_proto); } if (eth_type != ETHERTYPE_IP && eth_type != ETHERTYPE_IPV6) { sc->vtnet_stats.rx_csum_bad_ethtype++; return (1); } /* Use the offset to determine the appropriate CSUM_* flags. */ switch (hdr->csum_offset) { case offsetof(struct udphdr, uh_sum): if (m->m_len < hdr->csum_start + sizeof(struct udphdr)) return (1); udp = (struct udphdr *)(mtod(m, uint8_t *) + hdr->csum_start); if (udp->uh_sum == 0) return (0); /* FALLTHROUGH */ case offsetof(struct tcphdr, th_sum): m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xFFFF; break; case offsetof(struct sctphdr, checksum): m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID; break; default: sc->vtnet_stats.rx_csum_bad_offset++; return (1); } sc->vtnet_stats.rx_csum_offloaded++; return (0); } static int vtnet_rxeof_merged(struct vtnet_softc *sc, struct mbuf *m_head, int nbufs) { struct ifnet *ifp; struct virtqueue *vq; struct mbuf *m, *m_tail; int len; ifp = sc->vtnet_ifp; vq = sc->vtnet_rx_vq; m_tail = m_head; while (--nbufs > 0) { m = virtqueue_dequeue(vq, &len); if (m == NULL) { ifp->if_ierrors++; goto fail; } if (vtnet_newbuf(sc) != 0) { ifp->if_iqdrops++; vtnet_discard_rxbuf(sc, m); if (nbufs > 1) vtnet_discard_merged_rxbuf(sc, nbufs); goto fail; } if (m->m_len < len) len = m->m_len; m->m_len = len; m->m_flags &= ~M_PKTHDR; m_head->m_pkthdr.len += len; m_tail->m_next = m; m_tail = m; } return (0); fail: sc->vtnet_stats.rx_mergeable_failed++; m_freem(m_head); return (1); } static int vtnet_rxeof(struct vtnet_softc *sc, int count, int *rx_npktsp) { struct virtio_net_hdr lhdr; struct ifnet *ifp; struct virtqueue *vq; struct mbuf *m; struct ether_header *eh; struct virtio_net_hdr *hdr; struct virtio_net_hdr_mrg_rxbuf *mhdr; int len, deq, nbufs, adjsz, rx_npkts; ifp = sc->vtnet_ifp; vq = sc->vtnet_rx_vq; hdr = &lhdr; deq = 0; rx_npkts = 0; VTNET_LOCK_ASSERT(sc); while (--count >= 0) { m = virtqueue_dequeue(vq, &len); if (m == NULL) break; deq++; if (len < sc->vtnet_hdr_size + ETHER_HDR_LEN) { ifp->if_ierrors++; vtnet_discard_rxbuf(sc, m); continue; } if ((sc->vtnet_flags & VTNET_FLAG_MRG_RXBUFS) == 0) { nbufs = 1; adjsz = sizeof(struct vtnet_rx_header); /* * Account for our pad between the header and * the actual start of the frame. */ len += VTNET_RX_HEADER_PAD; } else { mhdr = mtod(m, struct virtio_net_hdr_mrg_rxbuf *); nbufs = mhdr->num_buffers; adjsz = sizeof(struct virtio_net_hdr_mrg_rxbuf); } if (vtnet_replace_rxbuf(sc, m, len) != 0) { ifp->if_iqdrops++; vtnet_discard_rxbuf(sc, m); if (nbufs > 1) vtnet_discard_merged_rxbuf(sc, nbufs); continue; } m->m_pkthdr.len = len; m->m_pkthdr.rcvif = ifp; m->m_pkthdr.csum_flags = 0; if (nbufs > 1) { if (vtnet_rxeof_merged(sc, m, nbufs) != 0) continue; } ifp->if_ipackets++; /* * Save copy of header before we strip it. For both mergeable * and non-mergeable, the VirtIO header is placed first in the * mbuf's data. We no longer need num_buffers, so always use a * virtio_net_hdr. */ memcpy(hdr, mtod(m, void *), sizeof(struct virtio_net_hdr)); m_adj(m, adjsz); if (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) { eh = mtod(m, struct ether_header *); if (eh->ether_type == htons(ETHERTYPE_VLAN)) { vtnet_vlan_tag_remove(m); /* * With the 802.1Q header removed, update the * checksum starting location accordingly. */ if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) hdr->csum_start -= ETHER_VLAN_ENCAP_LEN; } } if (ifp->if_capenable & IFCAP_RXCSUM && hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { if (vtnet_rx_csum(sc, m, hdr) != 0) sc->vtnet_stats.rx_csum_failed++; } VTNET_UNLOCK(sc); rx_npkts++; (*ifp->if_input)(ifp, m); VTNET_LOCK(sc); /* * The interface may have been stopped while we were * passing the packet up the network stack. */ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) break; } if (deq > 0) virtqueue_notify(vq); if (rx_npktsp != NULL) *rx_npktsp = rx_npkts; return (count > 0 ? 0 : EAGAIN); } static void vtnet_rx_intr_task(void *arg, int pending) { struct vtnet_softc *sc; struct ifnet *ifp; int more; sc = arg; ifp = sc->vtnet_ifp; VTNET_LOCK(sc); #ifdef DEVICE_POLLING if (ifp->if_capenable & IFCAP_POLLING) { VTNET_UNLOCK(sc); return; } #endif if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { vtnet_enable_rx_intr(sc); VTNET_UNLOCK(sc); return; } more = vtnet_rxeof(sc, sc->vtnet_rx_process_limit, NULL); if (!more && vtnet_enable_rx_intr(sc) != 0) { vtnet_disable_rx_intr(sc); more = 1; } VTNET_UNLOCK(sc); if (more) { sc->vtnet_stats.rx_task_rescheduled++; taskqueue_enqueue_fast(sc->vtnet_tq, &sc->vtnet_rx_intr_task); } } static int vtnet_rx_vq_intr(void *xsc) { struct vtnet_softc *sc; sc = xsc; vtnet_disable_rx_intr(sc); taskqueue_enqueue_fast(sc->vtnet_tq, &sc->vtnet_rx_intr_task); return (1); } static void vtnet_txeof(struct vtnet_softc *sc) { struct virtqueue *vq; struct ifnet *ifp; struct vtnet_tx_header *txhdr; int deq; vq = sc->vtnet_tx_vq; ifp = sc->vtnet_ifp; deq = 0; VTNET_LOCK_ASSERT(sc); while ((txhdr = virtqueue_dequeue(vq, NULL)) != NULL) { deq++; ifp->if_opackets++; m_freem(txhdr->vth_mbuf); uma_zfree(vtnet_tx_header_zone, txhdr); } if (deq > 0) { ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; if (virtqueue_empty(vq)) sc->vtnet_watchdog_timer = 0; } } static struct mbuf * vtnet_tx_offload(struct vtnet_softc *sc, struct mbuf *m, struct virtio_net_hdr *hdr) { struct ifnet *ifp; struct ether_header *eh; struct ether_vlan_header *evh; struct ip *ip; struct ip6_hdr *ip6; struct tcphdr *tcp; int ip_offset; uint16_t eth_type, csum_start; uint8_t ip_proto, gso_type; ifp = sc->vtnet_ifp; M_ASSERTPKTHDR(m); ip_offset = sizeof(struct ether_header); if (m->m_len < ip_offset) { if ((m = m_pullup(m, ip_offset)) == NULL) return (NULL); } eh = mtod(m, struct ether_header *); eth_type = ntohs(eh->ether_type); if (eth_type == ETHERTYPE_VLAN) { ip_offset = sizeof(struct ether_vlan_header); if (m->m_len < ip_offset) { if ((m = m_pullup(m, ip_offset)) == NULL) return (NULL); } evh = mtod(m, struct ether_vlan_header *); eth_type = ntohs(evh->evl_proto); } switch (eth_type) { case ETHERTYPE_IP: if (m->m_len < ip_offset + sizeof(struct ip)) { m = m_pullup(m, ip_offset + sizeof(struct ip)); if (m == NULL) return (NULL); } ip = (struct ip *)(mtod(m, uint8_t *) + ip_offset); ip_proto = ip->ip_p; csum_start = ip_offset + (ip->ip_hl << 2); gso_type = VIRTIO_NET_HDR_GSO_TCPV4; break; case ETHERTYPE_IPV6: if (m->m_len < ip_offset + sizeof(struct ip6_hdr)) { m = m_pullup(m, ip_offset + sizeof(struct ip6_hdr)); if (m == NULL) return (NULL); } ip6 = (struct ip6_hdr *)(mtod(m, uint8_t *) + ip_offset); /* * XXX Assume no extension headers are present. Presently, * this will always be true in the case of TSO, and FreeBSD * does not perform checksum offloading of IPv6 yet. */ ip_proto = ip6->ip6_nxt; csum_start = ip_offset + sizeof(struct ip6_hdr); gso_type = VIRTIO_NET_HDR_GSO_TCPV6; break; default: return (m); } if (m->m_pkthdr.csum_flags & VTNET_CSUM_OFFLOAD) { hdr->flags |= VIRTIO_NET_HDR_F_NEEDS_CSUM; hdr->csum_start = csum_start; hdr->csum_offset = m->m_pkthdr.csum_data; sc->vtnet_stats.tx_csum_offloaded++; } if (m->m_pkthdr.csum_flags & CSUM_TSO) { if (ip_proto != IPPROTO_TCP) return (m); if (m->m_len < csum_start + sizeof(struct tcphdr)) { m = m_pullup(m, csum_start + sizeof(struct tcphdr)); if (m == NULL) return (NULL); } tcp = (struct tcphdr *)(mtod(m, uint8_t *) + csum_start); hdr->gso_type = gso_type; hdr->hdr_len = csum_start + (tcp->th_off << 2); hdr->gso_size = m->m_pkthdr.tso_segsz; if (tcp->th_flags & TH_CWR) { /* * Drop if we did not negotiate VIRTIO_NET_F_HOST_ECN. * ECN support is only configurable globally with the * net.inet.tcp.ecn.enable sysctl knob. */ if ((sc->vtnet_flags & VTNET_FLAG_TSO_ECN) == 0) { if_printf(ifp, "TSO with ECN not supported " "by host\n"); m_freem(m); return (NULL); } hdr->flags |= VIRTIO_NET_HDR_GSO_ECN; } sc->vtnet_stats.tx_tso_offloaded++; } return (m); } static int vtnet_enqueue_txbuf(struct vtnet_softc *sc, struct mbuf **m_head, struct vtnet_tx_header *txhdr) { struct sglist sg; struct sglist_seg segs[VTNET_MAX_TX_SEGS]; struct virtqueue *vq; struct mbuf *m; int collapsed, error; vq = sc->vtnet_tx_vq; m = *m_head; collapsed = 0; sglist_init(&sg, VTNET_MAX_TX_SEGS, segs); error = sglist_append(&sg, &txhdr->vth_uhdr, sc->vtnet_hdr_size); KASSERT(error == 0 && sg.sg_nseg == 1, ("cannot add header to sglist")); again: error = sglist_append_mbuf(&sg, m); if (error) { if (collapsed) goto fail; m = m_collapse(m, M_DONTWAIT, VTNET_MAX_TX_SEGS - 1); if (m == NULL) goto fail; *m_head = m; collapsed = 1; goto again; } txhdr->vth_mbuf = m; return (virtqueue_enqueue(vq, txhdr, &sg, sg.sg_nseg, 0)); fail: m_freem(*m_head); *m_head = NULL; return (ENOBUFS); } static int vtnet_encap(struct vtnet_softc *sc, struct mbuf **m_head) { struct vtnet_tx_header *txhdr; struct virtio_net_hdr *hdr; struct mbuf *m; int error; m = *m_head; txhdr = uma_zalloc(vtnet_tx_header_zone, M_NOWAIT | M_ZERO); if (txhdr == NULL) { *m_head = NULL; m_freem(m); return (ENOMEM); } /* * Always use the non-mergeable header to simplify things. When * the mergeable feature is negotiated, the num_buffers field * must be set to zero. We use vtnet_hdr_size later to enqueue * the correct header size to the host. */ hdr = &txhdr->vth_uhdr.hdr; if (m->m_flags & M_VLANTAG) { m = ether_vlanencap(m, m->m_pkthdr.ether_vtag); if ((*m_head = m) == NULL) { error = ENOBUFS; goto fail; } m->m_flags &= ~M_VLANTAG; } if (m->m_pkthdr.csum_flags != 0) { m = vtnet_tx_offload(sc, m, hdr); if ((*m_head = m) == NULL) { error = ENOBUFS; goto fail; } } error = vtnet_enqueue_txbuf(sc, m_head, txhdr); fail: if (error) uma_zfree(vtnet_tx_header_zone, txhdr); return (error); } static void vtnet_start(struct ifnet *ifp) { struct vtnet_softc *sc; sc = ifp->if_softc; VTNET_LOCK(sc); vtnet_start_locked(ifp); VTNET_UNLOCK(sc); } static void vtnet_start_locked(struct ifnet *ifp) { struct vtnet_softc *sc; struct virtqueue *vq; struct mbuf *m0; int enq; sc = ifp->if_softc; vq = sc->vtnet_tx_vq; enq = 0; VTNET_LOCK_ASSERT(sc); if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING || ((sc->vtnet_flags & VTNET_FLAG_LINK) == 0)) return; #ifdef VTNET_TX_INTR_MODERATION if (virtqueue_nused(vq) >= sc->vtnet_tx_size / 2) vtnet_txeof(sc); #endif while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) { if (virtqueue_full(vq)) { ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } IFQ_DRV_DEQUEUE(&ifp->if_snd, m0); if (m0 == NULL) break; if (vtnet_encap(sc, &m0) != 0) { if (m0 == NULL) break; IFQ_DRV_PREPEND(&ifp->if_snd, m0); ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } enq++; ETHER_BPF_MTAP(ifp, m0); } if (enq > 0) { virtqueue_notify(vq); sc->vtnet_watchdog_timer = VTNET_WATCHDOG_TIMEOUT; } } static void vtnet_tick(void *xsc) { struct vtnet_softc *sc; sc = xsc; VTNET_LOCK_ASSERT(sc); #ifdef VTNET_DEBUG virtqueue_dump(sc->vtnet_rx_vq); virtqueue_dump(sc->vtnet_tx_vq); #endif vtnet_watchdog(sc); callout_reset(&sc->vtnet_tick_ch, hz, vtnet_tick, sc); } static void vtnet_tx_intr_task(void *arg, int pending) { struct vtnet_softc *sc; struct ifnet *ifp; sc = arg; ifp = sc->vtnet_ifp; VTNET_LOCK(sc); #ifdef DEVICE_POLLING if (ifp->if_capenable & IFCAP_POLLING) { VTNET_UNLOCK(sc); return; } #endif if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { vtnet_enable_tx_intr(sc); VTNET_UNLOCK(sc); return; } vtnet_txeof(sc); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) vtnet_start_locked(ifp); if (vtnet_enable_tx_intr(sc) != 0) { vtnet_disable_tx_intr(sc); sc->vtnet_stats.tx_task_rescheduled++; VTNET_UNLOCK(sc); taskqueue_enqueue_fast(sc->vtnet_tq, &sc->vtnet_tx_intr_task); return; } VTNET_UNLOCK(sc); } static int vtnet_tx_vq_intr(void *xsc) { struct vtnet_softc *sc; sc = xsc; vtnet_disable_tx_intr(sc); taskqueue_enqueue_fast(sc->vtnet_tq, &sc->vtnet_tx_intr_task); return (1); } static void vtnet_stop(struct vtnet_softc *sc) { device_t dev; struct ifnet *ifp; dev = sc->vtnet_dev; ifp = sc->vtnet_ifp; VTNET_LOCK_ASSERT(sc); sc->vtnet_watchdog_timer = 0; callout_stop(&sc->vtnet_tick_ch); ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); vtnet_disable_rx_intr(sc); vtnet_disable_tx_intr(sc); /* * Stop the host VirtIO adapter. Note this will reset the host * adapter's state back to the pre-initialized state, so in * order to make the device usable again, we must drive it * through virtio_reinit() and virtio_reinit_complete(). */ virtio_stop(dev); sc->vtnet_flags &= ~VTNET_FLAG_LINK; vtnet_free_rx_mbufs(sc); vtnet_free_tx_mbufs(sc); } static int vtnet_reinit(struct vtnet_softc *sc) { struct ifnet *ifp; uint64_t features; ifp = sc->vtnet_ifp; features = sc->vtnet_features; /* * Re-negotiate with the host, removing any disabled receive * features. Transmit features are disabled only on our side * via if_capenable and if_hwassist. */ if (ifp->if_capabilities & IFCAP_RXCSUM) { if ((ifp->if_capenable & IFCAP_RXCSUM) == 0) features &= ~VIRTIO_NET_F_GUEST_CSUM; } if (ifp->if_capabilities & IFCAP_LRO) { if ((ifp->if_capenable & IFCAP_LRO) == 0) features &= ~VTNET_LRO_FEATURES; } if (ifp->if_capabilities & IFCAP_VLAN_HWFILTER) { if ((ifp->if_capenable & IFCAP_VLAN_HWFILTER) == 0) features &= ~VIRTIO_NET_F_CTRL_VLAN; } return (virtio_reinit(sc->vtnet_dev, features)); } static void vtnet_init_locked(struct vtnet_softc *sc) { device_t dev; struct ifnet *ifp; int error; dev = sc->vtnet_dev; ifp = sc->vtnet_ifp; VTNET_LOCK_ASSERT(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) return; /* Stop host's adapter, cancel any pending I/O. */ vtnet_stop(sc); /* Reinitialize the host device. */ error = vtnet_reinit(sc); if (error) { device_printf(dev, "reinitialization failed, stopping device...\n"); vtnet_stop(sc); return; } /* Update host with assigned MAC address. */ bcopy(IF_LLADDR(ifp), sc->vtnet_hwaddr, ETHER_ADDR_LEN); vtnet_set_hwaddr(sc); ifp->if_hwassist = 0; if (ifp->if_capenable & IFCAP_TXCSUM) ifp->if_hwassist |= VTNET_CSUM_OFFLOAD; if (ifp->if_capenable & IFCAP_TSO4) ifp->if_hwassist |= CSUM_TSO; error = vtnet_init_rx_vq(sc); if (error) { device_printf(dev, "cannot allocate mbufs for Rx virtqueue\n"); vtnet_stop(sc); return; } if (sc->vtnet_flags & VTNET_FLAG_CTRL_VQ) { if (sc->vtnet_flags & VTNET_FLAG_CTRL_RX) { /* Restore promiscuous and all-multicast modes. */ vtnet_rx_filter(sc); /* Restore filtered MAC addresses. */ vtnet_rx_filter_mac(sc); } /* Restore VLAN filters. */ if (ifp->if_capenable & IFCAP_VLAN_HWFILTER) vtnet_rx_filter_vlan(sc); } #ifdef DEVICE_POLLING if (ifp->if_capenable & IFCAP_POLLING) { vtnet_disable_rx_intr(sc); vtnet_disable_tx_intr(sc); } else #endif { vtnet_enable_rx_intr(sc); vtnet_enable_tx_intr(sc); } ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; virtio_reinit_complete(dev); vtnet_update_link_status(sc); callout_reset(&sc->vtnet_tick_ch, hz, vtnet_tick, sc); } static void vtnet_init(void *xsc) { struct vtnet_softc *sc; sc = xsc; VTNET_LOCK(sc); vtnet_init_locked(sc); VTNET_UNLOCK(sc); } static void vtnet_exec_ctrl_cmd(struct vtnet_softc *sc, void *cookie, struct sglist *sg, int readable, int writable) { struct virtqueue *vq; void *c; vq = sc->vtnet_ctrl_vq; VTNET_LOCK_ASSERT(sc); KASSERT(sc->vtnet_flags & VTNET_FLAG_CTRL_VQ, ("no control virtqueue")); KASSERT(virtqueue_empty(vq), ("control command already enqueued")); if (virtqueue_enqueue(vq, cookie, sg, readable, writable) != 0) return; virtqueue_notify(vq); /* * Poll until the command is complete. Previously, we would * sleep until the control virtqueue interrupt handler woke * us up, but dropping the VTNET_MTX leads to serialization * difficulties. * * Furthermore, it appears QEMU/KVM only allocates three MSIX * vectors. Two of those vectors are needed for the Rx and Tx * virtqueues. We do not support sharing both a Vq and config * changed notification on the same MSIX vector. */ c = virtqueue_poll(vq, NULL); KASSERT(c == cookie, ("unexpected control command response")); } static void vtnet_rx_filter(struct vtnet_softc *sc) { device_t dev; struct ifnet *ifp; dev = sc->vtnet_dev; ifp = sc->vtnet_ifp; VTNET_LOCK_ASSERT(sc); KASSERT(sc->vtnet_flags & VTNET_FLAG_CTRL_RX, ("CTRL_RX feature not negotiated")); if (vtnet_set_promisc(sc, ifp->if_flags & IFF_PROMISC) != 0) device_printf(dev, "cannot %s promiscuous mode\n", ifp->if_flags & IFF_PROMISC ? "enable" : "disable"); if (vtnet_set_allmulti(sc, ifp->if_flags & IFF_ALLMULTI) != 0) device_printf(dev, "cannot %s all-multicast mode\n", ifp->if_flags & IFF_ALLMULTI ? "enable" : "disable"); } static int vtnet_ctrl_rx_cmd(struct vtnet_softc *sc, int cmd, int on) { struct virtio_net_ctrl_hdr hdr; struct sglist_seg segs[3]; struct sglist sg; uint8_t onoff, ack; int error; if ((sc->vtnet_flags & VTNET_FLAG_CTRL_RX) == 0) return (ENOTSUP); error = 0; hdr.class = VIRTIO_NET_CTRL_RX; hdr.cmd = cmd; onoff = !!on; ack = VIRTIO_NET_ERR; sglist_init(&sg, 3, segs); error |= sglist_append(&sg, &hdr, sizeof(struct virtio_net_ctrl_hdr)); error |= sglist_append(&sg, &onoff, sizeof(uint8_t)); error |= sglist_append(&sg, &ack, sizeof(uint8_t)); KASSERT(error == 0 && sg.sg_nseg == 3, ("error adding Rx filter message to sglist")); vtnet_exec_ctrl_cmd(sc, &ack, &sg, sg.sg_nseg - 1, 1); return (ack == VIRTIO_NET_OK ? 0 : EIO); } static int vtnet_set_promisc(struct vtnet_softc *sc, int on) { return (vtnet_ctrl_rx_cmd(sc, VIRTIO_NET_CTRL_RX_PROMISC, on)); } static int vtnet_set_allmulti(struct vtnet_softc *sc, int on) { return (vtnet_ctrl_rx_cmd(sc, VIRTIO_NET_CTRL_RX_ALLMULTI, on)); } static void vtnet_rx_filter_mac(struct vtnet_softc *sc) { struct virtio_net_ctrl_hdr hdr; struct vtnet_mac_filter *filter; struct sglist_seg segs[4]; struct sglist sg; struct ifnet *ifp; struct ifaddr *ifa; struct ifmultiaddr *ifma; int ucnt, mcnt, promisc, allmulti, error; uint8_t ack; ifp = sc->vtnet_ifp; filter = sc->vtnet_mac_filter; ucnt = 0; mcnt = 0; promisc = 0; allmulti = 0; error = 0; VTNET_LOCK_ASSERT(sc); KASSERT(sc->vtnet_flags & VTNET_FLAG_CTRL_RX, ("CTRL_RX feature not negotiated")); /* Unicast MAC addresses: */ if_addr_rlock(ifp); TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_LINK) continue; else if (ucnt == VTNET_MAX_MAC_ENTRIES) break; bcopy(LLADDR((struct sockaddr_dl *)ifa->ifa_addr), &filter->vmf_unicast.macs[ucnt], ETHER_ADDR_LEN); ucnt++; } if_addr_runlock(ifp); if (ucnt >= VTNET_MAX_MAC_ENTRIES) { promisc = 1; filter->vmf_unicast.nentries = 0; if_printf(ifp, "more than %d MAC addresses assigned, " "falling back to promiscuous mode\n", VTNET_MAX_MAC_ENTRIES); } else filter->vmf_unicast.nentries = ucnt; /* Multicast MAC addresses: */ if_maddr_rlock(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; else if (mcnt == VTNET_MAX_MAC_ENTRIES) break; bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), &filter->vmf_multicast.macs[mcnt], ETHER_ADDR_LEN); mcnt++; } if_maddr_runlock(ifp); if (mcnt >= VTNET_MAX_MAC_ENTRIES) { allmulti = 1; filter->vmf_multicast.nentries = 0; if_printf(ifp, "more than %d multicast MAC addresses " "assigned, falling back to all-multicast mode\n", VTNET_MAX_MAC_ENTRIES); } else filter->vmf_multicast.nentries = mcnt; if (promisc && allmulti) goto out; hdr.class = VIRTIO_NET_CTRL_MAC; hdr.cmd = VIRTIO_NET_CTRL_MAC_TABLE_SET; ack = VIRTIO_NET_ERR; sglist_init(&sg, 4, segs); error |= sglist_append(&sg, &hdr, sizeof(struct virtio_net_ctrl_hdr)); error |= sglist_append(&sg, &filter->vmf_unicast, - sizeof(struct vtnet_mac_table)); + sizeof(uint32_t) + filter->vmf_unicast.nentries * ETHER_ADDR_LEN); error |= sglist_append(&sg, &filter->vmf_multicast, - sizeof(struct vtnet_mac_table)); + sizeof(uint32_t) + filter->vmf_multicast.nentries * ETHER_ADDR_LEN); error |= sglist_append(&sg, &ack, sizeof(uint8_t)); KASSERT(error == 0 && sg.sg_nseg == 4, ("error adding MAC filtering message to sglist")); vtnet_exec_ctrl_cmd(sc, &ack, &sg, sg.sg_nseg - 1, 1); if (ack != VIRTIO_NET_OK) if_printf(ifp, "error setting host MAC filter table\n"); out: if (promisc) if (vtnet_set_promisc(sc, 1) != 0) if_printf(ifp, "cannot enable promiscuous mode\n"); if (allmulti) if (vtnet_set_allmulti(sc, 1) != 0) if_printf(ifp, "cannot enable all-multicast mode\n"); } static int vtnet_exec_vlan_filter(struct vtnet_softc *sc, int add, uint16_t tag) { struct virtio_net_ctrl_hdr hdr; struct sglist_seg segs[3]; struct sglist sg; uint8_t ack; int error; hdr.class = VIRTIO_NET_CTRL_VLAN; hdr.cmd = add ? VIRTIO_NET_CTRL_VLAN_ADD : VIRTIO_NET_CTRL_VLAN_DEL; ack = VIRTIO_NET_ERR; error = 0; sglist_init(&sg, 3, segs); error |= sglist_append(&sg, &hdr, sizeof(struct virtio_net_ctrl_hdr)); error |= sglist_append(&sg, &tag, sizeof(uint16_t)); error |= sglist_append(&sg, &ack, sizeof(uint8_t)); KASSERT(error == 0 && sg.sg_nseg == 3, ("error adding VLAN control message to sglist")); vtnet_exec_ctrl_cmd(sc, &ack, &sg, sg.sg_nseg - 1, 1); return (ack == VIRTIO_NET_OK ? 0 : EIO); } static void vtnet_rx_filter_vlan(struct vtnet_softc *sc) { device_t dev; uint32_t w, mask; uint16_t tag; int i, nvlans, error; VTNET_LOCK_ASSERT(sc); KASSERT(sc->vtnet_flags & VTNET_FLAG_VLAN_FILTER, ("VLAN_FILTER feature not negotiated")); dev = sc->vtnet_dev; nvlans = sc->vtnet_nvlans; error = 0; /* Enable filtering for each configured VLAN. */ for (i = 0; i < VTNET_VLAN_SHADOW_SIZE && nvlans > 0; i++) { w = sc->vtnet_vlan_shadow[i]; for (mask = 1, tag = i * 32; w != 0; mask <<= 1, tag++) { if ((w & mask) != 0) { w &= ~mask; nvlans--; if (vtnet_exec_vlan_filter(sc, 1, tag) != 0) error++; } } } KASSERT(nvlans == 0, ("VLAN count incorrect")); if (error) device_printf(dev, "cannot restore VLAN filter table\n"); } static void vtnet_set_vlan_filter(struct vtnet_softc *sc, int add, uint16_t tag) { struct ifnet *ifp; int idx, bit; KASSERT(sc->vtnet_flags & VTNET_FLAG_VLAN_FILTER, ("VLAN_FILTER feature not negotiated")); if ((tag == 0) || (tag > 4095)) return; ifp = sc->vtnet_ifp; idx = (tag >> 5) & 0x7F; bit = tag & 0x1F; VTNET_LOCK(sc); /* Update shadow VLAN table. */ if (add) { sc->vtnet_nvlans++; sc->vtnet_vlan_shadow[idx] |= (1 << bit); } else { sc->vtnet_nvlans--; sc->vtnet_vlan_shadow[idx] &= ~(1 << bit); } if (ifp->if_capenable & IFCAP_VLAN_HWFILTER) { if (vtnet_exec_vlan_filter(sc, add, tag) != 0) { device_printf(sc->vtnet_dev, "cannot %s VLAN %d %s the host filter table\n", add ? "add" : "remove", tag, add ? "to" : "from"); } } VTNET_UNLOCK(sc); } static void vtnet_register_vlan(void *arg, struct ifnet *ifp, uint16_t tag) { if (ifp->if_softc != arg) return; vtnet_set_vlan_filter(arg, 1, tag); } static void vtnet_unregister_vlan(void *arg, struct ifnet *ifp, uint16_t tag) { if (ifp->if_softc != arg) return; vtnet_set_vlan_filter(arg, 0, tag); } static int vtnet_ifmedia_upd(struct ifnet *ifp) { struct vtnet_softc *sc; struct ifmedia *ifm; sc = ifp->if_softc; ifm = &sc->vtnet_media; if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) return (EINVAL); return (0); } static void vtnet_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { struct vtnet_softc *sc; sc = ifp->if_softc; ifmr->ifm_status = IFM_AVALID; ifmr->ifm_active = IFM_ETHER; VTNET_LOCK(sc); if (vtnet_is_link_up(sc) != 0) { ifmr->ifm_status |= IFM_ACTIVE; ifmr->ifm_active |= VTNET_MEDIATYPE; } else ifmr->ifm_active |= IFM_NONE; VTNET_UNLOCK(sc); } static void vtnet_add_statistics(struct vtnet_softc *sc) { device_t dev; struct vtnet_statistics *stats; struct sysctl_ctx_list *ctx; struct sysctl_oid *tree; struct sysctl_oid_list *child; dev = sc->vtnet_dev; stats = &sc->vtnet_stats; ctx = device_get_sysctl_ctx(dev); tree = device_get_sysctl_tree(dev); child = SYSCTL_CHILDREN(tree); SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "mbuf_alloc_failed", CTLFLAG_RD, &stats->mbuf_alloc_failed, "Mbuf cluster allocation failures"); SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "rx_frame_too_large", CTLFLAG_RD, &stats->rx_frame_too_large, "Received frame larger than the mbuf chain"); SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "rx_enq_replacement_failed", CTLFLAG_RD, &stats->rx_enq_replacement_failed, "Enqueuing the replacement receive mbuf failed"); SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "rx_mergeable_failed", CTLFLAG_RD, &stats->rx_mergeable_failed, "Mergeable buffers receive failures"); SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "rx_csum_bad_ethtype", CTLFLAG_RD, &stats->rx_csum_bad_ethtype, "Received checksum offloaded buffer with unsupported " "Ethernet type"); SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "rx_csum_bad_start", CTLFLAG_RD, &stats->rx_csum_bad_start, "Received checksum offloaded buffer with incorrect start offset"); SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "rx_csum_bad_ipproto", CTLFLAG_RD, &stats->rx_csum_bad_ipproto, "Received checksum offloaded buffer with incorrect IP protocol"); SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "rx_csum_bad_offset", CTLFLAG_RD, &stats->rx_csum_bad_offset, "Received checksum offloaded buffer with incorrect offset"); SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "rx_csum_failed", CTLFLAG_RD, &stats->rx_csum_failed, "Received buffer checksum offload failed"); SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "rx_csum_offloaded", CTLFLAG_RD, &stats->rx_csum_offloaded, "Received buffer checksum offload succeeded"); SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "rx_task_rescheduled", CTLFLAG_RD, &stats->rx_task_rescheduled, "Times the receive interrupt task rescheduled itself"); SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "tx_csum_offloaded", CTLFLAG_RD, &stats->tx_csum_offloaded, "Offloaded checksum of transmitted buffer"); SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "tx_tso_offloaded", CTLFLAG_RD, &stats->tx_tso_offloaded, "Segmentation offload of transmitted buffer"); SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "tx_csum_bad_ethtype", CTLFLAG_RD, &stats->tx_csum_bad_ethtype, "Aborted transmit of checksum offloaded buffer with unknown " "Ethernet type"); SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "tx_tso_bad_ethtype", CTLFLAG_RD, &stats->tx_tso_bad_ethtype, "Aborted transmit of TSO buffer with unknown Ethernet type"); SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "tx_task_rescheduled", CTLFLAG_RD, &stats->tx_task_rescheduled, "Times the transmit interrupt task rescheduled itself"); } static int vtnet_enable_rx_intr(struct vtnet_softc *sc) { return (virtqueue_enable_intr(sc->vtnet_rx_vq)); } static void vtnet_disable_rx_intr(struct vtnet_softc *sc) { virtqueue_disable_intr(sc->vtnet_rx_vq); } static int vtnet_enable_tx_intr(struct vtnet_softc *sc) { #ifdef VTNET_TX_INTR_MODERATION return (0); #else return (virtqueue_enable_intr(sc->vtnet_tx_vq)); #endif } static void vtnet_disable_tx_intr(struct vtnet_softc *sc) { virtqueue_disable_intr(sc->vtnet_tx_vq); }